RRDtool - 윈디하나의 솔라나라
|
시간 기반의 데이터를 저장하는 산업 표준 오픈 소스 로깅/그래프 도구이다.라고 홈페이지에 나와있다. TSDB의 대표격이다. 쉽게 말하자면,
수신/발신 데이터의 양와 같이 시간의 흐름에 따라 쌓이는 데이터를 효율적으로 관리하고 표시하기 위한 툴이라는 의미다.
windy@wl ~ $ sudo pkg install pkg://solaris/image/rrdtool1.6 버전이 설치된다.
최신 버전이 필요하다면 Releases · oetiker/rrdtool-1.x · GitHub에서 최신 소스를 받아 빌드하자. 구 버전은 RRDtool Download Area에서 소스를 받을 수 있다. 위에서 설명한 패키지 설치 이후 진행해야 한다. 필요한 라이브러리 목록은 윈디하나의 솔라나라: rrdtool 1.4 (작성중)를 참고하자.
windy@wl ~/src $ wget https://github.com/oetiker/rrdtool-1.x/releases/download/v1.9.0/rrdtool-1.9.0.tar.gz windy@wl ~/src $ tar xvfz rrdtool-1.9.0.tar.gz windy@wl ~/src $ cd rrdtool-1.9.0 windy@wl ~/src/rrdtool-1.9.0 $ LD_LIBRARY_PATH="" CFLAGS="" LDFLAGS="-L/usr/lib -R/usr/lib" ./configure --disable-python --disable-perl 1) windy@wl ~/src/rrdtool-1.9.0 $ vi src/rrd_list.c 2) #include <limits.h> windy@wl ~/src/rrdtool-1.9.0 $ LD_LIBRARY_PATH="" make windy@wl ~/src/rrdtool-1.9.0 $ sudo make install1) 32비트로 빌드 한다. 솔라리스 11.4에 있는 펄 및 파이썬 라이브러리가 32비트이기 때문에 64비트로 빌드하면 오류가 발생한다. 추후 변경될 수 있다.
PATH_MAX상수가 limits.h에 있다. limits.h를 추가하자. 리눅스에서는 추가할 필요 없다.make install을 실행하면 /opt/rrdtool-1.9.0에 아래와 같은 파일이 설치된다.
windy@wl ~ $ ls -al /opt/rrdtool-1.9.0/bin -rwxr-xr-x 1 root root 121476 8월 10 11:24 rrdcached -rwxr-xr-x 1 root root 41504 8월 10 11:24 rrdcgi lrwxrwxrwx 1 root root 9 8월 10 11:24 rrdcreate -> rrdupdate lrwxrwxrwx 1 root root 9 8월 10 11:24 rrdinfo -> rrdupdate -rwxr-xr-x 1 root root 34344 8월 10 11:24 rrdtool -rwxr-xr-x 1 root root 309836 8월 10 11:24 rrdupdate
.rrd파일을 캐시해 처리속도를 높일 수 있는 유틸. RRDTool 1.8 미만 버전에서는 메모리 누수가 있으므로 1.8 이상의 최신버전을 사용하자.root@wll ~ # yum install rrdtoolRHEL 6에서는 1.3 버전이 설치된다. 1.3에는 rrdcached(1) 가 없기 때문에 캐시를 사용한 예제는 사용할 수 없다. 하지만 이 문서를 주된 내용인 그래프를 그리는 부분을 사용하는데에는 전혀 지장 없다.
Windows 기능에서
Subsystem for UNIX based Applications를 설치하고, Utilities and SDK for Subsystem for UNIX-based Applications in Microsoft Windows 7 and Windows Server 2008 R2를 설치하면 사용하기 쉬워질 것이다.
런 큐에 대기하고 있는 작업 개수의 평균을 의미하는 값으로 시스템에 걸리는 부하를 대표하는 값이다.
windy@wl ~ $ LANG=C uptime
00:00am up 1 day(s), 00:00, 1 user, load average: 0.15, 0.18, 0.18
windy@wl ~ $ snmpwalk -v 2c -c public localhost .1.3.6.1.4.1.2021.10 UCD-SNMP-MIB::laIndex.1 = INTEGER: 1 UCD-SNMP-MIB::laIndex.2 = INTEGER: 2 UCD-SNMP-MIB::laIndex.3 = INTEGER: 3 UCD-SNMP-MIB::laNames.1 = STRING: Load-1 UCD-SNMP-MIB::laNames.2 = STRING: Load-5 UCD-SNMP-MIB::laNames.3 = STRING: Load-15 UCD-SNMP-MIB::laLoad.1 = STRING: 0.23 UCD-SNMP-MIB::laLoad.2 = STRING: 0.26 UCD-SNMP-MIB::laLoad.3 = STRING: 0.23 UCD-SNMP-MIB::laConfig.1 = STRING: 12.00 UCD-SNMP-MIB::laConfig.2 = STRING: 12.00 UCD-SNMP-MIB::laConfig.3 = STRING: 12.00 UCD-SNMP-MIB::laLoadInt.1 = INTEGER: 23 UCD-SNMP-MIB::laLoadInt.2 = INTEGER: 25 UCD-SNMP-MIB::laLoadInt.3 = INTEGER: 23 UCD-SNMP-MIB::laLoadFloat.1 = Opaque: Float: 0.230469 UCD-SNMP-MIB::laLoadFloat.2 = Opaque: Float: 0.257812 UCD-SNMP-MIB::laLoadFloat.3 = Opaque: Float: 0.230469 UCD-SNMP-MIB::laErrorFlag.1 = INTEGER: noError(0) UCD-SNMP-MIB::laErrorFlag.2 = INTEGER: noError(0) UCD-SNMP-MIB::laErrorFlag.3 = INTEGER: noError(0) UCD-SNMP-MIB::laErrMessage.1 = STRING: UCD-SNMP-MIB::laErrMessage.2 = STRING: UCD-SNMP-MIB::laErrMessage.3 = STRING: windy@wl ~ $ snmpwalk -v 2c -c public localhost laLoadFloat.1 UCD-SNMP-MIB::laLoadFloat.1 = Opaque: Float: 0.230469SNMP서버를 설정해야 하는 번거로움이 있어 이 예시에서는 이 방법을 사용하지 않는다.
1분 간격으로 수집하고 그래프를 출력하도록 하자.
우선 데이터를 저장할 .rrd파일을 생성해야 한다. .rrd파일은 rrdtool create명령을 사용해 생성하며, 생성시 데이터 종류와 저장 방법, 업데이트 주기등을 지정해야 한다. 자세한 사항은 rrdcreate(1)을 참조하자.
시스템 부하의 경우 1분, 5분, 15분의 부동소수점 데이터가 된다. 따라서 아래와 같이 생성할 수 있다.
windy@wl ~ $ rrdtool create load.rrd \ --step 60 \ DS:1min:GAUGE:600:0:U \ DS:5min:GAUGE:600:0:U \ DS:15min:GAUGE:600:0:U \ RRA:AVERAGE:0.5:1:1440 \ RRA:AVERAGE:0.5:5:1440 \ RRA:AVERAGE:0.5:30:800 \ RRA:AVERAGE:0.5:120:800 \ RRA:AVERAGE:0.5:1440:800 \ RRA:MAX:0.5:60:2400
load.rrd: rrd 파일의 이름. 임의로 정해줄 수 있다.--step: 데이터 입력 주기. 단위는 초. 위에서는 1분으로 설정했다.DS: DataSource. 데이터 소스를 지정한다. DS:ds-name:DST:dst arguments 형식으로 지정해 준다. dst arguments는 DST 에 따라 두가지 문법을 가진다.
DS:ds-name:{GAUGE | COUNTER | DERIVE | DCOUNTER | DDERIVE | ABSOLUTE}:heartbeat:min:max
DS:ds-name:COMPUTE:rpn-expression
ds-name: 데이터 소스 이름. 임의로 지정한다.DST: Data Source Type. 데이터 소스의 형식. 아래와 같이 있다.
GAUGE: 입력된 값을 그대로 사용. 현재 1분 동안 솔라나라의 방문자수와 같은 의미이다.
COUNTER: 데이터주기에 의해 입력된 값과 이전 값을 비교해 초당 '비율'을 저장. 양수값만 취한다. [(현재값 - 이전값) / (현재시간(초) - 이전시간(초))] 로 계산된다. 솔라나라의 누적 방문자수를 처리하는데 유용하게 사용한다.
DCOUNTER: COUNTER와 비슷하나 부동 소숫점을 사용. RRDtool 1.5 부터 지원.DERIVE: 데이터주기에 의해 입력된 값과 이전 값을 비교해 초당 '비율'을 저장. COUNTER와 비슷하지만, 양수값 외에 음수값도 취할 수 있다.DDERIVE: DERIVE와 비슷하나 부동 소숫점을 사용. RRDtool 1.5 부터 지원.ABSOLUTE: 입력된 값은 증감값이다.COMPUTE: RRD의 외부 플러그인에 의해 계산된 값이다.GAUGE 이다.
heartbeat: 데이터 입력이 --step 인자에 명시된 대로 정시에 입력되면 좋겠지만 현실에서는 그렇지 않을 것이다. 여기에 지정된 값만큼의 오차를 감안한다. 단위는 초이다. 반드시 --step 에 지정된 값 보다 커야 하며, --step 에 지정된 값의 두 배 이상 지정해줘도 좋다. heartbeat 에 정의된 시간까지 기다려도 데이터가 입력되지 않은 경우 해당 시간의 데이터는 UNKNOWN으로 간주하고 MIN, MAX등을 계산해 보여준다.min: 입력할 값의 최소값. U는 unknown. 이 값보다 작은 값이 입력되면 UNKNOWN으로 처리한다.max: 입력할 값의 최대값. U는 unknown. 이 값보다 큰 값이 입력되면 UNKNOWN으로 처리한다.rpn-expression: RPN 표현식. RPN에 대한 설명은 RRDtool - rrdgraph_rpn페이지를 참조한다RRA: Round Robin Archive. 저장 방식을 지정한다. RRA:CF:cf arguments형식이며, 결과적으로 아래와 같은 문법을 가진다.
RRA:AVERAGE|MIN|MAX|LAST:xff:steps:rows
CF: Consolidation Function. AVERAGE, MIN, MAX, LAST 가 있다.
AVERAGE: 평균값을 저장MIN: 최소값을 저장MAX: 최대값을 저장LAST: 마지막값을 저장xff: xfiles factor. 알 수 없는 데이터가 들어오는 경우 얼마나 오래동안 KNOWN 데이터로 간주할 것인지 지정. 0~1 사이의 값이며, 보통 0.5로 설정한다. 예를 들어 xff를 0.5로 설정한 경우 1, unknown, unknown, 1의 50%가 데이터가 들어온 경우에 대한 평균은 1이다. 1, unknown, unknown, unknown와 같이 75%가 unknown인 경우 결과는 unknown 이 된다.steps: 평균값/최소값/최대값을 계산할때 사용되는 값의 개수. 이 값이 1이면 결과적으로 LAST와 같아진다.rows: RRA에 저장될 값의 개수. 이후에는 값을 제거한다. 1년 단위의 데이터를 저장하려면 이 값이 충분히 커야 한다. 하단의 참고데이터를 보자.RRA:HWPREDICT:rows:alpha:beta:seasonal period[:rra-num]자세한건 RRDtool create Example 을 읽어보자. 아래에 생성 예제가 있다.
RRA:MHWPREDICT:rows:alpha:beta:seasonal period[:rra-num]
RRA:SEASONAL:seasonal period:gamma:rra-num[:smoothing-window=fraction]
RRA:DEVSEASONAL:seasonal period:gamma:rra-num[:smoothing-window=fraction]
RRA:DEVPREDICT:rows:rra-num
RRA:FAILURES:rows:threshold:window length:rra-num
windy@wl ~ $ rrdtool create monitor.rrd --step 300 \ DS:ifOutOctets:COUNTER:1800:0:U \ RRA:AVERAGE:0.5:1:2016 \ RRA:HWPREDICT:1440:0.1:0.0035:288:3 \ RRA:SEASONAL:288:0.1:2 \ RRA:DEVPREDICT:1440:5 \ RRA:DEVSEASONAL:288:0.1:2 \ RRA:FAILURES:288:7:9:5
RRA에는 HWPREDICT 만 정의해도 나머지 값(SEASONAL, DEVPREDICT, DEVSESONAL, FAILURES)이 같이 정의된다. HWPREDICT 와 MHWPREDICT 는 계산된 오차범위를 합산 하느냐, 곱하느냐의 차이다. 잘 모르겠으면 두가지 다 해보고 적당한 방식을 찾으면 된다.
이외에도 RRDTool은 파일을 Ceph클러스터에 저장할 수 있다. Ceph 가 설정되고 RRDTool 과 같이 빌드되었다면, load.rrd 대신 ceph//load.rrd과 같이 명령 옵션을 주면 된다. 자세한 사항은 RRDTool - rrdrados를 읽어보자.
DS:ds1:COUNTER:600:0:U으로 생성한 경우, step 이 60인 경우 아래와 같이 계산된다.
시간 입력값 계산된값 .rrd 파일 저장 값 0 600 600 10 60 900 300 5 120 1500 600 10 180 300 약 232 약 232 / 60 240 900 600 10 ...입력값(rrdupdate 커맨드로 입력해주는 값)이 줄어드는 경우는 보통 시스템이 리부트 되었거나 오버플로가 일어난 경우다. 하지만 위의 경우 32비트 오버플로로 간주해 (232 - 1500 + 300)로 계산하고, 파일에는 이를 초당 비율로 계산한 값을 저장한다. 따라서 이 값을 토대로 그래프를 그리면 비 정상적인 모양이 나오게 된다.
DS:ds1:DERIVE:600:0:U아래와 같이 계산된다.
시간 입력값 계산된값 .rrd 파일 저장 값 0 600 600 10 60 900 300 5 120 1500 600 10 180 300 -1200 U 240 900 600 10 ...최소값은 0으로 지정했기 때문에 음수가 들어와 값을 취하지 않게 된다. 이 방법은 값을 하나 버리는 문제가 있지만 (심지어 오버플로가 발생해 줄어든 값이라도 버리게 된다) 카운터 리셋에 의한 문제는 방지할 수 있다.
DS:ds1:COUNTER:600:0:1000으로 가정하면
시간 입력값 계산된값 .rrd 파일 저장 값 0 600 600 10 60 900 300 5 120 1500 600 10 180 300 약 232 U 240 900 600 10 ...
COUNTER를 사용하였으며, 오버플로에 의해 낮아진 경우가 아니라면 rrdupdate 명령 실행시 입력값을 U로 주어 해결했다. 당장 60동안의 입력값은 문제가 되지만, 텀이 더 긴 그래프에서는 U는 무시되고 평균 값를 계산하기 때문에 문제가 되지 않는다.
시간 입력값 계산된값 .rrd 파일 저장 값 0 600 600 10 60 900 300 5 120 1500 600 10 180 U U U 240 900 900 15 ...DERIVE를 사용한 예와 비교하면 마지막 값이 다르다. 어떤것을 취할지는 각자의 판단에 맞긴다. rrdupdate 구문을 수행하는데 좀 더 계산을 해야 하지만 가능하다면 이 방법을 추천한다.
rrdupdate(1)은 .rrd파일을 업데이트한다. .rrd파일을 생성할때 지정했던 주기로 데이터를 업데이트해야 한다. 아래와 같은 형식을 지닌다.
rrdupdate 파일명 시간:데이터1[[:데이터2]...]
시스템 부하의 경우 1분 주기로 하기로 하였으며, 부동소수점을 가진 데이터 3개를 업데이트 해야 한다. 따라서 아래와 같이 명령을 줄 수 있다.
windy@wl ~ $ rrdupdate load.rrd N:1.0:0.8:0.5 1)
1) rrdupdate(1) 대신 rrdtool update 를 사용해도 된다. rrdupdate(1)는 정적 라이브러리를 사용해 빌드 되어있다.
각각의 인자는 아래와 같은 의미를 지닌다.
load.rrd: 업데이트할 rrd 파일의 이름. rrdtool create로 생성한 파일이다.N: 현재시간을 의미한다. 이외에 유닉스 타임스탬프(Unix Timestamp)를 사용할 수 있다.:value: 값을 지정해 준다. create 시 3개의 값을 지정했기 때문에 (DS가 3개)3개의 값을 업데이트 해야 한다.이외에 --template 옵션을 통해 갱신할 DS를 지정해줄 수 있다.
rrdtool info명령은 .rrd파일의 헤더 정보를 보여준다. 자세한 사항은 rrdinfo(1) 을 참조하자.
windy@wl ~ $ rrdtool info load.rrd filename = "load.rrd" rrd_version = "0003" step = 60 last_update = 1279679701 ds[1min].type = "GAUGE" ds[1min].minimal_heartbeat = 600 ds[1min].min = 0.0000000000e+00 ds[1min].max = NaN ds[1min].last_ds = "0.070000" ds[1min].value = 1.1743837000e-01 ds[1min].unknown_sec = 0 ... rra[0].cf = "AVERAGE" rra[0].rows = 1440 rra[0].cur_row = 107 rra[0].pdp_per_row = 1 rra[0].xff = 5.0000000000e-01 ... rra[1].cf = "MAX" rra[1].rows = 2400 rra[1].cur_row = 1001 rra[1].pdp_per_row = 60 rra[1].xff = 5.0000000000e-01
rrdtool fetch명령은 .rrd파일에 저장된 데이터를 보여준다. 파일에 저장된 모든 LAST 데이터를 보려면 아래와 같이 한다. 자세한 사항은 rrdfetch(1)을 참조하자.
windy@wl ~ $ rrdtool fetch load.rrd LAST ... 1267434480: 1.1095090793e+00 1.0797545397e+00 8.9975453967e-01 1267434540: 1.0607886517e+00 1.0701577303e+00 9.0984226967e-01 1267434600: 1.0797463910e+00 1.0798731955e+00 9.2974639100e-01 1267434660: 1.0408642967e+00 1.0702160742e+00 9.3000000000e-01 1267434720: -NaN -NaN -NaN
rrdtool last명령은 마지막으로 업데이트된 날짜를 보여준다. 자세한 사항은 rrdlast(1)을 참조하자.
windy@wl ~ $ rrdtool last load.rrd 1267434668
rrdtool lastupdate명령은 마지막으로 업데이트된 데이터를 보여준다. 자세한 사항은 rrdlastupdate(1)을 참조하자.
windy@wl ~ $ rrdtool lastupdate load.rrd 1min 5min 15min 1267434660: 1.0408642967e+00 1.0702160742e+00 9.3000000000e-01
windy@wl ~ $ rrdtool fetch localhost.rrd AVERAGE -r 864000 -s 1393599600 -e 1396278000
ds0 ds1
...
1395360000: 1.5381875000e+03 1.7920729167e+04
1395446400: 1.0164756944e+03 1.8497173611e+04
1395532800: 1.1983329861e+04 1.0146190972e+04
1395619200: 1.1118963194e+05 1.8114218750e+04
1395705600: 1.9604527950e+04 1.1155314485e+05
...
1395360000는 유닉스 타임스탬프다.
1970년 1월 1일(UTC)기준으로 지난
초를 저장한다. 따라서
1395360000는
2014.03.21 00:00:00이다. 인터넷에 유닉스 타임스탬프 변환기를 찾으면 시간 변환을 쉽게 할 수 있다.1.5381875000e+03 × 864000 = 132899400 ≒ 126 MiB
-r 값을 1시간이나 5분으로 변경시키면 그에 따라 자동으로 계산되어 값이 나온다. 문서 하단에서 설명할 예측 알고리즘을 사용하면 미래의 예측 데이터도 추출할 수 있다.
rrdtool tune명령은 .rrd파일의 구조를 수정할 수 있다. 자세한 사항은 rrdtune(1)을 참조하자.
windy@wl ~ $ rrdtool tune
RRDtool 1.7.1 Copyright by Tobias Oetiker <tobi@oetiker.ch>
Compiled Feb 00 2019 00:00:00
Usage: rrdtool [options] command command_options
* tune - Modify some basic properties of an RRD
rrdtool tune filename
[--heartbeat|-h ds-name:heartbeat]
[--data-source-type|-d ds-name:DST]
[--data-source-rename|-r old-name:new-name]
[--minimum|-i ds-name:min] [--maximum|-a ds-name:max]
[--deltapos|-p scale-value] [--deltaneg|-n scale-value]
[--failure-threshold|-f integer]
[--window-length|-w integer]
[--alpha|-x adaptation-parameter]
[--beta|-y adaptation-parameter]
[--gamma|-z adaptation-parameter]
[--gamma-deviation|-v adaptation-parameter]
[--smoothing-window|-s fraction-of-season]
[--smoothing-window-deviation|-S fraction-of-season]
[--aberrant-reset|-b ds-name]
[--step|-t newstep]
[--daemon|-D address]
[DEL:ds-name]
[DS:ds-spec]
[DELRRA:index]
[RRA:rra-spec]
[RRA#index:[+-=]number]
RRDtool is distributed under the Terms of the GNU General
Public License Version 2. (www.gnu.org/copyleft/gpl.html)
For more information read the RRD manpages
rrdtool dump명령은 .rrd포맷을 XML으로 변환한다. rrdtool restore명령은 그 역 변환이다. RRDtool은 컬럼 하나를 임의의 값으로 수정하는 방법을 제공하지 않는데, 이를 통해 변경할 수 있다. 자세한 사항은 rrddump(1), rrdrestore(1)을 참조하자.
windy@wl ~ $ rrdtool dump counter.rrd dump
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE rrd SYSTEM "http://oss.oetiker.ch/rrdtool/rrdtool.dtd">
<!-- Round Robin Database Dump -->
<rrd>
<version>0003</version>
<step>60</step> <!-- Seconds -->
<lastupdate>1397227383</lastupdate> <!-- 2014-04-11 23:43:03 KST -->
<ds>
<name> cnt </name>
<type> COUNTER </type>
<minimal_heartbeat>6000</minimal_heartbeat>
<min>0.0000000000e+00</min>
<max>NaN</max>
<!-- PDP Status -->
<last_ds>5745396</last_ds>
<value>2.1841500000e+03</value>
<unknown_sec> 0 </unknown_sec>
</ds>
<!-- Round Robin Archives -->
<rra>
<cf>AVERAGE</cf>
<pdp_per_row>1</pdp_per_row> <!-- 60 seconds -->
<params>
<xff>5.0000000000e-01</xff>
</params>
<cdp_prep>
<ds>
<primary_value>7.0959166667e+02</primary_value>
<secondary_value>NaN</secondary_value>
<value>NaN</value>
<unknown_datapoints>0</unknown_datapoints>
</ds>
</cdp_prep>
<database>
...
<!-- 2014-04-11 23:26:00 KST / 1397226360 --> <row><v>NaN</v></row>
<!-- 2014-04-11 23:27:00 KST / 1397226420 --> <row><v>1.4198816667e+03</v></row>
</database>
</rra>
</rrd>
windy@wl ~ $ rrdtool dump counter.rrd > counter.xml windy@wl ~ $ vi counter.xml ... windy@wl ~ $ rm counter.rrd windy@wl ~ $ rrdtool restore counter.xml counter.rrd
rrdtool resize명령은 RRA크기를 바꿔 .rrd파일을 생성한다. 현재 RRA 크기는 rrdinfo(1)을 사용해 알 수 있다. 자세한 사항은 rrdresize(1)을 참조하자.windy@wl ~ $ rrdtool info load.rrd | grep index ds[1min].index = 0 ds[5min].index = 1 ds[15min].index = 2 ds[uptime].index = 3 windy@wl ~ $ rrdtool info load.rrd | grep rows rra[0].rows = 1440 rra[1].rows = 1440 rra[2].rows = 800 rra[3].rows = 800 rra[4].rows = 800 windy@wl ~ $ rrdtool resize load.rrd 1min SHRINK 100 windy@wl ~ $ rrdtool info resize.rrd | grep rows rra[0].rows = 1440 rra[1].rows = 1340 rra[2].rows = 800 rra[3].rows = 800 rra[4].rows = 800
rrdtool 은 연산식을 입력할 때 RPN을 사용한다. 자세한 사항은 rrdgraph_rpn을 참고하자. 사용할 수 있는 연산자 목록을 아래에 정리했다.
UN, ISINF: 1개의 데이터를 스택에서 팝한 후, Unknown 및 음/양의무한대인경우 1을, 그렇지 않는 경우 0 을 스택에 푸시해준다.then,else,condition,IF: 3개의 데이터를 스택에서 팝한 후, condition 이 0 인경우 else 를, 그렇지 않는 경우 then 값을 스택에 푸시한다.MIN, MAX: 2개의 데이터를 스택에서 팝 한 후, 최대/최소 값을 스택에 푸시한다. 값중에 NaN 이 있다면 결과는 NaN이다.MINNAN, MAXNAN: 2개의 데이터를 스택에서 팝 한 후, 최대/최소 값을 스택에 푸시한다. 값중에 NaN 이 있다면 무시되지만, 두 값이 모두 NaN 이면 결과는 NaN이다.val,lower-limit,upper-limit,LIMIT: 3개의 데이터를 스택에서 팝한 후, 범위 안에 있으면 val 을, 그렇지 않으면 Unknown 을 스택에 푸시한다.ADDNAN: NaN 에 안전한 더하기. NaN 은 0으로 간주되어 계산된지만, 둘 다 NaN 인경우 NaN 이다.value,power,POW: 거듭제곱(valuepower)을 구한다.SIN, COS, LOG, EXP, SQRT: 사인(라디안), 코사인(라디안), 로그(자연로그), 지수(자연로그), 제곱근을 구한다.ATAN: 아크 탄젠트를 구한다. (라디안)y,x,ATAN2: 아크 탄젠트를 구한다(라디안)FLOOR, CEIL: 내림, 올림을 구한다.DEG2RAD, RAD2DEG: DEGrees, RADians 간 변환한다.ABS: 절대값을 구한다.count,SORT: 스택에서 지정한 개수만큼 데이터를 꺼내어 정렬한후 다시 스택에 넣는다.count,REV: 스택에서 지정한 개수만큼 데이터를 꺼내어 순서를 반대로 뒤집은 후 다시 스택에 넣는다.count,AVG: 스택에서 지정한 개수만큼 데이터를 꺼내어 평균을 구한후 다시 스택에 넣는다.count,SMIN 및 count,SMAX: 스택에서 지정한 개수만큼 데이터를 꺼내어 최대/최소를 구한후 다시 스택에 넣는다.count,MEDIAN: 스택에서 지정한 개수만큼 데이터를 꺼내어 중앙값을 구한후 다시 스택에 넣는다. UNKNOWN 값은 무시된다.count,STDEV: 스택에서 지정한 개수만큼 데이터를 꺼내어 표준편차를 구한후 다시 스택에 넣는다. NAN 값은 무시된다.percent,count,PERCENT: 스택에서 지정한 개수만큼 데이터를 꺼내어 정렬한 후 지정된 비율번째에 해당하는 값을 구한후 다시 스택에 넣는다.count,TREND 및 count,TRENDNAN: 평균값을 이용해 지정한 시간 만큼의 슬라이딩 윈도우를 생성한다.PREDICT, PREDICTSIGMA, PREDICTPERC: 평균값/시그마값/백분위수를 사용해 슬라이딩 윈도우를 생성한후 예측된 데이터를 넣는다. 미래의 값을 예측할때 사용한다.CDEF:x=v1,v2,v3,v4,v5,v6,6,SORT,POP,5,REV,POP,+,+,+,4,/ → v1 부터 v6중에서 최대/최소값 한개씩을 삭제한 후 평균을 구함
UNKN: 스택에 "Unknown" 값을 푸시INF, NEGINF: 스택에 "Infinite", "Negative Infinite"을 푸시한다.PREV: 이전값을 푸시한다 PREV(vname): 특정한 변수의 이전값을 푸시한다. 이전값이 없으면(첫번째 타임스탭이라면) Unknown 을 푸시한다.COUNT: 스택에 있는 변수의 개수 + 1 를 리턴한다.RRDtool 에서 시간은 타임스탬프다.
NOW: 현재 시간을 스택에 넣는다.STEPWIDTH: 현재 시간 단위 값을 푸시한다. 단위는 초이다.NEWDAY, NEWWEEK, NEWMONTH, NEWYEAR: 지정한 시점의 첫번째 값인 경우 1.0을 리턴한다. 예를 들어 CDEF:mtotal=rate,STEPWIDTH,*,NEWMONTH,0,PREV,IF,ADDNAN와 같이 사용할 수 있다.TIME: 현재 처리중인 시간을 스택에 넣는다.LTIME: 현재 처리중인 시간을 로컬 타임으로 변경해 스택에 넣는다.DUP, POP, EXC: 최상의 요소를 복제, 삭제, 상위 2개의 요소를 교체DEPTH: 현재 스택의 깊이를 푸시n,COPY: 상위 n개의 요소를 복제n,INDEX: 위에서 n번째의 요소를 복제n,m,ROLL: 상위 n개의 요소를 로테이트VDEF 구문에서만 사용할 수 있는 몇가지 변수가 있다.
MAXIMUM, MINIMUM, AVERAGE: 제공된 데이터셋의 최대, 최소, 평균값을 알려준다.STDEV: 제공된 데이터셋의 표준편차를 알려준다.LAST, FIRST: 제공된 데이터셋의 마지막값 및 첫번째값을 알려준다.TOTAL: 제공된 데이터 셋의 총합을 알려준다.PERCENT, PERCENTNAN: 비율. DEF 또는 CDEF 구문 바로 뒤에 나올 수 있다. (PERCENTNAN 는 Unknown 값을 무시해 계산한다)LSLSLOPE, LSLINT, LSLCORREL: 제공된 데이터 셋에 대해 LSL을 알려준다. 수식으로 표현하자면 y = mx + b에 해당된다. LSLSLOPE는 slope 값 (m), LSLINT는 y-intercept 값 (b), LSLCORREL는 상관계수(Correlation Coefficient)를 돌려준다.이제 .rrd파일에 저장된 데이터를 가지고 그래프를 그릴 차례다. 아래와 같이 실행해보자.
windy@wl ~ $ rrdtool graphv load-day.png \ --title "Load Average" \ --vertical-label "# of procs in run q" \ --start -1d \ --width 400 \ --height 100 \ --lower-limit 0 \ --units-exponent 0 \ --alt-autoscale-max \ --disable-rrdtool-tag \ --zoom=2 \ "DEF:1min=load.rrd:1min:AVERAGE" \ "DEF:5min=load.rrd:5min:AVERAGE" \ "DEF:15min=load.rrd:15min:AVERAGE" \ "CDEF:c1min=1min" \ "CDEF:c5min=5min" \ "CDEF:c15min=15min" \ "AREA:c1min#54EC48:1 MIN" \ "LINE:c5min#4D18E4:5 MIN" \ "LINE:c15min#DE48EC:15 MIN" graph_left = 67 graph_top = 28 graph_width = 400 graph_height = 100 image_width = 482 image_height = 168 graph_start = 1551061612 graph_end = 1551148012 value_min = 0.0000000000e+00 value_max = 2.3145188000e+00 legend[0] = " 1 MIN" coords[0] = "16,148,65,161" legend[1] = " 5 MIN" coords[1] = "213,148,262,161" legend[2] = " 15 MIN" coords[2] = "410,148,466,161"아래와 유사한 그래프가 그려질 것이다.

그래프를 그릴 때 몇가지 유용한 옵션에 대해 설명한다.
--title: 제목을 정의한다.--vertical-label: 세로로 된 레이블을 정의한다.--start: 그래프의 시작 위치를 정의한다. -1d는 1일 전을 의미한다. 이외는 별개로 --end를 지정해 그래프의 종료 위치를 정의할 수 있다.--width, --height: 그래프의 너비와 높이를 지정한다. 생성되는 이미지는 그래프 외에 타이틀과 범례를 포함하기 때문이 이보다 크며, 실제 이미지 크기는 이 명령의 출력값이다. (481px X 173px)--lower-limit: 최저값을 정의한다. 로드 평균은 0이 최저값이기 때문에 0으로 정의한다.--upper-limit: 최대값을 정의한다. 로드 평균의 최대값은 정해지지 않았기 때문에 지정하지 않는다.--logarithmic: 플롯에 로그함수를 사용한다. 값의 편차가 클 때 사용해볼만 하다.--base: 기본배수가 되는 값을 지정한다. 지정하지 않으면 1000이다. 메모리의 양이나 디스크및 볼륨의 양을 표시할 때에는 1024로 지정해야 한다. 네트워크 트래픽을 포함한 그외의 경우는 1000으로 지정해야 한다.--rigid: 지정한 상한과 하한값을 사용해 그래프를 그린다. 그래프가 오버런 되도록 한다.--units-exponent: 사용할 유닛의 배수를 지정한다. 0은 비활성화 시킨다. 로드 평균은 k, m, g 등으로 변환하지 않고 그대로 보여주는 것이 좋기 때문에 0으로 세팅한다.--alt-autoscale-max: 최대값을 계산하는 알고리즘을 기본 알고리즘이 아닌 다른 알고리즘을 사용하도록 한다. 좀 더 자세하게 계산하지만, 약간 더 느려진다. 필자의 생각으로는 감안할 수 있는 범위로 생각한다.--disable-rrdtool-tag: 그래프 오른쪽의 rrdtool 태그를 출력하지 않는다.--zoom: 주어진 값 만큼 확대. 높은 DPI를 가진 디스플레이에서 그래프를 봐야할 때 이 옵션을 주어야 한다.DEF: 데이터를 정의한다. 아래와 같은 구문을 가진다.
DEF:<vname>=<rrdfile>:<ds-name>:<CF>[:step=<step>][:start=<time>][:end=<time>][:reduce=<CF>][:daemon=<address>]예를 들어
DEF:5mind=load.rrd:5min:LAST의 의미는 load.rrd파일에서, DS=5min 및 RRA=LAST로 저장된 데이터를
5mind로 정의한다.
VDEF: 가상 데이터를 정의한다. 아래와 같은 구문을 가진다.
VDEF:vname=RPN expression예를 들어
VDEF:xa=x,AVERAGE의 의미는 x의 평균값을 구한다.
CDEF: 정의된 데이터를 RPN식을 사용해 수정한다. 아래와 같은 구문을 가진다.
CDEF:vname=RPN expression위의 예제에서는 변환하지 않고 그대로 사용하지만, 데이터를 변환할 수 있다. 예를 들어 5mind 값에 20을 더한후 10을 곱하고 싶다면
(5mind + 20) × 10이므로
CDEF:c5mind=5mind,20,+,10,*와 같이 적으면 된다.LINE: 데이터를 선으로 그린다. 아래와 같은 구문을 가진다.
LINE[width]:value[#color][:[legend][:STACK][:skipscale][:dashes[=on_s[,off_s[,on_s,off_s]...]][:dash-offset=offset]]]예를 들어
LINE:5mind#C0FFC0:5MIN의 의미는 5mind로 정의된 데이터를
#C0FFC0(■) 색상으로 LINE을 그리고,
5MIN이라 명명한다.
AREA: 데이터를 영역으로 그린다. 아래와 같은 구문을 가진다.
AREA:value[#color][:[legend][:STACK][:skipscale]]예를 들어
AREA:15mind#C0C0C0:15MIN의 의미는 15mind로 정의된 데이터를
#C0C0C0(■)색상으로
AREA형식으로 그리고,
15MIN이라 명명한다.
TICK: 틱 영역을 그린다. 아래와 같은 구문을 가진다.
TICK:vname#rrggbb[aa][:fraction[:legend]]예를 들어
TICK:value#FFFFA0의 의미는 value 이 0이 아니고 Unknown 이 아닌 경우 #FFFFA0(■)색상의 수직 라인을 그린다.
STACK: STACK구문은 사용하지 않는다. 기존에는 AREA형식으로 쌓아올리는 그래프를 그리는 구문이었으나, 현재는 LINE, AREA 구문에 통합되어있다.~/.fonts 에 폰트 파일(.ttf)을 넣어 설정하면 된다. 그래프를 그릴때 사용하는 폰트를 바꾸려면, RRD_DEFAULT_FONT환경변수를 설정한다. RRD_DEFAULT_FONT="sample.ttf"과 같이 설정하고 sample.ttf 파일을 ~/.fonts 에 넣으면 된다.RRA:AVERAGE:0.5:1:1440으로 그래프를 그릴 것이다.
RRDtool 에서는 LSLSLOPE, LSLINT 식을 사용해 미래의 예측 데이터를 그릴 수 있다. (하기 예제에서 rrdtool_volstat_rpool.rrd파일은 문서 하단에 설명되어있는 rrdtool_volstat.sh 스크립트를 사용해 생성한 파일이다)
windy@wl ~ $ export FN="rrdtool_volstat_rpool.rrd" windy@wl ~ $ rrdtool graph rrdtool_lsl.png \ --start -4w \ --end +4w \ --vertical-label "Volume Usage" \ --title "Volume Usage root@wl" \ --width 500 \ --height 100 \ --base 1024 \ --lower-limit 0 \ --alt-autoscale-max \ --disable-rrdtool-tag \ "DEF:TOTAL=$FN:TOTAL:AVERAGE" \ "DEF:USAGE=$FN:USAGE:AVERAGE:start=-8w" \ "CDEF:CTOTAL=TOTAL,1024,*" \ "CDEF:CUSAGE=USAGE,1024,*" \ "CDEF:CTOTAL60=TOTAL,0.6,*,1024,*" \ "CDEF:CAREAW=CUSAGE,CTOTAL60,GT,CUSAGE,0,IF" \ "CDEF:CAREAU=CUSAGE,CTOTAL60,GT,CTOTAL60,CUSAGE,IF" \ "VDEF:DLSL=CUSAGE,LSLSLOPE" \ "VDEF:HLSL=CUSAGE,LSLINT" \ "CDEF:ALSL=CUSAGE,POP,DLSL,COUNT,*,HLSL,+" \ "AREA:CAREAW#EC9D48" \ "AREA:CAREAU#68E4FC" \ "LINE:CUSAGE#1598C3:root Usage" \ "GPRINT:CUSAGE:AVERAGE:%.1lf %S" \ "LINE:CTOTAL#CC3118:root Total" \ "GPRINT:CTOTAL:AVERAGE:%.1lf %S" \ "LINE:ALSL#B415C7:root Usage Pred:dashes=5"
강조된 부분이 LSL 예측에 필요한 파라메터들이다. 아래와 유사한 그래프가 생성되며, 분홍 점선이 LSL 을 사용해 예측한 값이다. 현재 시간 이후 4주차까지 예측해 그렸다.
LSL외에도 PREDICT 식을 사용해 미래의 예측 데이터를 그릴 수 있다. (하기 예제에서 rrdtool_net_net.rrd파일은 문서 하단에 설명되어있는 rrdtool_net.sh 스크립트를 사용해 생성한 파일이다)
windy@wl ~ $ export FN="rrdtool_net_net.rrd"
windy@wl ~ $ rrdtool graph rrdtool_predict.png \
--start=-4w \
--end=+1w \
--vertical-label "bytes/s" \
--title "Network Traffics " \
--width 500 \
--height 100 \
--alt-autoscale-max \
--disable-rrdtool-tag \
DEF:value=$FN:bytes_in:AVERAGE:start=-8w \
LINE1:value#ff0000:bytes_in \
CDEF:predict=86400,-7,1800,value,PREDICT \
CDEF:sigma=86400,-7,1800,value,PREDICTSIGMA \
CDEF:upper=predict,sigma,3,*,+ \
CDEF:lower=predict,sigma,3,*,- \
LINE1:predict#00ff00:prediction \
LINE1:upper#0000ff:upper\ certainty\ limit \
LINE1:lower#0000ff:lower\ certainty\ limit \
CDEF:exceeds=value,UN,0,value,lower,upper,LIMIT,UN,IF \
TICK:exceeds#aa000080:1 \
CDEF:perc95=86400,-7,1800,95,value,PREDICTPERC \
LINE1:perc95#ffff00:95th_percentile
아래와 유사한 그래프가 생성된다. Week 14 까지가 데이터가 있는 구간이고, 그이후 구간중 초록색 선이 예측된 값, 파란색 선이 오차 범위를 나타내었다.
그래프상으로는 오차 구간이 크기 때문에 음수까지 나오는데, 이를 없애면 아래와 같이 그려진다. CDEF:lower=predict,sigma,3,*,- \ 대신 CDEF:lower=predict,sigma,3,*,-,0,MAX \를 사용했다.
HWPREDICT 지시자를 사용한 운전일탈 감지를 위한 그래프 예시다. 다른 예제와는 달리 RRA 에 HWPREDICT가 사전에 지정되어 있어야 한다.(하기 예제에서 rrdtool_net_net.rrd파일은 문서 하단에 설명되어있는 rrdtool_net.sh 스크립트를 사용해 생성한 파일이다)
windy@wl ~ $ export FN="rrdtool_net_net.rrd" windy@wl ~ $ rrdtool graph rrdtool_hwpredict.png \ --start -1d \ --vertical-label "bytes/s" \ --title "Network Traffics " \ --width 500 \ --height 100 \ --alt-autoscale-max \ --disable-rrdtool-tag \ --lower-limit 0 \ "DEF:bi=$FN:bytes_in:AVERAGE" \ "DEF:bipred=$FN:bytes_in:HWPREDICT" \ "DEF:bidev=$FN:bytes_in:DEVPREDICT" \ "DEF:bifail=$FN:bytes_in:FAILURES" \ "DEF:bo=$FN:bytes_out:AVERAGE" \ "DEF:bopred=$FN:bytes_out:HWPREDICT" \ "DEF:bodev=$FN:bytes_out:DEVPREDICT" \ "DEF:bofail=$FN:bytes_out:FAILURES" \ "CDEF:gbi=bi" \ "CDEF:gbo=bo" \ "CDEF:biupper=bipred,bidev,2,*,+" \ "CDEF:bilower=bipred,bidev,2,*,-" \ "CDEF:boupper=bopred,bodev,2,*,+" \ "CDEF:bolower=bopred,bodev,2,*,-" \ "TICK:bifail#FFFFA0:1.0" \ "TICK:bofail#FFFFA0:1.0" \ "LINE:biupper#FFC0C0:Input Upper" \ "LINE:bilower#FFC0C0:Input Lower" \ "LINE:boupper#00C000:Output Upper" \ "LINE:bolower#00C000:Output Lower" \ "AREA:gbi#FF8080:Input bytes/s" \ "GPRINT:bi:AVERAGE:%.1lf %S" \ "LINE:gbo#00A000:Output bytes/s" \ "GPRINT:bo:AVERAGE:%.1lf %S"
아래와 유사한 그래프가 나온다. 문제가 있다고 판단되는 경우 (범위를 벗어나는 경우) TICK 커맨드에 의해 노란색 배경으로 출력된다.
.rrd파일에 대한 캐시를 제공해주는 데몬이다. 특히 그래프를 그리기 위해 데이터를 읽을때 이로 인한 디스크 부하를 방지해준다. 이 데몬은 RRDtool 1.4 이상에 포함되어있다.windy@wl ~ $ /opt/rrdtool/bin/rrdcached -p /tmp/rrdcached.pid -b /export/home/windy/rrdtoolpid 파일을
/tmp/rrdcached.pid에 생성하고, 나머지는 모두 기본값으로 두었다. 소켓은 Unix Domain Socket 형태로 /tmp/rrdcached.sock에 생성된다. 자세한 사항은 RRDtool - rrdcached를 읽어보자.
unix:/tmp/rrdcached.sock--daemon옵션을 사용해 RRDCache 데몬을 지정할 수 있다. RRDCACHED_ADDRESS 환경변수를 지정하면 RRDtool에서 자동으로 읽어와 데몬으로 지정된다.
windy@wl ~ $ export RRDCACHED_ADDRESS="unix:/tmp/rrdcached.sock" windy@wl ~ $ rrdtool graph ...
rrdtool flushcached명령은 메모리에 캐시된 데이터를 .rrd 파일에 쓴다. 자세한 사항은 rrdflushcached(1)을 읽어보자.windy@wl ~ $ rrdtool flushcached --daemon unix:/tmp/rrdcached.sock load.rrd
rrdtool list 명령은 RRDCached 의 캐시 목록을 알려준다.windy@wl ~ $ rrdtool list / rrdtool_load.rrd rrdtool_mem.rrd ...
| 저장 단위 시간 | 1분 | 5분 | 30분 | 2시간 | 1일 |
|---|---|---|---|---|---|
| 저장 회수 | 1440 | 1440 | 800 | 800 | 800 |
| 총 저장 기간 1) | 1일 | 5일 | 16일 | 66일 | 2년 |
| 그래프 2) | 1일 | X 3) | 1주일 | 1달 | 1년 |
저장하는기간=그래프로그리는기간 × 2이다.
-1d를 -2d로 변경하기만 하면 된다. 개요 형식의 큰 그래프를 그릴 때 사용하면 알맞다고 생각한다.
~/rrdtool 디렉토리를 만들고 스크립트를 다운로드 받은 후 cron 에 1분마다 실행되도록 하면 설치는 완료된다.
windy@wl ~/rrdtool $ crontab -e * * * * * /export/home/windy/rrdtool/rrd.sh windy@wl ~/rrdtool $ cat rrd.sh #!/bin/bash cd ~/rrdtool #export RRDCACHED_ADDRESS="unix:/tmp/rrdcached.sock" ./rrdtool_load.sh ./rrdtool_net.sh ... windy@wl ~/rrdtool $ ls -alF -rwxr-xr-x 1 windy staff 313 4월 7일 00:00 rrd.sh* -rwxr-xr-x 1 windy staff 2647 4월 7일 00:00 rrdtool_net.sh* -rwxr-xr-x 1 windy staff 2101 4월 7일 00:00 rrdtool_load.sh* ... windy@wl ~/rrdtool $ ./rrd.sh windy@wl ~/rrdtool $ ls -alF *.rrd -rw-r--r-- 1 windy staff 172168 4월 7일 00:01 rrdtool_dskstat_sd0.rrd -rw-r--r-- 1 windy staff 129296 4월 7일 00:01 rrdtool_load.rrd -rw-r--r-- 1 windy staff 343656 4월 7일 00:01 rrdtool_mem.rrd -rw-r--r-- 1 windy staff 858120 4월 7일 00:01 rrdtool_mysql.rrd -rw-r--r-- 1 windy staff 295336 4월 7일 00:01 rrdtool_net_iprb0.rrd -rw-r--r-- 1 windy staff 86424 4월 7일 00:01 rrdtool_volstat_rpool.rrd ...
rrdtool_load.sh | (2,836 바이트) |
#!/bin/bash
# Monitoring load average for Solaris, Linux
# WindyHana's Solanara: RRDtool
# http://www.solanara.net/solanara/rrdtool
# last update: 2014.12.10
PATH=/bin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/rrdtool/bin:/opt/rrdtool/bin
LANG=C
if [ "$RRDTOOL_BIN" == "" ]; then
RRDTOOL_BIN=`which rrdtool`
if [ "$RRDTOOL_BIN" == "" ]; then
echo "Cannot find RRDTool. Set $RRDTOOL_BIN env."
exit 1;
fi
fi
if [ "$RRDTOOL_HOME" != "" ]; then
cd "$RRDTOOL_HOME"
else
cd `dirname $0`
fi
MI=`date +'%M'`
MI=$(expr $MI + 0)
HR=`date +'%H'`
HR=$(expr $HR + 0)
let MOD5=$MI%5;
let MOD30=$MI%30;
# /tmp/rrdtool_load file format
# 1min load average:5min load average, 15min load average
# ex) 1.00:0.50:0.10
#
TEMPFN="/tmp/rrdtool_load_$$"
uptime | sed -e 's/^.*load average.*: //' -e 's/ //g' | awk -F, "{ printf(\"%f:%f:%f\", \$1, \$2, \$3)}" > $TEMPFN
DATA=`cat $TEMPFN`
rm $TEMPFN
FNPREFIX="rrdtool_load"
FNRRD="$FNPREFIX.rrd"
OS=`uname -s`
if [ "$OS" == "SunOS" ]; then
CPUCOUNT=`kstat -m cpu_info -p | grep state | grep on-line | wc -l`
UPTIME=`kstat -p unix:0:system_misc:snaptime | awk '{print $2}' | awk -F. '{print $1}'`
elif [ "$OS" == "Linux" ]; then
CPUCOUNT=`grep -c processor /proc/cpuinfo`
UPTIME=`cat /proc/uptime | awk '{print $1}' | awk -F. '{print $1}'`
else
CPUCOUNT=2
UPTIME=1
fi
LOADWARNING=`expr $CPUCOUNT "*" 4`
DATA="$DATA:$UPTIME"
if [ ! -f $FNRRD ]; then
rrdtool create $FNRRD \
--step 60 \
"DS:1min:GAUGE:600:0:U" \
"DS:5min:GAUGE:600:0:U" \
"DS:15min:GAUGE:600:0:U" \
"DS:uptime:GAUGE:600:0:U" \
"RRA:AVERAGE:0.5:1:1440" \
"RRA:AVERAGE:0.5:5:1440" \
"RRA:AVERAGE:0.5:30:800" \
"RRA:AVERAGE:0.5:120:800" \
"RRA:AVERAGE:0.5:1440:800"
fi
$RRDTOOL_BIN update $FNRRD N:$DATA
function graphLoad {
FIRST=`rrdtool fetch --start $1 $FNPREFIX.rrd AVERAGE | grep -v "nan" | grep ":" | head -1 | awk -F: '{print $1}'`
if [ "$FIRST" == "" ]; then
FIRST=`nawk 'BEGIN {print srand()}'`
fi
$RRDTOOL_BIN graph ${FNPREFIX}_$2.png \
--start $1 \
--vertical-label "# of threads in run q" \
--title "Load averages ($HOSTNAME)" \
--width 500 \
--height 100 \
--lower-limit 0 \
--alt-autoscale-max \
--units-exponent 0 \
--disable-rrdtool-tag \
"DEF:1mind=$FNRRD:1min:AVERAGE" \
"DEF:5mind=$FNRRD:5min:AVERAGE" \
"DEF:15mind=$FNRRD:15min:AVERAGE" \
"CDEF:fail=1mind,UN,TIME,$FIRST,GT,1,0,IF,0,IF" \
"COMMENT:Current " \
"GPRINT:1mind:LAST:%.2lf" \
"GPRINT:5mind:LAST:%.2lf" \
"GPRINT:15mind:LAST:%.2lf" \
"LINE:1mind#54EC48:1 MIN" \
"LINE:5mind#4D18E4:5 MIN" \
"LINE:15mind#DE48EC:15 MIN" \
"HRULE:$LOADWARNING#FF0000" \
"TICK:fail#FFFF80:1.0" \
> /dev/null
}
if [ "$MOD5" == "0" ]; then
graphLoad -1d day
fi
if [ "$MOD30" == "0" ]; then
graphLoad -1w week
fi
if [ "$MI" == "0" ]; then
graphLoad -1m month
fi
if [ "$HR" == "0" ]; then
graphLoad -1y year
fi
rrdtool_cpu.sh | (7,613 바이트) |
#!/bin/bash
# Monitoring CPU for Solaris
# WindyHana's Solanara: RRDtool
# http://www.solanara.net/solanara/rrdtool
# last update: 2014.12.10
PATH=/bin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/rrdtool/bin:/opt/rrdtool/bin
LANG=C
if [ "$RRDTOOL_BIN" == "" ]; then
RRDTOOL_BIN=`which rrdtool`
if [ "$RRDTOOL_BIN" == "" ]; then
echo "Cannot find RRDTool. Set $RRDTOOL_BIN env."
exit 1;
fi
fi
if [ "$RRDTOOL_HOME" != "" ]; then
cd "$RRDTOOL_HOME"
else
cd `dirname $0`
fi
MI=`date +'%M'`
MI=$(expr $MI + 0)
HR=`date +'%H'`
HR=$(expr $HR + 0)
let MOD5=$MI%5;
let MOD30=$MI%30;
FNPREFIX="rrdtool_cpu"
FNSUFFIX=".rrd"
CPUCOUNT=`kstat -m cpu_info -p | grep state | grep on-line | wc -l`
i=0
while [ "$i" -lt $CPUCOUNT ]; do
FN=${FNPREFIX}_${i}$FNSUFFIX
if [ ! -f $FN ]; then
rrdtool create $FN \
--step 60 \
"DS:uptime:GAUGE:600:0:U" \
"DS:idle:DERIVE:600:0:U" \
"DS:user:DERIVE:600:0:U" \
"DS:kernel:DERIVE:600:0:U" \
"DS:wait:DERIVE:600:0:U" \
"DS:wait_io:DERIVE:600:0:U" \
"DS:wait_pio:DERIVE:600:0:U" \
"DS:wait_swap:DERIVE:600:0:U" \
"DS:iowait:DERIVE:600:0:U" \
"DS:pswitch:DERIVE:600:0:U" \
"DS:inv_swtch:DERIVE:600:0:U" \
"DS:syscall:DERIVE:600:0:U" \
"DS:sysread:DERIVE:600:0:U" \
"DS:syswrite:DERIVE:600:0:U" \
"DS:sysfork:DERIVE:600:0:U" \
"DS:sysvfork:DERIVE:600:0:U" \
"DS:sysexec:DERIVE:600:0:U" \
"DS:cow_fault:DERIVE:600:0:U" \
"DS:as_fault:DERIVE:600:0:U" \
"DS:prot_fault:DERIVE:600:0:U" \
"DS:hat_fault:DERIVE:600:0:U" \
"DS:softlock:DERIVE:600:0:U" \
"DS:kernel_asflt:DERIVE:600:0:U" \
"DS:maj_fault:DERIVE:600:0:U" \
"DS:intr:DERIVE:600:0:U" \
"DS:intrblk:DERIVE:600:0:U" \
"DS:intrthread:DERIVE:600:0:U" \
"DS:msg:DERIVE:600:0:U" \
"DS:sema:DERIVE:600:0:U" \
"DS:namei:DERIVE:600:0:U" \
"DS:cpumigrate:DERIVE:600:0:U" \
"DS:xcalls:DERIVE:600:0:U" \
"DS:trap:DERIVE:600:0:U" \
"RRA:AVERAGE:0.5:1:1440" \
"RRA:AVERAGE:0.5:5:1440" \
"RRA:AVERAGE:0.5:30:800" \
"RRA:AVERAGE:0.5:120:800" \
"RRA:AVERAGE:0.5:1440:800"
fi
STATI="cpu_stat:$i:"
# /usr/include/sys/sysinfo.h
uptime=`kstat -p unix:0:system_misc:snaptime | awk '{print $2}' | awk -F. '{print $1}'`
DATA=`kstat -p \
$STATI:idle $STATI:user $STATI:kernel $STATI:wait $STATI:wait_io $STATI:wait_pio $STATI:wait_swap $STATI:iowait \
$STATI:pswitch $STATI:inv_swtch \
$STATI:syscall $STATI:sysread $STATI:syswrite $STATI:sysfork $STATI:sysvfork $STATI:sysexec \
$STATI:cow_fault $STATI:as_fault $STATI:prot_fault $STATI:hat_fault $STATI:softlock $STATI:kernel_asflt $STATI:maj_fault \
$STATI:intr $STATI:intrblk $STATI:intrthread \
$STATI:msg $STATI:sema \
$STATI:namei \
$STATI:cpumigrate $STATI:xcalls $STATI:trap \
| awk '{print $2}' \
| sed -n -e 'N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;s/\n/\:/g;p' \
`
DATA=$uptime:$DATA
$RRDTOOL_BIN update $FN N:$DATA
if [ "$?" != "0" ]; then
echo "Cannot update $FN by previous error.";
exit;
fi
function graphCpu {
$RRDTOOL_BIN graph ${FNPREFIX}_${i}_utl_$2.png \
--start $1 \
--vertical-label "Util %" \
--title "CPU $i Utilization" \
--width 500 \
--height 100 \
--lower-limit 0 \
--upper-limit 100 \
--rigid \
--disable-rrdtool-tag \
"DEF:idle=$FN:idle:AVERAGE" \
"DEF:user=$FN:user:AVERAGE" \
"DEF:kernel=$FN:kernel:AVERAGE" \
"DEF:wait=$FN:wait:AVERAGE" \
"CDEF:totaltick=idle,user,+,kernel,wait,+,+" \
"CDEF:pkernel=kernel,100,*,totaltick,/" \
"CDEF:puser=user,100,*,totaltick,/" \
"CDEF:pwait=wait,100,*,totaltick,/" \
"CDEF:pidle=100,pkernel,-,puser,-,pwait,-" \
"AREA:pkernel#7648EC:Kernel" \
"AREA:puser#EA644A:User:STACK" \
"AREA:pwait#54EC48:Wait:STACK" \
"AREA:pidle#68E4FC:Idle:STACK" \
"LINE:100#1598C3" \
> /dev/null
# 'totaltick' always 100 on Solaris System. see hires_tick on Solaris Tunable Parameters Reference Manual
$RRDTOOL_BIN graph ${FNPREFIX}_${i}_sys_$2.png \
--start $1 \
--vertical-label "Total syscall/s" \
--title "CPU $i System Calls" \
--width 500 \
--height 100 \
--lower-limit 0 \
--right-axis-label 'Operations/s' \
--right-axis 0.01:0 \
--alt-autoscale-max \
--disable-rrdtool-tag \
"DEF:syscall=$FN:syscall:AVERAGE" \
"DEF:sysread=$FN:sysread:AVERAGE" \
"DEF:syswrite=$FN:syswrite:AVERAGE" \
"DEF:sysfork=$FN:sysfork:AVERAGE" \
"DEF:sysvfork=$FN:sysvfork:AVERAGE" \
"DEF:sysexec=$FN:sysexec:AVERAGE" \
"CDEF:csysread=sysread,20,*" \
"CDEF:csyswrite=syswrite,20,*" \
"CDEF:csysfork=sysfork,20,*" \
"CDEF:csysvfork=sysvfork,20,*" \
"CDEF:csysexec=sysexec,20,*" \
"LINE:syscall#48C4EC:total" \
"LINE:csysread#ECD748:read" \
"LINE:csyswrite#54EC48:write" \
"LINE:csysfork#EA644A:fork" \
"LINE:csysvfork#DE48EC:vfork" \
"LINE:csysexec#EC9D48:exec" \
> /dev/null
$RRDTOOL_BIN graph ${FNPREFIX}_${i}_xcl_$2.png \
--start $1 \
--vertical-label "cpumig,xcall/s" \
--title "CPU $i xcall/migration" \
--width 500 \
--height 100 \
--lower-limit 0 \
--right-axis-label 'traps/s' \
--right-axis 100:0 \
--alt-autoscale-max \
--disable-rrdtool-tag \
"DEF:xcalls=$FN:xcalls:AVERAGE" \
"DEF:cpumigrate=$FN:cpumigrate:AVERAGE" \
"DEF:trap=$FN:trap:AVERAGE" \
"CDEF:ctrap=trap,100,/" \
"LINE:cpumigrate#EA644A:cpu migrations" \
"LINE:xcalls#24BC14:xcalls" \
"LINE:ctrap#DE48EC:traps" \
> /dev/null
$RRDTOOL_BIN graph ${FNPREFIX}_${i}_sch_$2.png \
--start $1 \
--vertical-label "Context Switches/s" \
--title "CPU $i Context Switches" \
--width 500 \
--height 100 \
--lower-limit 0 \
--alt-autoscale-max \
--disable-rrdtool-tag \
"DEF:inv_swtch=$FN:inv_swtch:AVERAGE" \
"DEF:pswitch=$FN:pswitch:AVERAGE" \
"AREA:pswitch#68E4FC:Total Context Switches" \
"LINE:pswitch#1598C3" \
"AREA:inv_swtch#4D18E4:Involuntary Context Switches" \
> /dev/null
$RRDTOOL_BIN graph ${FNPREFIX}_${i}_ipc_$2.png \
--start $1 \
--vertical-label "operations/s" \
--title "CPU $i IPC" \
--width 500 \
--height 100 \
--lower-limit 0 \
--alt-autoscale-max \
--disable-rrdtool-tag \
"DEF:msg=$FN:msg:AVERAGE" \
"DEF:sema=$FN:sema:AVERAGE" \
"LINE:msg#EA644A:Message count" \
"AREA:sema#24BC14:Semaphore operations" \
> /dev/null
$RRDTOOL_BIN graph ${FNPREFIX}_${i}_int_$2.png \
--start $1 \
--vertical-label "operations/s" \
--title "CPU $i Interrupts" \
--width 500 \
--height 100 \
--lower-limit 0 \
--alt-autoscale-max \
--disable-rrdtool-tag \
"DEF:intr=$FN:intr:AVERAGE" \
"DEF:intrblk=$FN:intrblk:AVERAGE" \
"DEF:intrthread=$FN:intrthread:AVERAGE" \
"LINE:intr#EA644A:Interrupts" \
"LINE:intrblk#24BC14:Interrupts Blk" \
"LINE:intrthread#DE48EC:Interrupts Thread" \
> /dev/null
$RRDTOOL_BIN graph ${FNPREFIX}_${i}_flt_$2.png \
--start $1 \
--vertical-label "operations/s" \
--title "CPU $i Faults" \
--width 500 \
--height 100 \
--lower-limit 0 \
--alt-autoscale-max \
--disable-rrdtool-tag \
"DEF:maj_fault=$FN:maj_fault:AVERAGE" \
"DEF:cow_fault=$FN:cow_fault:AVERAGE" \
"DEF:as_fault=$FN:as_fault:AVERAGE" \
"DEF:prot_fault=$FN:as_fault:AVERAGE" \
"DEF:softlock=$FN:as_fault:AVERAGE" \
"LINE:maj_fault#EA644A:Major Faults" \
"LINE:cow_fault#24BC14:COW Faults" \
"LINE:as_fault#DE48EC:AS Faults" \
"LINE:prot_fault#EC9D48:Protection Faults" \
"LINE:softlock#48C4EC:Soft Lock" \
> /dev/null
}
if [ "$MOD5" == "0" ]; then
graphCpu -1d day
fi
if [ "$MOD30" == "0" ]; then
graphCpu -1w week
fi
if [ "$MI" == "0" ]; then
graphCpu -1m month
fi
if [ "$HR" == "0" ]; then
graphCpu -1y year
fi
i=`echo $i + 1 | bc`;
done





