제  목 : 여러 서버의 load를 터미널에서 실시간 모니터링
작성자 : 좋은진호(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


Posted by 두장