JMX MBean metrics の可視化を試す
今回は Observability の例として、ふとしたきっかけで知った JMX MBean metrics を扱ってみます。
JMX (Java Management Extensions) というと、Java program の debug や一時的な状態確認のような用途の印象があったのですが、これを用いて metrics を公開する application があるのははじめて知りました。この仕組みを利用することにより、application 特有の指標情報を収集して可観測性を向上できます。
検証環境をつくる
Docker Compose を使います。
末尾に compose.yaml
を置いておきますが、以下のような構造にしています。
workspace
├─ compose.yaml
├─ jmxexporter.yaml
└─ prom-data
└─ prometheus.yaml
JMX MBean metrics を公開する application として、Kafka を例にとっています。ちょっと大仰ですが、ちゃんと Kafka を cluster で起動しているので yaml (記事末尾) が大きくなっています。また、動作の様子を視覚的に確認できるように Kafka UI も使えるようにしています。
JMX Exporter
今回のポイントです。
JMX metrics を可視化するにあたり、今回は Prometheus を使ってみました。Prometheus は JMX metrics をそのまま扱うことはできず、その間の橋渡しが必要になるようです。
そこで、JMX Exporter を使います。
以下のような設定 jmxexporter.yaml
を使いました。
username
と password
の指定が可能ですが、認証無しで使用する場合は空欄のまま (下の例のまま) でだいじょうぶです。
startDelaySeconds: 3
hostPort: kafka0:1099
username:
password:
whitelistObjectNames: ["kafka.server:*"]
rules:
- pattern: ".*"
一番目の Kafka node に JMX で接続するように設定しています。
Kafka には、compose.yaml
で以下のように JMX の設定を入れています。
KAFKA_JMX_OPTS: "-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=kafka0 -Dcom.sun.management.jmxremote.rmi.port=1099"
JMX_PORT: 1099
認証や metrics の filtering 設定は最小限になっています (ほとんどしていません)。 whitelistObjectNames
は、 ["kafka.server:type=BrokerTopicMetrics,*"]
のようにして一部の metrics に限定することもできるようです。このあたりは、実運用環境に入れる際には慎重に設定が必要です。
この設定により、Kafka に JMX で接続し、取得した metrics を Prometheus に対して公開するようになります。
Prometheus
最終的に可視化を確認するために Prometheus を起動します。
以下の設定を使っています。
global:
scrape_interval: 10s
evaluation_interval: 10s
scrape_configs:
- job_name: 'jmx'
static_configs:
- targets:
- jmxexporter:5556
上で説明した、JMX Exporter に接続するように設定しています。
結果
Prometheus の UI で、Kafka の metrics が参照できるようになりました。
参考に、Kafka UI の画像も貼っておきます。
今回はじめて動作させたのですが、きれいにできていて開発・運用などで便利に使えそうですね。
まとめ
JMX metrics の取得について調べ、Kafka が公開する metrics を視覚化してみました。安定して値が取れると監視に大きく役立ちそうです。実際に運用に入れる前には、パフォーマンスやセキュリティも確認が必要そうです。
参考
ほか、様々なサイトを参考にさせていただきました。
compose.yaml
最後に、ちょっと長くなってしまいましたが compose.yaml
の全体を置いておきます。
version: "3.7"
# create Zookeeper service to manage Kafka
services:
zookeeper:
image: confluentinc/cp-zookeeper:5.2.2
environment:
ZOOKEEPER_CLIENT_PORT: "2181"
# create first Kafka node
kafka0:
image: confluentinc/cp-kafka:5.2.2
ports:
- "9092:9092"
environment:
KAFKA_BROKER_ID: 0
KAFKA_ZOOKEEPER_CONNECT: "zookeeper:2181"
KAFKA_ADVERTISED_LISTENERS: "INTERNAL://kafka0:19092,EXTERNAL://${DOCKER_HOST_IP:-127.0.0.1}:9092"
KAFKA_INTER_BROKER_LISTENER_NAME: "INTERNAL"
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: "INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT"
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: "1"
KAFKA_JMX_OPTS: "-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=kafka0 -Dcom.sun.management.jmxremote.rmi.port=1099"
JMX_PORT: 1099
depends_on:
- "zookeeper"
# create second Kafka node
kafka1:
image: confluentinc/cp-kafka:5.2.2
ports:
- "9093:9093"
environment:
KAFKA_BROKER_ID: 1
KAFKA_ZOOKEEPER_CONNECT: "zookeeper:2181"
KAFKA_ADVERTISED_LISTENERS: "INTERNAL://kafka1:19093,EXTERNAL://${DOCKER_HOST_IP:-127.0.0.1}:9093"
KAFKA_INTER_BROKER_LISTENER_NAME: "INTERNAL"
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: "INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT"
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: "1"
depends_on:
- "zookeeper"
# create third Kafka node
kafka2:
image: confluentinc/cp-kafka:5.2.2
ports:
- "9094:9094"
environment:
KAFKA_BROKER_ID: 2
KAFKA_ZOOKEEPER_CONNECT: "zookeeper:2181"
KAFKA_ADVERTISED_LISTENERS: "INTERNAL://kafka2:19094,EXTERNAL://${DOCKER_HOST_IP:-127.0.0.1}:9094"
KAFKA_INTER_BROKER_LISTENER_NAME: "INTERNAL"
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: "INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT"
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: "1"
depends_on:
- "zookeeper"
kafka-ui:
image: provectuslabs/kafka-ui:latest
ports:
- "8080:8080"
depends_on:
- "kafka0"
environment:
KAFKA_CLUSTERS_0_NAME: local
KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka0:19092
jmxexporter:
image: bitnami/jmx-exporter
command:
- "5556"
- "/opt/bitnami/jmx-exporter/config.yaml"
volumes:
- ./jmxexporter.yaml:/opt/bitnami/jmx-exporter/config.yaml
prometheus:
image: prom/prometheus
command: --config.file=/prometheus-data/prometheus.yaml
ports:
- "9090:9090"
volumes:
- ./prom-data:/prometheus-data