작성자 : 좋은진호(truefeel, http://coffeenix.net/ )
작성일 : 2008.2.13(수)~15(금)
'리눅스 터미널(CLI모드)에서 보는 실시간 시계' ( http://coffeenix.net/bbs/viewtopic.php?t=2836 )에 대해 소개한 적이 있다. 이 시계 스크립트는 터미널의 오른쪽 상단에 실시간으로 시간을 표시한다.
[ 터미널에서 시계 ] (1초단위로 시간이 바뀌면서 표시된다.)

1. 한화면 실시간 load 모니터링 개요
시계 스크립트를 응용하여, 여러 서버의 load를 한화면에서 볼 수 있도록 구현하였다. 스크립트는 다음과 같은 역할을 한다.
1) tput 명령과 ANSI코드를 이용하여 원하는 위치에 load를 표시한다.
2) 1~9초간격으로 새로운 데이터를 표시한다.
3) '로그 모니터링시 특정 문자를 highlight하기' ( http://coffeenix.net/board_view.php?bd_code=1562 , 글 좋은진호)에서 소개한 방법을 이용하여 load별로 다른 색을 표시한다.
------- --------
load 색깔
------- --------
0.0 하얀색
0.1~0.39 녹 색
0.4~0.79 파란색
0.8~이상 빨간색
------- --------
4) 숫자 1~9까지 누르면 시간 간격을 변경할 수 있다. (단, foreground로 실행시)
5) q를 누르면 종료한다. (단, foreground 실행시)
참고로 서버는 10여대 정도만 샘플로 표시하였으며, 이미지상의 서버명은 실제 사용하지 않는 임의의 서버명이다.
[ load 모니터링 샘플 화면 ]

