Kubernetes Java에서 JMX Exporter 설정하기

0

java

목록 보기
19/19

Java Memory 분석을 위한 JMX-expoter 설치와 Grafana 연동

https://devopscube.com/prometheus-jmx-exporter-on-kubernetes/

Java의 JVM 특성 상 OS에 힙 메모리 반환이 적극적이지 않다. c, c++과 같은 언매니지드 언어의 경우 graph를 보면 출렁출렁 하면서 바로바로 heap 메모리를 반환하는 것을 볼 수 있다.

           ---
   ---    /   \
  /   \  /     \   
 /     \/       \  /\
/                \/  ....

golang은 매니지드 언어임에도 불구하고 힙 메모리를 쭉 할당받았다가, OS에 메모리를 쑥 반환하는 그래프를 그린다.

                         --------
    -----    --------   /       |
   /    |   /       |  /        |
  /     |  /        | /         |
 /      | /         |/          |
/       |/                      |

반면에 Java의 경우는 OS로부터 할당한 힙 메모리 기준으로 보면 쭉 우상향을 그리다가 어느순간부터 일자 곡선을 그린다.

              --------------
             /
            /
         ---
        /
       /
  -----
 /
/

이는 java app을 prometheus의 container_memory_working_set_bytes를 기준으로 보았을 때이다. 즉, container에 할당된 memory를 보는 것이지 실제 app이 사용하고 있는 메모리 현재 사용량과는 다를 수 있다.

container는 heap memory 할당을 위해 500MB를 차지하고 있다면 java는 300MB까지 치솟다가 GC에 의해서 200MB까지 내려가고를 반복하고 있을 수 있다. 단지 go와 c언어의 경우는 바로바로 OS에 사용하지 않는 heap 메모리를 반환하는 반면에, Java는 heap 메모리를 사용하고 있지 않더라도 적극적으로 OS에 반환하지 않기 때문에 위의 그래프 처럼 java app을 구동하는 container의 메모리 현황은 쭉 증가만 하는 것이다. 돈 먹는 하마

때문에 java app을 제대로 instrumentation하려면 JMX를 사용하는 것이 좋다. 그리고 가장 전통적이기 때문에 고대 개발자분들이 좋아한다

문제는 container와 kubernetes 환경에서 Local에 설치한 JMX를 쓰기가 여간 귀찮은게 아니다. public cloud처럼 쉽게쉽게 설정할 수 있는 환경에서는 별반 문제가 될 것은 없지만, on-premise 환경의 private network를 사용하고 있다면, 내 의지와는 다르게 인프라 관리 부서나 하드웨어 담당자가 불허를 할 수도 있다.

그래서 JMX exporter를 설치해보고 prometheus와 연동한 뒤에 grafana에서 어떻게 나오는 지 확인해보도록 하자.

단, Java를 instrumentation하기 위해서 JMX exporter를 연동한다고 했지, Spring 방법으로 하겠다는 것은 아니다. 그러니 Spring-native한 방법(혹은 Spring Actuator) 다른 분의 실습을 참고하도록 하자.

https://devopscube.com/content/images/2025/03/jmx-on-kubernetes-1-1.gif
출처: https://devopscube.com/prometheus-jmx-exporter-on-kubernetes/

앞으로 할 실습의 예상도이다. 단, 해당 post를 보고 배낀 것이라서 아래에 우리가 앞으로 할 것들과는 약간 다르다.

MBean(Managed Bean) server는 Java App의 JVM에 있으며, application health와 성능에 연관된 data들을 관리한다. MBean은 application metrics인 status, actions, operation 정보들을 외부로 노출시켜 제공해주는 일을 해준다.

Mbean server에 접근하고 health와 성능 data를 가져간다. Prometheus JMX exporter는 Java Agent를 사용하여 Mbean server에 접근하여 java app에 관한 성능 데이터를 정제하고 가공해준다.