rrdtool_net.sh | (5,499 바이트) |
#!/bin/bash
# Monitoring network traffic/packets for solaris
# WindyHana's Solanara: RRDtool
# http://www.solanara.net/solanara/rrdtool
# last update: 2011.12.10
PATH=/bin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/rrdtool/bin:/opt/rrdtool/bin
LANG=C
if [ "$RRDTOOL_BIN" == "" ]; then
RRDTOOL_BIN=`which rrdtool`
if [ "$RRDTOOL_BIN" == "" ]; then
echo "Cannot find RRDTool. Set $RRDTOOL_BIN env."
exit 1;
fi
fi
if [ "$RRDTOOL_HOME" != "" ]; then
cd "$RRDTOOL_HOME"
else
cd `dirname $0`
fi
MI=`date +'%M'`
MI=$(expr $MI + 0)
HR=`date +'%H'`
HR=$(expr $HR + 0)
let MOD5=$MI%5;
let MOD30=$MI%30;
# /tmp/rrdtool_net file format
# InterfaceName InputPackets OutputPackets InputBytes OutputBytes InterfaceSpeed per each line.
# -> e1000g0 ipackets obytes opackets rbytes ifspeed
# Do not use 64bit integer for compatibility issue.
TEMPFN="/tmp/rrdtool_net_$$"
kstat -c net -p 'link:::[or]bytes' -p 'link:::[io]packets' -p 'link:::ifspeed' \
| sed '/lo/d' \
| awk 'BEGIN{FS=":"} {print $3,$4}' \
| awk '{print $1,$2,$3}' \
| sort -k 1,2 \
| sed -n -e 'N;N;N;N;s/\n/ /g;p' \
| awk '{print $1,$6,$12,$15,$9,$3}' \
> $TEMPFN
cat $TEMPFN | while read LINE
do
IFNAME=`echo $LINE | awk '{print $1}'`
DATA=`echo $LINE | awk '{print $2,$3,$4,$5}' | sed -e 's/ /:/g'`
IFSPEED=`echo $LINE | awk '{print $6}'`
FN="rrdtool_net_$IFNAME.rrd"
if [ "$IFSPEED" == "10000000" ]; then
IFSPEEDMSG="10Mbps"
elif [ "$IFSPEED" == "100000000" ]; then
IFSPEEDMSG="100Mbps"
elif [ "$IFSPEED" == "1000000000" ]; then
IFSPEEDMSG="1Gbps"
elif [ "$IFSPEED" == "10000000000" ]; then
IFSPEEDMSG="10Gbps"
elif [ "$IFSPEED" == "40000000000" ]; then
IFSPEEDMSG="40Gbps"
elif [ "$IFSPEED" == "100000000000" ]; then
IFSPEEDMSG="100Gbps"
else
IFSPEEDMSG="?"
fi
if [ ! -f $FN ]; then
rrdtool create $FN \
--step 60 \
"DS:packets_in:DERIVE:600:0:U" \
"DS:packets_out:DERIVE:600:0:U" \
"DS:bytes_in:DERIVE:600:0:U" \
"DS:bytes_out:DERIVE:600:0:U" \
"RRA:HWPREDICT:1440:0.1:0.0035:288" \
"RRA:AVERAGE:0.5:1:1440" \
"RRA:AVERAGE:0.5:5:1440" \
"RRA:AVERAGE:0.5:30:800" \
"RRA:AVERAGE:0.5:120:800" \
"RRA:AVERAGE:0.5:1440:800" \
"RRA:MAX:0.5:60:24"
fi
rrdtool update $FN N:$DATA
function graphNet {
rrdtool graph rrdtool_net_${IFNAME}_pks_$2.png \
--start $1 \
--vertical-label "Packets/s" \
--title "Network Traffics ${IFNAME}@$HOSTNAME ($IFSPEEDMSG)" \
--width 500 \
--height 100 \
--alt-autoscale-max \
--disable-rrdtool-tag \
"DEF:pi=$FN:packets_in:AVERAGE" \
"DEF:po=$FN:packets_out:AVERAGE" \
"DEF:fail=$FN:packets_out:FAILURES" \
"CDEF:gpi=pi" \
"CDEF:gpo=po" \
"TICK:fail#FFFFA0:1.0" \
"AREA:gpi#FF8080:Input Packets/s" \
"GPRINT:pi:AVERAGE:%.1lf %S" \
"LINE:gpo#00A000:Output Packets/s" \
"GPRINT:po:AVERAGE:%.1lf %S" \
> /dev/null
rrdtool graph rrdtool_net_${IFNAME}_bit_$2.png \
--start $1 \
--vertical-label "bytes/s" \
--title "Network Traffics ${IFNAME}@$HOSTNAME ($IFSPEEDMSG)" \
--width 500 \
--height 100 \
--alt-autoscale-max \
--disable-rrdtool-tag \
"DEF:bi=$FN:bytes_in:AVERAGE" \
"DEF:bipred=$FN:bytes_in:HWPREDICT" \
"DEF:bidev=$FN:bytes_in:DEVPREDICT" \
"DEF:bifail=$FN:bytes_in:FAILURES" \
"DEF:bo=$FN:bytes_out:AVERAGE" \
"DEF:bopred=$FN:bytes_out:HWPREDICT" \
"DEF:bodev=$FN:bytes_out:DEVPREDICT" \
"DEF:bofail=$FN:bytes_out:FAILURES" \
"CDEF:gbi=bi" \
"CDEF:gbo=bo" \
"CDEF:biupper=bipred,bidev,2,*,+" \
"CDEF:bilower=bipred,bidev,2,*,-" \
"CDEF:boupper=bopred,bodev,2,*,+" \
"CDEF:bolower=bopred,bodev,2,*,-" \
"TICK:bifail#FFFFA0:1.0" \
"TICK:bofail#FFFFA0:1.0" \
"AREA:gbi#FF8080:Input bytes/s" \
"GPRINT:bi:AVERAGE:%.1lf %S" \
"LINE:gbo#00A000:Output bytes/s" \
"GPRINT:bo:AVERAGE:%.1lf %S" \
> /dev/null
}
function graphNetSumm {
rrdtool graph rrdtool_net_${IFNAME}_bit_summ.png \
--start -2d \
--vertical-label "bytes/s" \
--title "Network Traffics Summary ${IFNAME}@$HOSTNAME ($IFSPEEDMSG)" \
--width 500 \
--height 150 \
--alt-autoscale-max \
--disable-rrdtool-tag \
--x-grid "HOUR:1:HOUR:6:HOUR:6:0:%H:00" \
"DEF:bi=$FN:bytes_in:AVERAGE" \
"DEF:bo=$FN:bytes_out:AVERAGE" \
"DEF:bi1w=$FN:bytes_in:AVERAGE:end=now-1w:start=end-1w" \
"DEF:bo1w=$FN:bytes_out:AVERAGE:end=now-1w:start=end-1w" \
"VDEF:bimax=bi,MAXIMUM" \
"VDEF:biavg=bi,AVERAGE" \
"VDEF:bimin=bi,MINIMUM" \
"VDEF:bomax=bo,MAXIMUM" \
"VDEF:boavg=bo,AVERAGE" \
"VDEF:bomin=bo,MINIMUM" \
"VDEF:bi1wmax=bi1w,MAXIMUM" \
"VDEF:bi1wavg=bi1w,AVERAGE" \
"VDEF:bi1wmin=bi1w,MINIMUM" \
"VDEF:bo1wmax=bo1w,MAXIMUM" \
"VDEF:bo1wavg=bo1w,AVERAGE" \
"VDEF:bo1wmin=bo1w,MINIMUM" \
"LINE:bi#EA644A:Input " \
"GPRINT:bimax:%6.1lf %S" \
"GPRINT:biavg:%6.1lf %S" \
"GPRINT:bimin:%6.1lf %S\l" \
"LINE:bi1w#EC9D48:Input (-1w)" \
"GPRINT:bi1wmax:%6.1lf %S" \
"GPRINT:bi1wavg:%6.1lf %S" \
"GPRINT:bi1wmin:%6.1lf %S\l" \
"LINE:bo#7648EC:Output " \
"GPRINT:bomax:%6.1lf %S" \
"GPRINT:boavg:%6.1lf %S" \
"GPRINT:bomin:%6.1lf %S\l" \
"LINE:bo1w#48C4EC:Output (-1w)" \
"GPRINT:bo1wmax:%6.1lf %S" \
"GPRINT:bo1wavg:%6.1lf %S" \
"GPRINT:bo1wmin:%6.1lf %S\l" \
"DEF:fail=$FN:bytes_in:FAILURES" \
"TICK:fail#000000:1.0:Fail" \
> /dev/null
}
if [ "$MOD5" == "0" ]; then
graphNet -1d day
fi
if [ "$MOD30" == "0" ]; then
graphNet -1w week
fi
if [ "$MI" == "0" ]; then
graphNet -1m month
fi
if [ "$HR" == "0" ]; then
graphNet -1y year
fi
# graphNetSumm
done
rm $TEMPFN


