OpenTelemetry Collector K8s 高可用部署:四个决策与三条红线
2026/9/20 7:14:24 网站建设 项目流程

OpenTelemetry Collector K8s 高可用部署:四个决策与三条红线

【免费下载链接】opentelemetry-collectorOpenTelemetry Collector项目地址: https://gitcode.com/GitHub_Trending/op/opentelemetry-collector

场景:凌晨三点的 OOM 重启,和一块数据空窗

凌晨三点十四分,告警群炸了。otel-gateway 的 Pod 当晚第七次被 OOMKilled,Trace 大盘上出现了一块肉眼可见的空窗,业务方开始追问。Pod 的内存限制是 2Gi,但 GC 追不上晚高峰,内核直接动手。

排查后发现,问题不在某一条配置,而是一整套 OpenTelemetry Collector 高可用部署的决策没做对。本文只讲单集群:节点 Agent 加中心 Gateway 的两级部署,规模是几十台节点、峰值每小时百万级 span。多集群联邦、后端存储选型,都不覆盖。

拿到的东西很直接:为什么拆两级、配置怎么写、跑起来之后盯什么。

为什么拆成 Agent 和 Gateway:先做部署形态决策

一套 Collector 直接跑,所有业务 Pod 都往里发,也能跑。但有两个问题。

一个是地址。每个业务 Pod 都要写死 Collector 的 Service 名,扩容、迁移、换命名空间之后地址漂移,排查一次要翻好几个工单。另一个是故障域。承载业务 Pod 的节点挂了,或者某个副本正在重启,这个窗口里的数据没人接,也没有本地缓冲。

拆成两级,两个问题一起解。Agent 用 DaemonSet 跑,每节点一个,把 OTLP 端口绑在 Pod IP 上,业务应用只指本地地址就行。Gateway 用 Deployment 跑,负责批处理、重试和对外输出,副本数和滚动更新都可以自由调度,Agent 一侧完全无感。

Gateway 为什么不再用 DaemonSet?它的数据流不依赖特定节点,放到每台节点上只是重复占资源。真要换输出后端,DaemonSet 得全节点滚动,Deployment 只改几个副本。两层职责分开,出事时也容易定位是哪一段断的。

拓扑简化成七个节点:

图中 Gateway 画了三个副本,后端是任意 OTLP 兼容存储。箭头走 gRPC 端口 4317,业务如果走 HTTP 就换成 4318。

Collector Gateway:最小可用部署怎么写

下面这份 Deployment 解决"Collector 第一次进集群"的问题,只保留必须对的三处。

apiVersion: apps/v1 kind: Deployment metadata: name: otel-gateway namespace: observability spec: replicas: 3 minReadySeconds: 5 # 新 Pod 初始化完再切流量 template: spec: containers: - name: collector image: otel/opentelemetry-collector:0.150.0 # 固定版本 args: ["--config=/conf/gateway.yaml"] resources: requests: {cpu: 500m, memory: 1Gi} limits: {cpu: "1", memory: 2Gi} ports: - containerPort: 4317 # OTLP gRPC - containerPort: 4318 # OTLP HTTP - containerPort: 8888 # 内部遥测

minReadySeconds: 5防止 Service 把流量切给还没初始化好流水线的 Pod。8888 是 Collector 默认暴露内部指标的端口,Prometheus 格式。把命名空间、资源数值、镜像版本换成你自己的,就能直接 apply。

跑通之后,更新策略上两个数字:maxSurge: 1, maxUnavailable: 0。滚动更新时旧 Pod 扛着流量,等新副本就绪才下线,配合 Agent 端的缓冲,配置变更基本不丢数据。

探针这里有个容易踩的坑。核心发行版没有独立的 HTTP 健康端点,那是 contrib 发行版里的扩展组件。可靠的做法是给 4317 做 TCP 探针,或者如果已经有 Prometheus,直接抓 8888 的 metrics 接口,返回 200 就算就绪。别对着不存在的路径写 httpGet。

跑起来之后:要盯的三件事

数据不丢:打开导出器的队列和重试

不处理这条,后端宕机两分钟,这两分钟的数据就是永久空窗。

下面这段是导出器内置的缓冲加重试,是第一道防线:

exporters: otlp: endpoint: backend:4317 sending_queue: queue_size: 10000 # 内存中最多缓存多少条 num_consumers: 4 # 消费协程并发数 retry_on_failure: enabled: true initial_interval: 5s max_elapsed_time: 300s # 5 分钟还没成功就放弃

