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 실행 절차는 다음과 같다.
- 먼저 "/:read-children-names(child-type=host)" CLI를 실행하여 호스트 목록을 가져온다.
- 해당 호스트마다 CLI 명령을 실행하여 서버 목록을 가져온다( :read-children-names(child-type=server) )
- 서버마다 배포된 애플리케이션 정보를 수집한다( :read-children-names(child-type=deployment) )
- 애플리케이션의 런타임 세션 정보를 가져온다( subsystem=web:read-resource(include-runtime=true) )
- 세션정보를 출력한다.
실행하는 방법은 스탠드얼론 모드에 대한 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();
}
}
}