일반적으로 JMX exporter는 Java Agent를 말하는 것인데, 일반적으로 jar 실행 시에 -javaagent로 JMX exporter jar를 주어서 실행시키는 방법이 대표적이다.

Java app에 JMX agent 설정

먼저 JMX를 설정해주어야 한다. jar 파일을 실행할 때 다음의 옵션을 주면된다.

java -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=5555 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -jar target.jar

이렇게만 하면 실행 중인 java app의 JVM을 instrument하는 jmx 서버가 켜지게 된다. 일반적인 visualVM 연동 방법과 별반 다를 바 없다. 그러나 이렇게만 설정하면 prometheus에서 데이터를 scrape할 수가 없다. 따라서, JVM metric 정보들을 prometheus에 export해줄 수 있는 expoter가 필요하다. 그것이 바로 jmx_prometheus_javaagent이다.

https://github.com/prometheus/jmx_exporter/releases

위의 링크에 접속하면 release가 있고, 여기서 jar를 다운로드 할 수 있다. 필자는 jmx_prometheus_javaagent-0.18.0.jar을 다운받았다. Dockerfile안 /opt/jmx_exporter/jmx_prometheus_javaagent-0.18.0.jar에 적재해두었다.

다음으로 jmx_prometheus_javaagent를 위한 config가 필요하다. config는 다음과 같이 만들 수 있다.

  • jmx_config.yaml
startDelaySeconds: 0
ssl: false
rules:
- pattern: ".*"

구체적인 configuration에 관련된 정보들은 https://prometheus.github.io/jmx_exporter/1.3.0/java-agent/http-mode/ 여기에서 볼 수 있다. 위의 config 내용은 ssl을 지원하지 않고 모든 metrics들을 export하겠다는 것이다.

참고로 kubernetes 환경에서 사용하고 있다면 configmap으로 빼내어 동작

이제 jmx_prometheus_javaagent의 jar와 config를 java app 실행 시에 옵션으로 알려주어야 한다. 다음의 옵션을 사용하면 된다.

java -javaagent:/opt/jmx_exporter/jmx_prometheus_javaagent-0.18.0.jar=7000:/opt/export-config/jmx_config.yaml -jar target.jar

7000 port로 export하겠다는 것이다. port 번호는 각자의 상황에 맞게 수정하도록 하자.

각자의 jmx_prometheus_javaagent jar 위치와 jmx_config.yaml의 위치를 설정해주도록 하자.

이제 jmx 설정과 jmx exporter인 jmx agent의 설정을 정리해보자.

YOUR_JAVA_OPT=""
JMX_EXPORTER_OPT="-javaagent:/opt/jmx_exporter/jmx_prometheus_javaagent-0.18.0.jar=7000:/opt/export-config/jmx_config.yaml"
JMX_OPT="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=5555 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false"
java ${YOUR_JAVA_OPT} ${JMX_EXPORTER_OPT} ${JMX_OPT} -jar target.jar

실행해보고 잘 jvm metric들이 export되는 지 확인하기 위해서 다음의 명령어를 입력하도록 하자.

curl localhost:7000

metric들이 쭉 나오면 성공이고 안나오면 실패이므로 뭔가 설정이 잘못된 것이다.

Prometheus, Grafana와의 연동

이제 Prometheus에 jmxagent를 연동하고 grafana로 dashboard를 만들어보도록 하자.

prometheus helm을 아래 링크에서 다운 받을 수 있다.
https://github.com/prometheus-community/helm-charts

위의 링크에서 https://github.com/prometheus-community/helm-charts/tree/main/charts/prometheus 이것을 선택해서 다운 받았다.

다음으로 grafana는 아래의 링크에서 helm을 다운 받을 수 있다.

https://github.com/grafana/helm-charts

위의 링크에서 https://github.com/grafana/helm-charts/tree/main/charts/grafana 이것을 선택해서 다운 받았다.

helm 설치가 완료되었다면, prometheus의 scrape 옵션을 주어야한다. values.yaml에 해당 scrape를 추가 설정하도록 하자.

  • values.yaml
