在 Kubernetes 中部署 OBI

了解如何在 Kubernetes 中部署 OBI。

配置 Kubernetes 元数据装饰

OBI 可以用以下 Kubernetes 标签来装饰您的追踪信息

  • k8s.namespace.name
  • k8s.deployment.name
  • k8s.statefulset.name
  • k8s.replicaset.name
  • k8s.daemonset.name
  • k8s.node.name
  • k8s.pod.name
  • k8s.container.name
  • k8s.pod.uid
  • k8s.pod.start_time
  • k8s.cluster.name

要启用元数据装饰,您需要

  • 创建一个 ServiceAccount 并绑定一个 ClusterRole,授予对 Pods 和 ReplicaSets 的 list 和 watch 权限。您可以通过部署此示例文件来完成此操作

    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: obi
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
      name: obi
    rules:
      - apiGroups: ['apps']
        resources: ['replicasets']
        verbs: ['list', 'watch']
      - apiGroups: ['']
        resources: ['pods', 'services', 'nodes']
        verbs: ['list', 'watch']
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
      name: obi
    subjects:
      - kind: ServiceAccount
        name: obi
        namespace: default
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: ClusterRole
      name: obi
    

    (如果您将 OBI 部署在其他命名空间中,则需要更改 namespace: default 值)。

  • 使用 OTEL_EBPF_KUBE_METADATA_ENABLE=true 环境变量或 attributes.kubernetes.enable: true YAML 配置来配置 OBI。

  • 不要忘记在您的 OBI Pod 中指定 serviceAccountName: obi 属性 (如稍后的部署示例所示)。

可选地,在 YAML 配置文件中的 discovery -> instrument 部分选择要仪器化的 Kubernetes 服务。有关更多信息,请参阅本页的服务发现部分以及提供外部配置文件部分。

部署 OBI

您可以通过两种不同的方式在 Kubernetes 中部署 OBI

  • 作为 sidecar 容器
  • 作为 DaemonSet

将 OBI 部署为 sidecar 容器

如果您想监控某个可能未部署在所有主机上的服务,可以使用此方法部署 OBI,这样您只需要为每个服务实例部署一个 OBI 实例。

将 OBI 部署为 sidecar 容器有以下配置要求

  • 进程命名空间必须在 Pod 的所有容器之间共享 (shareProcessNamespace: true pod 变量)
  • 自动仪器化容器必须在特权模式下运行 (容器配置的 securityContext.privileged: true 属性)。
    • 某些 Kubernetes 安装允许以下 securityContext 配置,但它可能不适用于所有容器运行时配置,因为有些运行时会限制容器并移除某些权限

      securityContext:
        runAsUser: 0
        capabilities:
          add:
            - SYS_ADMIN
            - SYS_RESOURCE # not required for kernels 5.11+
      

以下示例通过将 OBI 作为容器附加来仪器化 goblog pod (镜像可在 otel/ebpf-instrument:main 获取)。自动仪器化工具被配置为将指标和追踪转发到 OpenTelemetry Collector,该 Collector 在同一命名空间中的 otelcol 服务后面可访问

apiVersion: apps/v1
kind: Deployment
metadata:
  name: goblog
  labels:
    app: goblog
spec:
  replicas: 2
  selector:
    matchLabels:
      app: goblog
  template:
    metadata:
      labels:
        app: goblog
    spec:
      # Required so the sidecar instrument tool can access the service process
      shareProcessNamespace: true
      serviceAccountName: obi # required if you want kubernetes metadata decoration
      containers:
        # Container for the instrumented service
        - name: goblog
          image: mariomac/goblog:dev
          imagePullPolicy: IfNotPresent
          command: ['/goblog']
          ports:
            - containerPort: 8443
              name: https
        # Sidecar container with OBI - the eBPF auto-instrumentation tool
        - name: obi
          image: otel/ebpf-instrument:main
          securityContext: # Privileges are required to install the eBPF probes
            privileged: true
          env:
            # The internal port of the goblog application container
            - name: OTEL_EBPF_OPEN_PORT
              value: '8443'
            - name: OTEL_EXPORTER_OTLP_ENDPOINT
              value: 'http://otelcol:4318'
              # required if you want kubernetes metadata decoration
            - name: OTEL_EBPF_KUBE_METADATA_ENABLE
              value: 'true'

