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/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를 주어서 실행시키는 방법이 대표적이다.
먼저 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는 다음과 같이 만들 수 있다.
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에 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를 추가 설정하도록 하자.
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를 설정해주고 있다.
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
datasources
: 어디서 데이터를 가져올 지 결정할 수 있다. prometheus를 사용하고 있으므로 url
에 prometheus address를 써주도록 하자.dashboardProviders
: 어떤 dashboard를 쓸 지 결정하는 부분이다. 여기서 적은 name
의 value값이 아래에 나올 dashbaords
와 일치해야한다. 일치한 dashboard를 path
부분에 저장하는 것이다.dashboards
: 어떤 key값으로 dashboard를 정의할 지 지정한다. 여기서는 default
이고, dashboardProvider
를 통해서 /var/lib/grafana/dashboards/default
경로에 jmx.json
이라는 이름으로 저장된다. 이제 실행 시켜보면 이쁜 jmx dashbaord가 보일 것이다.