REMON

REMON은 제니퍼 독립 에이전트의 하나로, 비정형 데이터를 수집하고 가공하여 이를 제니퍼 서버에 전송한다. 일반적으로 모니터링 솔루션은 데이터 수집, 가공, 전송, 저장, 뷰등의 기능으로 구성되는데 REMON은 데이터 수집, 가공, 전송을 일반화한 프레임워크이다.

REMON을 사용하면, 임의의 운영 체계에서 임의의 스크립트를 실행시키거나, 임의의 데이터베이스에 대해서 임의의 SQL을 수행시키거나, 특정 자바 인터페이스를 구현한 자바 클래스를 실행시키는 방법등으로 비정형 데이터를 수집하여 제니퍼 서버에 전송할 수 있다.

이를 통해서 제니퍼 에이전트만으로는 수집이 불가능한 비즈니스 데이터와 시스템 운영체계에 대한 모니터링이 가능하다.

REMON 아키텍처

REMON 구성도

REMON은 데이터를 직접 수집하거나, 외부 데이터소스로부터 데이터를 받아들인다.

첫번째로 데이터를 직접 수집하는 모듈을 REMON 스크립트라고 한다. REMON 스크립트는 단순하게 유닉스 쉘 스크립트를 의미하는 것이 아니다. 유닉스 쉘 스크립트뿐만 아니라 SQL 스크립트, 자바 클래스 스크립트 등이 REMON 스크립트에 포함된다. 그리고 REMON 스크립트는 데이터 수집 위치에 따라서 로컬 스크립트와 원격 스크립트로 구분된다. 데이터 수집이 REMON 자체에서 이루어지면 로컬 스크립트라고 하고, 원격의 다른 운영 체계에서 이루어지면 원격 스크립트라고 한다.

로컬 스크립트의 종류는 다음과 같다.

원격 스크립트의 종류는 다음과 같다.

두번째로 REMON이 데이터를 직접 수집하지 않고 외부 데이터소스로부터 데이터를 받기도 한다. 외부 데이터소스는 다음과 같다.

이렇게 REMON 스크립트와 외부 데이터소스로부터 수집한 데이터를 REMON 데이터라고 한다. REMON 데이터는 데이터의 출처와 상관없이 동일한 형태를 갖는다.

REMON 데이터는 에이전트 아이디와 스크립트 아이디의 조합으로 고유하게 구분된다.

그리고 REMON 데이터는 1개 이상의 필드로 구성된다. 단, 모든 필드의 데이터 유형은 동일해야 한다. 필드 유형으로는 int, float, long, double, string 등이 가능하다.

기본적으로 필드의 개수에는 제한이 없다. 단, REMON 데이터를 제니퍼 서버에서 저장하는 경우에는 테이블 최대 칼럼 수에 영향을 받는다.

마지막으로 REMON은 REMON 데이터를 UDP 방식으로 제니퍼 서버에 전송한다. REMON 데이터에 대한 가공이 필요한 경우, 필터를 사용하여 REMON 데이터를 가공한 후에 제니퍼 서버에 전송할 수 있다. 하나의 REMON 데이터에 대해서 여러 개의 필터를 중첩해서 적용할 수 있다.

REMON 데이터를 2개 이상의 제니퍼 서버에 중복해서 혹은 선택적으로 전송할 수 있다.

그리고 REMON에 대한 전반적인 관리는 콘솔 기반의 제어 콘솔로 이루어진다.

설치 및 설정

제니퍼 설치 패키지에 있는 remon 디렉토리가 REMON 모듈이다. REMON의 디렉토리 구조는 다음과 같다.

REMON을 설치하려면 우선 이 디렉토리를 REMON을 설치할 운영 체계에 복사한다.

유닉스와 리눅스에 설치하는 경우에는 복사 후에 실행 파일에 대해서 실행 권한을 주어야 한다.

REMON은 자바로 구현되어 있기 때문에 자바 실행파일의 패스를 JAVA_HOME 환경 변수로 설정한다. 그리고 REMON을 설치한 디렉토리도 REMON_HOME 환경 변수로 설정한다.

REMON을 실행하려면 자바 1.4.2 이상을 사용해야 한다.

JAVA_HOME과 REMON_HOME 환경 변수는 REMON_HOME 디렉토리의 env.sh(env.bat) 파일에서 설정한다. 유닉스 혹은 리눅스의 경우에는 다음과 같이 수정한다.

REMON_HOME=/usr/jennifer/remon
JAVA_HOME=/usr/java

윈도우의 경우에는 다음과 같이 수정한다.

set REMON_HOME=C:/jennifer/remon
set JAVA_HOME=C:/Program Files/Java/jdk1.6.0_07

REMON이 사용하는 네트워크 정보와 REMON 데이터를 전송할 제니퍼 서버 등을 REMON_HOME/conf/remon.conf 파일에 설정한다.

우선 에이전트 아이디를 다음과 같이 설정한다. 데이터를 수집할 때 명시적으로 에이전트 아이디를 부여하지 않은 모든 REMON 데이터의 에이전트 아이디는 이 설정을 따른다.

remon.agent = R01

REMON이 외부 데이터소스로부터 데이터를 받아들이는 UDP 포트를 local.udp.port 옵션으로 설정한다. 기본 포트 번호는 7701이다.

local.udp.port = 7701

또한 REMON은 제어 콘솔과 제니퍼 서버로부터 TCP 요청을 받아들인다. 이를 위한 TCP 포트를 local.tcp.port 옵션으로 설정한다. 기본 포트 번호는 7701이다.

local.tcp.port = 7701

REMON에서는 데이터 혹은 요청을 받아들이는 UDP 포트와 TCP 포트로 같은 번호를 사용할 수 있다. 기본적으로 7701 포트 번호를 함께 사용한다.

그리고 REMON 자체의 IP 주소를 local.ip 옵션으로 설정한다. 제니퍼 서버가 REMON 서버로 역방향 TCP 호출을 할때 이 IP 주소를 사용한다.

local.ip = 127.0.0.1

마지막으로 REMON 데이터를 전송할 제니퍼 서버 정보를 설정한다.

jennifer.sys1.ip = 127.0.0.1
jennifer.sys1.port = 6902
jennifer.sys1.agent = *

sys1은 임의의 값으로 일반적으로 제니퍼 서버의 도메인 아이디를 의미한다. 여러 개의 제니퍼 서버에 REMON 데이터를 전송해야 하는 경우에는 sys1 대신에 다른 값을 사용해서 옵션을 추가한다.

jennifer.sys1.ip = 192.168.0.1
jennifer.sys1.port = 6902
jennifer.sys1.agent = *

jennifer.sys2.ip = 192.168.0.2
jennifer.sys2.port = 6902
jennifer.sys2.agent = *

jennifer.sys1.ip 옵션에는 제니퍼 서버의 IP 주소를 설정하고, jennifer.sys1.port 옵션에는 제니퍼 서버의 server_udp_listen_port 옵션으로 설정한 포트 번호를 설정한다. 기본 포트 번호는 6902이다.

일반적으로 모든 REMON 데이터는 모든 제니퍼 서버에 전송된다. 이는 jennifer.sys1.agent 옵션을 *로 설정했기 때문이다. 만약 특정 REMON 데이터만을 송신하려면 [,]를 구분자로 에이전트 아이디를 설정한다.

jennifer.sys1.agent = R01,R03,R05

스크립트 아이디가 아닌 에이전트 아이디만으로 REMON 데이터를 전송할 제니퍼 서버를 선택할 수 있다.

실행과 제어

실행하기

REMON을 실행하려면 REMON_HOME 디렉토리의 remon.sh를 실행한다.

./remon.sh

이 때 SeedKey를 입력해야 한다. 기본적으로 jennifer를 입력한다. SeedKey는 REMON 스크립트와 설정 파일에서 사용하는 패스워드를 암호화할 때 사용하는 키이다. 따라서 이 SeedKey 값을 잘못 입력하면 REMON이 정상적으로 동작하지 않는다.

SeedKey를 변경하려면 REMON을 실행할 때 변경할 값을 입력하면 된다. 그런데 SeedKey를 변경한 경우에는 REMON 설정 파일이나 스크립트에 존재하는 모든 패스워드를 다시 수정해야 한다.