有关不同配置选项的更多信息,请参阅本文件站点的配置部分。

将 OBI 部署为 Daemonset

您也可以将 OBI 部署为 Daemonset。如果您想

  • 仪器化 Daemonset
  • 仪器化单个 OBI 实例中的多个进程,甚至您集群中的所有进程。

使用前面的示例 (goblog pod),我们无法通过其开放端口来选择要仪器化的进程,因为该端口在 Pod 内部。同时,该服务的多个实例将具有不同的开放端口。在这种情况下,我们将需要通过使用应用程序服务可执行文件的名称来仪器化 (请参阅后面的示例)。

除了 sidecar 场景的权限要求外,您还需要在自动仪器化 pod 模板中启用 hostPID: true 选项,以便它可以访问同一主机上运行的所有进程。

---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: obi
  labels:
    app: obi
spec:
  selector:
    matchLabels:
      app: obi
  template:
    metadata:
      labels:
        app: obi
    spec:
      hostPID: true # Required to access the processes on the host
      serviceAccountName: obi # required if you want kubernetes metadata decoration
      containers:
        - name: autoinstrument
          image: otel/ebpf-instrument:main
          securityContext:
            privileged: true
          env:
            # Select the executable by its name instead of OTEL_EBPF_OPEN_PORT
            - name: OTEL_EBPF_AUTO_TARGET_EXE
              value: '*/goblog'
            - name: OTEL_EXPORTER_OTLP_ENDPOINT
              value: 'http://otelcol:4318'
              # required if you want kubernetes metadata decoration
            - name: OTEL_EBPF_KUBE_METADATA_ENABLE
              value: 'true'

以非特权模式部署 OBI

在迄今为止的所有示例中,OBI 部署的 securityContext 部分都使用了 privileged:trueSYS_ADMIN Linux 功能。虽然这在所有情况下都有效,但如果您的安全配置要求您这样做,也有办法在 Kubernetes 中以较低的权限部署 OBI。这是否可行取决于您的 Kubernetes 版本和使用的底层容器运行时 (例如 ContainerdCRI-ODocker)。

以下指南主要基于在 GKEkubeadmk3smicrok8skind 中运行 containerd 进行的测试。

要以非特权模式运行 OBI,您需要用一组 Linux 功能替换 privileged:true 设置。OBI 所需功能的完整列表可以在安全、权限和功能中找到。

注意 加载 BPF 程序要求 OBI 能够读取 Linux 性能事件,或至少能够执行 Linux 内核 API perf_event_open()

此权限由 CAP_PERFMON 授予,或者通过 CAP_SYS_ADMIN 更宽松地授予。由于 CAP_PERFMONCAP_SYS_ADMIN 都授予 OBI 读取性能事件的权限,因此您应该使用 CAP_PERFMON,因为它授予的权限较少。然而,在系统级别,对性能事件的访问是通过设置 kernel.perf_event_paranoid 来控制的,您可以使用 sysctl 或修改 /proc/sys/kernel/perf_event_paranoid 文件来读取或写入该设置。kernel.perf_event_paranoid 的默认设置为 2,这在内核文档perf_event_paranoid 部分有记录。一些 Linux 发行版为 kernel.perf_event_paranoid 定义了更高的级别,例如基于 Debian 的发行版也使用 kernel.perf_event_paranoid=3,这在没有 CAP_SYS_ADMIN 的情况下禁止访问 perf_event_open()。如果您运行的发行版的 kernel.perf_event_paranoid 设置高于 2,您可以选择修改您的配置将其降低到 2,或者使用 CAP_SYS_ADMIN 而不是 CAP_PERFMON

下面是一个 OBI 非特权容器配置的示例

...
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: obi
  namespace: obi-demo
  labels:
    k8s-app: obi
