OpenTelemetry + Kubernetes 无侵入式 Java 微服务指标监控部署指南
OpenTelemetry
OpenTelemetry(简称 OTel)是一个由 CNCF(云原生计算基金会)托管的顶级开源可观测性框架,它为分布式系统提供了一套标准化的 API 和工具,用于统一生成、收集、处理和导出核心的遥测数据(Metrics 指标、Traces 链路追踪、Logs 日志)。它的核心价值在于“厂商中立”——开发者只需在业务中做一次无侵入式的探针注入或 SDK 接入,就能将监控数据自由、无缝地推送到 Prometheus、Jaeger、Elastic 等任何主流后端平台,彻底打破了传统监控工具各自为战、强绑定的数据孤岛。
OpenTelemetry有两个最突出的特点:
-
零代码侵入的“自动挡” (Auto-Instrumentation) 对于 Java、Python、Node.js 等语言,OTel 提供了极其强大的自动注入探针。
-
灵活强大的“数据路由器” (OTel Collector) 它不仅提供探针,还提供了一个数据中转站(Collector)。在数据发往最终的存储后端(Prometheus等)之前,你可以在 Collector 里面对数据进行过滤、脱敏、采样或者格式转换(比如咱们刚才把 K8s 的底层属性完美转换成了 Prometheus 标签)。
架构概述
本方案采用 OpenTelemetry (OTel) 提供的自动注入机制,无需修改业务代码即可实现 JVM 及基础框架指标的采集。
-
数据链路:Java 探针 (OTel Agent) -> 通过 OTLP/gRPC 协议推送 -> OTel Collector -> 转换为 Prometheus 格式暴露 -> Prometheus 定时拉取。

零、 环境介绍
监控train-ticket微服务,部署在tt命名空间下

本方案介绍OpenTelemetry方案进行指标采集(自动注入java agent无需手动修改微服务项目原本的配置文件)
一、 前置准备与基础组件安装
OpenTelemetry Operator 依赖 cert-manager 来生成 Webhook 证书,必须先安装它。
安装 cert-manager
# 安装 cert-manager (建议使用官方最新稳定版)
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yaml
# 验证所有 Pod 是否运行正常 (大概需要等待 1-2 分钟)
kubectl get pods -n cert-manager
安装 OpenTelemetry Operator
# 安装 OTel Operator
kubectl apply -f https://github.com/open-telemetry/opentelemetry-operator/releases/latest/download/opentelemetry-operator.yaml
# 验证 Operator 是否启动成功
kubectl get pods -n opentelemetry-operator-system
二、 部署 OTel Collector (数据收集与转换网关)
我们需要在微服务所在的命名空间(本文以 tt 为例)部署一个 Collector,用于接收探针发来的数据,并转换为 Prometheus 可识别的格式。
创建 otel-collector.yaml:
apiVersion: opentelemetry.io/v1alpha1
kind: OpenTelemetryCollector
metadata:
name: otel-collector
namespace: tt
spec:
config: |
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317 # 接收 Java 探针数据的 gRPC 端口
exporters:
prometheus:
endpoint: 0.0.0.0:8889 # 暴露给 Prometheus 拉取数据的端口
# 【核心配置】将 K8s 的 Namespace, Pod, Container 等资源属性转换为 Prometheus 的独立标签,解决数据揉成一团的问题
resource_to_telemetry_conversion:
enabled: true
service:
pipelines:
metrics:
receivers: [otlp]
exporters: [prometheus]
执行部署:
kubectl apply -f otel-collector.yaml