정지하기

REMON을 정지하려면 REMON_HOME 디렉토리의 shutdown.sh를 실행한다.

./shutdown.sh

제어 콘솔

REMON은 콘솔 기반의 제어 콘솔로 관리한다.

제니퍼 3.x의 REMON은 웹 기반 사용자 인터페이스를 제공했지만 제니퍼 4.0의 REMON은 콘솔 기반 사용자 인터페이스만을 제공한다.

제어 콘솔을 실행하려면 REMON_HOME 디렉토리의 console.sh를 실행한다.

~/jennifer/remon$ ./console.sh

Jennifer REMON: Release 4.0.1.1(2008-10-31)
Copyright (c) JenniferSoft 1998,2008. All rights reserved.

REMON>

제어 콘솔을 종료하려면 exit 명령어를 수행한다.

REMON> exit

제어 콘솔에서 여러 명령어로 REMON 스크립트 제어, 필터 제어 등의 작업을 수행한다. 각 명령어에 대한 상세한 내용을 확인하려면 help 명령어를 수행한다.

REMON> help

다음은 주요 명령어에 대한 설명이다.

제어 콘솔에서 shutdown 명령어로 REMON을 종료할 수 있다. 이 경우에는 제어 콘솔도 함께 종료된다.

REMON> shutdown

REMON을 실행하는 자바의 환경 변수를 확인하려면 env 명령어를 수행한다.

REMON> env

특정 문자열로 시작하는 환경 변수만을 확인하려면 다음과 같이 명령어를 수행한다.

REMON> env [임의의 문자열]

예) env java

REMON의 환경 설정을 확인하려면 conf 명령어를 수행한다.

REMON> conf

특정 문자열로 시작하는 REMON 환경 설정만을 확인하려면 다음과 같이 명령어를 수행한다.

REMON> conf [임의의 문자열]

예) conf local

REMON의 자바 힙 메모리 사용량 상태와 데이터 큐 현황을 확인하려면 perf 명령어를 수행한다.

REMON> perf

REMON 운영 중에 발생한 에러 정보를 확인하려면 err 명령어를 수행한다.

REMON> err

REMON 운영 중에 발생한 에러 정보를 삭제하려면 errc 명령어를 수행한다.

REMON> errc

REMON 스크립트를 수행하려면 원격 서버 혹은 데이터베이스의 패스워드를 REMON 스크립트 파일에 설정하는 경우가 있다. 보안을 위해서 SeedKey를 기반으로 암호화한 값을 설정해야 하는데 이 때 enc 명령어를 사용한다.

REMON> enc <암호화할 문자열>

기타 다양한 명령어는 관련 부분에서 설명하도록 한다.

데이터 수집

REMON은 REMON 스크립트를 이용하여 데이터를 직접 수집하거나 REMONX, ExtraAgent, LogWatcher 등의 외부 데이터소스로부터 데이터를 받아들인다.

REMON 스크립트

모든 REMON 스크립트는 텍스트 파일로 확장자를 통해서 스트립트 유형이 구분된다. 그리고 REMON 스크립트는 REMON_HOME/script 디렉토리에 존재해야 한다. 하위 디렉토리와 다른 디렉토리는 인식할 수 없다.

REMON 스크립트 샘플은 REMON_HOME/script/sample 디렉토리에서 확인할 수 있다.

REMON 스크립트의 종류는 다음과 같다.

모든 REMON 스크립트는 실행을 제어하기 위한 속성을 갖는다. 다음은 REMON 스크립트 종류에 상관없이 공통적으로 사용 가능한 속성에 대한 설명이다.

REMON 스크립트 공통 속성

속성

설명

비고

script

데이터의 성격을 구분하는 역할을 하는 스크립트 아이디를 설정한다. 모든 스크립트에 대해서 이를 설정해야 한다. 에이전트 아이디와 함께 REMON 데이터를 지칭한다.

대문자, 숫자, _

agent

데이터의 출처를 구분하는 에이전트 아이디를 지정한다. 지정하지 않으면 REMON 설정 파일의 remon.agent 옵션 값을 기본 값으로 한다. 스크립트 아이디와 함께 REMON 데이터를 지칭한다.

X-ViewC 차트로 모니터링하려면 에이전트 아이디는 3자이어야 한다.

대문자, 숫자, _

interval

REMON 스크립트가 수행되는 주기를 설정한다. 단위는 초이고 기본 값은 1초이다.

양의 정수

fieldname

REMON 스크립트가 수집하는 REMON 데이터는 1개 이상의 필드로 구성된다. 이 필드에 대해서 이름을 지정할 때 사용한다. 콤마[,]를 구분자로 설정한다. 필드 이름을 설정하지 않으면 f0, f1, f1 등의 이름이 순차적으로 부여된다.

소문자, 숫자

fieldtype

REMON 데이터를 구성하는 필드의 유형을 지정한다. 모든 필드의 유형은 동일해야 한다. 따라서 fieldname 속성과는 달리 하나의 값만을 설정한다. 기본 값은 string이다.

[int | float | long | double | string]

request

REMON 스크립트가 제니퍼 서버의 요청을 받을때만 실행한다는 것을 의미한다. 기본값은 false이다.

[true | false]

autostart

REMON이 실행될 때 REMON 스크립트를 자동으로 실행할지에 대한 여부를 설정한다. false로 설정하면 REMON이 시작해도 REMON 스크립트는 동작하지 않는다. 반면에 true로 설정하면 REMON이 시작할 때 REMON 스크립트도 함께 동작한다. 기본 값은 true이다.

[true | false]

REMON 스크립트 속성은 #$로 시작하는 한 줄의 텍스트로 기술한다.

#$ agent = R01

단, 윈도우 bat 파일의 경우에는 앞에 REM을 적어준다.

REM #$ agent= R01

운영 체계 쉘 스크립트 (.sh / .bat)

REMON을 설치한 운영 체계의 쉘 스크립트를 REMON 스크립트로 사용할 수 있다. 이를 운영 체계 쉘 스크립트라고 한다. 운영 체계 쉘 스크립트는 로컬 스크립트로, 확장자가 sh 혹은 bat으로 끝나야 한다.

다음은 REMON을 설치한 유닉스 혹은 리눅스 운영 체계의 네트워크 소켓 연결 상태를 수집하는 예제이다. 다음 내용을 REMON_HOME/script 디렉토리에 net.sh라는 이름으로 저장한다.

#!/bin/sh

##############################################
# REMON Script Attribute
#$ script = NET
#$ agent = R01
#$ interval = 5
#$ fieldtype = int
#$ fieldname = est,tim,fin
##############################################

net=`netstat -an`
est=`echo $net | grep EST | wc -l`
tim=`echo $net | grep TIME | wc -l`
fin=`echo $net | grep FIN | wc -l`
echo $est,$tim,$fin

est, tim, fin 등의 필드로 ESTABLISHED 상태의 TCP 연결 개수, TIME_WAIT 상태의 TCP 연결 개수, FIN_WAIT 상태의 TCP 연결 개수를 int 유형으로 5초마다 수집한다. 그리고 이 REMON 데이터는 NET을 스크립트 아이디, R01을 에이전트 아이디로한다.

REMON이 운영 체계 쉘을 직접 실행하기 때문에 실행 권한이 있어야 한다.

텔넷 스크립트(.telnet)

텔넷을 이용해서 원격으로 다른 운영 체계에 접속해서 쉘 스크립트를 수행하여 데이터를 수집할 수 있다. 이를 텔넷 스크립트라고 한다. 텔넷 스크립트는 원격 스크립트로, 확장자가 telnet으로 끝나야 한다.

다음은 텔넷 스크립트에서 사용하는 속성에 대한 설명이다

텔넷 스크립트에서 사용하는 속성

속성

설명

비고

telnet.ip

접속 서버 IP

호스트 이름 또는 IP 주소

telnet.port

접속 서버 포트 번호

정수

telnet.user

접속 서버 사용자 아이디

문자열

telnet.password

접속 서버 사용자 패스워드