spec:
  selector:
    matchLabels:
      k8s-app: obi
  template:
    metadata:
      labels:
        k8s-app: obi
    spec:
      serviceAccount: obi
      hostPID: true           # <-- Important. Required in Daemonset mode so OBI can discover all monitored processes
      containers:
      - name: obi
        terminationMessagePolicy: FallbackToLogsOnError
        image: otel/ebpf-instrument:main
        env:
          - name: OTEL_EBPF_TRACE_PRINTER
            value: "text"
          - name: OTEL_EBPF_KUBE_METADATA_ENABLE
            value: "autodetect"
          - name: KUBE_NAMESPACE
            valueFrom:
              fieldRef:
                fieldPath: metadata.namespace
          ...
        securityContext:
          runAsUser: 0
          readOnlyRootFilesystem: true
          capabilities:
            add:
              - BPF                 # <-- Important. Required for most eBPF probes to function correctly.
              - SYS_PTRACE          # <-- Important. Allows OBI to access the container namespaces and inspect executables.
              - NET_RAW             # <-- Important. Allows OBI to use socket filters for http requests.
              - CHECKPOINT_RESTORE  # <-- Important. Allows OBI to open ELF files.
              - DAC_READ_SEARCH     # <-- Important. Allows OBI to open ELF files.
              - PERFMON             # <-- Important. Allows OBI to load BPF programs.
              #- SYS_RESOURCE       # <-- pre 5.11 only. Allows OBI to increase the amount of locked memory.
              #- SYS_ADMIN          # <-- Required for Go application trace context propagation, or if kernel.perf_event_paranoid >= 3 on Debian distributions.
            drop:
              - ALL
        volumeMounts:
        - name: var-run-obi
          mountPath: /var/run/obi
        - name: cgroup
          mountPath: /sys/fs/cgroup
      tolerations:
      - effect: NoSchedule
        operator: Exists
      - effect: NoExecute
        operator: Exists
      volumes:
      - name: var-run-obi
        emptyDir: {}
      - name: cgroup
        hostPath:
          path: /sys/fs/cgroup
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: some-service
  namespace: obi-demo
  ...
---

提供外部配置文件

在前面的示例中,OBI 是通过环境变量配置的。但是,您也可以通过外部 YAML 文件进行配置 (如本站点配置部分所述)。

要以文件形式提供配置,推荐的方法是部署一个包含所需配置的 ConfigMap,然后将其挂载到 OBI Pod 中,并通过 OTEL_EBPF_CONFIG_PATH 环境变量引用它。

包含 OBI YAML 文档的 ConfigMap 示例

apiVersion: v1
kind: ConfigMap
metadata:
  name: obi-config
data:
  obi-config.yml: |
    trace_printer: text
    otel_traces_export:
      endpoint: http://otelcol:4317
      sampler:
        name: parentbased_traceidratio
        arg: "0.01"
    routes:
      patterns:
        - /factorial/{num}

OBI DaemonSet 配置示例,挂载并访问前面的 ConfigMap

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: obi
spec:
  selector:
    matchLabels:
      instrumentation: obi
  template:
    metadata:
      labels:
        instrumentation: obi
    spec:
      serviceAccountName: obi
      hostPID: true #important!
      containers:
        - name: obi
          image: otel/ebpf-instrument:main
          imagePullPolicy: IfNotPresent
          securityContext:
            privileged: true
            readOnlyRootFilesystem: true
          # mount the previous ConfigMap as a folder
          volumeMounts:
            - mountPath: /config
              name: obi-config
            - mountPath: /var/run/obi
              name: var-run-obi
          env:
            # tell OBI where to find the configuration file
            - name: OTEL_EBPF_CONFIG_PATH
              value: '/config/obi-config.yml'
      volumes:
        - name: obi-config
          configMap:
            name: obi-config
        - name: var-run-obi
          emptyDir: {}

提供秘密配置

上一个示例适用于常规配置,但不应用于传递密码或 API 密钥等敏感信息。

要提供敏感信息,推荐的方法是部署 Kubernetes Secret。例如,此 secret 包含一些虚构的 OpenTelemetry Collector 凭证

apiVersion: v1
kind: Secret
metadata:
  name: otelcol-secret
type: Opaque
stringData:
  headers: 'Authorization=Bearer Z2hwX0l4Y29QOWhr....ScQo='

然后,您可以将 secret 值作为环境变量访问。遵循前面的 DaemonSet 示例,这将通过将以下 env 部分添加到 OBI 容器中来实现

env:
  - name: OTEL_EXPORTER_OTLP_HEADERS
    valueFrom:
      secretKeyRef:
        key: otelcol-secret
        name: headers

最后修改时间 2025 年 10 月 29 日: ISSUE 8251 将 obi 镜像标签更新为 main (#8252) (2a818d51)