在 Kubernetes 中使用 DataKit Operator 实现 Java 包级 APM 监控
文章介绍了如何在 Kubernetes 环境中通过 DataKit Operator 自动注入 Java Agent,并开启包级 APM 监控,无需修改业务代码即可追踪 Service 层方法调用与耗时,补全 Controller 到数据库之间的链路盲区,实现更细粒度的性能分析与问题定位。
前言
Java Agent 对 Spring MVC、Servlet、HTTP Client、JDBC 等常见组件进行自动插桩后,通常已经能够展示接口入口、跨服务调用和数据库访问。但当一条请求的大部分时间消耗在业务 Service 中时,默认链路往往只能告诉我们“Controller 到 SQL 之间很慢”,无法继续回答是哪一个业务方法慢、方法之间如何嵌套,以及耗时发生在哪个业务阶段。如下图所示,trace详情中只能看到SysUserController.list,无法看到具体的业务方法。

包级插桩可以在不修改应用代码的情况下,将指定 Java 包下的业务方法自动转换成 span。本文以 Kubernetes 中运行的 RuoYi-Cloud 为例,通过 DataKit Operator 批量注入观测云扩展版 DDTrace Java Agent,并对 com.ruoyi.system.service.impl 开启包级监控,最终实现业务方法的监控,如下图所示,可以看到自定义方法c.r.s.s.i.SysUserServiceImpl.selectUserList的耗时:

本文假定 RuoYi 或其他 Spring Boot 应用已经部署到 Kubernetes,重点介绍 APM 接入和包级插桩,不展开数据库、注册中心和前端的安装过程。
1. 包级监控解决什么问题
包级监控适合以下情况:
- 默认链路只有 Controller 和数据库 span,中间业务逻辑仍然是黑盒。
- 不方便修改源码,却希望定位 Service 层慢方法或异常热点。
- 希望建立 HTTP、Controller、Service、缓存和数据库的完整调用关系。
例如,用户列表接口调用 SysUserController.list 后会进入 SysUserServiceImpl.selectUserList。如果只看到 Controller 总耗时和 SQL 耗时,就无法判断分页初始化、权限范围处理还是 Service 自身逻辑消耗了时间。包级插桩生成 trace.annotation span 后,这段时间会在链路中单独展示。
自动插桩能够识别 Servlet、Spring MVC、JDBC 等框架边界,却不知道哪些普通 Java 方法具有业务意义。DD_TRACE_METHOD_PACKAGES 会在类加载时增强目标包,并把命中的方法作为 trace.annotation span 放入当前 Trace。
2. 测试环境说明
实施前确认:
- Kubernetes 中已有可正常访问的 Spring Boot 应用(本文档以开源项目 RuoYi-Cloud 为例进行演示)。
- DataKit 与应用位于同一集群,应用 Pod 能访问 DataKit Service。
- 集群 DNS 能解析 DataWay 域名。
- 具有创建 ConfigMap、Secret、Service、Operator 和重启 Deployment 的权限。
- 已确认需要增强的包,本例为
com.ruoyi.system.service.impl。
本案例验证通过的版本如下:
| 组件 | 示例版本 | 说明 |
|---|---|---|
| DataKit | 2.8.0 | 启用 ddtrace input |
| DataKit Operator | 1.8.10 | 使用 admission_inject_v2 |
| DDTrace Java Agent | 1.63.7-ext | 固定已验证的扩展版镜像 |
| RuoYi-Cloud | 3.6.8 | 案例业务版本,用于标识 DD_VERSION |
本案例包含 ruoyi-gateway、ruoyi-auth、ruoyi-system、ruoyi-gen、ruoyi-job 和 ruoyi-file 六个 Java 服务。观测云DataKit 以 DaemonSet 运行,DataKit Operator 通过 admission webhook 在 Pod 创建时注入 Java Agent。
RuoYi Java Pod
├── datakit-lib-init:复制 dd-java-agent.jar
├── JAVA_TOOL_OPTIONS:加载 -javaagent
│
▼
datakit-service.datakit.svc.cluster.local:9529
│
▼
DataKit ddtrace input → DataWay → 观测云 APM
Operator 只在 Pod 创建时修改 PodSpec,负责注入 initContainer、共享卷和 Agent 环境变量;Java Agent 则把 span 发送到 DataKit 的 9529/TCP,再经 DataWay 上报。六个服务都可获得基础 APM 能力,只有加载 com.ruoyi.system.service.impl 的 ruoyi-system 会额外生成该包的方法 span。
3. 安装并配置 DataKit Operator
3.1 安装 Operator,确保链路数据已经成功采集
参考DataKit安装文档和DataKit Operator安装文档,确保在观测云上可以看到应用的链路数据。

