基于 OpenTelemetry:Kubernetes 环境下 Node.js 应用无侵入接入落地实践
本文介绍如何通过 DataKit Operator,为 Kubernetes 中的 Node.js 应用自动注入 OpenTelemetry 探针。
背景:为什么 Node.js 上了 Kubernetes,问题反而更难定位
Node.js 应用运行在 Kubernetes 中以后,Pod 会随着发布、扩容和故障迁移不断变化。接口一旦变慢或报错,容器日志和 Kubernetes 指标通常只能回答“哪个 Pod 有异常”,却很难继续说明时间消耗在应用内部、数据库、Redis,还是下游服务。
真正的难点不是缺少数据,而是缺少一次请求从入口到下游的完整上下文。运维人员需要看到请求经过了哪些服务、每一步耗时多久、错误发生在哪个 Span,以及新版本发布前后的调用链是否发生变化。这正是 Trace 要解决的问题。
OpenTelemetry 能够自动采集 Node.js 调用链,但传统接入方式需要每个项目分别安装依赖、调整启动参数、配置上报地址。服务数量增加后,探针升级和配置一致性很快会成为新的维护负担。
这次实践选择 DataKit Operator,把探针接入从“每个项目单独改造”转为“平台统一配置、应用按标签启用”。业务镜像和业务代码保持不变,平台团队则可以集中管理探针版本、上报地址、资源属性和采样策略。
观测云如何解决这个问题
这套方案把职责分成三层。DataKit Operator 负责在新 Pod 创建时注入 OpenTelemetry 探针;OpenTelemetry 负责生成 Node.js Trace;DataKit 接收 OTLP 数据并发送到观测云。应用原有的容器日志和 Kubernetes 指标仍由 DataKit 采集,不会与 Trace 重复。

Node.js 应用
│ OpenTelemetry 自动生成 Trace
▼
DataKit Service
│ 接收并转发 OTLP 数据
▼
观测云应用性能监测
│
├── 服务与接口性能
├── 完整调用链和错误 Span
└── Pod、Namespace、Node 等资源属性
这种分工的价值在于,应用团队只需通过 Pod 标签声明“这个应用需要 Node.js 探针”,而平台团队只维护一套 Operator 规则。新 Pod 创建时,Operator 会加入 init Container、共享探针目录、NODE_OPTIONS 和 OTEL_* 环境变量,Node.js 进程启动后便能自动生成 Span。
实践:使用 DataKit Operator 自动注入 OpenTelemetry
前置条件
本文方案要求 DataKit Operator 不低于 v1.9.0,Node.js 不低于 20.6,并以 CommonJS 方式运行。普通 Linux 和 Alpine 镜像均可使用;ESM、bundler 打包、自定义 loader,或已经初始化另一套 OpenTelemetry SDK 的应用,应先在测试环境验证兼容性。
集群需要启用 MutatingAdmissionWebhook,节点能够拉取 Operator 和探针镜像,操作账号具备创建 Deployment、Service、RBAC 与 Admission Webhook 的权限。可以先检查 Node.js 版本和 Operator 是否已经存在:
kubectl -n production exec deploy/order-service -- node --version
kubectl -n datakit get deployment datakit-operator
安装 DataKit Operator
DataKit 与 DataKit Operator 是两个不同的组件:DataKit 负责采集和转发数据,Operator 负责监听新 Pod 并注入探针。因此,集群已经安装 DataKit 并不代表 Operator 已经存在。
使用 Helm 安装时,执行:
helm install datakit-operator datakit-operator \
--repo https://pubrepo.guance.com/chartrepo/datakit-operator \
-n datakit \
--create-namespace
如果需要下载 YAML 后审查和纳管配置,官方文件地址为:
https://static.guance.com/datakit-operator/datakit-operator.yaml
curl -LO https://static.guance.com/datakit-operator/datakit-operator.yaml
kubectl create namespace datakit
kubectl apply -f datakit-operator.yaml
kubectl -n datakit rollout status deployment/datakit-operator
datakit Namespace 已存在时,可以跳过创建命令。安装完成后,还应查看 Operator 镜像版本和最近日志,确认没有证书、RBAC 或 Webhook 注册错误:
kubectl -n datakit get deployment datakit-operator \
-o jsonpath='{.spec.template.spec.containers[0].image}{"\n"}'
kubectl -n datakit logs deployment/datakit-operator --tail=100
必须手动配置:新下载的 datakit-operator.yaml 默认没有 OpenTelemetry 注入规则
安装文件中的 admission_inject_v2.otels 默认是空数组 []。直接执行 kubectl apply -f datakit-operator.yaml 只会安装 Operator,不会自动启用 Node.js OpenTelemetry 注入。必须把 "otels": [] 替换为下文的 Node.js 规则,再重新应用 YAML 并重启 Operator。