2. 미리 준비되어 있어야할 사항
통합 로그 서버와 각 서버에서 load를 보내주도록 처리해야 한다. 구축 방법에 대해서는 간략히 얘기하고, 시간이 될 때 추가 문서를 통해 설명하겠다.
1) 각 서버에서는 logger를 활용하여 서버의 load를 syslogd로 보내준다.
LOAD="`uptime|awk -F': '{print $2}'`" logger -p 로그의레빌 -t LOAD "호스트명 -> 현재시간 load $LOAD" |
syslogd 는 load부분에 대한 로그를 통합로그로 보내지도록 설정해야 한다. 이 때 서버의 load는 5~10초 간격, 또는 더 긴 간격으로 체크하여 syslogd로 보내주면 된다.
2) 통합 로그 서버에서는 각 서버의 syslog 결과를 받을 수 있도록 설정한다. 통합서버는 FreeBSD를 권장하지만, 리눅스 서버를 사용할 경우 syslog-ng를 통해서 로그 서버 구축한다. 각 서버에서 오는 load정보는 서버별로 별도 파일에 저장하도록 하는 것이 중요하다. 예를 들어 cnx10.log, cnx22.log, cnx25.log, ... 처럼. 통합로그 서버의 서버별 로그 파일에는 다음과 같이 형태로 로그가 저장된다.
Jan 6 00:00:04 192.168.123.140 LOAD: cnx10 -> 00:00:04 load 0.07, 0.03, 0.00 |
FreeBSD에서 실행할 때는 다음 2가지 사항을 체크해야 한다.
FreeBSD의 tput은 cursor 이동을 지원하지 않으므로 OpenBSD의 소스(ftp://ftp.openbsd.org/pub/OpenBSD/src/usr.bin/tput )를 가져다가 사용해야 한다. 또한 스크립트에서 사용한 read 옵션 -n, -s는 bash에 의존적으로 동작한다. /usr/ports/shells/bash ports 디렉토리에서 설치한다.
3. load 모니터링 스크립트
* load_mon.sh 내려받기
#!/bin/bash # # system load를 실시간으로 모니터링 # # by 좋은진호(truefeel, http://coffeenix.net/ ) # 2008.2.12~ # foreground로 실행할 때는 0으로, background로 할 때는 1로 fg=0 # 데이터를 실시간으로 보여줄 간격 (초단위) sleep=2 # 오른쪽에서 몇번째 칸에 표시할 것인지 설정 cols=22 # LOGDIR="/var/log/load" TPUT="/usr/bin/tput" # color szColBk="^[[;30m"; szColBk1="^[[1;30m" # black szColRe="^[[;31m"; szColRe1="^[[1;31m" # red szColGr="^[[;32m"; szColGr1="^[[1;32m" # green szColYe="^[[;33m"; szColYe1="^[[1;33m" # yellow szColBl="^[[;34m"; szColBl1="^[[1;34m" # blue szColPu="^[[;35m"; szColPu1="^[[1;35m" # magenta(purple) szColCy="^[[;36m"; szColCy1="^[[1;36m" # cyan szColGy="^[[;37m"; szColWh="^[[1;37m" # white szInverse="^[[7m" szInvOff="^[[27m" szNormal="^[[;m" # szColLev1=$szColYe szColLev2=$szColBl szColLev3=$szColRe1 # ------------------------------------------------ # keyinput () { stty -icanon read -t $sleep -n 1 -s time stty icanon case $time in [1-9]) sleep=$time C=$((`$TPUT cols` - $cols)) $TPUT cup $row $C echo "${szInverse} Delay time : $time sec $szNormal" ;; [qQ]) echo -n "^[[u" exit 0 ;; esac } # ------------------------------------------------ # 실시간 처리 시작 # ------------------------------------------------ # foreground면 화면 clear if [ $fg -eq 0 ]; then clear fi cd $LOGDIR while : do # ------------------------------------------------ # 결과값 추출 # ------------------------------------------------ cmd_local=`uptime | awk -F': ' '{print $2}'|awk -F', ' '{print $1 "|" $2 "|" $3 " "}'` cmd=`tail -1 *.log | grep : | awk '{print $6 "|" $10 "|" $11 "|" $12 " "}'` result="LOC_|$cmd_local $cmd" # highlight result_color=`echo "$result" \ | sed \ -e "s/,//g" \ -e "s/\(0\.0\)/${szColGy}\\1/g" \ -e "s/\(0\.[1-3]\)/${szColGr}\\1/g" \ -e "s/\(0\.[4-7]\)/${szColBl1}\\1/g" \ -e "s/\(0\.[8-9]\)/${szColLev3}\\1/g" \ -e "s/\([1-9]\.\)/${szColLev3}\\1/g"` # ------------------------------------------------ # cursor 위치 저장 echo -n "^[[s" $TPUT el # cleans from position to end of line # ------------------------------------------------ # 결과값을 출력 # ------------------------------------------------ col_width=`$TPUT cols` # column 길이 #지정한 cols보다 좁으면 종료 if [ $col_width -lt $cols ]; then echo "터미널의 폭이 너무 좁습니다." exit fi C=$(($col_width - $cols)) # data를 표시할 column 위치 계산 row=0 $TPUT civis # cursor 숨김 for line in $result_color do $TPUT cup $row $C # cursor 이동 row=`expr $row + 1 ` # row 증가 echo -n "$szColYe$szInverse$line$szNormal" done $TPUT cnorm # cursor 다시 표시 # ------------------------------------------------ if [ $fg -eq 0 ]; then keyinput else # cursor 위치를 원래대로 echo -n "^[[u" sleep $sleep fi done |
foreground로 스크립트를 실행할 것이면 fg=0으로 하고, background로 실행할 때는 1로 해줘야 스크립트의 기능을 제대로 사용할 수 있다. sleep=2 는 실시간으로 조회할 데이터를 몇 초 간격으로 볼 것인가를 지정한다.
LOGDIR= : 서버 load가 저장되어 있는 로그 파일의 경로
TPUT= : tput 명령의 경로. 기본 경로는 /usr/bin/tput
상단 부분의 'szCol' 로 시작하는 변수는 색깔을 정의한 ANSI 코드이다. ^[ 문자는 ESC키를 의미한다. 쉘에서 입력할 때 Ctrl+V를 누른 후 ESC키를 누르면 입력할 수 있다.
4. 스크립트 부분별 이해
keyinput () { stty -icanon read -t $sleep -n 1 -s time stty icanon case $time in [1-9]) sleep=$time C=$((`$TPUT cols` - $cols)) $TPUT cup $row $C echo "${szInverse} Delay time : $time sec $szNormal" ;; [qQ]) echo -n "^[[u" exit 0 ;; esac } |
keyinput() 함수는 foreground로 실행할 때 키입력을 받아들이는 부분이다.
read 의 -t 옵션은 입력 대기 시간이다. -t 5라면 5초동안 입력 대기를 하게 되는데, 결국 sleep 5 를 한 것과 같은 효과를 볼 수 있다. -n 1 은 1자만 입력 받는 것이고, -s는 입력한 키를 화면에 출력하지 말라는 뜻이다. 키 입력이 생기면 바로 그 다음줄이 실행되며, 입력되지 않더라도 대기시간이 지나면 넘어가게 된다. 결국 keyinput()함수는 프로그램의 실행에 방해를 주지 않으면서 원하는 키입력을 받아들이는 역할을 하게 된다.
1~9까지 입력(즉, 1~9초에 해당)하면 delay time을 변경하고, 화면 변경됐음을 보여준다. q를 누르면, 적당한 곳으로 커서를 옮기고 종료한다.
cmd_local=`uptime | awk -F': ' '{print $2}'|awk -F', ' '{print $1 "|" $2 "|" $3 " "}'` cmd=`tail -1 *.log | grep : | awk '{print $6 "|" $10 "|" $11 "|" $12 " "}'` |
원하는 데이터 값을 가져오는 부분이다.
$cmd_local : 스크립트를 실행중인 서버의 load가 저장된다. '0.03|0.01|0.00'
$cmd : load가 저장된 각 서버별 로그파일의 맨 마지막 줄(가장 최근 load)를 읽어서 변수에 저장한다.
'cnx10|1.00,|1.00,|0.95 cnx22|0.39,|0.38,|0.35 cnx25|0.31,|0.24,|0.18 ... 생략...'
'서버명|1분load|5분load|15분load' 형태이며, 서버간의 구분은 반드시 1개 이상의 공백으로 해야 한다.
C=$(($col_width - $cols)) # data를 표시할 column 위치 계산 row=0 $TPUT civis # cursor 숨김 for line in $result_color do $TPUT cup $row $C # cursor 이동 row=`expr $row + 1 ` # row 증가 echo -n "$szColYe$szInverse$line$szNormal" done $TPUT cnorm # cursor 다시 표시 |
data를 표시할 위치를 오른쪽 상단, 오른쪽에서 $cols 번째칸으로 한다. 그런 후 서버마다 한줄에 한칸씩 보여준다. 화면에 표시될 때 커서는 안보이게 했다가(civis), 데이터가 다 뿌려지면 다시 보이도록 했다(cnorm).
다음은 실제 모니터링하는 화면이다. 3개의 화면을 연속으로 캡쳐한 것이다. 3초 간격, 3초 간격, 그리고 숫자 '5'를 누른 후 5초간격으로 보여주겠다는 화면이다.

highlight 처리 부분에 대한 설명은 참고문서로 대신한다. 그리고, 재미난 시계 스크립트를 소개한 'Sergio Gonzalez Duran'씨에게 감사드린다.
5. 참고자료
* CLI Magic: Use ANSI escape sequences to display a clock in your terminal
http://www.linux.com/feature/124918
* 로그 모니터링시 특정 문자를 highlight하기 (글 좋은진호, 2008.1)
http://coffeenix.net/board_view.php?bd_code=1562
* bash 맨페이지 중에 'read' 부분
* tput: Portable Terminal Control
http://www.gnu.org/software/termutils/manual/termutils-2.0/html_chapter/tput_1.html