제어 콘솔의 enc 명령어로 패스워드를 암호화해서 등록해야 한다.

SeedKey를 수정한 경우에는 다시 설정해야 한다.

prompt.user

로그인 prompt 문자열


prompt.password

패스워드 prompt 문자열


prompt.char

일반 prompt 문자열


prompt.beforelogin

로그인 prompt가 나타나기 전에 사용자가 입력해야 하는 문자가 있는 경우 등록


prompt.preprompt

로그인 패스워드를 입력하고 최초 prompt.char가 화면에 출력되기 전에 사용자가 입력해야 하는 문자가 있는 경우 등록


로컬 스크립트는 interval 속성으로 수행 간격을 조절하지만, 원격 스크립트는 데이터를 주기적으로 수집하기 위해서 반드시 무한 루프 구조로 작성되어야 한다.

while true
do
   netstat -an | wc -l
   sleep 5
done

SSH2 스크립트(.ssh2)

SSH2를 이용해서 원격으로 다른 운영 체계에 접속해서 쉘 스크립트를 수행하여 데이터를 수집할 수 있다. 이를 SSH2 스크립트라고 한다. SSH2 스크립트는 원격 스크립트로, 확장자가 ssh2로 끝나야 한다.

다음은 SSH2 스크립트에서 사용하는 속성에 대한 설명이다.

SSH2 스크립트에서 사용하는 속성

속성

설명

비고

ssh2.ip

접속 서버 IP

호스트 이름 또는 IP 주소

ssh2.port

접속 서버 포트 번호

정수

ssh2.user

접속 서버 사용자 아이디

문자열

ssh2.password

접속 서버 사용자 패스워드

제어 콘솔의 enc 명령어로 패스워드를 암호화해서 등록해야 한다.

SeedKey를 수정한 경우에는 다시 설정해야 한다.

로컬 스크립트는 interval 속성으로 수행 간격을 조절하지만, 원격 스크립트는 데이터를 주기적으로 수집하기 위해서 반드시 무한 루프 구조로 작성되어야 한다.

while true
do
   netstat -an | wc -l
   sleep 5
done

SQL 스크립트(.sql)

외부 데이터베이스에 대해서 SQL을 수행하여 데이터를 수집할 수 있다. 이를 SQL 스크립트라고 한다. SQL 스크립트는 원격 스크립트로, 확장자가 sql로 끝나야 한다.

다음은 SQL 스크립트에서 사용하는 속성에 대한 설명이다.

SQL 스크립트에서 사용하는 속성

속성

설명

비고

jdbc.driver

JDBC 드라이버 클래스 이름


jdbc.url

데이터베이스 접속 URL


jdbc.user

데이터베이스 사용자 아이디


jdbc.password

데이터베이스 사용자 패스워드

제어 콘솔의 enc 명령어로 패스워드를 암호화해서 등록해야 한다.

SeedKey를 수정한 경우에는 다시 설정해야 한다.

SQL로 조회한 칼럼 이름이 데이터의 필드 이름이 되기 때문에 fieldname 속성을 설정할 필요가 없다.

JDBC 드라이버 파일을 REMON_HOME/lib/script 디렉토리에 복사한 후에 REMON을 재시작해야 한다.

SQL 스크립트는 원격 스크립트이지만 interval 속성으로 실행 주기를 설정할 수 있다.

다음은 SQL 스크립트 예제이다.

########################################################
# REMON Script Attribute
#$ script = PERF_CNT
#$ agent = R01
#$ interval = 2
#$ fieldtype = int

#$ jdbc.driver = org.apache.derby.jdbc.ClientDriver
#$ jdbc.url = jdbc:derby://localhost:1527/jennifer
#$ jdbc.user = jennifer
#$ jdbc.password = rf7CtZ/Oy6YGEtFFY/fo2A==

SELECT COUNT(*) CNT FROM PERF_X_29

클래스 스크립트(.cls)

remon.IClassScript 인터페이스를 구현한 클래스를 실행시켜서 데이터를 수집할 수 있다. 이를 클래스 스크립트라고 한다. 클래스 스크립트는 로컬 스크립트로, 확장자가 cls로 끝나야 한다.

다음은 클래스 스크립트에서 사용하는 속성에 대한 설명이다.

클래스 스크립트에서 사용하는 속성

속성

설명

비고

exec

remon.IClassScript 인터페이스를 구현한 클래스 이름


remon.IClassScript 인터페이스를 구현한 클래스에서 데이터 수집에 필요한 대부분의 작업을 수행하기 때문에 다른 REMON 스크립트보다 단순한다. 다음은 REMON의 자바 힙 메모리 사용량 데이터를 수집하는 예제이다.

##############################################
# REMON Script Attribute
#$ script = REMON_HEAP
#$ agent = R01
#$ interval = 1

#$ exec = example.HeapMemoryClassScript

exec 속성으로 설정한 클래스는 remon.IClassScript 인터페이스를 구현해야 한다. 일반적으로 remon.IClassScript 인터페이스를 구현한 remon.AbstractClassScript 클래스를 상속하는 것을 권장한다.

컴파일을 하려면 REMON_HOME/lib/sys/remon.jar 파일을 클래스 패스에 등록해야 한다. 그리고 컴파일한 클래스를 JAR 파일로 패키지하여 REMON_HOME/lib/script 디렉토리에 복사한 후에 REMON을 재시작한다. 클래스를 수정하면 앞의 작업을 반복한 후에 REMON을 재시작한다.

remon.AbstractClassScript 클래스는 다음과 같다.

package remon;

import java.util.Properties;
import com.javaservice.jennifer.protocol.IRmPacket;

abstract public class AbstractClassScript implements IClassScript {

     final public Properties properties = new Properties();
     abstract public IRmPacket process(String[] param);

}

따라서 remon.AbstractClassScript 클래스를 상속한 후에 process 메소드를 구현해야 한다.

package example;

import remon.AbstractClassScript;

import com.javaservice.jennifer.protocol.IRmPacket;
import com.javaservice.jennifer.protocol.RmPacket;
import com.javaservice.jennifer.type.INT;

public class HeapMemoryClassScript extends AbstractClassScript {

   public IRmPacket process(String[] param) {
       return null;
   }

}

process 메소드에서는 com.javaservice.jennifer.protocol.IRmPacket 인터페이스를 구현한 com.javaservice.jennifer.protocol.RmPacket 객체를 생성하고 전송할 데이터를 이 객체에 담은 후에 반환한다.

private static final int MB = 1024 * 1024;

public IRmPacket process(String[] param) {
   long now = System.currentTimeMillis();
   RmPacket packet = new RmPacket(now, "SCR1", "A01");

   Runtime runtime = Runtime.getRuntime();
   int total = (int) (runtime.totalMemory() / MB);
   int used = (int) (runtime.totalMemory() / MB - runtime.freeMemory() / MB);

   packet.addField("total", new INT(total));
   packet.addField("used", new INT(used));

   return packet;
}

우선 데이터를 수집한 시간, 스크립트 아이디, 에이전트 아이디를 파라미터로 RmPacket 객체를 생성한다. 단, 스크립트 아이디와 에이전트 아이디는 클래스 스크립트에 설정한 내용이 있으면 그 값으로 변경된다. 따라서 예제에서 REMON 데이터의 스크립트 아이디는 HEAP이 아니고 REMON_HEAP이 된다.

그리고 RmPacket 클래스의 addField 메소드를 통해서 데이터를 추가한다. addField 메소드의 첫번째 파라미터로 필드명을 지정하고, 두번째 파라미터로 데이터를 지정한다. 두번째 파라미터의 유형은 com.javaservice.jennifer.type.TYPE 클래스의 하위 클래스로 이를 통해서 값을 지정한다.

다른 REMON 스크립트와는 다르게 하나의 remon.IClassScript 구현 클래스에서 다양한 유형의 TYPE 클래스를 사용할 수 있다.

RmPacket 클래스의 자세한 사용 방법은 [RmPacket 클래스의 사용]을 참조한다.

remon.AbstractClassScript 클래스의 properties 멤버 필드에 클래스 스크립트에 설정한 모든 속성이 전달된다. 따라서 클래스가 실행될 때 필요한 속성들을 클래스 스크립트에 등록하여 전달할 수 있다.