rrdtool_mem.sh | (3,293 바이트) |
#!/bin/bash
# Monitoring memory usage for solaris
# WindyHana's Solanara: RRDtool
# http://www.solanara.net/solanara/rrdtool
# last update: 2015.12.16
. /etc/profile
PATH=/opt/rrdtool/bin:/usr/bin
cd ~windy/rrdtool
LANG=C
# /tmp/rrdtool_mem file format
# Kernel ZFS Anon ExecLib Page FreeCache FreeList Total (in pages)
PAGESIZE=`pagesize`
echo '::memstat' | pfexec mdb -k > /tmp/rrdtool_mem
if [ "$?" != "0" ]; then
echo "Must run as root priv."
exit 1
fi
MDBLINES=`cat /tmp/rrdtool_mem | wc -l | awk '{print $1}'`
if [ "$MDBLINES" == "10" ]; then
# Solaris 11.4
cat /tmp/rrdtool_mem \
| sed '1,2d' \
| sort -k 1 \
| awk '{print substr($0, 18, 100) }' \
| awk '{print $1 }' \
| sed -n -e 'N;N;N;N;N;N;N;s/\n/ /g;p' \
| awk '{print $4,$8,$7,$1,$5,$3,$2,$6}' \
> /tmp/rrdtool_mem2
elif [ "$MDBLINES" == "11" ]; then
# Solaris 10
cat /tmp/rrdtool_mem \
| sed '1,2d' \
| sort -k 1 \
| awk '{print substr($0, 18, 100) }' \
| awk '{print $1 }' \
| sed -n -e 'N;N;N;N;N;N;N;N;s/\n/ /g;p' \
| awk '{print $5,$8,$1,$2,$6,$3,$4,$7}' \
> /tmp/rrdtool_mem2
elif [ "$MDBLINES" == "12" ]; then
# Solaris 11
cat /tmp/rrdtool_mem \
| sed '1,2d' \
| sort -k 1 \
| awk '{print substr($0, 18, 100) }' \
| awk '{print $1 }' \
| sed -n -e 'N;N;N;N;N;N;N;N;N;s/\n/ /g;p' \
| awk '{print $6,$9 + $10,$1,$2,$7,$3,$4,$8}' \
> /tmp/rrdtool_mem2
else
echo "ERROR: Cannot recognize MDB Output [$MDBLINES]"
exit 1
fi
rm /tmp/rrdtool_mem
DATA=`cat /tmp/rrdtool_mem2 | sed -e 's/ /:/g'`
rm /tmp/rrdtool_mem2
FN="rrdtool_mem.rrd"
if [ ! -f $FN ]; then
rrdtool create $FN \
--step 60 \
"DS:kern:GAUGE:600:0:U" \
"DS:zfsd:GAUGE:600:0:U" \
"DS:anon:GAUGE:600:0:U" \
"DS:exec:GAUGE:600:0:U" \
"DS:page:GAUGE:600:0:U" \
"DS:fcah:GAUGE:600:0:U" \
"DS:flst:GAUGE:600:0:U" \
"DS:tot:GAUGE:600:0:U" \
"RRA:AVERAGE:0.5:1:1440" \
"RRA:AVERAGE:0.5:5:1440" \
"RRA:AVERAGE:0.5:30:800" \
"RRA:AVERAGE:0.5:120:800" \
"RRA:AVERAGE:0.5:1440:800"
fi
rrdtool update $FN N:$DATA
function graphMem {
rrdtool graph rrdtool_mem_$2.png \
--start $1 \
--vertical-label "Memory Usage" \
--title "Memory Usage ($HOSTNAME)" \
--width 500 \
--height 100 \
--base 1024 \
--lower-limit 0 \
--disable-rrdtool-tag \
"DEF:kern=$FN:kern:AVERAGE" \
"DEF:zfsd=$FN:zfsd:AVERAGE" \
"DEF:anon=$FN:anon:AVERAGE" \
"DEF:exec=$FN:exec:AVERAGE" \
"DEF:page=$FN:page:AVERAGE" \
"DEF:fcah=$FN:fcah:AVERAGE" \
"DEF:flst=$FN:flst:AVERAGE" \
"DEF:tot=$FN:tot:AVERAGE" \
"CDEF:ckern=kern,$PAGESIZE,*" \
"CDEF:czfsd=zfsd,$PAGESIZE,*" \
"CDEF:canon=anon,$PAGESIZE,*" \
"CDEF:cexec=exec,$PAGESIZE,*" \
"CDEF:cpage=page,$PAGESIZE,*" \
"CDEF:cfcah=fcah,$PAGESIZE,*" \
"CDEF:cflst=flst,$PAGESIZE,*" \
"CDEF:ctot=tot,$PAGESIZE,*" \
"AREA:ckern#EA644A:Kernel" \
"AREA:canon#EC9D48:Anon:STACK" \
"AREA:cexec#ECD748:Exec:STACK" \
"AREA:cpage#54EC48:Page:STACK" \
"AREA:czfsd#48C4EC:ZFS:STACK" \
"AREA:cfcah#DE48EC:FreeCache:STACK" \
"AREA:cflst#7648EC:FreeList:STACK" \
"LINE:ctot#4D18E4:Total" \
> /dev/null
}
graphMem -1d day
MI=`date +'%M'`
if [ "$MI" -lt "5" ]; then
graphMem -1w week
graphMem -1m month
graphMem -1y year
fi
rrdtool_volstat.sh | (2,295 바이트) |
#!/bin/bash
# Monitoring disk usage for solaris
# WindyHana's Solanara: RRDtool
# http://www.solanara.net/solanara/rrdtool
# last update: 2014.03.26
PATH=/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/rrdtool/bin:/opt/rrdtool/bin
LANG=C
if [ "$RRDTOOL_BIN" == "" ]; then
RRDTOOL_BIN=`which rrdtool`
if [ "$RRDTOOL_BIN" == "" ]; then
echo "Cannot find RRDTool. Set $RRDTOOL_BIN env."
exit 1;
fi
fi
if [ "$RRDTOOL_HOME" != "" ]; then
cd "$RRDTOOL_HOME"
else
cd `dirname $0`
fi
FNPREFIX="rrdtool_volstat_"
FNCONF="rrdtool_volstat.conf"
FNRRDSUFFIX=".rrd"
FNGRASUFFIX=".png"
if [ ! -f "$FNCONF" ]; then
UFS=`mount -p | awk '{print $3}' | grep ufs`
if [ -x /usr/sbin/zpool ]; then
ZFS=`zpool list | sed 1d | awk '{print $1}'`
fi
echo "FS='$UFS $ZFS'" > $FNCONF
fi
. "$FNCONF"
if [ "$FS" == "" ]; then
echo "Cannot determine file systems."
exit 1
fi
for MNTPOINT in $FS; do
FNRRD=$FNPREFIX$MNTPOINT$FNRRDSUFFIX
if [ ! -f "$FNRRD" ]; then
$RRDTOOL_BIN create "$FNRRD" \
--step 60 \
DS:TOTAL:GAUGE:600:0:U \
DS:USAGE:GAUGE:600:0:U \
"RRA:AVERAGE:0.5:1:1440" \
"RRA:AVERAGE:0.5:5:1440" \
"RRA:AVERAGE:0.5:30:800" \
"RRA:AVERAGE:0.5:120:800" \
"RRA:AVERAGE:0.5:1440:800"
fi
DATA=`df -k $MNTPOINT | sed 1d | awk '{print $2, $2 - $4}' | sed -e 's/ /:/g'`
$RRDTOOL_BIN update $FNRRD N:$DATA
function graphVol {
$RRDTOOL_BIN graph ${FNPREFIX}${MNTPOINT}_$2$FNGRASUFFIX \
--start $1 \
--vertical-label "Volume Usage" \
--title "Volume Usage $MNTPOINT@$HOSTNAME" \
--width 500 \
--height 100 \
--base 1024 \
--lower-limit 0 \
--alt-autoscale-max \
--disable-rrdtool-tag \
"DEF:TOTAL=$FNRRD:TOTAL:AVERAGE" \
"DEF:USAGE=$FNRRD:USAGE:AVERAGE" \
"CDEF:CTOTAL=TOTAL,1024,*" \
"CDEF:CUSAGE=USAGE,1024,*" \
"CDEF:CTOTAL60=TOTAL,0.6,*,1024,*" \
"CDEF:CAREAW=CUSAGE,CTOTAL60,GT,CUSAGE,0,IF" \
"CDEF:CAREAU=CUSAGE,CTOTAL60,GT,CTOTAL60,CUSAGE,IF" \
"AREA:CAREAW#EC9D48" \
"AREA:CAREAU#68E4FC" \
"LINE:CUSAGE#1598C3:$MNTPOINT Usage" \
"GPRINT:CUSAGE:AVERAGE:%.1lf %S" \
"LINE:CTOTAL#CC3118:$MNTPOINT Total" \
"GPRINT:CTOTAL:AVERAGE:%.1lf %S" \
> /dev/null
}
graphVol -1d day
MI=`date +'%M'`
if [ "$MI" -lt "5" ]; then
graphVol -1w week
graphVol -1m month
graphVol -1y year
fi
done
rrdtool_dskstat.sh | (4,374 바이트) |
#!/bin/bash
# Monitoring disk activity for solaris
# WindyHana's Solanara: RRDtool
# http://www.solanara.net/solanara/rrdtool
# last update: 2014.03.17
PATH=/bin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/rrdtool/bin:/opt/rrdtool/bin
LANG=C
if [ "$RRDTOOL_BIN" == "" ]; then
RRDTOOL_BIN=`which rrdtool`
if [ "$RRDTOOL_BIN" == "" ]; then
echo "Cannot find RRDTool. Set $RRDTOOL_BIN env."
exit 1;
fi
fi
if [ "$RRDTOOL_HOME" != "" ]; then
cd "$RRDTOOL_HOME"
else
cd `dirname $0`
fi
CONFFN="rrdtool_dskstat.conf"
FNPREFIX="rrdtool_dskstat_"
FNSUFFIX=".rrd"
if [ ! -f $CONFFN ]; then
DISKS=`kstat -p :::class | grep disk | grep -v unix | awk -F: '{print $3}'`
echo "# Generated by rrdtool_dskstat.sh" > $CONFFN
echo "# If you want add DISK INSTANCES, edit carefully, must use space as seperater." >> $CONFFN
echo "DISKS='$DISKS'" >> $CONFFN
fi
. $CONFFN
if [ "$DISKS" == "" ]; then
echo "Cannot determine disks."
exit 1
fi
TEMPFN="/tmp/rrdtool_dskstat_status_$$"
RRDCREATECMD=""
DATA=""
RRDGRAPHCMD=""
NUM=0
for DISKID in $DISKS; do
IDX=$NUM
NUM=`expr $NUM + 1`
FN="$FNPREFIX$DISKID$FNSUFFIX"
STATI="::$DISKID"
DATA=`kstat -p \
$STATI:nread $STATI:nwritten \
$STATI:reads $STATI:writes \
$STATI:rtime $STATI:wtime \
$STATI:rlentime $STATI:wlentime \
$STATI:rcnt $STATI:wcnt \
$STATI:rlastupdate $STATI:wlastupdate \
| awk '{print $2}' \
| awk -F. '{print $1}' \
| sed -n -e 'N;N;N;N;N;N;N;N;N;N;N;s/\n/\:/g;p'`
if [ ! -f $FN ]; then
rrdtool create $FN \
--step 60 \
"DS:NREAD:DERIVE:600:0:U" \
"DS:NWRITE:DERIVE:600:0:U" \
"DS:READ:DERIVE:600:0:U" \
"DS:WRITE:DERIVE:600:0:U" \
"DS:RTIME:DERIVE:600:0:U" \
"DS:WTIME:DERIVE:600:0:U" \
"DS:RLENTIME:DERIVE:600:0:U" \
"DS:WLENTIME:DERIVE:600:0:U" \
"DS:RCNT:GAUGE:600:0:U" \
"DS:WCNT:GAUGE:600:0:U" \
"DS:RLASTUPDATE:DERIVE:600:0:U" \
"DS:WLASTUPDATE:DERIVE:600:0:U" \
"RRA:AVERAGE:0.5:1:1440" \
"RRA:AVERAGE:0.5:5:1440" \
"RRA:AVERAGE:0.5:30:800" \
"RRA:AVERAGE:0.5:120:800" \
"RRA:AVERAGE:0.5:1440:800"
fi
rrdupdate $FN N:$DATA
function graphDsk {
rrdtool graph $FNPREFIX${DISKID}_opr_$2.png \
--start $1 \
--vertical-label "Bytes/s" \
--title "Disk Stat ${DISKID}@$HOSTNAME" \
--width 500 \
--height 100 \
--base 1024 \
--right-axis-label 'Operations/s' \
--right-axis 0.0001:0 \
--lower-limit 0 \
--alt-autoscale-max \
--disable-rrdtool-tag \
"DEF:NREAD=$FN:NREAD:AVERAGE" \
"DEF:NWRITE=$FN:NWRITE:AVERAGE" \
"DEF:READ=$FN:READ:AVERAGE" \
"DEF:WRITE=$FN:WRITE:AVERAGE" \
"CDEF:CNREAD=NREAD" \
"CDEF:CNWRITE=NWRITE" \
"CDEF:CREAD=READ,10000,*" \
"CDEF:CWRITE=WRITE,10000,*" \
"LINE:CNREAD#EA644A:Read bytes" \
"GPRINT:NREAD:LAST:%.1lf %S/s" \
"LINE:CNWRITE#54EC48:Write bytes" \
"GPRINT:NWRITE:LAST:%.1lf %S/s" \
"LINE:CREAD#EC9D48:Read" \
"GPRINT:READ:LAST:%.1lf" \
"LINE:CWRITE#7648EC:Write" \
"GPRINT:WRITE:LAST:%.1lf" \
> /dev/null
rrdtool graph $FNPREFIX${DISKID}_que_$2.png \
--start $1 \
--vertical-label "Queue Waits" \
--title "Disk Stat Queue Waits ${DISKID}@$HOSTNAME" \
--width 500 \
--height 100 \
--base 1024 \
--lower-limit 0 \
--alt-autoscale-max \
--disable-rrdtool-tag \
"DEF:RCNT=$FN:RCNT:AVERAGE" \
"DEF:WCNT=$FN:WCNT:AVERAGE" \
"LINE:RCNT#EA644A:Read Queue Counts" \
"GPRINT:RCNT:LAST:%.0lf" \
"LINE:WCNT#54EC48:Write Queue Counts" \
"GPRINT:WCNT:LAST:%.0lf" \
> /dev/null
rrdtool graph $FNPREFIX${DISKID}_hrt_$2.png \
--start $1 \
--vertical-label "nano seconds/s" \
--title "Disk Stat Time ${DISKID}@$HOSTNAME" \
--width 500 \
--height 100 \
--base 1024 \
--lower-limit 0 \
--alt-autoscale-max \
--disable-rrdtool-tag \
"DEF:RTIME=$FN:RTIME:AVERAGE" \
"DEF:WTIME=$FN:WTIME:AVERAGE" \
"DEF:RLENTIME=$FN:RLENTIME:AVERAGE" \
"DEF:WLENTIME=$FN:WLENTIME:AVERAGE" \
"CDEF:GRTIME=RTIME,1000,*" \
"CDEF:GWTIME=WTIME,1000,*" \
"LINE:RTIME#EA644A:R Time" \
"GPRINT:GRTIME:LAST:%.0lf ps/s" \
"LINE:WTIME#54EC48:W Time" \
"GPRINT:GWTIME:LAST:%.0lf ps/s" \
"LINE:RLENTIME#EC9D48:R LenTime" \
"LINE:WLENTIME#7648EC:W LenTime" \
> /dev/null
}
graphDsk -1d day
MI=`date +'%M'`
if [ "$MI" -lt "5" ]; then
graphDsk -1w week
graphDsk -1m month
graphDsk -1y year
fi
done
rm -f $TEMPFN


