把 Collector 配置文件做成完整一套
目标
从头到尾编写一整套可用于生产环境的 Collector 配置文件。完成后,只需查看他人的 Collector 配置和 Processor 顺序,就能预测会发生什么事故。
为什么重要
Collector 配置中只要一行 YAML 有误,进程就无法启动;问题在于,大多数时候要等到在集群中看到 CrashLoopBackOff 后才会知道。更糟糕的是,某些配置能够启动,却悄无声息地出错。如果把 memory_limiter 放在后面,平时不会有问题,只有负载升高时才会因 OOM 崩溃;如果把 batch 放在 tail_sampling 前面,trace 会被随机截断,但指标不会报告任何异常。组件如果只定义却不加入 service,它虽然存在于配置文件中,却不会运行。这三种情况构成了 Collector 事故的大多数,而且只需阅读配置文件就能全部发现。
步骤
- 创建
/root/otca-collector/config.yaml,在receivers.otlp.protocols下写入grpc.endpoint: 0.0.0.0:4317和http.endpoint: 0.0.0.0:4318。 - 在
processors.memory_limiter中写入check_interval: 1s、limit_mib: 1638、spike_limit_mib: 328。(以容器内存限制 2Gi 为基准) - 在
processors.k8sattributes中写入auth_type: serviceAccount,并在extract.metadata中加入k8s.namespace.name、k8s.deployment.name、k8s.pod.name、k8s.node.name四项。 - 在
processors中创建attributes/redact,并在actions中加入三项。对http.request.header.authorization执行delete,对user.email执行delete,对db.query.text执行hash。 - 在
processors.batch中写入timeout: 5s、send_batch_size: 8192、send_batch_max_size: 16384。 - 在
exporters中定义三项。otlp/tempo的endpoint使用带 4317 端口的地址,otlp/gateway也使用带 4317 端口的地址;prometheusremotewrite使用以/api/v1/push结尾的 http URL,并添加sending_queue(enabled: true、num_consumers: 10、queue_size: 5000)和retry_on_failure(enabled: true、initial_interval: 5s、max_elapsed_time: 300s)。 - 在
extensions中定义health_check.endpoint: 0.0.0.0:13133和zpages.endpoint: 0.0.0.0:55679,并将两个名称都加入service.extensions。 - 在
service.pipelines中写入三条管道。traces使用 Processor[memory_limiter, k8sattributes, attributes/redact, batch]和 Exporter[otlp/tempo];metrics使用[memory_limiter, k8sattributes, batch]和[prometheusremotewrite];logs使用[memory_limiter, k8sattributes, attributes/redact, batch]和[otlp/gateway]。三条管道的 Receiver 都是[otlp]。
参考
- 像
attributes/redact这样带斜杠的名称表示同一类型的第二个实例。YAML 键中包含斜杠,因此引用时必须准确匹配名称。 - 常见错误 1:只定义组件,却不将其加入
service。扩展也是如此。 - 常见错误 2:遗漏
send_batch_max_size。send_batch_size只是触发条件。 - 常见错误 3:将
attributes/redact放在k8sattributes前面。随后新增的属性会绕过删除操作。
OTLP Receiver 的两个端口
创建 /root/otca-collector/config.yaml,在 receivers.otlp.protocols 下写入 grpc.endpoint: 0.0.0.0:4317 和 http.endpoint: 0.0.0.0:4318。
otlp Receiver 在 protocols 下分别包含 grpc 和 http。只开放其中一项时,不向该协议发送的 SDK 会直接连接失败,而且 Collector 指标中不会留下任何痕迹。在容器内应绑定所有接口,而不是 loopback。
计算 memory_limiter 的值
在 processors.memory_limiter 中写入 check_interval: 1s、limit_mib: 1638、spike_limit_mib: 328。(以容器内存限制 2Gi 为基准)
假设容器内存限制为 2Gi。惯例是将硬限制设为其 80%,将允许的峰值设为硬限制的 20%。如果设置得高于容器限制,内核会在 Processor 介入之前先终止进程。
附加 Kubernetes 元数据
在 processors.k8sattributes 中写入 auth_type: serviceAccount,并在 extract.metadata 中加入 k8s.namespace.name、k8s.deployment.name、k8s.pod.name、k8s.node.name 四项。
此 Processor 以 Pod IP 为线索,从 API 服务器查找元数据并将其附加为资源属性。因此必须指定认证方式,并在 extract 中只列出所需项目,以减少负载。
删除和哈希敏感属性
在 processors 中创建 attributes/redact,并在 actions 中加入三项。对 http.request.header.authorization 执行 delete,对 user.email 执行 delete,对 db.query.text 执行 hash。
在 Processor 名称中加入斜杠,可以创建同一类型的第二个实例。如果完全删除,之后就无法按相同值进行分组;因此,对于查询语句等需要分组的值,应进行哈希而不是删除。
batch 触发条件与上限
在 processors.batch 中写入 timeout: 5s、send_batch_size: 8192、send_batch_max_size: 16384。
send_batch_size 是“积累到这么多就立即发送”的触发条件,并不是批次大小的上限。必须用另一个键指定上限;如果不指定,批次可能远大于预期。
三个 Exporter 及队列与重试
在 exporters 中定义三项。otlp/tempo 的 endpoint 使用带 4317 端口的地址,otlp/gateway 也使用带 4317 端口的地址;prometheusremotewrite 使用以 /api/v1/push 结尾的 http URL,并添加 sending_queue(enabled: true、num_consumers: 10、queue_size: 5000)和 retry_on_failure(enabled: true、initial_interval: 5s、max_elapsed_time: 300s)。
sending_queue 会在后端短暂变慢时保留数据,retry_on_failure 会使用指数退避重试失败操作。如果两者都关闭,后端每次波动都会直接导致数据丢失。
health_check 与 zpages
在 extensions 中定义 health_check.endpoint: 0.0.0.0:13133 和 zpages.endpoint: 0.0.0.0:55679,并将两个名称都加入 service.extensions。
扩展仅定义并不会运行。还必须在 service 下列出名称才能启用。health_check 是 liveness/readiness 探针访问的端点,zpages 会显示近期 trace 和管道状态。
三条管道与 Processor 顺序
在 service.pipelines 中写入三条管道。traces 使用 Processor [memory_limiter, k8sattributes, attributes/redact, batch] 和 Exporter [otlp/tempo];metrics 使用 [memory_limiter, k8sattributes, batch] 和 [prometheusremotewrite];logs 使用 [memory_limiter, k8sattributes, attributes/redact, batch] 和 [otlp/gateway]。三条管道的 Receiver 都是 [otlp]。
列出的顺序就是处理顺序。内存保护必须放在最前面,才能向上游传播 backpressure;batch 必须放在最后,才能避免拆开后再重新组合的浪费。附加元数据的 Processor 可能创建新属性,因此删除操作应在它之后。