커피닉스, 시스템 엔지니어의 쉼터
  여러 서버의 load를 터미널에서 실시간 모니터링 작성일 : 2008/02/15 19:33
 
  • 글쓴이 : 좋은진호 ( http://coffeenix.net/ )
  • 조회수 : 13731
     
    제  목 : 여러 서버의 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


    커피닉스, 시스템 엔지니어의 쉼터 / URL : http://coffeenix.net/board_view.php?bd_code=1571