apachectl status명령의 결과를 사용하며, 초당 요청 정보와 초당 사용량, MPM의 클라이언트 사용량을 그려주는데, mod_status가 활성화 되어있어야 하며, lynx(1) (윈디하나의 솔라나라: lynx 참조)가 설치되어있어야 한다. mod_status를 활성화 하는 방법은 Apache Module mod_status를 읽어보자.
rrdtool_apache.sh | (4,855 바이트) |
#!/bin/bash
# Monitoring Apache for Solaris, Linux
# WindyHana's Solanara: RRDtool
# http://www.solanara.net/solanara/rrdtool
# last update: 2014.04.07
PATH=/bin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/rrdtool/bin:/opt/rrdtool/bin
LANG=C
if [ "$RRDTOOL_BIN" == "" ]; then
RRDTOOL_BIN=`which rrdtool`
if [ "$RRDTOOL_BIN" == "" ]; then
echo 'Cannot find RRDTool. Set $RRDTOOL_BIN env.'
exit 1;
fi
fi
if [ "$RRDTOOL_HOME" != "" ]; then
cd "$RRDTOOL_HOME"
else
cd `dirname $0`
fi
FNPREFIX="rrdtool_apache"
if [ "$LYNX" == "" ]; then
LYNX=`which lynx`
if [ ! -x "$LYNX" ]; then
echo 'Cannot run Lynx. Set $LYNX env.'
exit 1
fi
fi
if [ "$STATUSURL" == "" ]; then
STATUSURL="http://localhost/server-status"
fi
FN="$FNPREFIX.rrd"
if [ ! -f $FN ]; then
rrdtool create $FN \
--step 60 \
"DS:Accesses:DERIVE:600:0:U" \
"DS:kBytes:DERIVE:600:0:U" \
"DS:CPULoad:GAUGE:600:0:U" \
"DS:Uptime:GAUGE:600:0:U" \
"DS:ReqPerSec:GAUGE:600:0:U" \
"DS:BytesPerSec:GAUGE:600:0:U" \
"DS:BytesPerReq:GAUGE:600:0:U" \
"DS:BusyWorkers:GAUGE:600:0:U" \
"DS:IdleWorkers:GAUGE:600:0:U" \
"RRA:AVERAGE:0.5:1:1440" \
"RRA:AVERAGE:0.5:5:1440" \
"RRA:AVERAGE:0.5:30:800" \
"RRA:AVERAGE:0.5:120:800" \
"RRA:AVERAGE:0.5:1440:800"
fi
TMPFN=/tmp/rrdtool_apache_$$
$LYNX -dump $STATUSURL?auto > $TMPFN
if [ "$?" != "0" ]; then
echo "Cannot retrieve httpd status. '$STATUSURL' is invalid?";
rm $TMPFN
exit 1
fi;
#DATA=`cat $TMPFN | sed -n -e 'N;N;N;N;N;N;N;N;s/\n/ /g;p' | head -1 | awk '{printf("%d:%d:%f:%d:%f:%f:%f:%d:%d", $3, $6, $8, $10, $12, $14, $16, $18, $20)}'`
Accesses=`cat $TMPFN | grep 'Total Accesses' | awk '{printf("%d", $3)}'`
kBytes=`cat $TMPFN | grep 'Total kBytes' | awk '{printf("%d", $3)}'`
CPULoad=`cat $TMPFN | grep 'Load1:' | awk '{printf("%f", $2)}'`
Uptime=`cat $TMPFN | grep 'ServerUptimeSeconds' | awk '{printf("%d", $2)}'`
ReqPerSec=`cat $TMPFN | grep 'ReqPerSec' | awk '{printf("%f", $2)}'`
BytesPerSec=`cat $TMPFN | grep 'BytesPerSec' | tail -1 | awk '{printf("%f", $2)}'`
BytesPerReq=`cat $TMPFN | grep 'BytesPerReq' | tail -1 | awk '{printf("%f", $2)}'`
BusyWorkers=`cat $TMPFN | grep 'BusyWorkers' | tail -1 | awk '{printf("%d", $2)}'`
IdleWorkers=`cat $TMPFN | grep 'IdleWorkers' | tail -1 | awk '{printf("%d", $2)}'`
DATA="$Accesses:$kBytes:$CPULoad:$Uptime:$ReqPerSec:$BytesPerSec:$BytesPerReq:$BusyWorkers:$IdleWorkers"
rm $TMPFN
FACTOR=`echo $DATA | awk -F: '{print $7 / 1024}'`
#FACTOR="0.01"
# 484392:8276985:0.657552:776624:0.623715:10913.400000:17497.500000:1:249
# or use support/log_server_status
$RRDTOOL_BIN update $FN N:$DATA
if [ "$?" != "0" ]; then
echo "Cannot update $FN by previous error.";
exit;
fi
function graphApache {
$RRDTOOL_BIN graph ${FNPREFIX}_sec_$2.png \
--start $1 \
--vertical-label "Requests/s" \
--title "Apache httpd Transmit status" \
--width 500 \
--height 100 \
--lower-limit 0 \
--right-axis-label 'KBytes/s' \
--right-axis $FACTOR:0 \
--alt-autoscale-max \
--disable-rrdtool-tag \
"DEF:kBytes=$FN:kBytes:AVERAGE" \
"DEF:Accesses=$FN:Accesses:AVERAGE" \
"DEF:Uptime=$FN:Uptime:AVERAGE" \
"CDEF:ckBytes=Uptime,PREV(Uptime),GT,kBytes,UNKN,IF,$FACTOR,/" \
"CDEF:cAccesses=Uptime,PREV(Uptime),GT,Accesses,UNKN,IF" \
"CDEF:gBytes=kBytes,1024,*" \
"CDEF:gAccesses=Accesses" \
"AREA:ckBytes#24BC14: Transfered/s" \
"GPRINT:gBytes:LAST:%.1lf %S" \
"LINE:cAccesses#EA644A:Requests/s" \
"GPRINT:gAccesses:LAST:%.1lf" \
> /dev/null
$RRDTOOL_BIN graph ${FNPREFIX}_req_$2.png \
--start $1 \
--vertical-label "Bytes/Requests" \
--title "Apache httpd status" \
--width 500 \
--height 100 \
--lower-limit 0 \
--right-axis-label 'Bytes/Requests' \
--right-axis 1:0 \
--base 1024 \
--alt-autoscale-max \
--disable-rrdtool-tag \
"DEF:BytesPerReq=$FN:BytesPerReq:AVERAGE" \
"CDEF:cBytesPerReq=BytesPerReq" \
"AREA:cBytesPerReq#68E4FC:Bytes/Requests" \
"LINE:cBytesPerReq#1598C3" \
"GPRINT:cBytesPerReq:AVERAGE:%.2lf %S" \
> /dev/null
$RRDTOOL_BIN graph ${FNPREFIX}_worker_$2.png \
--start $1 \
--vertical-label "Workers" \
--title "Apache httpd Worker status" \
--width 500 \
--height 100 \
--lower-limit 0 \
--right-axis-label 'Workers' \
--right-axis 1:0 \
--alt-autoscale-max \
--disable-rrdtool-tag \
"DEF:BusyWorkers=$FN:BusyWorkers:AVERAGE" \
"DEF:IdleWorkers=$FN:IdleWorkers:AVERAGE" \
"CDEF:cBusyWorkers=BusyWorkers" \
"CDEF:cIdleWorkers=IdleWorkers" \
"CDEF:cTotalWorkers=IdleWorkers,BusyWorkers,+" \
"AREA:cBusyWorkers#7648EC:Busy Workers" \
"GPRINT:cBusyWorkers:AVERAGE:%.0lf" \
"AREA:cIdleWorkers#68E4FC:Idle Workers:STACK" \
"GPRINT:cIdleWorkers:AVERAGE:%.0lf" \
"LINE:cTotalWorkers#EA644A" \
> /dev/null
}
graphApache -1d day
MI=`date +'%M'`
if [ "$MI" -lt "5" ]; then
graphApache -1w week
graphApache -1m month
graphApache -1y year
fi



