使用 OBI 进行分布式追踪
简介
OBI 支持应用程序的分布式追踪,但存在一些限制和内核版本限制。
分布式追踪是通过传播 W3C traceparent 头部值来实现的。traceparent 上下文传播是自动的,不需要任何操作或配置。
OBI 读取任何传入的追踪上下文头部值,跟踪程序执行流程,并通过自动将 traceparent 字段添加到传出的 HTTP/gRPC 请求中来传播追踪上下文。如果应用程序已在传出的请求中添加了 traceparent 字段,OBI 将使用该值进行追踪,而不是使用其自身生成的追踪上下文。如果 OBI 找不到传入的 traceparent 上下文值,它将根据 W3C 规范生成一个。
实现
追踪上下文传播通过两种不同的方式实现
- 通过在网络层面写入传出的头部信息
- 通过在 Go 的库层面写入头部信息
根据您的服务是用什么编程语言编写的,OBI 会使用一种或两种上下文传播方法。我们使用这些多种方法来实现上下文传播,因为使用 eBPF 进行内存写入取决于内核配置以及授予 OBI 的 Linux 系统能力。有关此主题的更多详细信息,请参阅我们 KubeCon NA 2024 的演讲 So You Want to Write Memory with eBPF?。
网络层面的上下文传播默认是禁用的,可以通过设置环境变量 OTEL_EBPF_BPF_CONTEXT_PROPAGATION=all 或修改 OBI 配置文件来启用。
ebpf:
context_propagation: 'all'
网络层面的上下文传播
网络层面的上下文传播通过在 HTTP 传出头部和 TCP/IP 数据包层面写入追踪上下文信息来实现。HTTP 上下文传播与任何其他基于 OpenTelemetry 的追踪库完全兼容。这意味着 OBI 仪器化的服务在与使用 OpenTelemetry SDK 仪器化的服务进行通信时,可以正确地传播追踪信息。我们使用 Linux Traffic Control (TC) 来调整网络数据包,这要求其他使用 Linux Traffic Control 的 eBPF 程序与 OBI 进行适当的链式处理。关于 Cilium CNI 的特殊注意事项,请参考我们的 Cilium 兼容性指南。
对于 TLS 加密流量 (HTTPS),OBI 无法将追踪信息注入到传出的 HTTP 头部中,而是将其注入到 TCP/IP 数据包层面。由于这个限制,OBI 只能将追踪信息发送到其他 OBI 仪器化的服务。L7 代理和负载均衡器会干扰 TCP/IP 上下文传播,因为原始数据包会被丢弃并在下游重放。解析来自 OpenTelemetry SDK 仪器化服务的传入追踪上下文信息仍然有效。
gRPC 和 HTTP/2 目前不受支持。
这种类型的上下文传播适用于任何编程语言,并且不需要 OBI 以 privileged 模式运行或拥有 CAP_SYS_ADMIN 权限。有关更多详细信息,请参阅 分布式追踪与上下文传播 配置部分。
Kubernetes 配置
在 Kubernetes 上部署 OBI 并启用网络层面的分布式追踪支持的推荐方式是使用 DaemonSet。
必须使用以下 Kubernetes 配置
- OBI 必须作为
DaemonSet部署,并启用主机网络访问 (hostNetwork: true)。 - 主机上的
/sys/fs/cgroup路径必须作为本地/sys/fs/cgroup路径进行卷挂载。 - 必须授予 OBI 容器
CAP_NET_ADMIN能力。
以下 YAML 片段显示了一个 OBI 部署配置示例
spec:
serviceAccount: obi
hostPID: true # <-- Important. Required in DaemonSet mode so OBI can discover all monitored processes
hostNetwork: true # <-- Important. Required in DaemonSet mode so OBI can see all network packets
dnsPolicy: ClusterFirstWithHostNet
containers:
- name: obi
resources:
limits:
memory: 120Mi
terminationMessagePolicy: FallbackToLogsOnError
image: 'docker.io/otel/ebpf-instrument:main'
imagePullPolicy: 'Always'
env:
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: 'http://otelcol:4318'
- name: OTEL_EBPF_KUBE_METADATA_ENABLE
value: 'autodetect'
- name: OTEL_EBPF_CONFIG_PATH
value: '/config/obi-config.yml'
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.
- NET_ADMIN # <-- Important. Allows OBI to inject HTTP and TCP context propagation information.
volumeMounts:
- name: cgroup
mountPath: /sys/fs/cgroup # <-- Important. Allows OBI to monitor all newly sockets to track outgoing requests.
- mountPath: /config
name: obi-config
tolerations:
- effect: NoSchedule
operator: Exists
- effect: NoExecute
operator: Exists
volumes:
- name: obi-config
configMap:
name: obi-config
- name: cgroup
hostPath:
path: /sys/fs/cgroup
如果 /sys/fs/cgroup 未作为 OBI DaemonSet 的本地卷路径挂载,则某些请求可能不会传播其上下文。我们使用此卷路径来监听新创建的套接字。
内核版本限制
网络层面上下文传播的传入头部解析通常需要内核 5.17 或更高版本才能添加和使用 BPF 循环。
一些已打补丁的内核,例如 RHEL 9.2,可能已将此功能移植回去。在您的内核包含此功能但版本低于 5.17 的情况下,设置 OTEL_EBPF_OVERRIDE_BPF_LOOP_ENABLED 会跳过内核检查。
通过在库级别进行仪器化来实现 Go 上下文传播
这种类型的上下文传播仅支持 Go 应用程序,并使用 eBPF 用户内存写入支持 (bpf_probe_write_user)。这种方法的优点是它适用于 HTTP/HTTP2/HTTPS 和 gRPC(有一些限制),但是使用 bpf_probe_write_user 需要 OBI 被授予 CAP_SYS_ADMIN 权限或被配置为以 privileged 容器运行。
与 Go 手动仪器化的集成
OBI 可以通过 Auto SDK 自动集成手动跨度。请参阅 Auto SDK 的文档了解更多信息。
内核完整性模式限制
为了将 traceparent 值写入传出的 HTTP/gRPC 请求头部,OBI 需要使用 bpf_probe_write_user eBPF 帮助函数来写入进程内存。自内核 5.14(以及反向移植到 5.10 系列的修复程序)以来,如果 Linux 内核处于 integrity lockdown 模式,此帮助函数就会受到保护(并且 BPF 程序无法使用)。如果内核启用了 Secure Boot,内核完整性模式通常会默认启用,但也可以手动启用。
OBI 会自动检查是否可以使用 bpf_probe_write_user 帮助函数,并且仅在内核配置允许的情况下启用上下文传播。通过运行以下命令来验证 Linux 内核的 lockdown 模式
cat /sys/kernel/security/lockdown
如果该文件存在且模式不是 [none],则 OBI 无法执行上下文传播,分布式追踪将被禁用。
容器化环境(包括 Kubernetes)中的 Go 分布式追踪
由于内核 lockdown 模式的限制,Docker 和 Kubernetes 配置文件应将 OBI docker 容器的 /sys/kernel/security/ 卷从主机系统挂载。这样 OBI 就可以正确确定 Linux 内核的 lockdown 模式。以下是一个 Docker compose 配置示例,它确保 OBI 拥有足够的信息来确定 lockdown 模式
services:
...
obi:
image: 'docker.io/otel/ebpf-instrument:main'
environment:
OTEL_EBPF_CONFIG_PATH: "/configs/obi-config.yml"
volumes:
- /sys/kernel/security:/sys/kernel/security
- /sys/fs/cgroup:/sys/fs/cgroup
如果未挂载 /sys/kernel/security/ 卷,OBI 将假定 Linux 内核未处于完整性模式。