##############################################
# REMON Script Attribute
...
#$ max = 1000
...

public IRmPacket process(String param[]) {
   ...
   String max = properties.get("max");
   ....
}

REMON 스크립트 등록 및 제어

모든 REMON 스크립트는 텍스트 파일로 REMON_HOME/script 디렉토리에 존재해야 한다. 그런데 이 디렉토리에 REMON 스크립트를 추가했다고 해당 REMON 스크립트가 동작하는 것은 아니다. 제어 콘솔을 통해서 이를 등록해야 한다.

단, REMON을 재시작하면 REMON_HOME/script 디렉토리에 있는 모든 REMON 스크립트가 자동으로 등록된다.

실행중인 REMON 스크립트 목록을 확인하려면 제어 콘솔에서 ls 명령어를 수행한다.

REMON> ls

그리고 REMON_HOME/script 디렉토리에는 있지만 아직 REMON에 등록되지 않은 REMON 스크립트 목록을 확인하려면 제어 콘솔에서 lsn 명령어를 수행한다.

REMON> lsn
perf.sql
net.sh

이중에서 특정 REMON 스크립트를 등록하려면 REMON 스크립트 파일 이름을 파라미터로 해서 load 명령어를 수행한다.

REMON> load [REMON 스크립트 파일 이름 Prefix]

예) load net.sh

모든 REMON 스크립트를 등록하려면 파라미터 없이 load 명령어를 수행한다.

REMON> load

load 명령어를 통해서 새롭게 추가한 REMON 스크립트는 정지 상태에 있다. 이를 시작하려면 스크립트 아이디와 에이전트 아이디 조합을 파라미터로 start 명령어를 수행한다.

REMON> start [스크립트 아이디.에어전트 아이디 Prefix]
start NET

스크립트 아이디가 NET으로 시작하는 모든 REMON 스크립트를 시작한다.

start NET.R

스크립트 아이디가 NET이고 에이전트 아이디가 R로 시작하는 모든 REMON 스크립트를 시작한다.

start NET.R01

스크립트 아이디가 NET이고 에이전트 아이디가 R01로 시작하는 모든 REMON 스크립트를 시작한다.

모든 REMON 스크립트를 시작하려면 파라미터 없이 start 명령어를 수행한다. .

REMON> start

그리고 제어 콘솔에서 REMON 스크립트가 수집한 마지막 데이터를 확인하려면 스크립트 아이디와 에이전트 아이디 조합을 파라미터로 data 명령어를 수행한다.

REMON> data [스크립트 아이디.에어전트 아이디 Prefix]

예) data NET
2008-07-29 19:50:48.811:NET:R01(est=4,tim=3,fin=1)

모든 데이터를 확인하려면 파라미터 없이 data 명령어를 수행한다.

REMON> data

REMON 데이터를 삭제하려면 dataclear 명령어를 수행한다. 데이터 수집과 전송에 영향을 미치지 않는다.

REMON> dataclear

REMON 스크립트 내용을 수정한 후에 이를 반영하려면 스크립트 아이디와 에이전트 아이디 조합을 파라미터로 reload 명령어를 수행한다.

REMON> reload [스크립트 아이디.에어전트 아이디 Prefix]

모든 REMON 스크립트를 재시작하려면 파라미터 없이 reload 명령어를 수행한다.

REMON> reload

REMON 스크립트를 제거하려면 스크립트 아이디와 에이전트 아이디 조합을 파라미터로해서 unload 명령어를 수행한다. 단, 정지 상태에 있는 REMON 스크립트만을 제거할 수 있다.

REMON> unload [스크립트 아이디.에어전트 아이디 Prefix]

모든 REMON 스크립트를 제거하려면 파라미터 없이 unload 명령어를 수행한다.

REMON> unload

REMON 스크립트 파일 내용을 cat 명령어로 확인할 수 있다. 단, 이 경우에는 정확한 스크립트 아이디와 에이전트 아이디를 파라미터로 입력해야 한다.

REMON> cat NET.R01

외부 데이터소스

REMON은 데이터를 직접 수집하기도 하지만 외부 데이터소스로부터 데이터를 받아들이기도 한다.

REMONX

REMONX는 자바를 설치하기 어려운 환경에서 데이터를 REMON에 전송하기 위한 C 모듈이다. 레거시 쉘에 삽입하여 사용하거나 기존 프로그램에 REMONX를 추가해서 사용한다.

REMONX는 REMON_HOME/remonx 디렉토리에 운영 체계 별로 존재한다. 운영 체계에 맞는 실행 파일이 없는 경우에는 직접 컴파일을 해야한다. 컴파일 방법은 makefile을 참고한다.

REMONX의 실행 방법은 다음과 같다.

remonx [-multi] [-tcp] -h host_ip -p port -s script_id -a agent_id -N/S/D -d data -f fieldname

다음은 각 옵션에 대한 설명이다.

REMONX 실행 옵션

옵션명

설명

-multi

multi-row 데이터를 stdin으로 받아 전송할때 사용한다. 따라서 -multi가 지정되면 -d 옵션은 사용하지 않는다.

ex) tail -f access.log | remonx -multi ...

-tcp

REMONX가 데이터를 TCP 방식으로 전송할 때 사용한다. 기본적으로 UDP 방식으로 전송한다.

-h host_ip

데이터를 전송할 REMON IP 주소를 설정한다.

-p port

데이터를 전송할 REMON 포트 번호를 설정한다.

-s script_id

REMONX가 전송하는 REMON 데이터의 스크립트 아이디를 설정한다.

-a agent_id

REMONX가 전송하는 REMON 데이터의 에이전트 아이디를 설정한다.

X-ViewC 차트로 모니터링하려면 에이전트 아이디는 3자이어야 한다.

-N | -S | -D

REMONX가 전송하는 데이터 타입으로 -N은 숫자를, -S는 문자열을, -D는 증감량을 의미한다. 기본 값은 -S이다.

-d data

REMONX가 전송하는 데이터이다.

-f fieldname

필드 이름을 설정한다. -N 혹은 -D 유형의 데이터에만 의미가 있다.

다음은 REMONX로 유닉스 혹은 리눅스 운영 체계의 TCP 연결 상태를 수집하는 예제이다.

#!/bin/sh

while true
do
   est=`netstat -an | grep EST | wc -l`
   tim=`netstat -an | grep TIME | wc -l`
   fin=`netstat -an | grep FIN | wc -l`
   remonx -h 127.0.0.1 -p 7701 -a RX1 -s NET -N -d "$est,$tim,$fin" -f “est,tim,fin”
   sleep 1
done

est, tim, fin 등의 필드로 ESTABLISHED 상태의 TCP 연결 개수, TIME_WAIT 상태의 TCP 연결 개수, FIN_WAIT 상태의 TCP 연결 개수를 int 유형으로 1초마다 수집한다. 그리고 REMONX는 이 데이터를 NET을 스크립트 아이디로, RX1을 에이전트 아이디로해서 REMON에 전송한다.

REMONX를 이용해서 경보를 발령할 수도 있다.

remonx -h 127.0.0.1 -p 7701 -s ALERT -a R01 -S -d “FATAL: error message”

데이터는 FATAL, ERROR, WARN 중의 하나로 시작해야 한다. 이 구분에 따라서 경보 유형이 다음과 같이 결정된다.

C가 아닌 자바로 되어 있는 REMONX도 존재한다. 일반적으로 제니퍼 에이전트를 설치해서 모니터링하기에는 적합하지 않은 배치 형태로 동작하는 자바 애플리케이션을 모니터링할 때 사용한다. 이를 사용하려면 REMON_HOME/remonx/jar/remonx.jar 파일을 클래스 패스에 등록해서 사용한다.

ExtraAgent

ExtraAgent는 제니퍼 에이전트의 모니터링 기능을 확장하기 위한 기능이다. ExtraAgent가 수집한 데이터는 REMON 혹은 제니퍼 서버로 전송된다.

경보 발령이나 평균 처리와 같은 2차 데이터 가공이 필요한 경우에는 REMON으로 데이터를 전송한다.