rrdtool_growth.sh | (2,225 바이트) |
#!/bin/bash
# Monitoring File, Directory Growth for Solaris, Linux
# WindyHana's Solanara: RRDtool
# http://www.solanara.net/solanara/rrdtool
# last update: 2014.12.10
PATH=/bin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/rrdtool/bin:/opt/rrdtool/bin
LANG=C
if [ "$RRDTOOL_BIN" == "" ]; then
RRDTOOL_BIN=`which rrdtool`
if [ "$RRDTOOL_BIN" == "" ]; then
echo "Cannot find RRDTool. Set $RRDTOOL_BIN env."
exit 1;
fi
fi
if [ "$RRDTOOL_HOME" != "" ]; then
cd "$RRDTOOL_HOME"
else
cd `dirname $0`
fi
MI=`date +'%M'`
MI=$(expr $MI + 0)
HR=`date +'%H'`
HR=$(expr $HR + 0)
let MOD5=$MI%5;
let MOD30=$MI%30;
if [ "$1" == "" ]; then
echo "Usage: $0 rrdsuffix filepath[:filepath[:filepath[:filepath]]]";
exit 1;
fi
KEY="$1"
GROWTHPATH="$2"
FNPREFIX="rrdtool_growth_$KEY"
FNRRD="$FNPREFIX.rrd"
COLORS=("" "#EA644A" "#54EC48" "#48C4EC" "#DE48EC" "#7648EC")
OIFS="$IFS"
IFS=':' read -a GROWTHPATHARR <<< "$GROWTHPATH"
IFS="$OIFS"
N=0;
for GROWTHFILE in "${GROWTHPATHARR[@]}"; do
N=`expr $N + 1`
FN=`basename $GROWTHFILE`
if [ -f "$GROWTHFILE" ]; then
GROWTHSIZE=`ls -l "$GROWTHFILE" | awk -F" " '{ print $5 }'`
elif [ -d "$GROWTHFILE" ]; then
GROWTHSIZE=`du -sk "$GROWTHFILE" | awk '{print $1}'`
GROWTHSIZE=`expr $GROWTHSIZE "*" 1024`
else
GROWTHSIZE="-"
fi;
COLOR=${COLORS[N]}
CREATEDATA="$CREATEDATA DS:file$N:DERIVE:600:0:U"
GRAPHDATA="$GRAPHDATA DEF:file$N=$FNRRD:file$N:AVERAGE LINE:file$N$COLOR:$FN"
DATA="$DATA:$GROWTHSIZE"
done
if [ ! -f $FNRRD ]; then
rrdtool create $FNRRD \
--step 60 \
$CREATEDATA \
"RRA:AVERAGE:0.5:1:1440" \
"RRA:AVERAGE:0.5:5:1440" \
"RRA:AVERAGE:0.5:30:800" \
"RRA:AVERAGE:0.5:120:800" \
"RRA:AVERAGE:0.5:1440:800"
fi
rrdtool update $FNRRD N$DATA
function graphFile {
$RRDTOOL_BIN graph ${FNPREFIX}_$2.png \
--start $1 \
--vertical-label "bytes/s" \
--title "File/Directory Growth ($KEY)" \
--width 500 \
--height 100 \
--lower-limit 0 \
--alt-autoscale-max \
--disable-rrdtool-tag \
$GRAPHDATA \
> /dev/null
}
graphFile -1d day
if [ "$MOD5" == "0" ]; then
graphFile -1d day
fi
if [ "$MOD30" == "0" ]; then
graphFile -1w week
fi
if [ "$MI" == "0" ]; then
graphFile -1m month
fi
if [ "$HR" == "0" ]; then
graphFile -1y year
fi
rrdtool_mysql.sh | (9,183 바이트) |
#!/bin/bash
# MySQL Query usage
# WindyHana's Solanara: RRDtool
# http://www.solanara.net/solanara/rrdtool
# last update: 2018.12.10
PATH=/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/rrdtool/bin:/opt/rrdtool/bin:/usr/local/mysql/bin
LANG=C
if [ "$RRDTOOL_BIN" == "" ]; then
RRDTOOL_BIN=`which rrdtool`
if [ "$RRDTOOL_BIN" == "" ]; then
echo "Cannot find RRDTool. Set $RRDTOOL_BIN env."
exit 1;
fi
fi
if [ "$RRDTOOL_HOME" != "" ]; then
cd "$RRDTOOL_HOME"
else
cd `dirname $0`
fi
MI=`date +'%M'`
MI=$(expr $MI + 0)
HR=`date +'%H'`
HR=$(expr $HR + 0)
let MOD5=$MI%5;
let MOD30=$MI%30;
#MYSQLLOGINSTR=MySQL login command here
if [ "$MYSQL_BIN" == "" ]; then
MYSQL_BIN=`which mysql`
fi
if [ "$QUESTIONSFACTOR" == "" ]; then
QUESTIONSFACTOR=2
fi
if [ "$ROWSFACTOR" == "" ]; then
ROWSFACTOR=100
fi
# Set MySQL login string
if [ "$MYSQLLOGINSTR" == "" ]; then
echo Must be set MYSQLLOGINSTR env
echo MYSQLLOGINSTR=\"--user=username --password=password --host==localhost --port=3306\"\; export MYSQLLOGINSTR
exit 1
fi
STATUSFN="/tmp/rrdtool_mysql_status_$$"
GLOBALSTATUSFN="/tmp/rrdtool_mysql_globalstatus_$$"
VARFN="/tmp/rrdtool_mysql_var_$$"
# Get Total Questions and Queries Avg.
echo "status" | $MYSQL_BIN $MYSQLLOGINSTR > $STATUSFN
echo "show global status" | $MYSQL_BIN $MYSQLLOGINSTR > $GLOBALSTATUSFN
echo "show variables" | $MYSQL_BIN $MYSQLLOGINSTR > $VARFN
QUERIES=`echo "show global status like 'Com\_%'" | $MYSQL_BIN $MYSQLLOGINSTR | sed 1d | awk '{sum += $2} END { print sum }'`
QUESTIONS=`cat $STATUSFN | grep Questions | awk -e '{print $4}'`
QUERYSAVG=`cat $STATUSFN | grep "Queries per second avg" | awk -e '{print $20}'`
ROWS_READ=`cat $GLOBALSTATUSFN | grep 'Innodb_rows_read' | awk -e '{print $2}'`
ROWS_INSERT=`cat $GLOBALSTATUSFN | grep 'Innodb_rows_inserted' | awk -e '{print $2}'`
ROWS_UPDATE=`cat $GLOBALSTATUSFN | grep 'Innodb_rows_updated' | awk -e '{print $2}'`
ROWS_DELETE=`cat $GLOBALSTATUSFN | grep 'Innodb_rows_deleted' | awk -e '{print $2}'`
QUERYS_SELECT=`cat $GLOBALSTATUSFN | grep 'Com_select' | head -1 | awk -e '{print $2}'`
QUERYS_INSERT=`cat $GLOBALSTATUSFN | grep 'Com_insert' | head -1 | awk -e '{print $2}'`
QUERYS_INSERTSELECT=`cat $GLOBALSTATUSFN | grep 'Com_insert_select' | head -1 | awk -e '{print $2}'`
QUERYS_UPDATE=`cat $GLOBALSTATUSFN | grep 'Com_update' | head -1 | awk -e '{print $2}'`
QUERYS_UPDATEMULTI=`cat $GLOBALSTATUSFN | grep 'Com_update_multi' | head -1 | awk -e '{print $2}'`
QUERYS_DELETE=`cat $GLOBALSTATUSFN | grep 'Com_delete' | head -1 | awk -e '{print $2}'`
QUERYS_DELETEMULTI=`cat $GLOBALSTATUSFN | grep 'Com_delete_multi' | head -1 | awk -e '{print $2}'`
# Query Cache Supported?
QCACHE=`cat $VARFN | grep 'query_cache_type' | awk -e '{print $2}'`
if [ "$QCACHE" == "ON" ]; then
QCACHE_TOTAL=`cat $VARFN | grep 'query_cache_limit' | awk -e '{print $2}'`
QCACHE_FREE=`cat $GLOBALSTATUSFN | grep 'Qcache_free_memory' | awk -e '{print $2}'`
QCACHE_TOTALBLOCK=`cat $GLOBALSTATUSFN | grep 'Qcache_total_blocks' | head -1 | awk -e '{print $2}'`
QCACHE_FREEBLOCK=`cat $GLOBALSTATUSFN | grep 'Qcache_free_blocks' | head -1 | awk -e '{print $2}'`
QCACHE_QUERIES=`cat $GLOBALSTATUSFN | grep 'Qcache_queries_in_cache' | head -1 | awk -e '{print $2}'`
QCACHE_HIT=`cat $GLOBALSTATUSFN | grep 'Qcache_hits' | head -1 | awk -e '{print $2}'`
QCACHE_NOTCACHED=`cat $GLOBALSTATUSFN | grep 'Qcache_not_cached' | head -1 | awk -e '{print $2}'`
QCACHE_INSERTS=`cat $GLOBALSTATUSFN | grep 'Qcache_inserts' | head -1 | awk -e '{print $2}'`
QCACHE_PRUNES=`cat $GLOBALSTATUSFN | grep 'Qcache_lowmem_prunes' | head -1 | awk -e '{print $2}'`
else
QCACHE_TOTAL=0
QCACHE_FREE=0
QCACHE_TOTALBLOCK=0
QCACHE_FREEBLOCK=0
QCACHE_QUERIES=0
QCACHE_HIT=0
QCACHE_NOTCACHED=0
QCACHE_INSERTS=0
QCACHE_PRUNES=0
fi
rm $GLOBALSTATUSFN
rm $STATUSFN
rm $VARFN
FNRRD="rrdtool_mysql.rrd"
if [ ! -f $FNRRD ]; then
$RRDTOOL_BIN create $FNRRD \
--step 60 \
"DS:queriesavg:GAUGE:120:0:U" \
"DS:questions:DERIVE:120:0:U" \
"DS:rowsread:DERIVE:120:0:U" \
"DS:rowsinsert:DERIVE:120:0:U" \
"DS:rowsupdate:DERIVE:120:0:U" \
"DS:rowsdelete:DERIVE:120:0:U" \
"DS:queriesselect:DERIVE:120:0:U" \
"DS:queriesinsert:DERIVE:120:0:U" \
"DS:queriesupdate:DERIVE:120:0:U" \
"DS:queriesdelete:DERIVE:120:0:U" \
"DS:queries:DERIVE:120:0:U" \
"DS:qcachetotal:GAUGE:120:0:U" \
"DS:qcachefree:GAUGE:120:0:U" \
"DS:qcachetotalblock:GAUGE:120:0:U" \
"DS:qcachefreeblock:GAUGE:120:0:U" \
"DS:qcachequeries:GAUGE:120:0:U" \
"DS:qcachehit:DERIVE:120:0:U" \
"DS:qcachenotcached:DERIVE:120:0:U" \
"DS:qcacheinserts:DERIVE:120:0:U" \
"DS:qcacheprunes:DERIVE:120:0:U" \
"RRA:AVERAGE:0.5:1:1440" \
"RRA:AVERAGE:0.5:5:1440" \
"RRA:AVERAGE:0.5:30:800" \
"RRA:AVERAGE:0.5:120:800" \
"RRA:AVERAGE:0.5:1440:800"
fi
DATA=$QUESTIONS:$ROWS_READ:$ROWS_INSERT:$ROWS_UPDATE:$ROWS_DELETE:$QUERYS_SELECT:$QUERYS_INSERT:$QUERYS_UPDATE:$QUERYS_DELETE:$QUERIES
CACHEDATA=$QCACHE_TOTAL:$QCACHE_FREE:$QCACHE_TOTALBLOCK:$QCACHE_FREEBLOCK:$QCACHE_QUERIES:$QCACHE_HIT:$QCACHE_NOTCACHED:$QCACHE_INSERTS:$QCACHE_PRUNES
$RRDTOOL_BIN update $FNRRD N:$QUERYSAVG:$DATA:$CACHEDATA
#for debug
#DT=`date`
#TS=`perl -e 'print time(), "\n" '`
#echo $DT $TS N:$QUERYSAVG:$DATA1:$DATA1 >> /tmp/rrdtool_mysql.data
function graphMySQL {
$RRDTOOL_BIN graph rrdtool_mysql_query_$2.png \
--start $1 \
--alt-autoscale-max \
--vertical-label "Queries/s" \
--title "MySQL Queries ($HOSTNAME)" \
--width 500 \
--height 100 \
--lower-limit 0 \
--right-axis-label 'Questions/s' \
--right-axis $QUESTIONSFACTOR:0 \
--disable-rrdtool-tag \
"DEF:cntquestions=$FNRRD:questions:AVERAGE" \
"DEF:cntqueries=$FNRRD:queries:AVERAGE" \
"DEF:cntselect=$FNRRD:queriesselect:AVERAGE" \
"DEF:cntinsert=$FNRRD:queriesinsert:AVERAGE" \
"DEF:cntupdate=$FNRRD:queriesupdate:AVERAGE" \
"DEF:cntdelete=$FNRRD:queriesdelete:AVERAGE" \
"CDEF:cquestions=cntquestions,$QUESTIONSFACTOR,/" \
"CDEF:cquestionsgprint=cntquestions" \
"CDEF:cqueries=cntqueries" \
"CDEF:cselect=cntselect" \
"CDEF:cinsert=cntinsert" \
"CDEF:cupdate=cntupdate" \
"CDEF:cdelete=cntdelete" \
"AREA:cselect#FF9090:select" \
"AREA:cinsert#00FF00:insert:STACK" \
"AREA:cupdate#8080FF:update:STACK" \
"AREA:cdelete#FFFF00:delete:STACK" \
"LINE:cqueries#4D18E4:Queries/s" \
"GPRINT:cqueries:AVERAGE:%.1lf" \
"LINE:cquestions#A0A0A0:Questions/s" \
"GPRINT:cquestionsgprint:AVERAGE:%.1lf" \
> /dev/null
$RRDTOOL_BIN graph rrdtool_mysql_rows_$2.png \
--start $1 \
--alt-autoscale-max \
--vertical-label "Rows Affected/s" \
--title "MySQL Query Rows ($HOSTNAME)" \
--width 500 \
--height 100 \
--lower-limit 0 \
--right-axis-label 'Rows read/s' \
--right-axis $ROWSFACTOR:0 \
--disable-rrdtool-tag \
"DEF:read=$FNRRD:rowsread:AVERAGE" \
"DEF:insert=$FNRRD:rowsinsert:AVERAGE" \
"DEF:update=$FNRRD:rowsupdate:AVERAGE" \
"DEF:delete=$FNRRD:rowsdelete:AVERAGE" \
"CDEF:cread=read,$ROWSFACTOR,/" \
"CDEF:cinsert=insert" \
"CDEF:cupdate=update" \
"CDEF:cdelete=delete" \
"LINE:cread#7648EC:Rows read" \
"AREA:cinsert#EA644A:Rows insert" \
"AREA:cupdate#54EC48:Rows update:STACK" \
"AREA:cdelete#48C4EC:Rows delete:STACK" \
> /dev/null
if [ "$QCACHE" == "ON" ]; then
$RRDTOOL_BIN graph rrdtool_mysql_cache_$2.png \
--start $1 \
--vertical-label "Cache (bytes)" \
--title "MySQL Query Cache ($HOSTNAME)" \
--width 500 \
--height 100 \
--lower-limit 0 \
--base 1024 \
--right-axis-label "Queries in cache" \
--right-axis 0.0009765625:0 \
--right-axis-format %.0lf \
--alt-autoscale-max \
--disable-rrdtool-tag \
"DEF:qcachetotal=$FNRRD:qcachetotal:AVERAGE" \
"DEF:qcachefree=$FNRRD:qcachefree:AVERAGE" \
"DEF:qcachequeries=$FNRRD:qcachequeries:AVERAGE" \
"CDEF:cqcachetotal=qcachetotal" \
"CDEF:cqcacheuse=qcachetotal,qcachefree,-" \
"CDEF:cqcachequeries=qcachequeries,1024,*" \
"LINE:cqcachetotal#7648EC:Cache Total" \
"AREA:cqcacheuse#54EC48:Cache Used" \
"LINE:cqcachequeries#EA644A:Queries in cache" \
> /dev/null
$RRDTOOL_BIN graph rrdtool_mysql_ratio_$2.png \
--start $1 \
--vertical-label "counts/s" \
--title "MySQL Query Ratio ($HOSTNAME)" \
--width 500 \
--height 100 \
--lower-limit 0 \
--right-axis-label "counts/s" \
--right-axis 1:0 \
--alt-autoscale-max \
--disable-rrdtool-tag \
"DEF:qcachehit=$FNRRD:qcachehit:AVERAGE" \
"DEF:qcachenotcached=$FNRRD:qcachenotcached:AVERAGE" \
"DEF:qcacheinserts=$FNRRD:qcacheinserts:AVERAGE" \
"DEF:qcacheprunes=$FNRRD:qcacheprunes:AVERAGE" \
"CDEF:cqcachehit=qcachehit" \
"CDEF:cqcachenotcached=qcachenotcached" \
"CDEF:cqcacheinserts=qcacheinserts" \
"CDEF:cqcacheprunes=qcacheprunes" \
"LINE:cqcachenotcached#EA644A:Cache Miss" \
"LINE:cqcacheinserts#54EC48:Cache Inserts" \
"LINE:cqcacheprunes#DE48EC:Cache Prunes" \
"LINE:cqcachehit#7648EC:Cache Hits" \
> /dev/null
fi
}
if [ "$MOD5" == "0" ]; then
graphMySQL -1d day
fi
if [ "$MOD30" == "0" ]; then
graphMySQL -1w week
fi
if [ "$MI" == "0" ]; then
graphMySQL -1m month
fi
if [ "$HR" == "0" ]; then
graphMySQL -1y year
fi




