基于 OpenTelemetry:Kubernetes 环境下 Python 应用无侵入接入落地实践
本文介绍如何通过观测云实现 Kubernetes 环境下 Python 应用的无侵入式可观测接入,还原跨服务调用关系,快速定位慢请求与异常,降低接入和运维成本。
面向已经运行在 Kubernetes 中的 Python 应用,通过自动注入零代码采集链路数据并上报观测云,无需修改业务启动命令或手工创建 Span。
背景
微服务应用通常包含多个内部调用。只查看接口状态和容器日志,可以知道请求失败,却难以回答请求经过了哪些服务、耗时集中在哪里,以及异常由哪个下游引起。
OpenTelemetry(OTel)提供统一的可观测数据标准,但逐个修改 Python 应用、安装依赖和调整启动命令,会增加接入和维护成本。本实践从“Python 业务已经部署并正常运行”出发,使用 Kubernetes 自动注入完成接入。
实践目标包括:
- 不修改业务代码和原有启动命令;
- 通过标签控制需要接入的 Python 工作负载;
- 自动采集 HTTP 服务端与客户端调用;
- 将 Trace、Metrics 和 Logs 通过 OTLP 发送到观测云;
- 在观测云中还原跨服务调用关系,定位慢请求和异常调用。
观测云介绍
观测云是一站式可观测平台,可统一接入和分析基础设施、容器、指标、日志、链路等数据。不同类型的观测数据使用统一的服务、环境和资源属性建立关联,使排查过程可以从服务异常继续下钻到一次请求、相关日志及其运行环境。
应用性能监测(APM)以分布式链路追踪为核心,将一次请求经过的服务和组件组织成完整调用链。接入链路数据后,可以在观测云中查看:
- 服务性能:请求数、错误率、平均响应时间以及 P90、P99 等延迟指标;
- 服务关系:通过服务拓扑了解上下游依赖和调用方向;
- 链路详情:通过火焰图、瀑布图和 Span 列表分析各调用环节的耗时与状态;
- 关联分析:根据
trace_id、service等字段关联请求产生的日志、指标和基础设施信息。
观测云通过 DataKit 接收 OpenTelemetry、DDTrace、Jaeger、Zipkin 等协议产生的链路数据。本实践选择 OpenTelemetry:Python OTel 探针负责生成可观测数据,DataKit 在 Kubernetes 集群内接收 OTLP 数据并上报观测云,DataKit Operator 则负责在应用 Pod 创建时自动注入探针。下文将围绕这条接入链路展开配置。
实践
方案架构与接入流程
本方案由 DataKit Operator、Python OTel 探针和 DataKit 共同完成。整个接入过程分为探针注入和数据上报两个阶段。

