今日分享 – 一文搞懂基于 Prometheus Stack 监控 Java 容器

Hello folks,我是 Luga,今天我们来分享一下如何基于 Prometheus Stack 可视化监控运行在 Kubernetes Cluster 上的 Spring Boot 微服务容器实例。这里,主要针对每一个 Java 容器实例的指标进行监控,具体涉及:CPU、内存、线程信息、日志信息、HTTP 请求以及 JVM 等。

01

背景概述‍‍‍‍‍

随着云原生技术体系的崛起以及周边生态理念的日渐成熟,越来越多的公司开始将自身原有的基于传统模型的业务开始迁移至云原生,然而,随着迁移的不断进行,而原有的观测模式也逐渐发生变化,从而使得原有的技术体系在新的生态环境中开始出现水土不服。

在传统的监控模型体系中,由于所构建的微服务大多数都是运行在传统的虚拟机平台,使得数据的获取相对来说比较容易,无论这些微服务是基于传统的 Zabbix 组件还是新兴的 Prometheus 平台。然而,基于云化改造后的微服务实例,它们以成千上万个 Pod 模型运行在 Kubernetes Cluster 中提供服务,并且分布在不同的 Namespace 中,除此之外,这些 Pod 可能因各种不同的原因频繁重启或重建,导致其对应的 IP 地址发生变化,使得容器中实例的数据采集以及监控成为一个头痛的难题。

02

技术方案

基于不同的业务场景需求,我们的技术方案参考模型,如下图所示:

基于上述模型图,我们可以看到,假设 Spring Boot 微服务组件以 Pod 形式运行,我们需要借助相关插件将其内部所关联的指标信息进行暴露并予以 Prometheus 组件采集、加工分析,然后将其展现或对接至第三方系统进行维护。‍‍‍‍‍‍‍‍‍‍‍‍‍‍

在本方案的实现过程中,我们需要重点关注以下 2 部分核心内容,具体如下所示:

1、集成 Actuator 与 Micrometer 插件‍‍‍‍

通常情况下,若我们基于 Prometheus 进行应用级别的数据采集及观测,那么,需要在 Spring Boot 应用中使用 Spring Boot Actuator 插件监控应用、暴露指标,并使用 Micrometer Prometheus 将 Actuator 监控指标转换为 Prometheus 格式。

同时,Micrometer 为 Java 平台上的性能数据收集提供了一个通用的 API,类似于 SLF4J ,只不过它关注的不是 Logging(日志),而是 Application Metrics(应用指标)。

2、配置 Prometheus 自动发现

作为一个开源系统监控和告警工具链组件, 基于其特性,Prometheus 能够采集相关监控指标,并存储为时间序列数据,同时,Prometheus 还提供了灵活的查询语言 PromQL 来查询数据。

Prometheus 通过拉模型采集指标,因此,我们需要在 Prometheus 集群中配置服务发现(Service Monitor)来定期从应用中抓取指标。

03

实现思路及策略

在上面的章节中,我们已大概的规划出整体的技术实现方案:Pod 中取数据——>存放 Prometheus——>数据展现等。从上面的流程来看,整个数据的提取、加工以及到展现并非我们想象的那么难以搞定。‍‍‍‍‍‍‍‍‍‍‍‍‍‍

这里,笔者主要分享常用的 2 种方案供大家参考,具体如下。

1、基于 Service 实现‍‍

言简意赅,即在 Kubernetes Cluster 中,我们需要通过 Service 对外提供 Spring Boot 应用的指标接口。

基于业务需要,我们的 Spring Boot 微服务组件以 Pod 形式运行在 Kubernetes Cluster 中,因此,需要为 Pod 添加 Service 以便对外提供 HTTP 服务,这样 Prometheus 才可以抓取监控指标。

具体模型如下所示:

如下为一个简单的添加 Service 的 Yaml 文件示例,具体可参考:

apiVersion: v1
kind: Service
metadata:
  labels:
    app: spring-boot-demo-exporter
  name: spring-boot-demo-exporter
  namespace: default
spec:
  ports:
    - name: spring-boot-demo-exporter
      port: 9999
      protocol: TCP
      targetPort: 9999
  selector:
    app: spring-boot-demo
  type: NodePort

这里需要注意的是 spec.selector 需要与 Pod 的标签对应。例如,我们使用 Deployment 进行应用部署,那么,则需要与 Deployment 的 spec.template.metadata.labels 对应,这样 Service 才能知道对应的 Pod。

在实际的业务场景中,我们基于 Kubernetes 的 Service 实现了监控 Pod 中 Java应用的相关信息,可能是最常见的一种方案。但其实这并不适用于所有的环境,毕竟,基于不同的架构设计,在实际环境中并不是所有的 Pod(微服务)都会有自己对应的 Service,所以那些没有使用到 Service 的 Pod 就无法通过 Service 这种机制来实现微服务应用层面的监控。

2、基于 Pod Controller 实现

此种实现主要基于 JMX Exporter 暴露 JVM 监控指标然后进行数据的采集及监控展示。

与 Service 不同的是,作为一种 Sidecar Docker 容器, JMX Exporter 用于将Prometheus JMX Exporter 注入到我们所构建的 Kubernetes Cluster 中的 Java 应用程序中,然后将相关数据进行暴露。

具体模型如下所示:

针对 JMX Exporter,需要将其相关参数定义或添加至 Deployment 或 StatefulSet 的 initContainers 中,如下为一个简单的添加 Service 的 Yaml 文件示例,具体可参考:

...
spec:
  initContainers:
  - name: prometheus-jmx-exporter
    image: spdigital/prometheus-jmx-exporter-kubernetes:0.3.1
    env:
    - name: SHARED_VOLUME_PATH
      value: /shared-volume
    volumeMounts:
    - mountPath: /shared-volume
      name: shared-volume
...

通常,定义的 init 容器和我们所部署的的 Spring Boot 组件容器将共享一个卷,具体如下所示:

volumes:
- name: shared-volume
  emptyDir: {}

同时,在 Spring Boot 组件容器中,将 JAVA_OPTS 设置为引用 prometheus-jmx-exporter 容器放置到共享卷中的文件,具体如下所示:

- name: JAVA_OPTS
  value: -javaagent:/shared-volume/jmx_prometheus_javaagent.jar=19000:/shared-volume/configs/springboot-config.yaml

当然,除了上述最为常见的实现策略外,市面上也有其他可参考的成熟案例可供参考,例如 sfKubeAgent 等,作为一个 Sidecar 容器,监控应用程序 Pod 的 JVM 相关指标信息等。‍

04

部署实施

针对上述 2 种不同的技术实现而言,前置的部署相对来说,基本上都相差无几,具体差异主要体现在技术细节层面。接下来,我们简要地描述一下整个监控流程的相关部署操作。

这里,我主要以基于 Service 实现方式进行部署实施,具体如下:‍‍‍

1、业务应用配置

在 POM.XML 文件中引入相关插件,具体如下所示:‍‍‍‍‍‍‍‍‍‍‍

...
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
 
    <!--桥接Prometheus-->
    <dependency>
      <groupId>io.micrometer</groupId>
      <artifactId>micrometer-registry-prometheus</artifactId>
      <version>1.6.0</version>
    </dependency>
 
    <!--micrometer核心包, 按需引入, 使用Meter注解或手动埋点时需要-->
    <dependency>
      <groupId>io.micrometer</groupId>
      <artifactId>micrometer-core</artifactId>
      <version>1.6.0</version>
    </dependency>
    <!--micrometer获取JVM相关信息, 并展示在Grafana上-->
    <dependency>
      <groupId>io.github.mweirauch</groupId>
      <artifactId>micrometer-jvm-extras</artifactId>
      <version>0.2.0</version>
    </dependency>
...

通常情况下,由于 Spring Boot Actuator 因为安全原因默认只开启了 Health 和 Info 接口,故此,我们需要调整下 application.yml 文件,将 Prometheus 组件相关接口开放,具体配置可参考如下:

# metircs
management:
  endpoints:
    web:
      exposure:
        include: prometheus, health
  metrics:
    export:
      simple:
        enabled: false
    tags:
      application: ${spring.application.name}

至此,应用配置已更新完成,我们可以通过 /actuator/prometheus 接口查看配置是否生效,具体如下所示:

[leonli@Leon ~ ] % curl 'http://localhost:8080/actuator/prometheus' -i -X GET

2、Prometheus 自动发现配置

在实际生产环境中,每个服务都有对应的 Service,而此 Service 下面通常会挂载有较多 Pod,此时,我们需要通过 Prometheus 的自动发现来将所有实例 Metrics 信息进行采集。毕竟,Kubernetes Cluster 中的 Prometheus 可以基于 Kubernetes Cluster 自身的机制完成此项操作。

此时,我们需要在所部署的微服务的 Yaml 文件中定义 annotations,主要用于自动发现,具体如下所示:

 ...
  annotations:
    prometheus.io/port: "7070" #端口配自己服务的端口
    prometheus.io/spring: "true"
    prometheus.io/path: "actuator/prometheus"
...

备注:

上述参数主要配置在 Service 的 metadata 中,具体可参考上述文件配置。

然后,我们在 Prometheus 配置文件中添加 Spring Boot 项目 Metrics 相关信息,具体可参考如下:

   ...
      - job_name: 'spring metrics'
 
        kubernetes_sd_configs:
        - role: endpoints
 
        relabel_configs:
        - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_spring]
          action: keep
          regex: true
        - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path]
          action: replace
          target_label: __metrics_path__
          regex: (.+)
        - source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port]
          action: replace
          target_label: __address__
          regex: ([^:]+)(?::\d+)?;(\d+)
          replacement: $1:$2
        - action: labelmap
          regex: __meta_kubernetes_service_label_(.+)
        - source_labels: [__meta_kubernetes_namespace]
          action: replace
          target_label: spring_namespace
        - source_labels: [__meta_kubernetes_service_name]
          action: replace
          target_label: spring_name
...

完成如上的配置后,重新进行配置文件加载操作,此时,我们在 Prometheus 控制台可以看到新加进来的 Target 信息。此时,整个监控部署实施完成。

Adiós !

··································

Hello folks,我是 Luga,一个 10 年+ 技术老司机,从 IT 屌丝折腾到码畜,最后到“酱油“架构师。如果你喜欢技术,不喜欢呻吟,那么恭喜你,来对地方了,关注我,共同学习、进步、超越~

您的每一个点赞、在看及分享,我都认真当成了喜欢 ~

正文完