rrdtool_pi.sh | (6,085 바이트) |
#!/bin/bash
# Monitoring Raspberry Pi
# WindyHana's Solanara: RRDtool
# http://www.solanara.net/solanara/rrdtool
# last update: 2016.06.18
PATH=/bin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/rrdtool/bin:/opt/rrdtool/bin
LANG=C
if [ "$RRDTOOL_BIN" == "" ]; then
RRDTOOL_BIN=`which rrdtool`
if [ "$RRDTOOL_BIN" == "" ]; then
echo "Cannot find RRDTool. Set $RRDTOOL_BIN env or run 'sudo apt-get install rrdtool'"
exit 1;
fi
fi
if [ -f /etc/os-release ]; then
ISPI=`cat /etc/os-release | grep -e "^ID=raspbian"`
if [ "$ISPI" != "ID=raspbian" ]; then
echo "This script run only on Raspbian"
exit 1;
fi
else
echo "This script run only on Raspbian"
exit 1;
fi
if [ "$RRDTOOL_HOME" != "" ]; then
cd "$RRDTOOL_HOME"
else
cd `dirname $0`
fi
FNPREFIX="rrdtool_pi_"
FNCONF="rrdtool_pi.conf"
FNRRDSUFFIX=".rrd"
FNGRASUFFIX=".png"
if [ -f "$FNCONF" ]; then
. "$FNCONF"
fi
if [ "$PI_TEMP" == "" ]; then
if [ -x "/usr/bin/vcgencmd" ]; then
PI_TEMP=`/usr/bin/vcgencmd measure_temp | cut -d'=' -f2 | cut -d"'" -f1`
else
echo "Cannot get Temp";
exit 1;
fi
fi
if [ "$PI_MEM" == "" ]; then
if [ -x "/usr/bin/free" ]; then
PI_MEM=`/usr/bin/free -o | tail -2 | sed -n -e 'N;s/\n/ \:/g;p' | awk '{printf("%d:%d:%d:%d:%d:%d:%d:%d:%d", $2, $3, $4, $5, $6, $7, $9, $10, $11)}'`
else
echo "Cannot get MEM";
exit 1;
fi
fi
if [ "$PI_VOLTS" == "" ]; then
if [ -x "/usr/bin/vcgencmd" ]; then
V1=`/usr/bin/vcgencmd measure_volts core | cut -d"=" -f2 | cut -d"V" -f1`
V2=`/usr/bin/vcgencmd measure_volts sdram_c | cut -d"=" -f2 | cut -d"V" -f1`
V3=`/usr/bin/vcgencmd measure_volts sdram_i | cut -d"=" -f2 | cut -d"V" -f1`
V4=`/usr/bin/vcgencmd measure_volts sdram_p | cut -d"=" -f2 | cut -d"V" -f1`
PI_VOLTS="$V1:$V2:$V3:$V4"
else
echo "Cannot get Volts";
exit 1;
fi
fi
FNRRD_TEMP="${FNPREFIX}temp${FNRRDSUFFIX}"
FNRRD_MEM="${FNPREFIX}mem${FNRRDSUFFIX}"
FNRRD_VOLTS="${FNPREFIX}volts${FNRRDSUFFIX}"
if [ ! -f "$FNRRD_TEMP" ]; then
$RRDTOOL_BIN create "$FNRRD_TEMP" \
--step 60 \
"DS:TEMPERATURE:GAUGE:600:0:U" \
"RRA:AVERAGE:0.5:1:1440" \
"RRA:AVERAGE:0.5:5:1440" \
"RRA:AVERAGE:0.5:30:800" \
"RRA:AVERAGE:0.5:120:800" \
"RRA:AVERAGE:0.5:1440:800"
fi
if [ ! -f "$FNRRD_MEM" ]; then
$RRDTOOL_BIN create "$FNRRD_MEM" \
--step 60 \
"DS:TOTAL:GAUGE:600:0:U" \
"DS:USED:GAUGE:600:0:U" \
"DS:FREE:GAUGE:600:0:U" \
"DS:SHARED:GAUGE:600:0:U" \
"DS:BUFFERS:GAUGE:600:0:U" \
"DS:CACHED:GAUGE:600:0:U" \
"DS:SWAPTOTAL:GAUGE:600:0:U" \
"DS:SWAPUSED:GAUGE:600:0:U" \
"DS:SWAPFREE:GAUGE:600:0:U" \
"RRA:AVERAGE:0.5:1:1440" \
"RRA:AVERAGE:0.5:5:1440" \
"RRA:AVERAGE:0.5:30:800" \
"RRA:AVERAGE:0.5:120:800" \
"RRA:AVERAGE:0.5:1440:800"
fi
if [ ! -f "$FNRRD_VOLTS" ]; then
$RRDTOOL_BIN create "$FNRRD_VOLTS" \
--step 60 \
"DS:CORE:GAUGE:600:0:U" \
"DS:SDRAMC:GAUGE:600:0:U" \
"DS:SDRAMI:GAUGE:600:0:U" \
"DS:SDRAMP:GAUGE:600:0:U" \
"RRA:AVERAGE:0.5:1:1440" \
"RRA:AVERAGE:0.5:5:1440" \
"RRA:AVERAGE:0.5:30:800" \
"RRA:AVERAGE:0.5:120:800" \
"RRA:AVERAGE:0.5:1440:800"
fi
$RRDTOOL_BIN update $FNRRD_TEMP N:$PI_TEMP
$RRDTOOL_BIN update $FNRRD_MEM N:$PI_MEM
$RRDTOOL_BIN update $FNRRD_VOLTS N:$PI_VOLTS
function graphPi {
$RRDTOOL_BIN graph ${FNPREFIX}temp_$2$FNGRASUFFIX \
--start $1 \
--vertical-label "SoC ('C)" \
--title "Raspberry Pi Temperature@$HOSTNAME" \
--width 500 \
--height 100 \
--lower-limit 0 \
--alt-autoscale-max \
--disable-rrdtool-tag \
"DEF:TMP=$FNRRD_TEMP:TEMPERATURE:AVERAGE" \
"CDEF:CTEMP=TMP" \
"CDEF:CTEMP85=TMP,POP,85,0,+" \
"CDEF:CAREAW=CTEMP,CTEMP85,GT,CTEMP,0,IF" \
"CDEF:CAREAU=CTEMP,CTEMP85,GT,CTEMP85,CTEMP,IF" \
"AREA:CAREAW#CC3118" \
"AREA:CAREAU#48C4EC" \
"LINE:CTEMP#1598C3:Temprature" \
"GPRINT:CTEMP:LAST:%.1lf %S" \
> /dev/null
$RRDTOOL_BIN graph ${FNPREFIX}mem_$2$FNGRASUFFIX \
--start $1 \
--vertical-label "Memory Usage" \
--title "Raspberry Pi Memory Usage@$HOSTNAME" \
--width 500 \
--height 100 \
--base 1024 \
--lower-limit 0 \
--alt-autoscale-max \
--disable-rrdtool-tag \
"DEF:TOTAL=$FNRRD_MEM:TOTAL:AVERAGE" \
"DEF:USED=$FNRRD_MEM:USED:AVERAGE" \
"DEF:FREE=$FNRRD_MEM:FREE:AVERAGE" \
"DEF:SHARED=$FNRRD_MEM:SHARED:AVERAGE" \
"DEF:BUFFERS=$FNRRD_MEM:BUFFERS:AVERAGE" \
"DEF:CACHED=$FNRRD_MEM:CACHED:AVERAGE" \
"DEF:SWAPTOTAL=$FNRRD_MEM:SWAPTOTAL:AVERAGE" \
"DEF:SWAPUSED=$FNRRD_MEM:SWAPUSED:AVERAGE" \
"DEF:SWAPFREE=$FNRRD_MEM:SWAPFREE:AVERAGE" \
"CDEF:GFREE=FREE,BUFFERS,CACHED,+,+,1024,*" \
"CDEF:CTOTAL=TOTAL,1024,*" \
"CDEF:CUSED=USED,BUFFERS,-,CACHED,-,SHARED,-,1024,*" \
"CDEF:CFREE=FREE,1024,*" \
"CDEF:CSHARED=SHARED,1024,*" \
"CDEF:CBUFFERS=BUFFERS,1024,*" \
"CDEF:CCACHED=CACHED,1024,*" \
"CDEF:CSWAPTOTAL=SWAPTOTAL,1024,*" \
"CDEF:CSWAPUSED=SWAPUSED,1024,*" \
"CDEF:CSWAPFREE=SWAPFREE,1024,*" \
"AREA:CSHARED#C9B215:Shared" \
"AREA:CUSED#ECD748:Used:STACK" \
"AREA:CBUFFERS#24BC14:Buffers:STACK" \
"AREA:CCACHED#48C4EC:Cached:STACK" \
"AREA:CFREE#1598C3:Free:STACK" \
"LINE:CSWAPTOTAL#CC3118:Swap Total" \
"LINE:CSWAPFREE#CC7016:Swap Free" \
"LINE:CTOTAL#1598C3:" \
"GPRINT:CTOTAL:LAST:Mem Total %.1lf %S" \
"GPRINT:GFREE:LAST:Mem Free %.1lf %S" \
"GPRINT:CSWAPFREE:LAST:Swap Free %.1lf %S" \
> /dev/null
$RRDTOOL_BIN graph ${FNPREFIX}volts_$2$FNGRASUFFIX \
--start $1 \
--vertical-label "Volts (V)" \
--title "Raspberry Pi Volts@$HOSTNAME" \
--width 500 \
--height 100 \
--lower-limit 0 \
--alt-autoscale-max \
--disable-rrdtool-tag \
"DEF:CORE=$FNRRD_VOLTS:CORE:AVERAGE" \
"DEF:SDRAMC=$FNRRD_VOLTS:SDRAMC:AVERAGE" \
"DEF:SDRAMI=$FNRRD_VOLTS:SDRAMI:AVERAGE" \
"DEF:SDRAMP=$FNRRD_VOLTS:SDRAMP:AVERAGE" \
"LINE:SDRAMC#4D18E4:SDRAM Controller" \
"LINE:SDRAMI#1598C3:SDRAM I/O" \
"LINE:SDRAMP#24BC14:SDRAM Physcal" \
"LINE:CORE#CC3118:Core" \
"GPRINT:CORE:LAST:%.3lf V" \
> /dev/null
}
graphPi -1d day
MI=`date +'%M'`
if [ "$MI" -lt "5" ]; then
graphPi -1w week
graphPi -1m month
graphPi -1y year
fi
rrdtool_temperature.sh | (2,777 바이트) |
#!/bin/bash
# System Temperature Gauge
# WindyHana's Solanara: RRDtool
# http://www.solanara.net/solanara/rrdtool
# last update: 2024.12.31
PATH=/bin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/rrdtool/bin:/opt/rrdtool/bin
LANG=C
if [ "$RRDTOOL_BIN" == "" ]; then
RRDTOOL_BIN=`which rrdtool`
if [ "$RRDTOOL_BIN" == "" ]; then
echo "Cannot find RRDTool. Set $RRDTOOL_BIN env."
exit 1;
fi
fi
if [ "$RRDTOOL_HOME" != "" ]; then
cd "$RRDTOOL_HOME"
else
cd `dirname $0`
fi
MI=`date +'%M'`
MI=$(expr $MI + 0)
HR=`date +'%H'`
HR=$(expr $HR + 0)
let MOD5=$MI%5;
let MOD30=$MI%30;
SENSORS_BIN=`which sensors`
if [ "$SENSORS_BIN" == "" ]; then
echo "Cannot find sensors. Install sensors. 'sudo apt-get lm-sensors' and run 'sensors-detect' "
exit 1;
fi
PECI_TEMP=`sensors | grep "PECI Agent 0 Calibration" | awk '{print $5}'`
CPU=`printf '%.1f' ${PECI_TEMP//°C}`
GPU_TEMP=`nvidia-smi --query-gpu=temperature.gpu --format=csv,noheader`
GPU=`printf '%.1f' ${GPU_TEMP}`
AUX_TEMP=`sensors | grep "AUXTIN0" | awk '{print $2}'`
SYS=`printf '%.1f' ${AUX_TEMP//°C}`
DRV_TEMP=`sensors drivetemp-scsi-0-0 -u | grep temp1_input | awk '{print $2}'`
DRV=`printf '%.1f' ${DRV_TEMP//°C}`
FNRRD="rrdtool_temperature2.rrd"
if [ ! -f $FNRRD ]; then
$RRDTOOL_BIN create $FNRRD \
--step 60 \
DS:CPU:GAUGE:600:0:U \
DS:GPU:GAUGE:600:0:U \
DS:SYS:GAUGE:600:0:U \
DS:DRV:GAUGE:600:0:U \
RRA:AVERAGE:0.5:1:1440 \
RRA:AVERAGE:0.5:5:1440 \
RRA:AVERAGE:0.5:30:800 \
RRA:AVERAGE:0.5:120:800 \
RRA:AVERAGE:0.5:1440:800 \
RRA:MAX:0.5:150:2400
fi
rrdupdate $FNRRD N:$CPU:$GPU:$SYS:$DRV
function graphTemp {
$RRDTOOL_BIN graph rrdtool_temperature2_$2.png \
--start $1 \
--title "Temperature" \
--vertical-label "°C" \
--width 400 \
--height 100 \
--lower-limit 0 \
--units-exponent 0 \
--alt-autoscale-max \
--disable-rrdtool-tag \
"DEF:CPU=$FNRRD:CPU:AVERAGE" \
"DEF:GPU=$FNRRD:GPU:AVERAGE" \
"DEF:SYS=$FNRRD:SYS:AVERAGE" \
"DEF:DRV=$FNRRD:DRV:AVERAGE" \
"CDEF:cCPU=CPU" \
"CDEF:cGPU=GPU" \
"CDEF:cSYS=SYS" \
"CDEF:cDRV=DRV" \
"LINE:cCPU#EA644A:CPU" \
"GPRINT:cCPU:LAST:%4.1lf%S°C" \
"LINE:cSYS#48C4EC:SYS" \
"GPRINT:cSYS:LAST:%4.1lf%S°C" \
"LINE:cDRV#7648EC:DRV" \
"GPRINT:cDRV:LAST:%4.1lf%S°C" \
"LINE:cGPU#24BC14:GPU" \
"GPRINT:cGPU:LAST:%4.1lf%S°C" \
"COMMENT:\\n" \
> /dev/null
}
graphTemp -1d day
if [ "$MOD30" == "0" ]; then
graphTemp -1w week
fi
if [ "$MI" == "0" ]; then
graphTemp -1m month
fi
if [ "$HR" == "0" ]; then
graphTemp -1y year
fi

