主题
eBPF 全链路观测 部署文档(Beyla + Tempo on uat1)
目标集群:uat1(
192.168.122.31:6443,RKE2 v1.35.6,6 节点,Rocky 9.8 内核 5.14 带 BTF,Calico CNI) 命名空间:observability;复用cattle-monitoring-system的 rancher-monitoring(Prometheus + Grafana + Alertmanager) 本地 kubectl:/tmp/bin/kubectl,kubeconfig~/.kube/config-122.31
一、方案与选型
| 决策点 | 选择 | 理由 |
|---|---|---|
| 采集器 | Grafana Beyla v3.30.0 | 零侵入 eBPF 自动埋点;弃 Pixie(依赖外部 Cloud)、DeepFlow(太重) |
| Trace 存储 | Tempo v2.10.7 monolithic | 轻量单实例;不选 3.0(2026-06 架构大改,UAT 求稳) |
| 是否部署 OTel Collector | 否 | Beyla 直发 Tempo OTLP,减少一跳;后续需要采样/脱敏再插入 |
| Tempo 存储后端 | local backend(NFS PVC) | UAT 够用;生产必须换对象存储(S3/MinIO) |
| 服务拓扑 | Tempo metrics-generator | spanmetrics + servicegraph 聚合后 remote_write 回 Prometheus,Node Graph 直接用 |
| Grafana 数据源 | provisioning ConfigMap 注入 | rancher-monitoring 数据源是 provisioning 只读,API 改不动 |
数据流
进程 ──eBPF──► Beyla ──┬── :9090 Prometheus export ──► Prometheus(RED 指标)
└── OTLP/gRPC ──► Tempo:4317 ──► local blocks(NFS)
Tempo metrics-generator ──remote_write──► Prometheus(spanmetrics / servicegraph)
Grafana:Prometheus(exemplar→Tempo)+ Tempo 数据源 + RED 看板二、前置条件
- 内核 ≥ 5.8 且带 BTF:
ls /sys/kernel/btf/vmlinux(Rocky 9.8 内核 5.14 ✅) - Harbor
192.168.122.156:30000已建observability项目(须先在 Harbor API/UI 创建,skopeo push 不会自动建) - skopeo 已登录 Harbor(凭据在
~/.docker/config.json) - rancher-monitoring 正常运行(Prometheus CR 名
rancher-monitoring-prometheus)
三、部署步骤
3.1 同步镜像(离线环境)
bash
cd worker/apps/midddleware/ebpf-observability
export HARBOR_PASSWORD='xxx' # 或放 .secrets.env
./deploy.sh --sync镜像清单(images.txt,共 2 个):
grafana/beyla:3.30.0 # 注意:DaoCloud 上 beyla 的 tag 无 v 前缀(带 v 会 401)
grafana/tempo:2.10.7源站 docker.m.daocloud.io → Harbor observability 项目。attestation 层导致 --all 失败时脚本自动 fallback --override-os linux --override-arch amd64。
3.2 一键部署
bash
./deploy.shdeploy.sh 依次做 6 件事(全部幂等):
- 建 ns
observability - 部署 Tempo(Deployment + PVC 10Gi + ConfigMap + svc 4317/4318/3200)
- 部署 Beyla(SA + ClusterRole + ConfigMap + DaemonSet + metrics Service)
- apply
monitoring.yaml(ServiceMonitor + PrometheusRule) - 给 Prometheus CR patch
--web.enable-remote-write-receiver(已存在则跳过) - 覆盖 Grafana 数据源 ConfigMap(加 Tempo + exemplar 联动)并重启 Grafana pod
3.3 关键配置说明
Beyla(beyla.yaml 中 ConfigMap beyla-config):
yaml
discovery:
instrument:
- k8s_namespace: "*" # ⚠️ v3 的 k8s_* 选择器是 glob,不是正则!
exclude_instrument:
- k8s_namespace: "kube-system" # 逐个 glob 枚举,不能用 ^(...)$ 正则
- k8s_namespace: "cattle-*"
# ... 见 beyla.yaml
attributes:
kubernetes:
enable: true # 指标/trace 自动带 k8s_* 标签
prometheus_export:
port: 9090 # RED 指标出口
internal_metrics:
prometheus:
port: 9099 # Beyla 自身内部指标(排障用)
otel_traces_export: # ⚠️ YAML 键名必须是 otel_traces_export
endpoint: http://tempo.observability:4317
protocol: grpcDaemonSet 安全上下文:非 privileged,仅加 BPF / PERFMON / NET_ADMIN / SYS_PTRACE 四个 caps + hostPID: true。
Tempo(tempo.yaml 中 ConfigMap):
- receivers:OTLP grpc:4317 / http:4318
- storage:local backend,WAL + blocks 在 NFS PVC(
/var/tempo),block_retention: 48h - metrics_generator:开
span-metrics+service-graphsprocessor,registry 暴露,remote_write →http://rancher-monitoring-prometheus.cattle-monitoring-system:9090/api/v1/write - overrides:metrics_generator processor 默认全租开启
3.4 Grafana 数据源(provisioning 方式)
rancher-monitoring 的 Grafana 数据源由 ConfigMap rancher-monitoring-grafana-datasource provisioning(API 修改会报 read-only),deploy.sh 第 6 步已渲染完整配置:
- Tempo:
http://tempo.observability:3200,开 Node Graph(数据源指向 Prometheus 的 servicegraph 指标)、traces→metrics 跳转(spanmetrics) - Prometheus:加
exemplarTraceIdDestinations(Beyla 指标 exemplar 的trace_id一键跳 Tempo)
⚠️ 该 ConfigMap 属 rancher-monitoring chart 管理,chart 升级会还原,升级后需重跑 deploy.sh 第 6 步。 ⚠️ provisioning sidecar 是 init 模式,改 ConfigMap 后必须删 Grafana pod 才生效(deploy.sh 已包含)。
3.5 导入看板
Grafana UI → Dashboards → Import → 上传 grafana/dashboard-ebpf-apm-red.json(uid ebpf-apm-red)。面板:请求速率 / 错误率 / P95 延迟(Beyla RED)+ Node Graph 服务拓扑 + 最近 Traces。
四、验证清单
bash
alias k='/tmp/bin/kubectl'; export KUBECONFIG=~/.kube/config-122.31
# 1. Pod 状态
k -n observability get pods -o wide # tempo 1/1,beyla 6/6
# 2. Beyla 进程埋点(关键!len 必须 >0)
BPOD=$(k -n observability get pods -l app=beyla -o jsonpath='{.items[0].metadata.name}')
k -n observability logs $BPOD | grep -c 'instrumenting process' # >0
# 3. Beyla trace 导出计数(内部指标 :9099)
k -n observability port-forward pod/$BPOD 19099:9099 &
curl -s localhost:19099/metrics | grep -E '^beyla_otel_trace_exports_total|^beyla_ebpf_tracer_flushes_count'
# 两个计数都应 >0 且持续增长;flushes=0 说明 span 事件根本没产出(见踩坑①)
# 4. 制造流量后查 Tempo
for i in $(seq 10); do curl -s -o /dev/null http://192.168.122.31/ -H 'Host: helloworld-go-default.knative.ai-ear.cn'; done
k -n observability port-forward svc/tempo 13200:3200 &
curl -s 'localhost:13200/api/search?limit=10' # traces 非空
# 5. RED 指标进 Prometheus
k -n cattle-monitoring-system port-forward svc/rancher-monitoring-prometheus 19090:9090 &
curl -s 'localhost:19090/api/v1/query?query=sum(rate(http_server_request_duration_seconds_count[5m]))'
# 6. spanmetrics / servicegraph 回写
curl -s 'localhost:19090/api/v1/query?query=traces_spanmetrics_calls_total' # series 100+
curl -s 'localhost:19090/api/v1/query?query=traces_service_graph_request_total' # 边 10+
# 7. 告警规则加载
curl -s 'localhost:19090/api/v1/rules?type=alert' | grep -o 'AppHighErrorRate\|AppHighLatencyP99\|BeylaDown\|TempoDown'五、踩坑记录(实测)
| # | 坑 | 现象 | 正确做法 |
|---|---|---|---|
| ① | Beyla v3 的 k8s_namespace 等选择器是 glob 不是正则 | k8s_namespace: .+ / ^(a|b)$ 静默匹配 0 个进程,debug 日志 processes matching selection criteria len=0、metadata does not match attr=k8s_namespace value=elasticsearch;RED 指标仍在(见④),极易误判为"工作正常" | glob 写法:k8s_namespace: "*"、排除逐个枚举 cattle-* |
| ② | YAML 键名 otel_traces_export | 误写 otlp_traces_export 被静默忽略,无任何报错,trace 不导出 | 键名 otel_traces_export;调 log_level: debug 看启动日志应有 component=otelcfg.TracesConfig ... endpoint=... |
| ③ | ConfigMap 变更不触发 DS 重启 | 改了 beyla-config 没生效 | 每次改后 kubectl -n observability rollout restart ds beyla |
| ④ | Beyla v3 指标走内核聚合,trace 走 ring buffer,是两条路 | RED 指标正常 ≠ trace 管线正常;beyla_ebpf_tracer_flushes_count=0 才是 trace 侧的真实信号 | 排障先看内部指标 :9099 的 flushes 与 beyla_otel_trace_exports_total |
| ⑤ | Prometheus 新版 remote-write 开关改名 | --enable-feature=remote-write-receiver 报 Unknown option | 用 --web.enable-remote-write-receiver,patch Prometheus CR 的 additionalArgs;空 POST 返 400 = 端点存在 |
| ⑥ | Tempo 镜像 distroless | kubectl exec 无 sh/wget,无法容器内自测 | 用 docker.m.daocloud.io/curlimages/curl 起临时 pod 打 OTLP/HTTP JSON 到 :4318/v1/traces 验证接收器 |
| ⑦ | DaoCloud beyla tag 无 v 前缀 | grafana/beyla:v3.30.0 拉取 401 | 用 grafana/beyla:3.30.0 |
| ⑧ | Grafana 数据源 provisioning 只读 | API PUT/POST 报 Cannot update read-only data source;匿名访问是 Viewer 无权 create | 改 ConfigMap rancher-monitoring-grafana-datasource + 删 pod 重启;chart 升级会还原 |
| ⑨ | Grafana admin 密码 DB 与 secret 漂移 | basic auth 401、/api/user Unauthorized(匿名 Viewer 下 GET /api/org 仍 200,迷惑性强) | pod 重启时 Grafana 从 GF_SECURITY_ADMIN_PASSWORD env(来自 secret)重新同步,重启后用 /login 拿 cookie |
| ⑩ | Knative 缩容到零干扰 trace 验证 | 打几个 curl 后 70s pod 消失,span 停发,怀疑链路坏了 | 验证时持续打流量保持 pod 存活;或用常驻服务(redis/kibana)验证 |
六、生产注意事项
- Tempo 换对象存储:local backend 仅适合 UAT;生产用 S3/MinIO + 多副本(distributed 模式)
- 跨进程 trace 串链:加 CAP_SYS_ADMIN 开启 Go context propagation(需评估内核 lockdown 与 bpf_probe_write_user 安全面)
- 采样:
otel_traces_export.sampler默认全采;上量后改traceidratio按比例采样 - 高基数控制:
attributes.select裁剪指标标签;routes.patterns收敛 HTTP route - resource 限额:Beyla DS 默认无 limit,生产建议 requests/limits(内存 ~120Mi/实例起步)
- chart 升级还原点:Grafana 数据源 CM、Prometheus additionalArgs 都可能被 rancher-monitoring 升级还原,升级后重跑 deploy.sh
七、卸载
bash
kubectl delete -f monitoring.yaml -f beyla.yaml -f tempo.yaml
kubectl delete ns observability
# 回滚 Prometheus remote-write(可选)
kubectl -n cattle-monitoring-system get prometheus rancher-monitoring-prometheus -o json \
| jq '.spec.additionalArgs' # 手动移除 web.enable-remote-write-receiver 后 apply
# 回滚 Grafana 数据源:从 ConfigMap 删 Tempo 段落与 exemplarTraceIdDestinations,删 grafana pod