在 Kubernetes 中部署 OBI
本文档介绍如何手动在 Kubernetes 中部署 OBI,并自己设置所有必需的实体。
配置 Kubernetes 元数据装饰
OBI 可以用以下 Kubernetes 标签来装饰您的追踪信息
k8s.namespace.namek8s.deployment.namek8s.statefulset.namek8s.replicaset.namek8s.daemonset.namek8s.node.namek8s.pod.namek8s.container.namek8s.pod.uidk8s.pod.start_timek8s.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: trueYAML 配置来配置 OBI。不要忘记在您的 OBI Pod 中指定
serviceAccountName: obi属性 (如稍后的部署示例所示)。
可选地,在 YAML 配置文件中的 discovery -> instrument 部分选择要仪器化的 Kubernetes 服务。有关更多信息,请参阅本页的服务发现部分以及提供外部配置文件部分。
部署 OBI
您可以通过两种不同的方式在 Kubernetes 中部署 OBI
- 作为 sidecar 容器
- 作为 DaemonSet
将 OBI 部署为 sidecar 容器
如果您想监控某个可能未部署在所有主机上的服务,可以使用此方法部署 OBI,这样您只需要为每个服务实例部署一个 OBI 实例。
将 OBI 部署为 sidecar 容器有以下配置要求
- 进程命名空间必须在 Pod 的所有容器之间共享 (
shareProcessNamespace: truepod 变量) - 自动仪器化容器必须在特权模式下运行 (容器配置的
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:true 或 SYS_ADMIN Linux 功能。虽然这在所有情况下都有效,但如果您的安全配置要求您这样做,也有办法在 Kubernetes 中以较低的权限部署 OBI。这是否可行取决于您的 Kubernetes 版本和使用的底层容器运行时 (例如 Containerd、CRI-O 或 Docker)。
以下指南主要基于在 GKE、kubeadm、k3s、microk8s 和 kind 中运行 containerd 进行的测试。
要以非特权模式运行 OBI,您需要用一组 Linux 功能替换 privileged:true 设置。OBI 所需功能的完整列表可以在安全、权限和功能中找到。
注意 加载 BPF 程序要求 OBI 能够读取 Linux 性能事件,或至少能够执行 Linux 内核 API perf_event_open()。
此权限由 CAP_PERFMON 授予,或者通过 CAP_SYS_ADMIN 更宽松地授予。由于 CAP_PERFMON 和 CAP_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