TT Lab
开始
学习 学习路径 课程

OTCA — OpenTelemetry 认证助理

Collector 返回了 200,Span 却哪儿都找不到

在 TT Lab 中继续学习

目标

当后端停止响应或拒绝请求时,实际测量 Collector 的队列与重试设置会如何改变调用方收到的响应和内部遥测指标,并设计出能在不丢失数据的前提下度过短暂故障的配置。

为什么重要

应用收到 200,并不意味着跨度(span)已经到达存储。队列让调用方与故障隔开,代价是把丢失转移到 Collector 内部;重试只能挽救暂时性失败;一旦超过限度,数据只会留下一行日志和一个指标,然后消失。只有学会并排阅读 otelcol_receiver_accepted_spans、otelcol_exporter_sent_spans、send_failed、enqueue_failed,才能诊断管道中的数据丢失。

已准备的环境

python3 /opt/fixtures/otca_backpressure_lab.py init 会在 /root/otca-export/ 中放入 broken.yaml 和 spans.json(每个请求含 2 个 span)。python3 /opt/fixtures/otca_backpressure_lab.py run 설정.yaml [--backend down|ok|reject] [--backend-after 초] [--requests N] [--wait 초](占位符依次为配置文件名与秒数)会真正启动 lab-k8s 中的 otelcol-contrib 0.116.0,每隔 0.2 秒发送一次请求,等待之后读取 Collector 内部遥测中的 otelcol_* 指标并汇总。otlphttp exporter 的 endpoint 会被替换为实验用接收器(down 是无人监听的端口,ok 返回 200,reject 返回 400)。评分器会在相同条件下重新运行你的配置,并与你填写的数字对照。

步骤

  1. 用 python3 /opt/fixtures/otca_backpressure_lab.py init 生成材料后,运行 otelcol-contrib validate --config /root/otca-export/broken.yaml。在 /root/otca-export/01-validate.txt 中写入 missing_component=(错误所指向的不存在的组件),并创建改为导出到管道中已定义的 otlphttp/backend 的 /root/otca-export/fixed.yaml。
  2. 由 fixed.yaml 创建 /root/otca-export/no-queue.yaml,并在 otlphttp/backend 中加入 sending_queue.enabled: false 与 retry_on_failure.enabled: false。将 python3 /opt/fixtures/otca_backpressure_lab.py run /root/otca-export/no-queue.yaml(后端 down,5 次请求 × 每次 2 个 span)的结果按 codes=、accepted_spans=、refused_spans=、send_failed_spans= 抄入 /root/otca-export/02-no-queue.txt。
  3. 在 /root/otca-export/queue.yaml 中启用 sending_queue 与 retry_on_failure(其他值保持默认)。以同样的方式(后端 down)运行,并在 /root/otca-export/03-queue.txt 中写入 codes=、accepted_spans=、sent_spans=、send_failed_spans=。
  4. 在 /root/otca-export/small-queue.yaml 中放入 sending_queue: {enabled: true, queue_size: 2, num_consumers: 1} 与 retry_on_failure: {enabled: true, initial_interval: 1s, max_interval: 1s}。以后端 down 运行,并在 /root/otca-export/04-full.txt 中写入 codes=、accepted_spans=、refused_spans=、enqueue_failed_spans=、queue_size=。
  5. 用 --backend ok --backend-after 2.5 --wait 6 运行同一个 small-queue.yaml(2.5 秒后接收器启动)。在 /root/otca-export/05-recover.txt 中写入 accepted_spans=、sent_spans=、send_failed_spans=。
  6. 用 --backend reject(接收器返回 HTTP 400)运行 queue.yaml。在 /root/otca-export/06-permanent.txt 中写入 codes=、sent_spans=、send_failed_spans=、dropping_logged=(日志中是否出现过 Dropping data,true/false)、retried=(接收器收到的请求数 backend_requests 是否多于 5 次请求,true/false)。
  7. 在 /root/otca-export/short-retry.yaml 中把 retry_on_failure 设为 initial_interval: 500ms、max_interval: 500ms、max_elapsed_time: 2s(队列保持默认)。以后端 down、--wait 6 运行,并在 /root/otca-export/07-give-up.txt 中写入 accepted_spans=、sent_spans=、send_failed_spans=、dropping_logged=。
  8. 设计 /root/otca-export/resilient.yaml。评分器会在 --backend ok --backend-after 4 --requests 8 --wait 8 条件下运行,检查调用方的拒绝为 0 次、16 个 span 在 8 秒内全部发送、失败为 0 次。先用 python3 /opt/fixtures/otca_backpressure_lab.py run /root/otca-export/resilient.yaml --backend ok --backend-after 4 --requests 8 --wait 8 确认。