开启 OpenTelemetry 采集器
DataKit 需要启用 opentelemetry 采集器,才能接收应用发送的 Trace。在 DaemonSet 的 ENV_DEFAULT_ENABLED_INPUTS 中加入 opentelemetry:
env:
- name: ENV_DEFAULT_ENABLED_INPUTS
value: >-
cpu,disk,diskio,mem,system,container,opentelemetry
本文使用的 OTLP Trace 地址为:
http://datakit-service.datakit.svc.cluster.local:9529/otel/v1/traces
如果 DataKit 的 Namespace 或 Service 名称不同,需要同步调整后面的 Endpoint。接着在 datakit-operator.yaml 中搜索:
"otels": []
将其整体替换为下面的数组,不要重复粘贴第二个 admission_inject_v2,也不要删除同级的 ddtraces、logfwds 等配置:
"otels": [
{
"name": "otel-nodejs",
"language": "nodejs",
"namespace_selectors": ["*"],
"label_selectors": [
"admission.datakit/otel-language=nodejs"
],
"check_annotation": false,
"image": "ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-nodejs:0.78.0",
"envs": {
"POD_NAME": "{fieldRef:metadata.name}",
"POD_NAMESPACE": "{fieldRef:metadata.namespace}",
"NODE_NAME": "{fieldRef:spec.nodeName}",
"OTEL_SERVICE_NAME": "{fieldRef:metadata.labels['app']}",
"OTEL_RESOURCE_ATTRIBUTES": "k8s.pod.name=$(POD_NAME),k8s.namespace.name=$(POD_NAMESPACE),k8s.node.name=$(NODE_NAME)",
"OTEL_TRACES_EXPORTER": "otlp",
"OTEL_LOGS_EXPORTER": "none",
"OTEL_METRICS_EXPORTER": "none",
"OTEL_EXPORTER_OTLP_PROTOCOL": "http/protobuf",
"OTEL_EXPORTER_OTLP_TRACES_ENDPOINT": "http://datakit-service.datakit.svc.cluster.local:9529/otel/v1/traces"
},
"resources": {
"requests": {
"cpu": "100m",
"memory": "64Mi"
},
"limits": {
"cpu": "500m",
"memory": "512Mi"
}
}
}
]
这里有三个真正决定接入结果的配置:label_selectors 决定哪些 Pod 会被注入,OTEL_SERVICE_NAME 决定观测云中显示的服务名,OTEL_EXPORTER_OTLP_TRACES_ENDPOINT 决定 Trace 发送到哪个 DataKit。如果集群无法访问 ghcr.io,应先把自动探针镜像同步到企业私有仓库,再替换 image。
保存文件后重新应用并检查配置:
kubectl apply -f datakit-operator.yaml
kubectl -n datakit rollout restart deployment/datakit-operator
kubectl -n datakit rollout status deployment/datakit-operator
kubectl -n datakit get configmap -o yaml | grep -A 15 '"otels"'
应用启用探针
平台规则准备完成后,应用 Deployment 只需修改 Pod Template。下面示例中的 app 标签会成为观测云中的服务名:
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
namespace: production
spec:
replicas: 2
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
admission.datakit/otel-language: nodejs
annotations:
admission.datakit/ddtrace.enabled: "false"
admission.datakit/otel.enabled: "true"
spec:
containers:
- name: app
image: registry.example.com/order-service:1.0.0
ports:
- name: http
containerPort: 3000
标签和注解必须位于 spec.template.metadata,而不是 Deployment 最外层的 metadata。其中 ddtrace.enabled: "false" 用于避免同时加载 DDTrace 与 OpenTelemetry,otel.enabled: "true" 明确启用本次注入。

