OpenTelemetry OpenTelemetry

使用 OpenTelemetry Operator 管理 Collector 与自动插桩

将 Collector 和插桩声明为 Kubernetes 资源,并通过 Pod 注解注入 agent。

15 min 高级

OpenTelemetry Operator 将两样东西作为 Kubernetes 资源来管理:Collector(一个 OpenTelemetryCollector 对象)和自动插桩(一个 Instrumentation 对象,外加告诉 Operator 注入 agent 的 Pod 注解)。应用团队只需添加一个注解就能获得遥测数据,而不必编辑 Dockerfile,平台团队则把设置集中保存在 Git 的一处。

安装 Operator

Operator 使用准入 webhook,需要 TLS 证书。最简单的途径是 cert-manager,chart 默认期望它。如果没有 cert-manager,让 Helm 生成自签名证书:

helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts
helm repo update

helm install my-opentelemetry-operator open-telemetry/opentelemetry-operator \
  --namespace opentelemetry-operator-system --create-namespace \
  --set "manager.collectorImage.repository=otel/opentelemetry-collector-k8s" \
  --set admissionWebhooks.certManager.enabled=false \
  --set admissionWebhooks.autoGenerateCert.enabled=true

如果已经安装了 cert-manager,你可以保持证书设置为默认值,或者通过 admissionWebhooks.certManager.issuerRef 引用你自己的 issuer。用 --version 固定 chart 版本,并把 values 放在文件或 Flux HelmRelease 中,而不是 --set 参数里。

声明一个 Collector

apiVersion: opentelemetry.io/v1beta1
kind: OpenTelemetryCollector
metadata:
  name: demo
  namespace: observability
spec:
  mode: deployment        # deployment, daemonset, statefulset, or sidecar
  replicas: 2
  config:
    receivers:
      otlp:
        protocols:
          grpc:
            endpoint: 0.0.0.0:4317
          http:
            endpoint: 0.0.0.0:4318
    processors:
      memory_limiter:
        check_interval: 1s
        limit_percentage: 80
        spike_limit_percentage: 20
      batch: {}
    exporters:
      debug: {}
    service:
      pipelines:
        traces:
          receivers: [otlp]
          processors: [memory_limiter, batch]
          exporters: [debug]

config 块就是你一直在编写的同样的 Collector YAML,嵌入在资源中。Operator 创建工作负载和一个名为 <name>-collector(这里是 demo-collector)的 Service,暴露你的 receiver 所使用的端口。可用的 mode 值对应于部署指南中的模式:daemonset 用于 agent,deployment 用于 gateway,sidecar 用于注入到应用 pod 中的 Collector。请检查你集群中可用的 CRD 版本(kubectl get crd opentelemetrycollectors.opentelemetry.io),因为 API 版本在各个发布版本中有过变化。

声明插桩设置

Instrumentation 资源保存 Operator 注入到被注解 pod 中的 SDK 设置:

apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
  name: demo-instrumentation
  namespace: webapp
spec:
  exporter:
    endpoint: http://demo-collector.observability.svc.cluster.local:4318
  propagators:
    - tracecontext
    - baggage
  sampler:
    type: parentbased_traceidratio
    argument: "0.25"
  java:
    env:
      - name: OTEL_INSTRUMENTATION_COMMON_DEFAULT_ENABLED
        value: "true"

重要的细节:

  • 如果省略 exporter.endpoint,它默认为 http://localhost:4317,这通常什么也送不到。请始终把它设为你的 Collector 的 Service。
  • 对于位于另一个命名空间中的 Collector,请使用完整的 DNS 名称 <service>.<namespace>.svc.cluster.local。
  • 文档中的示例对 .NET、Go、Java 和 Python 使用端口 4318(HTTP),对 Node.js 使用 4317(gRPC)。请让端口与该语言 agent 所使用的协议相匹配。
  • 特定于语言的环境变量放在 spec.<language>.env 下,比如 spec.java.env 或 spec.python.env。

