如何确保OpenTelemetry Sidecar等待客户端完成数据刷入?
问题背景
采用Sidecar架构部署OpenTelemetry:业务服务生成指标/追踪数据,AWS OTel Collector Sidecar负责收集并推送到AWS。Docker Compose配置如下:
services: service: build: context: . image: service container_name: service ports: - "3000:3000" depends_on: - aws-otel-collector aws-otel-collector: image: public.ecr.aws/aws-observability/aws-otel-collector:latest container_name: aws-otel-collector ports: - "4317:4317"
业务服务在关闭时会通过defer逻辑刷入指标并关闭exporter:
shutdown, err := initMetricProvider(ctx) if err != nil { log.Fatal(err) } defer func() { log.Printf("Shutting down metric provider") if err := shutdown(ctx); err != nil { log.Fatal(fmt.Errorf("failed to shutdown metric provider: %w", err)) } }() meter := global.MeterProvider().Meter("service") counter, err := meter.SyncInt64().Counter("test")
重启服务时偶尔出现连接失败错误:
max retry time elapsed: rpc error: code = Unavailable desc = connection error: desc = "transport: Error while dialing dial tcp 127.0.0.1:4317: connect: connection refused"
根因是Sidecar在业务服务完成指标刷入前被暂停,导致服务无法连接Collector提交数据。
解决方案
1. 调整Docker Compose容器停止策略
Docker Compose默认停止顺序为启动顺序的反向(先停业务服务,再停Sidecar),但默认仅等待10秒就会强制杀死未退出的容器。可以延长业务服务的停止等待时间,确保其完成指标刷入:
修改业务服务的Compose配置,添加stop_grace_period:
services: service: # ... 原有配置 ... stop_grace_period: 30s # 延长停止等待窗口,给足指标刷入时间 aws-otel-collector: # ... 原有配置 ...
此配置会让Compose在发送停止信号后,等待30秒再强制杀死业务服务,同时保证Sidecar在业务服务完全退出后才停止。
2. 给Sidecar添加关闭延迟
通过自定义入口脚本,让AWS OTel Collector在收到停止信号后先等待一段时间再关闭,给业务服务预留数据提交时间:
- 创建
entrypoint.sh脚本:
#!/bin/bash # 启动Collector进程 /otelcol-contrib & COLLECTOR_PID=$! # 处理停止信号 stop_collector() { echo "Received stop signal, waiting 10s before shutting down collector..." sleep 10 kill $COLLECTOR_PID wait $COLLECTOR_PID } # 捕获SIGTERM和SIGINT信号 trap stop_collector SIGTERM SIGINT # 等待Collector进程结束 wait $COLLECTOR_PID
- 修改Sidecar的Compose配置,挂载脚本并替换入口点:
services: aws-otel-collector: # ... 原有配置 ... volumes: - ./entrypoint.sh:/entrypoint.sh entrypoint: ["/bin/bash", "/entrypoint.sh"]
3. 优化服务端关闭逻辑
增强业务服务的shutdown逻辑,添加超时控制和重试机制,提高指标刷入的成功率:
defer func() { log.Printf("Shutting down metric provider") // 创建带超时的上下文,延长刷入等待时间 shutdownCtx, cancel := context.WithTimeout(ctx, 15*time.Second) defer cancel() if err := shutdown(shutdownCtx); err != nil { log.Printf("Initial shutdown failed: %v", err) // 重试2次,避免短暂的Sidecar不可用 for i := 0; i < 2; i++ { log.Printf("Retrying shutdown attempt %d...", i+1) time.Sleep(2 * time.Second) if err := shutdown(shutdownCtx); err == nil { log.Printf("Shutdown succeeded on retry") return } } log.Fatal(fmt.Errorf("Final shutdown failed: %w", err)) } }()
4. 修正服务与Sidecar的连接地址
在Docker Compose环境中,业务容器应通过服务名aws-otel-collector访问Sidecar,而非127.0.0.1(后者指向业务容器自身)。检查服务的OpenTelemetry exporter配置,确保endpoint设置为aws-otel-collector:4317,避免因网络地址错误导致的连接失败。
内容的提问来源于stack exchange,提问作者k-sever