rrdtool_fan.sh | (4,688 바이트) |
#!/bin/bash
# Fan RPM Gauge
# WindyHana's Solanara: RRDtool
# http://www.solanara.net/solanara/rrdtool
# last update: 2024.12.31
FAN_COUNTS=4
PATH=/bin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/rrdtool/bin:/opt/rrdtool/bin
LANG=C
if [ "$RRDTOOL_BIN" == "" ]; then
RRDTOOL_BIN=`which rrdtool`
if [ "$RRDTOOL_BIN" == "" ]; then
echo "Cannot find RRDTool. Set $RRDTOOL_BIN env."
exit 1;
fi
fi
if [ "$RRDTOOL_HOME" != "" ]; then
cd "$RRDTOOL_HOME"
else
cd `dirname $0`
fi
MI=`date +'%M'`
MI=$(expr $MI + 0)
HR=`date +'%H'`
HR=$(expr $HR + 0)
let MOD5=$MI%5;
let MOD30=$MI%30;
SENSORS_BIN=`which sensors`
if [ "$SENSORS_BIN" == "" ]; then
echo "Cannot find sensors. Install sensors. 'sudo apt-get lm-sensors' and run 'sensors-detect' "
exit 1;
fi
# read fan rpm from sensors
FAN1=`$SENSORS_BIN -u nct6793-isa-0290 | grep fan1_input | awk '{print $2}'`
FAN2=`$SENSORS_BIN -u nct6793-isa-0290 | grep fan2_input | awk '{print $2}'`
FAN3=`$SENSORS_BIN -u nct6793-isa-0290 | grep fan3_input | awk '{print $2}'`
FAN4=`$SENSORS_BIN -u nct6793-isa-0290 | grep fan4_input | awk '{print $2}'`
FAN5=U
FAN6=U
FAN7=U
FAN8=U
FNRRD="rrdtool_fan.rrd"
if [ ! -f $FNRRD ]; then
$RRDTOOL_BIN create $FNRRD \
--step 60 \
DS:FAN1:GAUGE:600:1:U \
DS:FAN2:GAUGE:600:1:U \
DS:FAN3:GAUGE:600:1:U \
DS:FAN4:GAUGE:600:1:U \
DS:FAN5:GAUGE:600:1:U \
DS:FAN6:GAUGE:600:1:U \
DS:FAN7:GAUGE:600:1:U \
DS:FAN8:GAUGE:600:1:U \
RRA:AVERAGE:0.5:1:1440 \
RRA:AVERAGE:0.5:5:1440 \
RRA:AVERAGE:0.5:30:800 \
RRA:AVERAGE:0.5:120:800 \
RRA:AVERAGE:0.5:1440:800 \
RRA:MAX:0.5:9999:2400
fi
rrdupdate $FNRRD N:$FAN1:$FAN2:$FAN3:$FAN4:$FAN5:$FAN6:$FAN7:$FAN8
function graphFan {
if [ "$FAN_COUNTS" == "4" ]; then
$RRDTOOL_BIN graph rrdtool_fan_$2.png \
--start $1 \
--title "Fan" \
--vertical-label "RPM" \
--width 400 \
--height 100 \
--lower-limit 0 \
--units-exponent 0 \
--alt-autoscale-max \
--disable-rrdtool-tag \
"DEF:FAN1=$FNRRD:FAN1:AVERAGE" \
"DEF:FAN2=$FNRRD:FAN2:AVERAGE" \
"DEF:FAN3=$FNRRD:FAN3:AVERAGE" \
"DEF:FAN4=$FNRRD:FAN4:AVERAGE" \
"DEF:FAN5=$FNRRD:FAN5:AVERAGE" \
"DEF:FAN6=$FNRRD:FAN6:AVERAGE" \
"DEF:FAN7=$FNRRD:FAN7:AVERAGE" \
"DEF:FAN8=$FNRRD:FAN8:AVERAGE" \
"CDEF:cFAN1=FAN1" \
"CDEF:cFAN2=FAN2" \
"CDEF:cFAN3=FAN3" \
"CDEF:cFAN4=FAN4" \
"CDEF:cFAN5=FAN5" \
"CDEF:cFAN6=FAN6" \
"CDEF:cFAN7=FAN7" \
"CDEF:cFAN8=FAN8" \
"LINE:cFAN1#EA644A:FAN1" \
"GPRINT:cFAN1:LAST:%4.0lf" \
"LINE:cFAN2#ECD748:FAN2" \
"GPRINT:cFAN2:LAST:%4.0lf" \
"LINE:cFAN3#48C4EC:FAN3" \
"GPRINT:cFAN3:LAST:%4.0lf" \
"LINE:cFAN4#7648EC:FAN4" \
"GPRINT:cFAN4:LAST:%4.0lf" \
"COMMENT:\\n" \
> /dev/null
else
$RRDTOOL_BIN graph rrdtool_fan_$2.png \
--start $1 \
--title "Fan" \
--vertical-label "RPM" \
--width 400 \
--height 100 \
--lower-limit 0 \
--units-exponent 0 \
--alt-autoscale-max \
--disable-rrdtool-tag \
"DEF:FAN1=$FNRRD:FAN1:AVERAGE" \
"DEF:FAN2=$FNRRD:FAN2:AVERAGE" \
"DEF:FAN3=$FNRRD:FAN3:AVERAGE" \
"DEF:FAN4=$FNRRD:FAN4:AVERAGE" \
"DEF:FAN5=$FNRRD:FAN5:AVERAGE" \
"DEF:FAN6=$FNRRD:FAN6:AVERAGE" \
"DEF:FAN7=$FNRRD:FAN7:AVERAGE" \
"DEF:FAN8=$FNRRD:FAN8:AVERAGE" \
"CDEF:cFAN1=FAN1" \
"CDEF:cFAN2=FAN2" \
"CDEF:cFAN3=FAN3" \
"CDEF:cFAN4=FAN4" \
"CDEF:cFAN5=FAN5" \
"CDEF:cFAN6=FAN6" \
"CDEF:cFAN7=FAN7" \
"CDEF:cFAN8=FAN8" \
"LINE:cFAN1#EA644A:FAN1" \
"GPRINT:cFAN1:LAST:%4.0lf" \
"LINE:cFAN2#ECD748:FAN2" \
"GPRINT:cFAN2:LAST:%4.0lf" \
"LINE:cFAN3#48C4EC:FAN3" \
"GPRINT:cFAN3:LAST:%4.0lf" \
"LINE:cFAN4#7648EC:FAN4" \
"GPRINT:cFAN4:LAST:%4.0lf" \
"COMMENT:\\n" \
"LINE:cFAN5#EC9D48:FAN5" \
"GPRINT:cFAN5:LAST:%4.0lf" \
"LINE:cFAN6#54EC48:FAN6" \
"GPRINT:cFAN6:LAST:%4.0lf" \
"LINE:cFAN7#68E4FC:FAN7" \
"GPRINT:cFAN7:LAST:%4.0lf" \
"LINE:cFAN8#DE48EC:FAN8" \
"GPRINT:cFAN8:LAST:%4.0lf" \
"COMMENT:\\n" \
> /dev/null
fi
}
graphFan -1d day
if [ "$MOD30" == "0" ]; then
graphFan -1w week
fi
if [ "$MI" == "0" ]; then
graphFan -1m month
fi
if [ "$HR" == "0" ]; then
graphFan -1y year
fi