参考

启动前就能发现的错误

用 python3 /opt/fixtures/otca_backpressure_lab.py init 生成材料后,运行 otelcol-contrib validate --config /root/otca-export/broken.yaml。在 /root/otca-export/01-validate.txt 中写入 missing_component=(错误所指向的不存在的组件),并创建改为导出到管道中已定义的 otlphttp/backend 的 /root/otca-export/fixed.yaml。

validate 不会启动 Collector,只检查配置中的引用关系。请对比管道所调用的名称与 exporters 中定义的名称。

既没有队列也没有重试时

由 fixed.yaml 创建 /root/otca-export/no-queue.yaml,并在 otlphttp/backend 中加入 sending_queue.enabled: false 与 retry_on_failure.enabled: false。将 python3 /opt/fixtures/otca_backpressure_lab.py run /root/otca-export/no-queue.yaml(后端 down,5 次请求 × 每次 2 个 span)的结果按 codes=、accepted_spans=、refused_spans=、send_failed_spans= 抄入 /root/otca-export/02-no-queue.txt。

没有队列时,exporter 调用会在 receiver 的请求内同步发生。请用响应码确认失败会一路传回到哪里。

收到了 200,发送却是 0

在 /root/otca-export/queue.yaml 中启用 sending_queue 与 retry_on_failure(其他值保持默认)。以同样的方式(后端 down)运行,并在 /root/otca-export/03-queue.txt 中写入 codes=、accepted_spans=、sent_spans=、send_failed_spans=。

有队列时,receiver 在请求入队的瞬间就会返回成功。接受与送达是不同的指标。

队列满了就会收到拒绝

在 /root/otca-export/small-queue.yaml 中放入 sending_queue: {enabled: true, queue_size: 2, num_consumers: 1} 与 retry_on_failure: {enabled: true, initial_interval: 1s, max_interval: 1s}。以后端 down 运行,并在 /root/otca-export/04-full.txt 中写入 codes=、accepted_spans=、refused_spans=、enqueue_failed_spans=、queue_size=。

一个消费者占住第一个批次并不断重试期间,队列里只有两个位置。请求每隔 0.2 秒到达一次。

后端恢复后,什么能存活

用 --backend ok --backend-after 2.5 --wait 6 运行同一个 small-queue.yaml(2.5 秒后接收器启动)。在 /root/otca-export/05-recover.txt 中写入 accepted_spans=、sent_spans=、send_failed_spans=。

已经入队的数据可以靠重试存活,但没能入队而被拒绝的数据,Collector 并没有保存。重新发送是调用方的责任。

400 不会被重新发送

用 --backend reject(接收器返回 HTTP 400)运行 queue.yaml。在 /root/otca-export/06-permanent.txt 中写入 codes=、sent_spans=、send_failed_spans=、dropping_logged=(日志中是否出现过 Dropping data,true/false)、retried=(接收器收到的请求数 backend_requests 是否多于 5 次请求,true/false)。

重试是为暂时性失败(连接被拒绝、503 等)设计的机制。对于“格式有误”的响应,无论发送多少次,结果都一样。

重试也有尽头

在 /root/otca-export/short-retry.yaml 中把 retry_on_failure 设为 initial_interval: 500ms、max_interval: 500ms、max_elapsed_time: 2s(队列保持默认)。以后端 down、--wait 6 运行,并在 /root/otca-export/07-give-up.txt 中写入 accepted_spans=、sent_spans=、send_failed_spans=、dropping_logged=。

max_elapsed_time 到期后会停止重试,并丢弃该批次。调用方已经收到过 200,所以丢失只会留在 Collector 日志和内部指标中。

能扛过 4 秒故障的配置

设计 /root/otca-export/resilient.yaml。评分器会在 --backend ok --backend-after 4 --requests 8 --wait 8 条件下运行,检查调用方的拒绝为 0 次、16 个 span 在 8 秒内全部发送、失败为 0 次。先用 python3 /opt/fixtures/otca_backpressure_lab.py run /root/otca-export/resilient.yaml --backend ok --backend-after 4 --requests 8 --wait 8 确认。

需要两样东西:一是能容纳故障期间到达的所有批次的位置(重试中的批次由消费者占着,所以位置要同时看 queue_size 与 num_consumers),二是后端恢复后,在等待时间内出现第一次重试的间隔。请在文档中确认默认的首次重试间隔。