JBoss EAP 6과 친해지기 22탄 - JBoss EAP 6 모니터링 #2
본문 바로가기
IT 이야기/JBoss EAP

JBoss EAP 6과 친해지기 22탄 - JBoss EAP 6 모니터링 #2

by 찬찬이 아빠 2021. 1. 11.
반응형
  2. Groovy 스크립트를 이용한 모니터링

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

 

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

 

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

 

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

 

 

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

다음은 스탠드얼론 모드의 데이텃스의 런타임 정보를 주기적으로 남기는 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, DestoryCount, MaxCreationTime, MaxUsedCount, MaxWaitTime, TimeOut, 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/daa-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("TimeOut").asInt() + ", ")
                        print( stats.get("TotalBlockingTime").asInt() + ", ")
                        print( stats.get("TotalCreationTime").asInt() )
                        println()
                    }
                }
            }
            
            sleep(1000)
            
        } catch (e) {
        	println("  disconnected !!! ")
            sleep(1000)
            try {
            	cli.disconnect(0
                cli.connect(System.getProperty("host", "localhost").toString(), System.getProperty("port", "9999").toInteger(), null, null)
            } catch (y) {}
        }
    }
cli.disconnect()

 

 

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

다음은 도메인 모드에서 웹 애플리케이션 세션 정보를 주기적으로 수집하여 출력하는 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(0, System.getProperty("port", "9999").oInteger(), 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, virrtual-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-name(child-type=host)")
            response = result.getResponse()
            for(host in response.get("result").asList()) {
            	result = cli.cmd("/ost=" + host.asString() + "/:read-children-names(child-type=server)")
                
                if (result.inSuccess()) {
                	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("contest-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()

 

 

(3) 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-name(child-type=host)");

 

 

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

package com.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","test123!".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 hhostname = 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();
        }
    }
}
            

 

 

 

  3. JMX 모니터링

JMX는 Java Management eXtensions의 약자로 Java 기반의 모든 애플리케이션을 모니터링하기 위해 만든 표준 기술입니다. JDK에서 지원하는 표준입니다. Java EE 애플리케이션 섭들은 JMX로 모니터링할 수 있도록 서버의 데이터 값들을 MBean(Management Bean) 형태로 제공하고 있습니다.

 

여기서 MBean은JMX에서 모니터링 값을 제공하기 위한 빈입니다. MBean의 종류를 표준 MBean, 동적 MBean, 모델 MBean, 오픈 MBean으로 4가지가 있습니다. MBean들은 데이터값과 오퍼레이션(메소드)을 제공하기 때문에 JMX를 통해 모니터링 값을 가져오고 메소드도 실행할 수 있습니다.

 

 

(1) JConsole

MBean을 모니터링하기 위한 jconsole이라는 툴을 JDK에서 제공하고 있습니다. JBoss EAP 6에서는 MBean과 JVM에 대한 모니터링뿐만 아니라 CLI 명령을 실행할 수 있도록 JConsole을 확장하였습니다. $JBOSS_HOME/bin/jconsole.sh 파일이 JBoss에서 확장한 JConsole을 실행하는 스크립트입니다.

 

 

(2) 연결 방법

JConsole을 실행하려면 $JBOSS_HOME/bin/jconsole.sh 파일을 실행하면 JConsole 초기 화면이 나타납니다.

 

연결 방법에 Local Process와 Remote Process 두 가지 방식이 표시됩니다. Local Process에는 로컬 머신에서 실행된 Java 프로세스들이 표시되고 있습니다. JBoss EAP 6가 실행된 jboss-module.jar로 시작하는 프로세스를 클릭하면 JBoss 인스턴스에 접속됩니다.

 

Remote Process는 URI를 입력하여 원격의 JBoss 인스턴스에 접속할 수 있습니다. 여기서 URI는 Java에서 기본적으로 제공하는 방식과 다른 JBoss URI를 입력해야 합니다.

 

JBoss EAP 6에서는 RMI를 사용하지 않고 NIO(Non Blocking IO)를 사용한 remoting을 사용합니다. NIO를 사용하기 때문에 네트워크 데이터 전송 및 처리 속도가 RMI보다 훨씬 빠릅니다. JMX 연결에도 remoting을 사용하기 위하여 remoting-jmx라는 프로토콜을 제공하고 있습니다.

 

원격의 JBoss EAP 6에 연결하려면 URI로 'service:jmx:remoting-jmx://192.168.0.101:9999'와 같이 입력하고 연결하면 됩니다. 여기서 9999 포트는 스탠드얼론 모드 서버의 관리 포트입니다.

service:jmx:remoting-jmx://192.168.0.101:9999

 

도메인 모드일 경우에느느 접속 방법이 다릅니다. 도메인 컨트롤러에 접속하는 것이 아닙니다. 도메인 모드에서 JMX 정보는 도메인 컨트롤러가 가지고 있지 않습니다. 각각의 JBoss 인스턴스들이 JMX 정보를 개별적으로 제공하기 때문에 개별 인스턴스의 Remoting 포트로 접속해야 합니다. 아래와 같이 서버 인스턴스의 포트 오프셋이 0일 경우 4447 포트로 접속합니다. 오프셋이 100인 인스턴스에 접속하려면 4547 포트로 접속하면 됩니다.

service:jmx:remoting-jmx://192.168.0.101:4447

 

또, 도메인 모드에서 JMX를 사용하려면 domain.xml 파일에서 JMX 서브시스템의 use-management-endpoint를 false로 설정을 변경해야 합니다.

<subsystem xmlns="urn:jboss:domain:jmx:1.3">
	<expose-resolved-model/>
    <expose-expression-model/>
    	<remoting-connector use-management-endpoint="false"/>
    ... 생략 ...
</subsystem>

 

JConsole을 사용하여 서버에 접속하면 Java의 메모리 정보를 그래프로 출력합니다. 탭을 변경하여 메모리, 스레드, 클래스 로딩 상황, MBean 정보들을 확인할 수 있습니다.

 

 

(3) MBean 모니터링

JConsole의 MBeans 탭을 클릭하면 JVM이 제공하는 MBean 정보들과 JBoss EAP 6가 제공하는 MBean 정보를 확인할 수 있습니다. MBean들은 각가 Attribute와 Operation을 제공하여 모니터링 값을 확인하거나 MBean이 제공하는 메소드를 호출할 수 있습니다.

 

 

 

(4) Java 코드에서 MBean 호출 방법

CLI를 호출하여 데이터를 파일로 저장하여 분석했던 것처럼, JMX가 제공하는 MBean 값들을 수집하여 주기적으로 저장하면 분석에 활용할 수 있습니다. JBoss에서는 일반적인 Java의 RMI가 아닌remoting-jmx를 사용하기 때문에 다음과 같은 코드로 JBoss EAP 6의 MBean 값을 가져올 수 있습니다.

// Get a connection to the JBoss EAP MBean server on localhost 
String host = "localhost";
int port = 4447;	//remoting-jmx port
String urlString = System.getProperty("jmx.service.url", "service:jmx:remoting-jmx://" + host + ":" + port);
JMXServiceURL serviceURL = new JMXServiceURL(urlString);

Hashtable h = new Hashtable();
String[] credentials = new String[] { "admin", "test123!" };
h.put("jmx.remote.credentials", credentials);

JMXConnector jmxConnector = JMXConectorFactory.connect(serviceURL, h);
MBeanServerConnection connection = jmxConnector.getMBeanServerConnection();

Hashtable<String, String> table = new Hashtable<String, String>();
table.put("subsystem", "messaging");
table.put("hornetq-server", "default");

ObjectName name = ObjectName.getInstance("jboss.as", table);
Boolean started = (Boolean) connection.getAttribute(name, "started");
System.out.println(started);

jmxConnector.close();

 

 

참고 서적 : 거침없이 배우는 JBoss

반응형

댓글