queue_size 是内存缓冲上限,retry 会一直按指数退避重试到 max_elapsed_time,之后数据丢弃并计入失败指标。Agent 到 Gateway 这一跳用同一份配置,单个 Gateway 副本挂了,数据缓存在 Agent 侧,被其他副本接管。

判断标准:盯otelcol_exporter_send_failed_spans。瞬时尖峰正常,持续上涨不回落,说明后端挂了或者队列写满了。

资源不爆:GOMEMLIMIT 与 Collector HPA 扩缩容配置

不处理这条,流量一高峰就整池 OOM,或者 Agent 把业务 Pod 的资源挤掉。

下面这段配置把内存红线卡在两个层面:GC 层和接收层。

# Deployment 的 env 段 env: - name: GOMEMLIMIT value: 1600MiB # 约为 2Gi 限制的 80% --- # Collector 配置 processors: memory_limiter: limit_mib: 1500 # 熔断阈值 spike_limit_mib: 512 # 额外允许的突发 check_interval: 5s

两层是互补的。Go 运行时默认 GC 目标约为容器限制的一半,Collector 往往在撞到限制之前就被内核杀了。GOMEMLIMIT 把它抬到限制的八成,给 GC 留出反应时间。官方 K8s 示例正是这个比例:2Gi 限制的 Pod 配 1600MiB,500Mi 的 Agent 配 400MiB。memory_limiter 则在接收层做保险丝,超了阈值就用 503 拒掉新数据,好过整个进程被杀。实测加上这两层之后,原来一天 OOM 重启几次的 2Gi Pod 在峰值期不再重启。

副本数交给 HPA(HorizontalPodAutoscaler,K8s 里根据负载自动增减副本数的控制器):

apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: otel-gateway spec: scaleTargetRef: {kind: Deployment, name: otel-gateway} minReplicas: 3 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: {type: Utilization, averageUtilization: 70}

CPU 目标 70% 给重试风暴留了余量。想按吞吐量扩,把 metrics 换成 Pods 类型,用otelcol_receiver_accepted_spans做每副本均值目标。Agent 是每节点固定的,参考值 requests 100m/100Mi、limits 500m/500Mi,节点上业务 Pod 多就调大 limit。

判断标准:8888 导出的进程内存长期贴着 limit,先怀疑后端变慢、数据在积压,再考虑加副本。

安全不裸奔:OTLP 端到端加密与 NetworkPolicy

不处理这条,集群里任意 Pod 都能读到 Trace,Gateway 到后端的流量也能被嗅探。

下面这段把加密和访问控制一次配齐:Gateway 到后端加密,接收端口只对 Agent 开放。

# Collector 的 exporter 段 exporters: otlp: endpoint: secure-backend:4317 tls: min_version: VersionTLS12 cert_file: /secrets/client.crt key_file: /secrets/client.key ca_file: /secrets/ca.crt --- # NetworkPolicy apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: gateway-ingress spec: podSelector: {matchLabels: {app: otel-gateway}} policyTypes: [Ingress] ingress: - from: - podSelector: {matchLabels: {app: otel-agent}} ports: - {protocol: TCP, port: 4317} - {protocol: TCP, port: 4318}

证书从 Secret 挂载。用 cert-manager 签发时,把有效期设短、提前续期,Collector 会自动加载新证书,不用重启。

判断标准:随便 exec 进一个业务 Pod,尝试连 Gateway 的 4317,拒绝连接才算合格。

下周就要上线:先做这几件事

  • ✅ 镜像标签从 latest 换成固定版本,配置跑一遍校验
  • ✅ 每个 Collector Pod 加上 GOMEMLIMIT(约限制的 80%)和 memory_limiter
  • ✅ 导出器打开 sending_queue 加 retry_on_failure,max_elapsed_time 要盖过后端最长恢复时间
  • Gateway 的接收端口用 NetworkPolicy 锁死,只允许 Agent 访问
  • otelcol_exporter_send_failed_spans加进告警,别只盯 CPU 和内存

五条做完,这条链路才敢接生产流量。

完整的示例清单在仓库的 examples/k8s/otel-config.yaml,Agent、Gateway、Service 和配置都在里面,可以直接拿来做基线。

【免费下载链接】opentelemetry-collectorOpenTelemetry Collector项目地址: https://gitcode.com/GitHub_Trending/op/opentelemetry-collector

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询