一文搞懂基于 Prometheus Stack 监控 Java 容器

本文涉及的产品
EMR Serverless StarRocks,5000CU*H 48000GB*H
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
简介: Hello folks,我是 Luga,今天我们来分享一下如何基于 Prometheus Stack 可视化监控运行在 Kubernetes Cluster 上的 Spring Boot 微服务容器实例。这里,主要针对每一个 Java 容器实例的指标进行监控,具体涉及:CPU、内存、线程信息、日志信息、HTTP 请求以及 JVM 等。

    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 !

相关实践学习
容器服务Serverless版ACK Serverless 快速入门:在线魔方应用部署和监控
通过本实验,您将了解到容器服务Serverless版ACK Serverless 的基本产品能力,即可以实现快速部署一个在线魔方应用,并借助阿里云容器服务成熟的产品生态,实现在线应用的企业级监控,提升应用稳定性。
相关文章
|
16天前
|
Java Linux Maven
java依赖冲突解决问题之容器加载依赖jar包如何解决
java依赖冲突解决问题之容器加载依赖jar包如何解决
|
19天前
|
安全 算法 Java
【Java集合类面试二】、 Java中的容器,线程安全和线程不安全的分别有哪些?
这篇文章讨论了Java集合类的线程安全性,列举了线程不安全的集合类(如HashSet、ArrayList、HashMap)和线程安全的集合类(如Vector、Hashtable),同时介绍了Java 5之后提供的java.util.concurrent包中的高效并发集合类,如ConcurrentHashMap和CopyOnWriteArrayList。
【Java集合类面试二】、 Java中的容器,线程安全和线程不安全的分别有哪些?
|
19天前
|
Java 容器
【Java集合类面试一】、 Java中有哪些容器(集合类)?
这篇文章列出了Java中的四大类集合接口:Set、List、Queue和Map,以及它们的常用实现类,如HashSet、TreeSet、ArrayList、LinkedList、ArrayDeque、HashMap和TreeMap。
【Java集合类面试一】、 Java中有哪些容器(集合类)?
|
18天前
|
Java 测试技术 数据库
容器镜像解析问题之解析 Java 应用依赖时识别 jar 包如何解决
容器镜像解析问题之解析 Java 应用依赖时识别 jar 包如何解决
14 0
|
18天前
|
存储 安全 Java
【Java 第四篇章】流程控制、容器
本文档详细介绍了Java中的流程控制、集合类型、数组声明及容器的声明与遍历等内容。在流程控制部分,包括了if、if...else、if...else if...else、switch等语句的使用方法,并提供了具体示例。接着,文档对比分析了Java中单列集合(如HashSet、LinkedHashSet、TreeSet等)与双列集合(如HashMap、LinkedHashMap、Hashtable等)的特点及底层实现原理。此外,还介绍了如何声明与初始化数组,并提供了多种循环结构的使用示例。最后,通过具体的代码示例展示了不同集合类型的声明、基本操作(如添加、删除、更新、查找)以及遍历方法。
11 0
|
2月前
|
Java Scala 流计算
实时计算 Flink版产品使用问题之Docker镜像中的Java路径和容器内的Java路径不一致,是什么导致的
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
2月前
|
Kubernetes Cloud Native 持续交付
云原生架构的核心组成部分通常包括容器化(如Docker)、容器编排(如Kubernetes)、微服务架构、服务网格、持续集成/持续部署(CI/CD)、自动化运维(如Prometheus监控和Grafana可视化)等。
云原生架构的核心组成部分通常包括容器化(如Docker)、容器编排(如Kubernetes)、微服务架构、服务网格、持续集成/持续部署(CI/CD)、自动化运维(如Prometheus监控和Grafana可视化)等。
|
2月前
|
缓存 Prometheus 监控
Java面试题:如何监控和优化JVM的内存使用?详细讲解内存调优的几种方法
Java面试题:如何监控和优化JVM的内存使用?详细讲解内存调优的几种方法
64 3
|
2月前
|
存储 Java 对象存储
Java虚拟机(JVM)中的栈(Stack)和堆(Heap)
在Java虚拟机(JVM)中,栈(Stack)和堆(Heap)是存储数据的两个关键区域。它们在内存管理中扮演着非常重要的角色,但各自的用途和特点有所不同。
35 0
|
2月前
|
存储 安全 Java
Java集合篇之逐渐被遗忘的Stack,手写一个栈你会吗?
总之,虽然在日常开发中,`java.util.Stack`正逐渐被其他类如 `Deque`接口的实现所取代,但手写一个栈(无论是基于数组还是链表)都是一次很好的编程练习,它可以帮助开发者更加深入地理解栈这种数据结构的工作原理和各种操作。
23 0