Operator 只处理新创建的 Pod,因此修改规则或 Deployment 后必须重新创建 Pod:
kubectl apply -f deployment.yaml
kubectl -n production rollout restart deployment/order-service
kubectl -n production rollout status deployment/order-service
检查
Operator 采用 fail-open 方式,注入失败时业务 Pod 仍可能正常创建。验证时应直接检查 NODE_OPTIONS 和 OTEL_* 环境变量:
kubectl -n production exec deploy/order-service -- \
sh -c 'echo "$NODE_OPTIONS"; env | grep "^OTEL_" | sort'
正常情况下,Pod 中还能看到 datakit-otel-lib-init、datakit-otel-auto-instrument 共享卷和 /otel-auto-instrumentation-nodejs 挂载目录。探针最终会通过下面的启动参数先于业务代码加载:
--require /otel-auto-instrumentation-nodejs/autoinstrumentation.js
如果这些内容不存在,优先检查 Pod 标签位置、Operator 中的 otels 是否仍为空、是否同时匹配 DDTrace 规则,以及 Operator 日志中的 Warning。使用 runAsNonRoot: true 时还要显式设置非零 runAsUser;探针镜像拉取失败时则需要检查 GHCR 网络或私有镜像地址。
效果
接入完成后,访问一个会经过数据库、Redis 或下游 HTTP 服务的真实接口:
curl http://<业务访问地址>/api/orders
随后进入观测云“应用性能监测”,使用下面的条件定位数据:
service:order-service
source:opentelemetry
此时不仅能看到入口 HTTP Span,还能沿着火焰图查看数据库、缓存和下游服务调用。Span 的耗时、状态、错误信息以及 Pod、Namespace、Node 等 Kubernetes 资源属性,会被放在同一条 Trace 上下文中。排障人员可以从“接口慢”继续下钻到“哪一个调用慢、慢了多久、运行在哪个 Pod”。

上图中已经出现 sdk_language=nodejs 与 sdk_name=opentelemetry,说明数据确实来自 Node.js OpenTelemetry 探针。火焰图还能直观看到各 Span 的时间占比,错误 Span 会被单独标识,便于定位失败发生在哪一层。
生产推广时,建议先选一个非核心服务验证应用启动、CPU、内存和 Trace 完整性,再逐步灰度。低流量验证阶段可以暂时使用 100% 采样;数据稳定后,再根据请求量和成本配置 parentbased_traceidratio 等采样策略。
总结
DataKit Operator 将 Node.js OpenTelemetry 接入从项目级改造变成平台级能力。应用团队无需安装 npm 探针包或维护启动参数,只需在 Pod Template 中加入标签和注解;平台团队则统一管理探针版本、DataKit 地址、资源属性和采样策略。
落地时最容易遗漏的不是业务代码,而是 Operator 配置本身:新下载的 datakit-operator.yaml 中 "otels": [] 默认为空,必须手动补齐 Node.js 注入规则。完成配置后,还要重新创建 Pod,并通过 NODE_OPTIONS、OTEL_* 环境变量和观测云中的真实 Trace 三层验证接入结果。
当 Trace 与 Kubernetes 资源上下文进入观测云后,团队不仅能够定位一次慢请求,还可以让 AI 对多条 Trace、错误 Span 和版本差异做辅助归纳。这使可观测数据从“能看见”进一步走向“能解释、能验证、能形成排查闭环”。