请在应用之前创建 Instrumentation。 Operator 在创建 pod 时注入 agent。如果该资源当时还不存在,pod 会在没有 init 容器的情况下启动,你就得重启它。

用注解让工作负载选择加入

在 Deployment 的 pod 模板上添加一个注解:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: checkout
  namespace: webapp
spec:
  selector:
    matchLabels: { app: checkout }
  template:
    metadata:
      labels: { app: checkout }
      annotations:
        instrumentation.opentelemetry.io/inject-java: "true"
    spec:
      containers:
        - name: checkout
          image: myorg/checkout:1.4.2

每种语言的注解:

语言注解
Javainstrumentation.opentelemetry.io/inject-java: "true"
Pythoninstrumentation.opentelemetry.io/inject-python: "true"
Node.jsinstrumentation.opentelemetry.io/inject-nodejs: "true"
.NETinstrumentation.opentelemetry.io/inject-dotnet: "true"
Goinstrumentation.opentelemetry.io/inject-go: "true"(外加目标可执行文件注解)
仅 SDK 设置instrumentation.opentelemetry.io/inject-sdk: "true"

值可以是 "true"(使用此命名空间中的 Instrumentation)、某个 Instrumentation 的名称("my-instrumentation")、带命名空间的名称("other-ns/my-instrumentation")或 "false"。把注解放在命名空间上,可以为其中每个工作负载插桩。

你会用到的相关注解:

  • instrumentation.opentelemetry.io/container-names: "app,worker" 选择要插桩哪些容器。默认只有第一个容器会被插桩。
  • resource.opentelemetry.io/service.name: checkout 设置资源属性,比如服务名称。其他还有 service.version 和 deployment.environment.name。
  • instrumentation.opentelemetry.io/inject-sdk: "true" 只注入环境变量,适用于已经包含 SDK 的应用。
  • 单个容器不能注入多于一种语言。多容器、多语言的设置需要 enable-multi-instrumentation 功能开关,以及按语言划分的容器名称注解。
  • Go 需要 enable-go-instrumentation 功能门、otel-go-auto-target-exe 注解(或 OTEL_GO_AUTO_TARGET_EXE 设置),以及一个特权 sidecar,所以请先审视其中的安全影响。

应用与验证

kubectl apply -f collector.yaml -f instrumentation.yaml
kubectl apply -f checkout-deployment.yaml

kubectl get opentelemetrycollectors,instrumentations -A
kubectl get pod -n webapp -l app=checkout -o jsonpath='{.items[0].spec.initContainers[*].name}'; echo
kubectl logs -n observability deploy/demo-collector | grep -i 'service.name'

应用 pod 上出现名为 opentelemetry-auto-instrumentation 的 init 容器,意味着注入成功。生成一些流量,Collector 的 debug exporter 应当打印出带有你的服务名称的 span。

故障排查

  1. Instrumentation 资源在吗? kubectl describe otelinst -n <namespace>。
  2. Operator 有错误吗? 阅读 Operator 命名空间中 manager 容器的日志。
  3. 它是在应用之前创建的吗? 如果不是,请重启 Deployment,使 pod 被重新创建并被注入。检查 kubectl get events -n <namespace> 中的 init 容器。
  4. 注解对吗? 它必须位于 spec.template.metadata.annotations 上,并且语言必须与应用匹配。
  5. 端点对吗? 错误的协议或端口,或跨命名空间使用简短的 Service 名称,会悄无声息地什么也发不出去。

何时优先选择 Operator

当许多团队部署到集群,并且你希望遥测通过注解选择加入、设置由中央掌管时,请使用它。当你只需要一个 Collector,并在每个应用自己的清单中处理 SDK 配置时,请使用普通的 Helm chart。Operator 和 chart 可以共存,但不要用两者同时管理同一个 Collector。

热门指南