LogWatcher

LogWatcher는 로그 감시기의 역할을 수행한다. 다양한 애플리케이션이나 운영 체계가 기록하는 텍스트 로그 파일에서 임의로 지정한 패턴을 검출하고 이벤트를 발생시켜서 관련 데이터를 REMON에 전달한다. REMON은 LogWatcher로부터 전송받은 데이터를 2차 가공하여 제니퍼 서버에 전달한다.

데이터 제어

REMON 스크립트 혹은 외부 데이터소스로부터 수집한 데이터를 REMON을 통해서 가공할 수 있다. 데이터 가공은 remon.IFilter 인터페이스를 구현한 클래스로 이루어진다. 이를 중첩해서 사용할 수 있기 때문에 필터라고 지칭한다.

기본적으로 자주 사용하는 필터를 제공하며, 사용자가 임의의 필터를 작성하여 추가할 수 있다.

REMON 스크립트 단위로 필터를 설정한다.

기본으로 제공하는 필터

사용할 수 있는 필터는 확인하려면 제어 콘솔에서 filter 명령어를 수행한다.

REMON> filter

특정 이름으로 시작하는 필터에 대한 정보를 확인하려면 이름을 파라미터로 filter 명령어를 수행한다.

REMON> filter [필터 이름 Prefix]
예) filter ALERT

필터는 에이전트 아이디와 상관없이 스크립트 아이디만을 기준으로 REMON 스크립트에 적용된다. REMON 스크립트 별 필터 적용 현황을 확인하려면 lsf 명령어를 수행한다.

REMON> lsf

특정 스크립트에 대한 필터 적용 현황을 확인하려면 스크립트 아이디를 파라미터로 lsf 명령어를 수행한다.

REMON> lsf [스크립트 아이디 Prefix]
예) lsf NET

특정 REMON 스크립트에 특정 필터를 추가하려면 addf 명령어를 수행한다.

REMON> addf <스크립트 아이디> <필터 이름>

필터 설정을 변경한 후에 이를 반영하려면 스크립트 아이디를 파라미터로 appf 명령어를 수행한다. 그 전에는 필터 변경 사항이 반영되지 않는다.

REMON> appf NET

REMON 스크립트에서 필터를 제거하려면 delf 명령어를 수행한다. 필터 인덱스는 lsf 명령어로 확인할 수 있다.

REMON> delf <스크립트 아이디> <필터 인덱스>
예) delf NET 1

REMON 스크립트에 설정된 모든 필터를 제거하려면 스크립트 아이디를 파라미터로 rmf 명령어를 수행한다.

REMON> rmf <스크립트 아이디 Prefix>
예) rmf NET

REMON 스크립트에 설정된 필터들 중에서 특정 필터를 다른 필터로 변경하려면 스크립트 아이디와 필터 인덱스와 필터 이름을 파라미터로 setf 명령어를 수행한다.

REMON> setf <스크립트 아이디> <필터 인덱스> <변경할 필터 이름>
예) setf NET 1 ALERT

REMON 스크립트에 설정된 필터들 사이에 새로운 필터를 추가하려면 스크립트 아이디와 필터 인덱스와 필터 이름을 파라미터로 insf 명령어를 수행한다.

REMON> insf <스크립트 아이디> <필터 인덱스> <추가할 필터 이름>
예) insf NET 1 ALERT

다음은 주요 필터에 대한 설명이다. 기타 다른 필터들에 대한 설명은 제어 콘솔에서 filter 명령어로 확인한다.

DB_SAVE

기본적으로 REMON이 제니퍼 서버에 전송하는 REMON 데이터는 제니퍼 서버 성능 데이터베이스에 저장되지 않는다. 만약 특정 REMON 데이터를 제니퍼 서버 성능 데이터베이스에 저장하려면 SERVER_DB_SAVE 필터를 사용한다. 예를 들어, 스크립트 아이디가 NET인 REMON 스크립트가 전송한 REMON 데이터를 제니퍼 서버 성능 데이터베이스에 저장하려면 다음 명령어를 수행한다.

REMON> addf NET SERVER_SAVE 30
...
REMON> appf NET
...

addf 명령어의 마지막 파라미터는 저장 주기를 의미한다. 단위는 초이다. 앞의 예제에서는 30으로 지정했기 때문에, 제니퍼 서버는 30초마다 해당 REMON 데이터를 데이터베이스에 저장한다.

AVERAGE

기본적으로 마지막에 수집한 REMON 데이터를 제니퍼 서버에 전송한다. 그런데 특이치를 제거하기 위해서 특정 기간 동안의 평균 값을 제니퍼 서버에 전송할 필요도 있다. 이를위해 사용하는 필터를 AVERAGE 필터라고 하고, AVG_30S, AVG_5M, AVG_60S 등의 필터가 존재한다.

AVG_30S 실행 모습

AVG_30S는 30초 평균 값을 생성한다. 필터에 전달되는 데이터 중에서 30초 이내의 데이터들을 메모리에 보관하고 있다가 새로운 데이터가 전달되면 이것들을 평균 데이터로 변형하여 반환한다.

NET을 스크립트 아이디로 하는 REMON 스크립트에 AVG_30S 필터를 적용하려면 다음 명령어를 수행한다.

REMON> addf NET AVG_30S
...
REMON> appf NET
...

다음 차트는 AVG_30S 필터를 적용하기 전과 적용한 이후의 데이터의 변화를 보여준다.

AVG_30S 필터 적용 예

AVG_5M 필터는 데이터를 5분 평균 값으로 가공하고, AVG_60S 필터는 데이터를 1분 평균 값으로 가공한다.

SEND

SEND 필터는 데이터 전송 주기를 수정할 때 사용한다.

NET을 스크립트 아이디로 하는 REMON 스크립트의 REMON 데이터 전송 주기를 10초로 변경하려면 SEND 필터를 다음과 같이 적용한다.

REMON> addf NET SEND 10
...
REMON> appf NET
...

addf의 마지막 파라미터는 전송 주기로 단위는 초이다.

다음 차트는 SEND 필터를 적용하기 전과 적용한 이후의 데이터의 변화를 보여준다.

SEND 필터 적용 예

REMON 스크립트가 1초 단위로 데이터를 수집하더라도 SEND 필터는 마지막 데이터 전송 시간에서 10초가 지난 경우에만 데이터를 전송하고 나머지는 전송하지 않는다. 따라서 SEND 필터가 적용되면 차트에서 관찰되는 모습은 앞의 그림과 같다.

STOP

STOP 필터는 REMON 스크립트가 수집한 데이터를 제니퍼 서버에 전송하지 않을 때 사용한다. 이 필터를 적용하면 REMON 스크립트가 데이터를 수집하기는 하지만 이를 제니퍼 서버에 전송하지는 않는다.

REMON> addf NET STOP
...
REMON> appf NET
...

ALERT

ALERT 필터를 이용하여 REMON 스크립트가 수집한 데이터에 대한 경보 처리를 할 수 있다. ALERT 필터를 추가할 때는 경보 조건과 경보 메시지 등의 파라미터를 설정해야 한다.

예를 들어, NET을 스크립트 아이디로 하는 REMON 스크립트는 est, tim, fin 등의 필드로 int 형의 데이터를 수집하고 있다.

REMON> data NET
2008-07-31 21:20:11.809:NET:R01(est=5,tim=2,fin=1)

이 때 est 필드의 값이 20 보다 큰 경우에 USER_DEFINED_FATAL 경보를 발령하려면 다음과 같이 ALERT 필터를 적용한다.

REMON> addf NET ALERT "est > 20" "FATAL: The est is over 20(${est})"
...
REMON> appf NET
...