MRTG에서 RRD파일을 생성하도록 설정했다면 MRTG에서 자동으로 RRD파일을 생성하고, 갱신하게 된다. 인터페이스의 모니터링 항목 한개당 94616 바이트의 작은 rrd 파일이 생성되며, 이 파일에 1년치 평균 데이터까지 모두 저장되어있다.
| 구분 | 저장 단위 시간 | |||
|---|---|---|---|---|
| 5분 | 30분 | 2시간 | 1일 | |
| 저장 회수 | 600 | 700 | 775 | 796 |
| 총 저장 기간 | 2일 | 14일 | 64일 | 2년 |
| 그래프 | 2일 | 1주일 | 1개월 | 1년 |
아래는 MRTG에서 RRDtool 을 사용하도록 하는 경우, 자동으로 변환되는 .rrd파일의 구조이다.
windy@wl ~ $ rrdtool info localhost_2.rrd filename = "localhost_2.rrd" rrd_version = "0003" step = 300 last_update = 1395623401 header_size = 2760 ds[ds0].index = 0 ds[ds0].type = "COUNTER" ds[ds0].minimal_heartbeat = 600 ds[ds0].min = 0.0000000000e+00 ds[ds0].max = 1.2500000000e+08 ds[ds0].last_ds = "547836283" ds[ds0].value = 0.0000000000e+00 ds[ds0].unknown_sec = 0 ds[ds1].index = 1 ds[ds1].type = "COUNTER" ds[ds1].minimal_heartbeat = 600 ds[ds1].min = 0.0000000000e+00 ds[ds1].max = 1.2500000000e+08 ds[ds1].last_ds = "3207582535" ds[ds1].value = 0.0000000000e+00 ds[ds1].unknown_sec = 0 rra[0].cf = "AVERAGE" rra[0].rows = 599 rra[0].cur_row = 1 rra[0].pdp_per_row = 1 rra[0].xff = 5.0000000000e-01 rra[0].cdp_prep[0].value = 9.9667774086e-01 rra[0].cdp_prep[0].unknown_datapoints = 0 rra[0].cdp_prep[1].value = 0.0000000000e+00 rra[0].cdp_prep[1].unknown_datapoints = 0 rra[1].cf = "AVERAGE" rra[1].rows = 700 rra[1].cur_row = 0 rra[1].pdp_per_row = 6 rra[1].xff = 5.0000000000e-01 rra[1].cdp_prep[0].value = 2.8752777778e+01 rra[1].cdp_prep[0].unknown_datapoints = 0 rra[1].cdp_prep[1].value = 1.7576666667e+01 rra[1].cdp_prep[1].unknown_datapoints = 0 rra[2].cf = "AVERAGE" rra[2].rows = 775 rra[2].cur_row = 0 rra[2].pdp_per_row = 24 rra[2].xff = 5.0000000000e-01 rra[2].cdp_prep[0].value = 2.8669861111e+01 rra[2].cdp_prep[0].unknown_datapoints = 0 rra[2].cdp_prep[1].value = 1.7910000000e+01 rra[2].cdp_prep[1].unknown_datapoints = 0 rra[3].cf = "AVERAGE" rra[3].rows = 796 rra[3].cur_row = 0 rra[3].pdp_per_row = 288 rra[3].xff = 5.0000000000e-01 rra[3].cdp_prep[0].value = 2.8746377315e+01 rra[3].cdp_prep[0].unknown_datapoints = 0 rra[3].cdp_prep[1].value = 1.7604444444e+01 rra[3].cdp_prep[1].unknown_datapoints = 0 rra[4].cf = "MAX" rra[4].rows = 600 rra[4].cur_row = 1 rra[4].pdp_per_row = 1 rra[4].xff = 5.0000000000e-01 rra[4].cdp_prep[0].value = 1.0000000000e+00 rra[4].cdp_prep[0].unknown_datapoints = 0 rra[4].cdp_prep[1].value = 0.0000000000e+00 rra[4].cdp_prep[1].unknown_datapoints = 0 rra[5].cf = "MAX" rra[5].rows = 700 rra[5].cur_row = 0 rra[5].pdp_per_row = 6 rra[5].xff = 5.0000000000e-01 rra[5].cdp_prep[0].value = 2.7753333333e+01 rra[5].cdp_prep[0].unknown_datapoints = 0 rra[5].cdp_prep[1].value = 1.7576666667e+01 rra[5].cdp_prep[1].unknown_datapoints = 0 rra[6].cf = "MAX" rra[6].rows = 775 rra[6].cur_row = 0 rra[6].pdp_per_row = 24 rra[6].xff = 5.0000000000e-01 rra[6].cdp_prep[0].value = 2.7753333333e+01 rra[6].cdp_prep[0].unknown_datapoints = 0 rra[6].cdp_prep[1].value = 1.7576666667e+01 rra[6].cdp_prep[1].unknown_datapoints = 0 rra[7].cf = "MAX" rra[7].rows = 796 rra[7].cur_row = 0 rra[7].pdp_per_row = 288 rra[7].xff = 5.0000000000e-01 rra[7].cdp_prep[0].value = 2.7753333333e+01 rra[7].cdp_prep[0].unknown_datapoints = 0 rra[7].cdp_prep[1].value = 1.7576666667e+01 rra[7].cdp_prep[1].unknown_datapoints = 0
등록된 노트가 없습니다
| RSS ATOM XHTML 5 CSS3 |
Copyright © 2004-2025 Jo HoSeok. All rights reserved. |