3.2 对比基础 APM 与包级 APM 配置
Operator 已经完成 Java Agent 注入,开启包级监控不需要改变注入方式。核心差异是在原有 envs 中增加 DD_TRACE_METHOD_PACKAGES,将需要添加的包名填写进去:
"envs": {
"DD_AGENT_HOST": "datakit-service.datakit.svc.cluster.local",
"DD_TRACE_AGENT_PORT": "9529",
"DD_SERVICE": "{fieldRef:metadata.labels['app']}",
+ "DD_TRACE_METHOD_PACKAGES": "com.ruoyi.system.service.impl",
+ "DD_TRACE_STARTUP_LOGS": "true"
}
其中 DD_TRACE_METHOD_PACKAGES 是启用包级插桩的必要配置;DD_TRACE_STARTUP_LOGS 只用于接入阶段输出 Agent 配置,方便确认规则是否加载。下面是可直接应用的完整 ConfigMap:
apiVersion: v1
kind: ConfigMap
metadata:
name: datakit-operator-config
namespace: datakit
data:
jsonconfig: |-
{
"server_listen": "0.0.0.0:9543",
"log_level": "info",
"admission_inject_v2": {
"ddtraces": [
{
"namespace_selectors": ["^ruoyi$"],
"label_selectors": ["apm=enabled"],
"check_annotation": false,
"image": "pubrepo.guance.com/datakit-operator/dd-lib-java-init:v1.63.7-ext",
"language": "java",
"envs": {
"DD_AGENT_HOST": "datakit-service.datakit.svc.cluster.local",
"DD_TRACE_AGENT_PORT": "9529",
"DD_SERVICE": "{fieldRef:metadata.labels['app']}",
"DD_ENV": "demo",
"DD_VERSION": "3.6.8",
"DD_TRACE_SAMPLE_RATE": "1",
"DD_TRACE_METHOD_PACKAGES": "com.ruoyi.system.service.impl",
"DD_TRACE_STARTUP_LOGS": "true",
"POD_NAME": "{fieldRef:metadata.name}",
"POD_NAMESPACE": "{fieldRef:metadata.namespace}",
"NODE_NAME": "{fieldRef:spec.nodeName}",
"DD_TAGS": "pod_name:$(POD_NAME),pod_namespace:$(POD_NAMESPACE),host:$(NODE_NAME)"
}
}
]
},
"admission_inject": {
"ddtrace": {}
}
}
Operator Deployment 从 datakit-operator-config 的 jsonconfig 键读取配置。若使用 Helm 或平台模板安装,应先确认 Deployment 实际引用的 ConfigMap 名称和键名。命名空间与标签 selector 需要同时匹配。
关键配置说明:
| 配置 | 作用 |
|---|---|
| namespace_selectors | 使用正则限定命名空间,避免全局注入 |
| label_selectors | 只注入明确标记的业务 Pod |
| image | 固定扩展版 Java Agent 镜像 |
| DD_SERVICE | 从 Pod 的 app 标签生成 APM 服务名 |
| DD_TRACE_AGENT_PORT | 显式使用 DataKit DDTrace 端口 9529 |
| DD_TRACE_METHOD_PACKAGES | 对指定业务包生成方法 span |
| DD_TRACE_STARTUP_LOGS | 输出 Agent 启动配置,便于验证 |
应用 ConfigMap 后重启 Operator。Operator 从 ConfigMap 读取启动配置,仅更新 ConfigMap 不能保证运行中的 Operator 已加载新值。
kubectl apply -f datakit-operator-config.yaml
kubectl rollout restart deployment/datakit-operator -n datakit
kubectl rollout status deployment/datakit-operator -n datakit --timeout=180s
4. 标记并重建 Java Pod
Operator webhook 只在 Pod 创建时 注入 initContainer、共享卷和环境变量,不会修改已经运行的 Pod。因此修改 Operator 配置或包名后,必须重新创建目标 Pod。
为六个 Java Deployment 的 Pod 模板增加标签:
for service in gateway auth system gen job file; do
kubectl patch deployment "ruoyi-${service}" -n ruoyi --type=merge \
-p "{\"spec\":{\"template\":{\"metadata\":{\"labels\":{\"app\":\"ruoyi-${service}\",\"apm\":\"enabled\"}}}}}"
done
标签必须写入 spec.template.metadata.labels。如果 app 已用于 Deployment selector,应保留与 selector 一致的值。
等待滚动发布完成:
for service in gateway auth system gen job file; do
kubectl rollout status deployment/"ruoyi-${service}" \
-n ruoyi --timeout=300s
done
如果环境变量无法由平台注入,也可以在 JVM 启动参数中使用等价配置:
-Ddd.trace.method.packages=com.ruoyi.system.service.impl
环境变量和 JVM 参数二选一即可。必须重建 Pod,是因为 webhook 只处理新建 Pod,Agent 也只在 JVM 启动时读取包级参数并增强目标类。
5. 验证 Agent 和包级规则
5.1 检查 Pod 注入结果
可以通过kubectl describe pod命令来检查应用Pod的环境变量,如下是本实验环境的输出
kubectl -n ruoyi describe pod ruoyi-system-7fd88b644d-4j8hm
...省略配置...
Readiness: tcp-socket :8080 delay=20s timeout=1s period=5s #success=1 #failure=60
Environment:
SPRING_CLOUD_NACOS_DISCOVERY_SERVER_ADDR: ruoyi-nacos:8848
SPRING_CLOUD_NACOS_CONFIG_SERVER_ADDR: ruoyi-nacos:8848
DD_SERVICE: ruoyi-gateway
JAVA_TOOL_OPTIONS: -javaagent:/datadog-lib/dd-java-agent.jar
DD_AGENT_HOST: datakit-service.datakit.svc.cluster.local
DD_TRACE_AGENT_PORT: 9529
DD_JMXFETCH_STATSD_HOST: datakit-service.datakit.svc.cluster.local
DD_JMXFETCH_STATSD_PORT: 8125
DD_ENV: demo
DD_VERSION: 3.6.8
DD_TRACE_SAMPLE_RATE: 1
DD_TRACE_METHOD_PACKAGES: com.ruoyi.system.service.impl
DD_TRACE_STARTUP_LOGS: true
POD_NAME: ruoyi-gateway-67c8d6cd84-85b6v (v1:metadata.name)
POD_NAMESPACE: ruoyi (v1:metadata.namespace)
NODE_NAME: (v1:spec.nodeName)
DD_TAGS: pod_name:$(POD_NAME),pod_namespace:$(POD_NAMESPACE),host:$(NODE_NAME)
...省略配置...
本次修改最重要的配置为:
DD_TRACE_METHOD_PACKAGES值需要与源码包声明完全一致。

