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