본문으로 건너뛰기

19.2 Groovy스크립트를 이용한 모니터링

모니터링에서 현재 시점의 데이터도 중요하지만, 모니터링 항목들의 값을 수집하여 분석하면 시스템의 상황이나 사용량에 대한 추세를 확인할 수 있다. 즉, 사용자가 어떤 시간대에 애플리케이션을 많이 사용하는지, 어떤 시간에 리소스가 부족한지 등 시간에 따라 변화하는 값들을 분석하면 장애 상황을 미리 대처하는 데 도움이 된다.

많은 모니터링 툴들이 있지만, 스크립트나 자바 코딩을 통해 JBoss에서 제공하는 값을 이용하여 파일로 데이터를 남기는 간단한 코딩 방법을 살펴보자.

Groovy는 자바 플랫폼에서 실행할 수 있는 동적 스크립트 언어이다. 자바 언어와 유사하고 기존의 모든 자바 객체와 라이브러리를 그대로 사용할 수 있기 때문에 자바 언어에서 컴파일하지 않고 스크립트 파일로 사용하고 싶을 때 장벽 없이 쉽게 사용할 수 있다.

다음에서 스탠드얼론 모드의 데이터소스 런타임 정보를 수집하는 스크립트와 도메인 모드에서 애플리케이션별 세션의 런타임 정보를 수집하는 스크립트를 설명한다.

이 스크립트 파일은 CLI 명령을 실행하는 것이기 때문에 조금만 수정하면, 앞서 설명한 다양한 모니터링 항목들을 모두 수집할 수 있을 것이다.

데이터소스 정보 수집 스크립트

다음은 스탠드얼론 모드의 데이터소스의 런타임 정보를 주기적으로 남기는 Groovy 스크립트이다. JBoss에서는 JBoss의 CLI 명령을 Java API에서 사용할 수 있도록 jboss-cli-client.jar 파일을 제공하고 있다. Groovy 스크립트에서 CLI API를 사용하여 JBoss에 CLI 명령을 전송하고 결과를 받아 처리할 수 있다.