三、 解决探针镜像拉取难题 (针对 Containerd 内网/网络受限环境)
由于官方探针镜像存放在 ghcr.io,国内网络极易拉取失败。即便手动 docker pull,K8s 默认仍会尝试联网校验。我们需要利用 Containerd 的本地镜像机制进行“伪装”。
预拉取并重命名镜像 (所有 Node 节点执行)
将镜像拉取到 K8s 专用的 k8s.io 命名空间,并打上一个带有“伪造本地仓库域名”的标签(例如 dev.local),以彻底绕过 K8s 的公网校验机制。
# 1. 使用国内加速源拉取探针镜像
ctr -n k8s.io image pull ghcr.nju.edu.cn/open-telemetry/opentelemetry-operator/autoinstrumentation-java:latest
# 2. 重新打标签,伪装成私有/本地仓库镜像,并去除 latest 标签 (改用 v1)
ctr -n k8s.io image tag \
ghcr.nju.edu.cn/open-telemetry/opentelemetry-operator/autoinstrumentation-java:latest \
dev.local/otel-java-agent:v1
四、 配置 Java 探针注入规则 (Instrumentation)
定义探针的行为:使用什么镜像、往哪里发数据、使用什么协议。
创建 instrumentation.yaml:
apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
name: java-instrumentation
namespace: tt
spec:
exporter:
# 指向本命名空间内 Collector 的 gRPC 服务地址
endpoint: http://otel-collector-collector.tt.svc.cluster.local:4317
sampler:
type: always_on
java:
# 【避坑】指向我们刚才伪装好的本地镜像
image: dev.local/otel-java-agent:v1
env:
# 【避坑】强制指定传输协议为 grpc,避免默认 http 协议导致的 Connection reset 或 404 错误
- name: OTEL_EXPORTER_OTLP_PROTOCOL
value: grpc
# 【避坑】由于我们只配置了 metrics 流水线,禁用 traces 和 logs 导出,避免探针在微服务日志中疯狂打印 404 WARN 报错
- name: OTEL_TRACES_EXPORTER
value: none
- name: OTEL_LOGS_EXPORTER
value: none
执行部署:
kubectl apply -f instrumentation.yaml
五、 为微服务注入探针并重启生效
OTel Operator 通过监听 Namespace 或 Deployment 上的 Annotations (注解) 来决定是否注入探针。
开启命名空间级别的自动注入
# 告诉 Operator:请在这个命名空间下自动注入 Java 探针,配置依据名为 java-instrumentation
kubectl annotate namespace tt instrumentation.opentelemetry.io/inject-java="java-instrumentation"
重启微服务,触发注入
清理旧 Pod,Kubernetes 会根据注解自动将带有探针 jar 包的 InitContainer 挂载到新 Pod 中。
# 批量重启 tt 命名空间下所有前缀为 ts- 的 Deployment
kubectl rollout restart -n tt $(kubectl get deploy -n tt -o name | grep ts-)
# 观察启动状态,确保新 Pod 进入 1/1 Running 状态
kubectl get pods -n tt

六、 配置 Prometheus 抓取数据
最后,将 Collector 暴露出来的指标对接到 Prometheus。
编辑 Prometheus 的配置文件 (prometheus.yml),新增一个 Job:
scrape_configs:
- job_name: 'otel-k8s-metrics'
scrape_interval: 15s
static_configs:
# 指向 OTel Collector 的服务地址和 Prometheus Exporter 端口
- targets: ['otel-collector-collector.tt.svc.cluster.local:8889']
热加载 Prometheus 配置:
curl -X POST http://<prometheus-ip>:9090/-/reload
七、 验收与数据可视化
-
Prometheus 验证: 在 Prometheus Web UI 中搜索
jvm_memory_used_bytes,如果能查出数据,且数据带有k8s_namespace_name、k8s_pod_name、k8s_container_name等详细标签,说明全链路打通。