探针注入流程:
- 为 Python 应用的 Pod 模板添加语言标签;
- Kubernetes 创建 Pod 时调用 DataKit Operator 注册的 Mutating Admission Webhook;
- Operator 根据注入规则修改 Pod 配置,加入 initContainer、共享卷和 OTel 环境变量;
- Pod 启动时自动加载 Python OTel 探针,无需修改业务代码和启动命令。
数据上报流程:
- OTel 探针自动采集应用产生的 Trace、Metrics 和 Logs;
- 探针通过 OTLP/HTTP 将数据发送到集群内的 DataKit;
- DataKit 对数据进行处理后上报观测云;
- 用户在观测云中查看服务调用链、运行指标和关联日志。
下面依次完成 DataKit、DataKit Operator 和业务应用的配置,并验证最终接入效果。
配置 DataKit
如果集群尚未安装 DataKit,先按照官方文档完成 Kubernetes 安装;已有 DataKit 时只需确认以下配置,不要覆盖原有采集器:
| 配置 | 要求 |
|---|---|
| ENV_DEFAULT_ENABLED_INPUTS | 包含 opentelemetry |
| ENV_HTTP_LISTEN | 设置为 0.0.0.0:9529 |
| datakit-service | 暴露 9529 端口 |
应用配置后确认 DataKit 正常运行并已启用 OpenTelemetry input:
kubectl rollout status daemonset/datakit -n datakit --timeout=5m
kubectl get pod,service,endpointslice -n datakit -o wide
kubectl logs -n datakit daemonset/datakit --tail=100 | grep -i opentelemetry
业务探针通过以下地址访问 DataKit:
http://datakit-service.datakit.svc.cluster.local:9529
配置 DataKit Operator
安装 DataKit Operator 官方清单,并在 admission_inject_v2.otels 中添加 Python 规则。本文使用语言标签选择工作负载,不绑定具体服务名:
{
"name": "otel-python",
"language": "python",
"namespace_selectors": ["*"],
"label_selectors": ["admission.datakit/otel-language=python"],
"check_annotation": false,
"image": "ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-python:0.64b0",
"image_pull_policy": "IfNotPresent",
"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_PROPAGATORS": "tracecontext,baggage",
"OTEL_TRACES_SAMPLER": "parentbased_always_on",
"OTEL_TRACES_EXPORTER": "otlp",
"OTEL_LOGS_EXPORTER": "otlp",
"OTEL_METRICS_EXPORTER": "otlp",
"OTEL_PYTHON_LOG_AUTO_INSTRUMENTATION": "true",
"OTEL_PYTHON_FLASK_EXCLUDED_URLS": "healthz",
"OTEL_EXPORTER_OTLP_PROTOCOL": "http/protobuf",
"OTEL_EXPORTER_OTLP_TRACES_ENDPOINT": "http://datakit-service.datakit.svc.cluster.local:9529/otel/v1/traces",
"OTEL_EXPORTER_OTLP_LOGS_ENDPOINT": "http://datakit-service.datakit.svc.cluster.local:9529/otel/v1/logs",
"OTEL_EXPORTER_OTLP_METRICS_ENDPOINT": "http://datakit-service.datakit.svc.cluster.local:9529/otel/v1/metrics"
},
"resources": {
"requests": {"cpu": "100m", "memory": "64Mi"},
"limits": {"cpu": "500m", "memory": "512Mi"}
}
}
OTEL_SERVICE_NAME 复用业务已有的 app 标签生成服务名。如果集群使用其他标签规范,应同步调整这里的字段映射。OTEL_PYTHON_FLASK_EXCLUDED_URLS 用于排除高频健康检查路径。
完整 Operator 配置见 manifests/datakit-operator.yaml。修改 ConfigMap 后重启 Operator,使配置重新加载:
kubectl apply -f manifests/datakit-operator.yaml
kubectl rollout restart deployment/datakit-operator -n datakit
kubectl rollout status deployment/datakit-operator -n datakit --timeout=5m
应用接入
在业务 Deployment 的 spec.template.metadata.labels 中加入语言标签。app 用于生成 service.name,admission.datakit/otel-language 用于匹配 Python 注入规则:
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
namespace: <业务命名空间>
spec:
template:
metadata:
labels:
app: order-service
admission.datakit/otel-language: python
将标签合并到业务 Deployment,并通过现有发布流程应用
kubectl apply -f <业务 Deployment 清单>
kubectl rollout status deployment/<应用名> -n <业务命名空间> --timeout=5m
新增上述标签会改变 Pod 模板,Deployment 将自动滚动创建新 Pod,无需额外执行重启。如果标签在安装 Operator 前已经存在,或者只修改了 Operator 注入规则,现有 Pod 不会被重新处理,此时需要主动触发滚动更新:
kubectl rollout restart deployment/<应用名> -n <业务命名空间>
kubectl rollout status deployment/<应用名> -n <业务命名空间> --timeout=5m
应用验证
首先检查新 Pod 是否已经注入探针:
kubectl get pod -n <业务命名空间> -o wide
kubectl describe pod -n <业务命名空间> -l app=<应用名>
注入成功后,Pod 中应出现:
datakit-otel-lib-initinitContainer;/otel-auto-instrumentation-python共享卷挂载;OTEL_SERVICE_NAME和 OTLP endpoint 等环境变量;- 状态为
Running,业务容器处于Ready。
通过应用原有入口发送一次能够触发下游调用的请求,并使用唯一的 X-Request-ID 标记本次验证流量。先在观测云日志中按 Request ID 找到对应的 trace_id,再进入 应用性能监测 → 链路 查询。重点确认:
- 入口服务和下游服务均产生 Span;
- Span 之间具有正确的父子关系;
- 请求方法、路由、状态码和耗时符合实际请求;
- 慢请求和异常请求能够在链路中定位到具体服务。
如果 Pod 没有注入,优先检查语言标签位置、Operator 日志和探针镜像拉取状态;如果 Pod 已注入但没有数据,检查 DataKit 的 9529 端口、OpenTelemetry input 和业务容器中的 exporter 错误。
实践效果
完成自动注入后,向订单服务发起一次正常请求。观测云共采集到 9 个 Span,覆盖订单、商品和库存三个服务;入口 POST /orders 成为完整链路的根 Span,持续时间为 13.24 ms,本次请求没有错误 Span。
链路全貌与关联日志
火焰图展示了订单请求及其四次下游调用,可以直观看到每个调用在整条链路中的发生时间和耗时占比。链路详情同时按 trace_id 关联出三个服务产生的 5 条业务日志。

服务调用关系
服务调用关系还原出以 order-service 为入口的调用拓扑:订单服务分别调用 catalog-service 和 inventory-service,每个下游服务均被调用两次。该视图能够快速确认服务依赖、调用次数及各服务在本次请求中的耗时占比。

Span 时序与明细
瀑布图进一步展示了 Span 的父子关系和执行顺序,说明 Trace 上下文已经在服务间完成传播。

总结
通过 DataKit Operator 与 OpenTelemetry 自动注入,Python 应用无需改造业务代码即可获得完整的跨服务链路和关联日志。该方案将探针管理从应用侧收敛到 Kubernetes 平台侧,兼顾标准化、低侵入和规模化接入,为云原生 Python 应用接入观测云提供了一条清晰、可复制的路径。