예제를 실행하려면 Groovy(http://groovy.codehaus.org/)를 다운로드 받아 설치하고, jboss-cli-client.jar 파일을 클래스 패스에 추가해야 한다.

export GROOVY_HOME=$DIRNAME/groovy-2.1.1

export CLASSPATH=$CLASSPATH:/$DIRNAME/lib/jboss-cli-client.jar

export PATH=$PATH:$GROOVY_HOME/bin

다음과 같이 Groovy 스크립트를 실행한다.

groovy standalone_datasource.groovy

스크립트의 결과는 표준 출력(stdout)에 출력되기 때문에 백그라운드 프로세스로 실행하면서 출력을 리다이렉트하면 파일에 결과를 저장해 놓을 수 있다.

nohup groovy -Dhost=${HOST} -Dport=${PORT} scripts/standalone_datasource.groovy >> logs/${HOST}-${PORT}-standalone_datasource.log &

출력 형식은 CSV(Comma Separated Values)이기 때문에 파일을 엑셀에서 불러들여서 그래프를 그려 분석할 수 있다.

다음은 standalone_datasource.groovy 파일의 내용이다.

import org.jboss.as.cli.scriptsupport.*

try {
cli = CLI.newInstance()
cli.connect(System.getProperty("host", "localhost").toString(), System.getProperty("port", "9999").toInteger(), null, null)
} catch (e) {
println("Can't connect to jboss management")
return
}

println("==============================================================================================================")
println("Time, DataSource, ActiveCount, AvailableCount, AverageBlockingTime, AverageCreationTime, CreatedCount, DestroyedCount, MaxCreationTime, MaxUsedCount, MaxWaitTime, TimedOut, TotalBlockingTime, TotalCreationTime")
println("==============================================================================================================")

while(true) {
try {
// ./jboss-cli.sh --controller=localhost:9999 --connect --command="/subsystem=datasources/data-source=MySQLDS/statistics=pool:read-resource(include-runtime=true)"
result = cli.cmd("/subsystem=datasources/:read-children-names(child-type=data-source)")

if (result.isSuccess()) {
for (datasource in result.getResponse().get("result").asList()) {
print(new Date().format("yyyy-MM-dd kk:mm:ss Z") + ", ")
print(datasource.asString() + ", ")

result = cli.cmd("/subsystem=datasources/data-source=" + datasource.asString() + "/statistics=pool:read-resource(include-runtime=true)")

if (result.isSuccess()) {
stats = result.getResponse().get("result")
print( stats.get("ActiveCount").asInt() + ", ")
print( stats.get("AvailableCount").asInt() + ", ")
print( stats.get("AverageBlockingTime").asInt() + ", ")
print( stats.get("AverageCreationTime").asInt() + ", ")
print( stats.get("CreatedCount").asInt() + ", ")
print( stats.get("DestroyedCount").asInt() + ", ")
print( stats.get("MaxCreationTime").asInt() + ", ")
print( stats.get("MaxUsedCount").asInt() + ", ")
print( stats.get("MaxWaitTime").asInt() + ", ")
print( stats.get("TimedOut").asInt() + ", ")
print( stats.get("TotalBlockingTime").asInt() + ", ")
print( stats.get("TotalCreationTime").asInt() )
println( )

}

}

}
sleep(1000)

} catch (e) {

println(" disconnected !!! ")
sleep(1000)

try {

cli.disconnect()

cli.connect(System.getProperty("host", "localhost").toString(), System.getProperty("port", "9999").toInteger(), null, null)

} catch (y) {}
}
}

cli.disconnect();

애플리케이션 세션 정보수집 스크립트

다음은 도메인 모드에서 웹 애플리케이션 세션 정보를 주기적으로 수집하여 출력하는 groovy 스크립트이다.

스탠드얼론 모드에서는 CLI에서 JBoss 인스턴스 하나에 대한 정보만 가져오기 때문에 간단하지만, 도메인 모드에서는 도메인 컨트롤러가 관리하는 호스트의 서버 인스턴스 정보를 수집하여 각 서버의 런타임 정보를 수집하여야 한다.

아래 groovy 스크립트에서 CLI 실행 절차는 다음과 같다.

  1. 먼저 "/:read-children-names(child-type=host)" CLI를 실행하여 호스트 목록을 가져온다.
  2. 해당 호스트마다 CLI 명령을 실행하여 서버 목록을 가져온다( :read-children-names(child-type=server) )
  3. 서버마다 배포된 애플리케이션 정보를 수집한다( :read-children-names(child-type=deployment) )
  4. 애플리케이션의 런타임 세션 정보를 가져온다( subsystem=web:read-resource(include-runtime=true) )
  5. 세션정보를 출력한다.

실행하는 방법은 스탠드얼론 모드에 대한 Groovy 스크립트와 같다. 실행 시 HOST와 PORT 환경변수는 도메인 컨트롤러의 IP와 네이티브 관리 포트를 지정해야 한다.

export HOST=192.168.0.101

export PORT=9999

nohup groovy -Dhost=${HOST} -Dport=${PORT} scripts/domain_session.groovy >> logs/${HOST}-${PORT}-domain_session.log &

위와 같이 실행하면 파일이 생성되기 때문에, 이 파일을 엑셀에서 분석하여 시간별, 애플리케이션 별 세션의 상황을 모니터링 할 수 있다.

다음은 domain_session.groovy 스크립트 파일이다.

import org.jboss.as.cli.scriptsupport.*

try {

cli = CLI.newInstance()
cli.connect(System.getProperty("host", "localhost").toString(), System.getProperty("port", "9999").toInteger(), null, null)

} catch (e) {

println("Can't connect to jboss management")

return

}

println("==============================================================================================================")

println("Time, Host, Server, Application, active-sessions, context-root, duplicated-session-ids, expired-sessions, max-active-sessions, rejected-sessions, session-avg-alive-time, session-max-alive-time, sessions-created, virtual-host")

println("==============================================================================================================")

while(true) {
try {
// ./jboss-cli.sh --controller=localhost:9999 --connect --command="/deployment=session.war/subsystem=web:read-resource(include-runtime=true)"
result = cli.cmd("/:read-children-names(child-type=host)")
response = result.getResponse()

for(host in response.get("result").asList()) {
result = cli.cmd("/host=" + host.asString() + "/:read-children-names(child-type=server)")

if (result.isSuccess()) {
for (server in result.getResponse().get("result").asList()) {
result = cli.cmd("/host=" + host.asString() + "/server=" + server.asString() + ":read-children-names(child-type=deployment)")

if (result.isSuccess()) {
for (application in result.getResponse().get("result").asList()) {
print(new Date().format("yyyy-MM-dd kk:mm:ss Z") + ", ")
print(host.asString() + ", ")
print(server.asString() + ", ")
print(application.asString() + ", ")
result = cli.cmd("/host=" + host.asString() + "/server=" + server.asString() +

"/deployment=" + application.asString() + "/subsystem=web:read-resource(include-runtime=true)")

if (result.isSuccess()) {
stats = result.getResponse().get("result")
print( stats.get("active-sessions").asInt() + ", ")
print( stats.get("context-root").asString() + ", ")
print( stats.get("duplicated-session-ids").asInt() + ", ")
print( stats.get("expired-sessions").asInt() + ", ")
print( stats.get("max-active-sessions").asInt() + ", ")
print( stats.get("rejected-sessions").asInt() + ", ")
print( stats.get("session-avg-alive-time").asInt() + ", ")
print( stats.get("session-max-alive-time").asInt() + ", ")
print( stats.get("sessions-created").asInt() + ", ")
print( stats.get("virtual-host").asString() )
println( )
}
}
}
}
}
}
sleep(1000)

} catch (e) {
println(" disconnected !!! ")
e.printStackTrace()
sleep(1000)

try {
cli.disconnect()
cli.connect(System.getProperty("host", "localhost").toString(), System.getProperty("port", "9999").toInteger(), null, null)
} catch (y) {}
}
}

cli.disconnect();

Java 코드에서 CLI 명령어 사용하기

Groovy는 Java의 모든 객체와 라이브러리를 그대로 사용할 수 있다고 설명하였다. 스크립트 실행에 필요한 jboss-cli-client.jar 파일은 원래 Java에서 CLI API를 사용하기 위해 만들어진 것이다. Java 코드에서 아래와 같이 JBoss 컨트롤러에 접속하여 CLI 명령을 실행할 수 있다.

코드의 주요 내용은 다음과 같다.

  • CLI를 실행할 CommandContext 인스턴스를 생성한다.

CommandContext ctx = CommandContextFactory.getInstance();

  • 컨트롤러에 접속한다.

ctx.connectController("192.168.0.101", 9999);

  • CLI명령을 실행한다.

ctx.handle(“cd /”);

  • CLI 오퍼레이션을 실행한다.

ModelNode hostname = ctx.buildRequest(":read-children-names(child-type=host)");

다음의 코드는 CLI로 도메인 컨트롤러에 접속하여 호스트의 목록을 가져와 출력하는 것이다.

package com.khan.jbosscli.test;
import java.util.Iterator;
import java.util.List;
import org.jboss.as.cli.CliInitializationException;
import org.jboss.as.cli.CommandContext;
import org.jboss.as.cli.CommandContextFactory;
import org.jboss.as.cli.CommandLineException;
import org.jboss.dmr.ModelNode;

public class TestCLI {
public static void main(String[] args) {
// Initialize the CLI context
final CommandContext ctx;

try {
ctx = CommandContextFactory.getInstance().newCommandContext("admin", "opennaru!234".toCharArray());
} catch(CliInitializationException e) {
throw new IllegalStateException("Failed to initialize CLI context", e);
}

try {
// connect to the server controller
ctx.connectController("192.168.0.101", 9999);
// execute commands and operations
ctx.handle("cd /");

ModelNode hostname = ctx.buildRequest(":read-children-names(child-type=host)");
List<ModelNode> list = hostname.asList();
Iterator i = list.iterator();

while( i.hasNext() ) {
ModelNode a = (ModelNode) i.next();
System.out.println( a.toString() );
}
} catch (CommandLineException e) {
// the operation or the command has failed
} finally {
// terminate the session and
// close the connection to the controller

ctx.terminateSession();
ctx.disconnectController();
}
}
}