八、深入研究-什么是webhook
要理解 Webhook 这个词,我们可以把它拆成两半来看:Web 和 Hook。
什么是 Hook(钩子)?
在计算机编程的历史里,“Hook(钩子)”是一个非常古老且核心的概念。 想象一条流水线,数据在管道里从 A 流向 B。如果你想在这条流水线中间“截胡”或者“偷窥”一下,你就在中间挂一个“钩子”。当数据经过时,触发你挂在钩子上的自定义代码。
-
比如:Windows 键盘钩子(记录你敲了什么键)、Vue/React 里的生命周期钩子(
mounted、created,在组件加载到某个阶段时触发)。
什么是 Web?
Web 就是指 HTTP 网络请求。
合体:Webhook(网络钩子)
把这两个概念合起来,Webhook 的本质就是:通过 HTTP 请求实现的回调机制(HTTP Callback)。
Kubernetes 里的 Webhook 是怎么工作的?
当你自己开发 Operator 并实现 replicas <= 10 这个功能时,你其实是在 K8s 的 API Server 内部署了一个“HTTP 钩子”。流程是这样的:
-
用户发起请求: 某人执行了
kubectl scale deploy my-app --replicas=15。 -
API Server 走到钩子点: API Server 收到请求,但在真正写入数据库(etcd)之前,它走到了一个叫做“准入控制(Admission Control)”的关卡。
-
触发 Webhook(发起 HTTP 请求): API Server 发现你注册了一个 Webhook 规则。于是,API Server 变成了一个客户端,向你写的 Operator 代码发送了一个 HTTP POST 请求,把那个要把 replicas 改成 15 的 JSON 数据发给了你。
-
你的代码做“裁判”: 你的 Operator 收到这个 HTTP 请求,解析出
replicas: 15,发现大于 10。 -
返回结果: 你的 Operator 给 API Server 返回一个 HTTP 响应:
{"allowed": false, "message": "Replicas cannot exceed 10!"}。 -
API Server 拦截: API Server 收到你的拒绝响应,立刻中止操作,并把你的
message报错打印在用户的终端上。
Webhook 家族的两大门派(对比 OTel)
在 K8s 的准入控制里,Webhook 明确分为两大门派。这正好把咱们今天聊的 OTel 和你之前的开发经验串起来了:
-
门派一:Validating Webhook(验证型 - 你之前写的)
-
特点: 只看不改,扮演**“安检员”或“裁判”**。
-
作用: 检查配置合不合法。比如你写的“副本数不能大于 10”,或者“镜像必须来自私有仓库”、“必须要有资源限制(requests/limits)”。它只能回答 Yes 或 No。
-
-
门派二:Mutating Webhook(变更型 - OTel Operator 用的)
-
特点: 先看后改,扮演**“整容医生”或“魔术师”**。
-
作用: 在资源存入数据库前,悄悄修改它的内容。比如咱们刚搞定的 OTel Operator,它发现你的 Pod 有特殊的 Annotation,就毫不客气地在你的 YAML 里塞进去了 InitContainer 和一堆环境变量,然后再还给 API Server。
-
九、 深入研究-如何实现自动注入
这个“自动注入”的过程看起来像魔法一样(无需改业务代码,探针就自动跑起来了),但其实它的底层原理是 Kubernetes 中一个非常经典且强大的机制:Mutating Admission Webhook(变更准入控制 Webhook)。
为了让你在技术文档里或者跟团队分享时能讲得透彻,我把 OTel Operator 的“拦截-注入”全流程拆解为 核心原理 和 分步流程 两个部分。
核心原理:Kubernetes 的准入控制 (Admission Control)
当你在 K8s 里执行 kubectl apply 创建一个 Pod 时,请求并不是直接丢给 Node 节点去运行的,而是要经过 API Server 的重重关卡。
其中有一关叫做 Mutating Admission(变更准入)。 OTel Operator 在安装的时候(其实是 cert-manager 帮它签发证书的时候),向 K8s API Server 注册了一个自己的“哨卡”(Webhook)。
它告诉 K8s:“兄弟,以后集群里但凡有人要创建或更新 Pod,你在存入 etcd 数据库之前,先把 Pod 的配置草稿发给我看一眼,我可能要改点东西。”
OTel Operator 拦截与注入的完整流程