파라미터에 공백이 포함되면 파라미터의 시작과 끝을 ["]로 묶어야 한다.

첫번째 파라미터는 조건식으로 변수와 연산자를 사용할 수 있다. 변수로는 데이터 필드 이름을 사용하고, 연산자에는 사칙 연산자(+, -, *, / ), 부울 연산자(&&, ||), 괄호, 삼항 연산자(? :) 등을 사용할 수 있다.

두번째 파라미터는 경보 메시지로 FATAL, ERROR, WARN 중의 하나로 시작해야 한다. 이 구분에 따라서 경보 유형이 다음과 같이 결정된다.

메시지에는 데이타 필드의 특정 값을 포함시킬 수 있다. ${필드 이름} 형식으로 기술하면 메시지에서 해당 영역은 데이터 값으로 치환되서 서버로 전달된다.

경보 차트

REMON에서 발령한 경보도 다른 경보와 같이 제니퍼 서버의 ALERT_01~31 테이블에 저장된다. 그래서 필요에 따라서 보고서 템플릿과 쿼리 수행기로 과거 데이터를 조회할 수 있다.

동일한 경보가 동일한 REMON 데이터에서 연속으로 발생하면 첫번째 경보만이 제니퍼 서버에 전달되고 나머지는 모두 무시된다. 만약 모든 경보를 매번 전달하려면 ALERT_ALWAYS 필터를 사용한다.

REMON> addf NET ALERT_ALWAYS
...
REMON> appf NET
...

새로운 필터 추가

사용자가 새로운 필터를 작성하여 사용할 수 있다.

필터 클래스 구현

우선 remon.IFilter 인터페이스를 구현한 클래스를 작성해야 한다. 일반적으로 remon.IFilter 인터페이스를 구현한 remon.AbstractFilter 클래스를 상속해서 새로운 필터를 구현한다.

컴파일을 하려면 REMON_HOME/lib/sys/remon.jar 파일을 클래스 패스에 등록해야 한다. 그리고 컴파일한 클래스를 JAR 파일로 패키지하여 REMON_HOME/lib/script 디렉토리에 복사한 후에 REMON을 재시작한다. 클래스를 수정하면 앞의 작업을 반복한 후에 REMON을 재시작한다.

remon.IFilter 인터페이스는 다음과 같다.

package remon;

public interface IFilter {
   public void open(String script, String[] args);
   public RmPacket process(RmPacket pack);
   public void close();
}

remon.AbstractFilter 클래스는 다음과 같다

package remon;

import com.javaservice.jennifer.protocol.RmPacket;

abstract public class AbstractFilter implements IFilter {

   public void open(String script, String[] args) {
   }

   public void close() {
   }

   public RmPacket process(RmPacket rmdata) {
      return null;
   }

}

기본적으로 open, close, process 메소드를 구현해야 한다. 각 메소드는 다음과 같은 특징을 갖는다.

필터는 각 REMON 스크립트별로 적용된다. 따라서 REMON 스크립트에 필터가 처음 적용될 때 open 메소드가 호출된다. 이 메소드에서 필터에 대한 초기화 작업을 수행한다. open 메소드의 첫번째 파라미터는 java.lang.String 유형으로 스크립트 아이디이다. 그리고 두번째 파라미터는 java.lang.String[] 유형으로 제어 콘솔에서 addf 명령어로 필터를 추가할 때 설정하는 파라미터 목록을 나타낸다. 예를 들어, 다음과 같이 필터를 추가한다고 가정하자.

REMON> addf NET DOUBLE 1O true
// script는 NET
public void open(String script, String[] args) {
   String second = args[0]; // 10
   String isOrder = args[1]; // true
}

그리고 close 메소드는 필터가 제거될 때 호출된다. 따라서 close 메소드에서는 open 메소드나 process 메소드에서 할당한 자원을 해제하는 로직을 구현한다. 그리고 실제 데이터의 가공은 process 메소드에서 구현한다. 이 메소드는 REMON 데이터가 수집될 때마다 호출된다. 만약 이 메소드가 null을 반환하면 다음 필터나 제니퍼 서버로 REMON 데이터가 전달되지 않는다. 원본 REMON 데이터는 process 메소드의 RmPacket 클래스 유형의 파라미터로 전달된다. 예를 들어, 데이터의 값을 2배로 증가시키는 필터는 다음과 같이 구현한다.

package example;

import remon.AbstractFilter;

import com.javaservice.jennifer.protocol.RmPacket;
import com.javaservice.jennifer.type.FIELD;
import com.javaservice.jennifer.type.INT;
import com.javaservice.jennifer.type.TYPE;

public class DoubleFilter extends AbstractFilter {

   public RmPacket process(RmPacket pack) {
      int count = pack.count();
      for (int i = 0; i < count; i++) {
         FIELD field = pack.getField(i);
         TYPE type = field.getValue();
         if (type.getType() == TYPE.INT) {
            INT t = (INT) type;
            t.value *= 2;
         }
      }
      return pack;
   }
}

다음 차트는 DOUBLE 필터를 적용하기 전과 적용한 이후의 데이터의 변화를 보여준다.

DOUBLE 필터 적용 예

RmPacket 클래스의 자세한 사용 방법은 [RmPacket 클래스의 사용]을 참조한다.

필터 등록

새로운 필터를 등록하는 방법은 다음과 같다.

  1. 우선 remon.IFilter 인터페이스를 구현한 클래스를 JAR 파일로 패키지하여 REMON_HOME/lib/script 디렉토리에 복사한다.

  2. 그리고 REMON_HOME/conf/filter.conf 파일에 필터 정보를 설정한다.

[DOUBLE]
   class = example.DoubleFilter
   option =
   desc = Make the value into a double.

[] 사이에 있는 DOUBLE이 필터 이름이 된다.

그리고 class 속성으로 remon.IFilter 인터페이스를 구현한 클래스를 설정한다. option 속성에는 필터가 사용하는 파라미터에 대한 설명을 기술하고, desc 속성에는 필터 자체에 대한 설명을 기술한다.

  1. REMON을 재시작한다.

  2. 제어 콘솔에서 filter 명령어로 필터가 올바르게 등록되었는지를 확인한다.

REMON> filter DOUBLE
Available Filters
------------------
[DOUBLE]
   class = example.DoubleFilter
   option =
   desc = Make the value into a double.

데이터 관리 및 뷰

REMON은 수집한 REMON 데이터를 제니퍼 서버에 전송한다. 제니퍼 서버에서 REMON 데이터를 관리하고 확인하는 방법을 설명한다.

제니퍼 서버에서 REMON 데이터 디버깅

제니퍼 서버 시작 콘솔에서 REMON이 전송하는 REMON 데이터를 확인하려면 제니퍼 서버의 remon_debug 옵션을 true로 설정한다. 기본 값은 false이다.

debug_remon = true

데이터베이스 저장

REMON데이터는 제니퍼서버에서 파일과 DB에 저장될 수 있다.

데이터베이스 저장

DB_SAVE 필터가 적용된 REMON 스크립트가 전송하는 데이터는 파일과 데이터베이스에 동시에 저장한다. 단, 제니퍼 서버의 enable_remon_db_save 옵션을 false로 설정하면 데이터를 저장하지 않는다. 기본 값은 true이다.

REMON 데이터가 저장되는 테이블의 이름은 다음과 같다.

RM_<스크립트_아이디>_01~31

예를 들어, 스크립트 아이디가 CPU인 REMON 스크립트가 전송하는 데이터는 RM_CPU_01~31 테이블에 일자별로 저장된다.

REMON 데이터를 저장하는 모든 테이블에는 다음 칼럼이 존재한다.

REMON 테이블

칼럼

유형

제약 조건

설명

LOG_TIME

BIGINT


기록 시간

SCRIPT

VARCHAR(256)


스크립트 아이디

AGENT

VARCHAR(128)


에이전트 아이디

LOG_DT

CHAR(8)


날짜(YYYYMMDD)

LOG_HH

CHAR(2)


시간(HH)

LOG_MM

CHAR(2)


분(MM)

LOG_SS

CHAR(2)


초(SS)

MESSAGE

VARCHAR(32672)


REMON 메시지

그리고 REMON 데이터의 각 필드 이름으로 칼럼이 추가된다. 필드 유형에 따라서 칼럼유형도 결정된다.

필드 유형이 변경되면 REMON 데이터가 정상적으로 저장되지 않는다. 따라서 이 경우에는 REMON 스크립트 아이디를 수정하는 것을 권장한다.

파일 저장

FILE_SAVE 필터가 적용된 REMON 스크립트가 전송하는 데이터는 파일과 데이터베이스에 동시에 저장한다. 단, 제니퍼 서버의 enable_remon_file_save 옵션을 false로 설정하면 데이터를 저장하지 않는다. 기본 값은 true이다.

저장된 데이터에 대한 조회는 [TOOLS | REMON Data Serarch]를 참조한다.

데이터 삭제

제니퍼 서버는 데이터베이스에 저장된 REMON 데이터를 CleanerActor 스케줄러로 자동으로 삭제한다. 기본적으로 제니퍼 서버에 다음과 같이 설정되어 있다.

time_actor_11 = com.javaservice.jennifer.server.timeactor.CleanerActor 02 REMON DAY 7

기본 설정에 따르면 7일이 지난 REMON 데이터는 매일 새벽 2시에 자동으로 삭제된다.

사용자 정의 대시보드를 통한 REMON 데이터 모니터링

사용자 정의 대시보드를 통해서 REMON 데이터를 차트로 모니터링할 수 있다. 사용자 정의 대시보드에 대한 자세한 사항은 [사용자 정의 대시보드(62 페이지)]를 참조한다.

우선 [구성 관리 | 메뉴 관리] 메뉴에서 사용자 정의 대시보드 유형의 메뉴를 추가한다.

사용자 정의 대시보드 유형의 메뉴 추가

메뉴를 추가한 후에 해당 메뉴로 이동한다. 그리고 화면 오른쪽에 있는 [편집] 버튼을 클릭하여 사용자 정의 대시보드 편집 화면으로 이동한다.

사용자 정의 대시보드 화면

차트 선택 영역의 [사용자 정의 차트] 그룹에서 LINE 차트를 선택한다. 그러면 화면에 차트가 나타난다. 이 차트를 드래그 앤 드랍으로 차트 배치 영역에 놓는다.

LINE 차트 선택

차트 배치 영역에 놓은 차트의 오른쪽 하단에 있는 [옵션 설정] 아이콘을 클릭하면 옵션 설정 팝업 창이 나타난다.

옵션 설정 팝업 창

옵션 설정 팝업 창에서 [REMON 목록] 버튼을 클릭한다. 그리고 모니터링할 REMON 데이터를 선택한 후에 하단에 있는 [선택] 버튼을 클릭한다. 그러면 에이전트와 스크립트 필드에 REMON 데이터의 에이전트 아이디와 스크립트 아이디가 입력된다.

그리고 옵션 설정 팝업 창 하단에 있는 [저장] 버튼을 클릭하면 LINE 차트에 REMON 데이터가 표시된다.

LINE 차트로 REMON 데이터 모니터링

LogWatcher

LogWatcher는 로그 감시기의 역할을 수행한다. 다양한 애플리케이션이나 운영 체계가 기록하는 텍스트 로그 파일에서 임의로 지정한 패턴을 검출하고 이벤트를 발생시켜서 관련 데이터를 REMON에 전달한다.

설치와 구성

LogWatcher는 REMON_HOME/logwatcher 디렉토리에 존재하며, log.sh, logwatcher.jar, logw.conf 등으로 구성되어 있다. 설치를 하려면 우선 logwatcher 디렉토리를 모니터링이 필요한 운영 체계에 복사한다.

그리고 logw.conf 파일에 주요 설정을 한다. 우선 logw.conf 파일 앞단에서 공통 옵션을 설정한다. 이 옵션들은 [XXX]로 시작하는 로그 파일 설정보다 앞에 놓여야 한다.

주요 옵션은 다음과 같다.

agent = L01
control_port = 6001
target_host = 127.0.0.1
target_port = 7701

다음은 공통 옵션에 대한 설명이다.

그리고 LogWatcher로 모니터링할 로그 파일들에 대한 설정도 logw.conf 파일에 한다. 각각의 로그 파일 설정은 [XXX]로 시작하는데 XXX에는 대문자와 숫자로 구성된 임의의 값을 입력한다. 이는 LogWatcher로 수집하는 REMON 데이터의 스크립트 아이디가 된다. REMON의 스크립트 아이디와 동일한 제약 조건을 갖는다. 다음은 ALOG, BLOG라는 이름으로 2개의 로그 파일 모니터링을 설정한 예제이다.

[ALOG]
...

[BLOG]
...

다음은 ALOG에 대한 상세 설정이다.

[ALOG]
file = /tmp/${today}.log
rule_01 = [*]ABC, ok
rule_02 = [4]###, fatal

룰을 적용하지 않고 로그 라인을 그대로 REMON에 전송하려면 send_raw_data 옵션을 true로 설정한다.

[BLOG]
file = /tmp/access.log
send_raw_data = true

logw.conf 파일을 변경하면 반드시 LogWatcher를 재시작해야 한다.

LogWatcher는 자바로 구현되어 있기 때문에 자바를 JAVA_HOME 환경 변수로 설정한다.

LogWatcher를 실행하려면 자바 1.4.2 이상을 사용해야 한다.

JAVA_HOME 환경 변수는 REMON_HOME/logwatcher 디렉토리의 env.sh(env.bat) 파일에서 설정한다. 유닉스 혹은 리눅스의 경우에는 다음과 같이 수정한다.

JAVA_HOME=/usr/java

윈도우의 경우에는 다음과 같이 수정한다.

set JAVA_HOME=C:/Program Files/Java/jdk1.6.0_07

LogWatcher를 실행하려면 REMON_HOME/logwatcher 디렉토리의 logw.sh를 실행한다.

./logw.sh

LogWatcher를 정지하려면 REMON_HOME/logwatcher 디렉토리의 shutdown.sh를 실행한다.

./shutdown.sh

제어 콘솔로 LogWatcher를 관리할 수 있다. 제어 콘솔을 시작하려면 REMON_HOME/logwatcher 디렉토리의 console.sh를 실행한다.

./console.sh

모니터링 현황을 확인하려면 제어 콘솔에서 ls 명령어를 입력한다.

logw> ls

LogWatcher와 REMON 데이터

LogWatcher는 줄 단위로 로그 파일을 읽어서 모든 룰에 대한 매치 여부를 확인한다. 그리고 각 룰에 대한 매치 횟수를 카운팅하여 REMON 데이터를 구성한다. 따라서 각 룰은 필드가 되고, 룰의 매치 회수는 필드에 대한 값이 된다.

그리고 5초 주기로 이 REMON 데이터를 REMON에 전송한다.

send_raw_data 속성을 true로 지정한 경우에는 1초마다 데이터를 전송한다.

예를 들어, ALOG에 대해서 5초 동안 쌓인 로그가 100 줄이 있다고 가정하자. 그리고 10개의 줄이 rule_01에 부합하고, 5개의 줄이 rule_02에 부합하면 다음 데이터가 REMON에 전송된다.

에이전트 아이디 - L01
스크립트 아이디 - ALOG
ok 필드 값 - 10
fatal 필드 값 - 5

룰 설정

룰은 [검사 조건] 과 [필드 이름]으로 구성된다. 검사 조건은 [단어 위치] 를 지정하는 키워드와 검출할 단어로 구성된다. 그리고 여러 개의 조건을 조합하기 위해서 && 혹은 || 연산자를 검사 조건에 사용할 수 있다. 그러나 하나의 룰에 &&과 ||를 동시에 사용할 수는 없다.

[*] - any position
[n] - n's position (n is any positive number like 0, 1... 999)
[$] - end of the line

다음은 설정 가능한 검사 조건 예제이다. 모든 룰은 줄 단위로 적용된다.

[*]ABC - 임의의 위치에 ABC 문자열이 존재하면 룰에 부합한다.

[4]BBB - 5번째 글자에서 시작하는 BBB 문자열이 존재하면 룰에 부합한다.

[$]XXX - 줄이 XXX 문자열로 끝나면 룰에 부합한다.

[*]AAA || [$]XXX - 임의의 위치에 AAA 문자열이 존재하거나 줄이 XXX 문자열로 끝나면 룰에 부합한다.

[*]AAA || [$]XXX && [*]BB와 같이 &&와 ||를 혼용해서 사용할 수 없다.

아파치 웹 서버 액세스 로그를 X-ViewC 차트로 모니터링하기

아파치 웹 서버 액세스 로그를 LogWatcher와 REMON과 X-ViewC 차트로 모니터링하는 방법을 설명한다.

X-ViewC 차트는 포멧이 일정한 로그 파일을 분포도 형태로 보여주는 차트이다. 로그의 각 라인에는 시간, 값(응답 시간 혹은 사용량 등), 그리고 이름(URL 등)이 포함되어 있어야 한다. 자바 GC 로그, 아파치 웹 서버 액세스 로그 등을 X-ViewC 차트로 모니터링할 수 있다.

우선 LogWatcher로 아파치 웹 서버 액세스 로그 파일을 읽는다. 아파치 웹 서버가 설치된 서버에 LogWatcher를 설치하고 다음과 같이 logw.conf 파일을 설정한다.

agent = L01
control_port = 6001
target_host = 127.0.0.1
target_port = 7701

[ACCESS]
send_raw_data = true
file = /usr/local/apache2/logs/www_access_log

X-ViewC 차트로 모니터링하려면 에이전트 아이디는 3자로 설정해야 한다.

LogWatcher와 REMON을 실행한다. 그리고 REMON 제어 콘솔에서 LogWatcher가 보내는 REMON 데이터에 XVIEW_APACHE 필터를 적용한다.

REMON> addf ACCESS XVIEW_APACHE
...
REMON> appf ACCESS
...

기본적으로 X-ViewC 차트의 Y축은 액세스 바이트이다. Y축을 응답 시간으로 하려면 다음과 같이 필터를 적용한다.

REMON> addf ACCESS XVIEW_APACHE 2
...
REMON> appf ACCESS
...

단, 아파치 웹 서버 액세스 로그 파일에 응답 시간도 기록되어야 한다. 설정 방법은 아파치 웹 서버 매뉴얼을 참고한다.

마지막으로 제니퍼 서버의 사용자 정의 대시보드 편집 화면으로 이동한다. 차트 선택 영역의 [사용자 정의 차트] 그룹에서 X-ViewC 차트를 선택한다. 그러면 화면에 차트가 나타난다. 이 차트를 드래그 앤 드랍으로 차트 배치 영역에 놓는다.

X-ViewC 차트

RmPacket 클래스의 사용

클래스 스크립트, 필터, ExtraAgent 등을 구현하려면 RmPacket 클래스를 이해해야 한다. 이를 위해서 RmPacket 클래스를 사용하는 방법을 설명한다.

패키지 이름을 포함한 RmPacket 클래스의 이름은 다음과 같다.

com.javaservice.jennifer.protocol.RmPacket

RmPacket은 com.javaservice.jennifer.protocol.IRmPacket 인터페이스를 구현한 클래스로 REMON 데이터를 나타낸다. IRmPacket 인터페이스가 제공하는 메소드를 통해서 REMON 데이터의 기본 정보를 확인할 수 있다.

package com.javaservice.jennifer.protocol;

public interface IRmPacket {
   public String getAgent();
   public String getScript();
   public long getTime();
   public byte[] toBytes() throws Exception;
}

getAgent 메소드는 REMON 데이터의 에이전트 아이디를 반환하고, getScript 메소드는 REMON 데이터의 스크립트 아이디를 반환한다. 그리고 getTime 메소드는 REMON 데이터가 생성된 시간을 반환한다. 마지막으로 toBytes 메소드는 REMON 데이터를 바이트 형식의 배열로 반환한다. 클래스 스크립트, 필터, ExtraAgent 등을 구현할 때 toBytes 메소드를 사용해야 하는 경우는 없다.

REMON 데이터를 구성하고 읽는 작업은 RmPacket 클래스가 제공하는 메소드로 수행한다. 먼저 클래스 스크립트와 ExtraAgent 등에서 새로운 RmPacket 객체를 만들어서 REMON 데이터를 수집하는 방법을 설명한다.

RmPacket 객체를 생성하는 첫번째 방법은 다음과 같다. 스크립트 아이디와 에이전트 아이디를 파라미터로 생성자를 수행한다.

RmPacket packet = new RmPacket("HEAP", "R01");

이 경우에는 REMON 데이터를 수집한 시간은 현재 시간이 된다. 다음과 같은 방식으로 REMON 데이터를 수집한 시간을 지정할 수 있다.

long time = ...
RmPacket packet = new RmPacket(time, "HEAP", "R01");

REMON 데이터는 필드로 구성된다. 따라서 각 필드는 RmPacket 클래스의 addField 메소드로 추가한다. addField 메소드의 첫번째 파라미터는 java.lang.String 유형으로 필드 이름을 의미한다. 그리고 두번째 파라미터는 com.javaservice.jennifer.type.TYPE 유형으로 필드 데이터 유형에 맞는 하위 클래스를 사용한다.

RmPacket packet = ...
packet.addField("max", new INT(4));

INT 클래스는 int 유형의 데이터를 추가할 때 사용한다. 다음은 TYPE 클래스의 하위 클래스에 대한 설명이다.

TYPE 클래스

클래스

설명

데이터베이스 칼럼 유형

BTYE

1개의 바이트 데이터

이 유형의 필드는 데이터베이스에 저장되지 않는다.

BYTES

32,767개의 바이트 데이터(Short 최대값)

이 유형의 필드는 데이터베이스에 저장되지 않는다.

TINY_BYTES

256개의 바이트 데이터

이 유형의 필드는 데이터베이스에 저장되지 않는다.

LONG_BYTES

2,147,483,647개의 바이트 데이터(Integer 최대 값)

이 유형의 필드는 데이터베이스에 저장되지 않는다.

SHORT

short 형의 데이터

INT

INT

int 형의 데이터

INT

LONG

long 형의 데이터

INT

FLOAT

float 형의 데이터

REAL

DOUBLE

double 형의 데이터

REAL

STRING

java.lang.String 형의 데이터로, 가능한 최대 글자수는 32,672이다.

VARCHAR(32672)

TINY_STRING

java.lang.String 형의 데이터로, 가능한 최대 글자수는 256이다.

VARCHAR(257)

LONG_STRING

java.lang.String 형의 데이터로, 가능한 최대 글자수는 2,147,483,647이다.

이 유형의 필드는 데이터베이스에 저장되지 않는다.

각 TYPE 클래스는 생성자를 통해서 값을 할당한다.

new INT(4);
new FLOAT(3.2f);
new STRING( “WARNING” );

그리고 필터를 구현하는 경우와 같이 전달받은 RmPacket 객체로 REMON 데이터의 내용을 확인하고 값을 수정하는 방법은 다음과 같다. 우선 RmPacket 클래스의 count 메소드로 필드의 개수를 파악할 수 있다.

int count = packet.count();

그리고 인덱스를 파라미터로하는 getField 메소드로 필드를 나타내는 FIELD 객체를 획득한다.

int count = packet.count();
for (int i = 0; i < count; i++) {
   FIELD field = packet.getField(i);
}

FIELD 객체의 getName 메소드로 필드 이름을 확인할 수 있고, getValue 메소드로 값을 나타내는 TYPE 객체를 획득할 수 있다.

for (int i = 0; i < count; i++) {
   FIELD field = packet.getField(i);
   String name = field.getName();
   TYPE type = field.getValue():
}

TYPE 객체의 실제 유형은 TYPE 객체의 getType 메소드가 반환하는 값과 TYPE 클래스의 상수를 비교해서 파악한다. 상수의 이름은 TYPE 하위 클래스의 이름과 동일하다.

TYPE type = field.getValue();
switch (type.getType()) {
case TYPE.INT:
   INT t = (INT) type;
   ...
   break.
case TYPE.STRING:
   STRING t = (STRING) type;
   ...
   break;
}

각 TYPE 객체의 값을 확인하려면 value 멤버 필드를 사용한다. 이 필드의 유형은 TYPE 하위 클래스에 따라서 상이하다.

INT t = (INT) type;
int value = t.value;

해당 값을 변경하는 경우에도 TYPE 유형의 value 멤버 필드를 직접 수정한다.

TYPE type = field.getValue();
if (type.getType() == TYPE.INT) {
   INT t = (INT) type;
   t.value *= 2;
}