6. 触发业务请求并在观测云验证
6.1 对比启用前后的链路
包级监控前后的变化集中在 Controller 与下游组件之间:
| 阶段 | Operator 环境变量 | 用户列表链路 |
|---|---|---|
| 基础 APM | 未配置 DD_TRACE_METHOD_PACKAGES | servlet.request → spring.handler → mysql.query |
| 包级 APM | DD_TRACE_METHOD_PACKAGES=com.ruoyi.system.service.impl | servlet.request → spring.handler → trace.annotation → mysql.query |
基础 APM 能证明请求进入了 Controller 并执行了 SQL;包级 APM 进一步展示 SysUserServiceImpl.selectUserList,从而把 Controller 与数据库之间的业务耗时单独拆分出来。
6.2 触发请求并确认结果
登录 RuoYi 后进入“系统管理 → 用户管理”,打开或刷新用户列表。也可以使用已有访问令牌调用接口:
curl -sS -H 'Authorization: Bearer <ACCESS_TOKEN>' \
'https://<RUOYI_HOST>/prod-api/system/user/list?pageNum=1&pageSize=10'
记录请求时间,在观测云进入“应用性能监测 → 链路”,筛选 service=ruoyi-system ,打开 GET /user/list 链路并确认其中包含 SysUserServiceImpl.selectUserList。

本案例的脱敏验收结果如下:
| operation | resource | span 类型 | 状态 |
|---|---|---|---|
| servlet.request | GET /user/list | entry | ok |
| spring.handler | SysUserController.list | local | ok |
| trace.annotation | c.r.s.s.i.SysUserServiceImpl.selectUserList | local | ok |
c.r.s.s.i 是探针对 com.ruoyi.system.service.impl 的缩写。同一批业务请求还可观察到:
SysUserServiceImpl.selectUserByUserNameSysUserServiceImpl.updateLoginInfoSysPermissionServiceImpl.getRolePermission
最终验收应确认入口请求、Controller 和自定义方法使用同一 Trace ID,父子关系正确。截图时需遮盖真实 Trace ID、用户标识、访问令牌和业务参数。
写在最后
包级插桩会增加 span 数量和字节码增强范围。不建议一开始就配置 com.ruoyi、公司根包或第三方框架包,也应避免工具包、实体类包和高频 getter/setter。对于流量较大的生产服务,应先选择一个具体的 Service 实现包,在小流量或预发布环境验证后再扩大范围。


