引言
最近在测试环境中部署了一个Java容器,但在查看容器监控时,发现了一个奇怪的现象:CPU使用量一直在波动,而内存使用量却始终保持不变。
经过详细分析,发现KubeSphere平台监控内存使用的指标是container_memory_working_set_bytes / container_spec_memory_limit_bytes。由于Java容器在启动时就已经申请了所需的内存(包括JVM内存和预留内存等),所以监控指标的数据一直保持不变。
然而,我们希望能够实时查看JVM内存的实际使用情况。为此,我们需要让容器暴露相关的JVM监控指标,并将这些指标接入Prometheus,再通过Grafana进行展示。这样可以更精确地监控Java应用的内存使用情况,并根据实际情况进行优化和调整。
环境
| 组件名称 | 版本 |
|---|---|
| Kubernetes | v1.25.10 |
| KubeSphere | 3.4.1 |
| SpringBoot | 3.2.0 |
| JDK | 17 |
| JMX Exporter | 1.0.1 |
| Prometheus | v2.39.1 |
| Grafana | 10.0.3 |
实现
JMX Exporter
官方简介:
JMX to Prometheus exporter: a collector that can configurable scrape and expose mBeans of a JMX target.
This exporter is intended to be run as a Java Agent, exposing a HTTP server and serving metrics of the local JVM. It can be also run as a standalone HTTP server and scrape remote JMX targets, but this has various disadvantages, such as being harder to configure and being unable to expose process metrics (e.g., memory and CPU usage). In particular all the
jvm_*metrics likejvm_classes_loaded_total,jvm_threads_current,jvm_threads_daemonandjvm_memory_bytes_usedwon't be availabe if using the standalone http server.
简单翻译下就是:
此导出器旨在作为 Java 代理运行,公开 HTTP 服务器并提供本地 JVM 的指标。它也可以作为独立的 HTTP 服务器运行并抓取远程 JMX 目标,但这有各种缺点,例如更难配置和无法公开进程指标(例如,内存和 CPU 使用率)。特别是,如果使用独立的 http 服务器,则所有
jvm_*指标(如jvm_classes_loaded_total、jvm_threads_current和jvm_threads_daemonjvm_memory_bytes_used)都将不可用。JMX 到 Prometheus 导出器:一个收集器,可以配置、抓取和公开 JMX 目标的 mBeans。
并且提供了两种启动方式:
- Running the Java Agent 运行 Java 代理
启动示例:java -javaagent:./jmx_prometheus_javaagent-1.0.1.jar=12345:config.yaml -jar yourJar.jar
- Running the Standalone HTTP Server 运行独立 HTTP 服务器
启动示例:java -jar jmx_prometheus_httpserver-1.0.1.jar 12345 config.yaml
说明:官方强烈建议使用Java Agent方式运行,并且使用独立进程运行的话,- jvm_* 将不可用。
镜像构建
根据官方建议,我们也采用Java Agent方式运行。
启动Jar时,需要指定JMX Exporter的Jar包以及配置文件。目前官方最新版本是1.0.1。
配置文件参考:
ssl: false
lowercaseOutputName: false
lowercaseOutputLabelNames: false
JMX Exporter的Jar包地址:
https://repo1.maven.org/maven2/io/prometheus/jmx/jmx_prometheus_javaagent/1.0.1/jmx_prometheus_javaagent-1.0.1.jar
Dockerfile文件参考:
FROM openjdk:17
ENV LANG C.UTF-8
ADD test.jar /container/application.jar
ADD start.sh /container/start.sh
ADD prometheus-jmx-config.yaml /container/prometheus-jmx-config.yaml
ADD jmx_prometheus_javaagent-1.0.1.jar /container/jmx_prometheus_javaagent-1.0.1.jar
RUN [ "chmod", "777", "/container/start.sh" ]
RUN echo "Asia/Shanghai" > /etc/timezone
ENTRYPOINT ["/container/start.sh"]
EXPOSE 8080
JVM启动脚本 start.sh
#!/bin/sh
if [ "$MEM_OPTS" = "" ]; then
SYS_PARAMS="$SYS_PARAMS -Xms2048m -Xmx2048m -XX:NewRatio=1 -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m -XX:+AlwaysPreTouch -Xss256k"
else
SYS_PARAMS="$SYS_PARAMS $MEM_OPTS"
fi
if [ "$OPTIMIZE_OPTS" = "" ]; then
SYS_PARAMS="$SYS_PARAMS -XX:AutoBoxCacheMax=20000"
else
SYS_PARAMS="$SYS_PARAMS $OPTIMIZE_OPTS"
fi
if [ "$SHOOTING_OPTS" = "" ]; then
SYS_PARAMS="$SYS_PARAMS -XX:-OmitStackTraceInFastThrow -XX:ErrorFile=errorGcLogs/hs_err_%p.log"
else
SYS_PARAMS="$SYS_PARAMS $SHOOTING_OPTS"
fi
JAR_AGENT_PARAMS="$JAR_AGENT_PARAMS -javaagent:/container/jmx_prometheus_javaagent-1.0.1.jar=8088:/container/prometheus-jmx-config.yaml"
nohup java $JAR_AGENT_PARAMS -jar -server $SYS_PARAMS /container/application.jar $JAR_PARAMS &
最后可以使用docker build构建镜像。
部署
Deployment部署上述构建的镜像即可。
在Service中需要将 JMX Exporter的端口做个映射
spec:
ports:
- name: http-0
protocol: TCP
port: 8080
targetPort: 8085
- name: jmx-metrics
protocol: TCP
port: 8088
targetPort: 8088
部署成功之后,可以访问容器中JMX Exporter端口的服务,就可以看到采集的JVM指标数据了。
接入Prometheus
由于使用的是Kubesphere,直接可以接入平台内部的Prometheus,可以使用ServiceMonitor实现。
部署文件参考:
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
labels:
app: test-service
name: test-service
namespace: test
spec:
endpoints:
- interval: 1m
path: /metrics
port: jmx-metrics
scheme: http
scrapeTimeout: 10s
namespaceSelector:
matchNames:
- test
selector:
matchLabels:
app: test-service
注意:命名空间和标签需要和Service保持一致
添加Grafana监控面板
Grafana需要接入Promethues,添加数据源即可。
JMX Exporter本身提供了一个监控面板,ID为8563,但我实际使用下来,很多指标数据拿不到,可能是很久没更新了,跟JMX Exporter版本对不上。。。
于是用了7727的监控面板,并根据实际的指标,对dashboard做了修改。修改之后的效果图如下:
JMX Exporter使用最新版本之后,有些指标名称发生了变化,具体映射关系如下:
-
jvm_memory_bytes_committed->jvm_memory_committed_bytes -
jvm_memory_bytes_init->jvm_memory_init_bytes -
jvm_memory_bytes_max->jvm_memory_max_bytes -
jvm_memory_pool_bytes_committed->jvm_memory_pool_committed_bytes -
jvm_memory_pool_bytes_init->jvm_memory_pool_init_bytes -
jvm_memory_pool_bytes_max->jvm_memory_pool_max_bytes -
jvm_memory_pool_bytes_used->jvm_memory_pool_used_bytes -
jvm_memory_pool_collection_bytes_committed->jvm_memory_pool_collection_committed_bytes -
jvm_memory_pool_collection_bytes_init->jvm_memory_pool_collection_init_bytes -
jvm_memory_pool_collection_bytes_max->jvm_memory_pool_collection_max_bytes -
jvm_memory_pool_collection_bytes_used->jvm_memory_pool_collection_used_bytes -
jvm_info->jvm_runtime_info
总结
根据以上步骤,就能实现在Kubernetes环境中对Java容器进行监控了,这样就可以根据应用的实际负载情况,调整相应的参数。