让我们以你刚才部署的 ts-travel-service 为例,看看 Operator 是怎么“偷梁换柱”的:
第一步:触发创建 (Trigger)
K8s 的 Deployment 控制器决定要创建一个 ts-travel-service 的 Pod,于是向 API Server 发送了创建 Pod 的请求。
第二步:拦截请求 (Intercept)
API Server 收到请求后,由于配置了 Mutating Webhook,它暂停了创建过程,把这个原始的 Pod YAML(JSON 格式)发送给了 OTel Operator 的 Webhook 服务。
第三步:规则匹配 (Evaluate)
OTel Operator 收到这个 Pod 的配置后,开始检查:
-
看命名空间: 这个 Pod 所在的命名空间
tt或者 Pod 自身,有没有打上instrumentation.opentelemetry.io/inject-java这个 Annotation(注解)? -
找配置字典: 发现了注解的值是
java-instrumentation。于是 Operator 去 K8s 里寻找名为java-instrumentation的Instrumentation自定义资源(CRD),读取里面的配置(比如镜像地址、gRPC 端点、环境变量)。
第四步:执行注入 (Mutate / Inject)
一旦匹配成功,Operator 就会像做外科手术一样,直接在内存里修改这个 Pod 的配置(YAML)。它做了三件极其核心的事情:
-
塞入一个共享卷 (Volume): 增加一个类型为
emptyDir的存储卷,挂载名为opentelemetry-auto-instrumentation。这相当于在 Pod 里划出了一块 InitContainer 和主容器都能访问的“共享U盘”。 -
塞入一个 InitContainer(初始化容器): 使用你配置的探针镜像(
dev.local/otel-java-agent:v1)作为 InitContainer。 这个容器启动后只执行一条命令:cp /javaagent.jar /otel-auto-instrumentation/javaagent.jar。把探针文件拷贝到上面的“共享U盘”里,然后自己就结束退出了。 -
篡改主容器的环境变量 (Env Vars): 向你的
ts-travel-service容器中强行塞入一堆环境变量:-
OTEL_EXPORTER_OTLP_ENDPOINT=http://...:4317 -
OTEL_EXPORTER_OTLP_PROTOCOL=grpc -
最致命的一招:
JAVA_TOOL_OPTIONS="-javaagent:/otel-auto-instrumentation/javaagent.jar"
-
第五步:放行并持久化 (Admit & Persist)
Operator 把修改后的 Pod 配置返回给 API Server。API Server 将这个“被加料”的 Pod 保存到 etcd 数据库中。
第六步:Kubelet 执行运行
Node 节点上的 Kubelet 拿到配置开始启动 Pod:
-
先跑 InitContainer,把探针
.jar包准备好。 -
再跑你的 Java 主应用。此时,由于环境变量里有
JAVA_TOOL_OPTIONS,JVM 在启动你的 Spring Boot 之前,会极其听话地优先加载-javaagent指向的探针包。
💡 总结:Operator 的双重身份
在整个 OTel 体系里,Operator 其实扮演了两个角色(它是一个典型的 Reconciliation Loop 控制循环):
-
Webhook 拦截器(Mutating Webhook): 专门负责拦截 Pod 创建请求,利用 InitContainer 和
JAVA_TOOL_OPTIONS实现对业务应用“神不知鬼不觉”的探针注入。 -
CRD 控制器(Controller): 负责监听
OpenTelemetryCollector这个自定义资源。当你 apply 那个 YAML 时,它负责真正在 K8s 里帮你拉起 Collector 的 Deployment、Service 和 ConfigMap。
十、 深入研究-为什么要有cert-manager
一句话总结就是:Kubernetes 的 API Server 有一个极其严格的“安全强迫症”——它调用任何 Webhook 时,绝对不允许走普通的 HTTP,必须、强制、毫无商量余地要求走 HTTPS(TLS 加密)!
以下是具体的因果关系和 cert-manager 在其中扮演的“救火队长”角色:
为什么 API Server 强制要求 HTTPS?
当你在 K8s 里创建一个 Pod 时(比如带有数据库密码的环境变量),API Server 会把这个 Pod 的完整原始数据打包发送给 Webhook(比如 OTel Operator)。 如果走明文 HTTP,集群内部网络一旦被嗅探,所有的密码、密钥、核心配置将一览无余。所以,K8s 源码里写死了:Webhook 必须提供 TLS 证书,否则 API Server 拒绝通信。
没有 cert-manager 时,搞证书有多痛苦?
既然必须要 HTTPS,那你的 Operator Pod 跑起来的时候,就得自己准备好 tls.crt(公钥)和 tls.key(私钥)。 如果不借助自动化工具,你需要纯手工完成以下极其反人类的步骤:
-
自己用
openssl命令生成一个根证书(CA)。 -
签发一个服务器证书,且证书的 SAN(备用名称)必须精确匹配你 Operator 的 K8s Service 域名(比如
otel-webhook-service.tt.svc)。 -
把证书变成 K8s Secret 挂载到 Operator Pod 里。
-
最变态的一步: 你必须修改 K8s 的
MutatingWebhookConfiguration这个 YAML 资源,把刚才那个 CA 证书的 Base64 编码,硬编码填到caBundle这个字段里。以此告诉 API Server:“请信任这个 CA 签发的证书”。
而且,证书是会过期的!一旦过期,Webhook 就会瘫痪,导致你整个集群连最基本的 Pod 都创建不出来(因为 API Server 调不通 Webhook 就会拦截所有请求)。
cert-manager 的“魔法”登场
cert-manager 就是为了终结上述所有痛苦而诞生的。它是一个 K8s 原生的证书管理专家。
当 OTel Operator 配合 cert-manager 部署时,发生了什么?
-
自动发证: cert-manager 会自动在集群里充当一个“内部发证机关(CA)”,默默给 OTel Operator 签发有效期内的 TLS 证书,并挂载进去。
-
自动注入
caBundle(核心神技): OTel 的 Webhook YAML 里有一行特殊的注解(Annotation):cert-manager.io/inject-ca-from: ...。cert-manager 看到这个注解后,会自动把自己的 CA 证书 Base64 编码,精准地填入 Webhook 的caBundle字段中。 -
自动续期: 证书快过期了?cert-manager 会在后台静默为你重新签发并替换,API Server 和 Operator 之间永远保持着合法的 HTTPS 连接。

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)