prometheus:
  ...
  extraScrapeConfigs: |
    - job_name: jmx-exporter
      metrics_path: '/metrics'
      scheme: http
      kubernetes_sd_configs:
      - role: pod
        namespaces:
          names:
            - {YOUR_NAMESPACE}
      relabel_configs:
      - source_labels: [__meta_kubernetes_pod_name]
        action: keep
        regex: "{REGEX_WITH_YOUR_POD_NAME}"
      - source_labels: [__meta_kubernetes_pod_name]
        target_label: __address__
        replacement: "{KUBERNETES_SERVICE_NAME}"

필자의 경우는 statefulset이였기 때문에 매칭되는 pod를 headless service에 연결하도록 했다.
아래의 내용은 각자의 상황에 맞게 채우면 된다.
1. {YOUR_NAMESPACE}: resource가 있는 namespace를 입력해준다.
2. {REGEX_WITH_YOUR_POD_NAME}: scrape할 대상 pod의 이름, 정규 표현식이 가능하다.
3. {KUBERNETES_SERVICE_NAME}: 대상 pod에 대해서 어떤 address로 metric을 얻어올 지에 대한 정의

설정이 완료되었다면 prometheus에 들어가서 jvm_memory_bytes_used metrics을 검색해보자. 잘 나오면 성공이다.

다음으로 grafana를 구성해보도록 하자. 먼저 dashboard를 구성해보도록 하자. JMX metric들을 이쁘게 볼 수 있는 dashboard들이 여러개 있는데, 7727이 가장 유명하다. 필자의 경우는 14845을 사용했다.

아래 링크에 접속하여 화면 아래로 내려가면 download 버튼이 있다. 가장 최신 것을 선택해서 사용하도록 하자.
https://grafana.com/grafana/dashboards/14845-jmx-dashboard-basic/

다운로드 후에는 dashboard의 내용이 적힌 json 파일이 만들어진다. helm에서 grafana dashboard를 설정하기 위해서는 아래에 directory path에 dashboard json 파일을 넣어주면 된다.

grafana/dashboards
  - custom-dashboard.json
  - jmx-dashboard_v1.json

dashboards 디렉터리에 json으로 구성된 dashboard를 넣어주고, values.yaml에서 해당 jmx-dashboard_v1.json path를 지정해주면 된다.

아래는 grafana helm value 설정으로 prometheus랑 jmx-dashboard를 설정해주고 있다.

  • values.yaml
grafana:
  enabled: true
  service:
    type: NodePort
    nodePort: {"YOUR_NODE_PORT"}
  adminPassword: "admin"
  datasources:
    datasources.yaml:
      apiVersion: 1
      datasources:
      - name: Prometheus
        type: prometheus
        url: {"PROMETHEUS_SERVICE_FQDN"}
        access: proxy
        isDefault: true
  dashboardProviders:
    dashboardproviders.yaml:
      apiVersion: 1
      providers:
      - name: 'default'
        orgId: 1
        folder: ''
        type: file
        disableDeletion: false
        editable: true
        options:
          path: /var/lib/grafana/dashboards/default
  dashboards:0
    default:
      jmx:
        file: dashboards/jmx-dashboard_v1.json
  1. datasources: 어디서 데이터를 가져올 지 결정할 수 있다. prometheus를 사용하고 있으므로 url에 prometheus address를 써주도록 하자.
  2. dashboardProviders: 어떤 dashboard를 쓸 지 결정하는 부분이다. 여기서 적은 name의 value값이 아래에 나올 dashbaords와 일치해야한다. 일치한 dashboard를 path 부분에 저장하는 것이다.
  3. dashboards: 어떤 key값으로 dashboard를 정의할 지 지정한다. 여기서는 default이고, dashboardProvider를 통해서 /var/lib/grafana/dashboards/default 경로에 jmx.json이라는 이름으로 저장된다.

이제 실행 시켜보면 이쁜 jmx dashbaord가 보일 것이다.

0개의 댓글