前言 第一次接手系统时无从入手的感觉,对于一个初出茅庐的小卡拉米(博主)影响颇深,只能非常蛋疼的从 Linux基础指标 逐一从进程开始排查,使用top、systat、perf、sar等工具去查询CPU使用率/内存占用、平均负载、软中断、某进程活跃连接数等基础指标。
显然基础指标不是云原生系统入手排查的第一选择,它只适合用于Linux系统性能优化 。
那么对于系统规模扩大,服务之间的调用链条变得越来越复杂的云原生系统,对现代请求响应协议(截至目前常用的HTTP/1.1、HTTP/2.0)进行埋点与解析则尤为重要,一个完善的链路追踪系统可以帮助运维成员提升MTBF(平均故障时间间隔),降低MTTR(故障平均修复时间) 。
所以针对该背景的研究方向,博主基于自身工作需求进行了追踪系统的分析,并在这里分享,希望可以帮助路过的各位。
虽然没什么人看
从需求出发 对于一个团队的系统,链路追踪系统虽然重要,但是该系统实现方式不一,需要根据各团队的需求和已知能力进行研判。
方法
侵入性
数据粒度
支持多语言
典型工具
日志
低
中低
高
Fluentd, ELK, OpenTelemetry Logging
Envoy/Proxy
无
中
高
Envoy, Istio, OpenTelemetry Collector
eBPF
无
高(系统级)
低
Pixie, Cilium Hubble
SDK/Agent
中
高
高
OpenTelemetry Agent, Jaeger Agent
手动埋点
高
高
高
OpenTelemetry SDK, Jaeger Client
博主这里按侵入程度 和数据来源方式 来分类:
非侵入式/低侵入式:
基于日志:直接在业务逻辑中打印日志(可包含 trace_id、span_id、parent_id),通过日志收集系统(如 ELK、Fluentd、Loki)聚合并分析调用链。
不过粒度有限,难以捕获网络层细节。
L7网络代理:在服务入口/出口部署 Envoy、Istio sidecar 或 Nginx/L7 代理,自动注入 Trace Header(traceparent 等),捕获请求信息并上报到 Collector。
中间层的加入可能增加系统复杂度,对管理系统的选手有一定挑战,例如http/1.1、http/2.0的代理层管控。 同时使用代理可能会影响一定性能,会有一定资源占用。
内核追踪 — eBPF:利用 Linux eBPF 在内核层捕获网络包、系统调用、函数调用等事件,生成 trace。
显然的学习成本高,调试困难。团队有高手的话可以使用。
代码SDK:在服务中加载 OpenTelemetry / Jaeger SDK 或 Java/Go agent,自动 hook HTTP、gRPC、数据库等客户端调用。
目前已经有现成的工具可用,即otel。但是在某些场景下可能与业务框架冲突!
侵入式 有且只有一种,即开发者在关键业务逻辑处手动创建 span 并设置 trace_id。这非常费人力,特别在已成型的IT系统中更改(很蛋疼了)。不过完全可控,可以精确捕获自定义事件。
涉及的组件介绍 简单介绍一下本次实践分享使用的组件与工具。
OpenTelemetry 是一个可观测性 框架和工具包,详见什么是OpenTelemetry 。
其中OpenTelemetry Collector是核心组件,它可以收集、处理,也可以导出遥测数据 (traces、metrics、logs),应用只需发给 Collector,不用直接对接 Jaeger/Prometheus/Elastic。
Jaeger 是一个开源的 分布式追踪(Distributed Tracing)系统 ,提供UI和API。
从架构上来说jaeger由agent、collector、query和ingester这四个部分组成。
agent:数据源,将追踪数据发送到collector,如本次实践的envoy
collector:接收Agent发送的数据,验证、索引、转换、存储,如ElasticSearch、Kafka
query:提供查询服务API和用户界面
ingester:从Kafka读取数据并写入其他存储
对于单主机或者性能要求不大的直接使用 jaegertracing/all-in-one 这一个容器就可以满足部分需求了,通常内存占用50MB。
基于sidecar+otel-sdk的实践 博主从自身工作出发,尝试对一个已搭建了一段时间的系统实现链路追踪系统。
首要考虑就是不费大量人力,毕竟只作一个突出绩效的功能。
采集方式选型:envoy(完成L7代理的同时可自行生成traceID)+otel-SDK(针对java进行探针,可以hook HTTP请求和SQL相关操作)。
otel+jaeger搭建 docker-compose.yaml部分:注意otel-collector的挂载,有一个config文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 version: '3.8' services: otel-collector: image: otel/opentelemetry-collector:v0.83.0 container_name: otel-collector volumes: - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml:ro command: ["--config" , "/etc/otel-collector-config.yaml" ] ports: - "4317:4317" restart: unless-stopped jaeger: image: jaegertracing/all-in-one:1.70.0 container_name: jaeger ports: - "16686:16686" restart: unless-stopped
下面是otel-collector-config.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 receivers: otlp: protocols: grpc: http: processors: batch: timeout: 5s exporters: jaeger: endpoint: "jaeger:14250" tls: insecure: true logging: loglevel: info service: pipelines: traces: receivers: [otlp ] processors: [batch ] exporters: [jaeger , logging ]
要确保otel与jaeger在一个容器,否则通过”jaeger”连接时gRPC会报错"error": "failed to push trace data via Jaeger exporter: rpc error: code = Unavailable desc = connection error: desc = 'transport: Error while dialing: dial tcp: lookup jaeger on 114.114.114.114:53: no such host'"
envoy实践 envoy适合项目中使用容器管理的服务,只需利用envoy将流量经由其流入流出即可。当然前提得是bridge网络。
这里使用的是envoy1.30.2。
envoy.yaml部分:请注意注释
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 static_resources: listeners: - name: grpc_listener address: socket_address: address: 0.0 .0 .0 port_value: 23333 filter_chains: - filters: - name: envoy.filters.network.http_connection_manager typed_config: "@type" : type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager stat_prefix: ingress_http route_config: name: local_route virtual_hosts: - name: grpc_service domains: ["*" ] routes: - match: { prefix: "/" } route: cluster: FishingrodService_svc http_filters: - name: envoy.filters.http.router typed_config: "@type" : type.googleapis.com/envoy.extensions.filters.http.router.v3.Router tracing: provider: name: envoy.tracers.opentelemetry typed_config: "@type" : type.googleapis.com/envoy.config.trace.v3.OpenTelemetryConfig grpc_service: envoy_grpc: cluster_name: otel_collector timeout: 0. 5s service_name: "FishingrodService-envoy" spawn_upstream_span: true access_log: - name: envoy.access_loggers.file typed_config: "@type" : type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog path: /tmp/envoy-access.log log_format: text_format: "[%START_TIME%] %REQ(:AUTHORITY)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL% %RESPONSE_CODE% %UPSTREAM_HOST%\n" clusters: - name: FishingrodService_svc connect_timeout: 0. 5s type: logical_dns lb_policy: round_robin http2_protocol_options: {} load_assignment: cluster_name: FishingrodService_svc endpoints: - lb_endpoints: - endpoint: address: socket_address: address: FishingrodService port_value: 23333 - name: otel_collector type: logical_dns lb_policy: round_robin http2_protocol_options: {} load_assignment: cluster_name: otel_collector endpoints: - lb_endpoints: - endpoint: address: socket_address: address: otel-collector port_value: 4317
docker-compose.yaml改造部分:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 version: "3" services: FishingrodService: ... expose: - 23333 networks: - FishingrodService_net envoy: container_name: FishingrodService-envoy image: envoyproxy/envoy:v1.30.2 depends_on: - FishingrodService networks: - FishingrodService_net volumes: - /opt/FishingrodService/envoy.yaml:/etc/envoy/envoy.yaml ports: - "23333:23333" extra_hosts: - "otel-collector:169.254.169.254" command: > /usr/local/bin/envoy -c /etc/envoy/envoy.yaml --log-level debug # 这行command可以打印envoy的debug日志 networks: FishingrodService_net: driver: bridge
otel-SDK实践: java 参考otel示例入门 ,在Github仓库 下载SDK。
对于java容器就不使用envoy了,sdk也能生成traceID。
docker-compose.yaml改造部分:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 services: IamJava: container_name: IamJava image: IamJava:latest volumes: ... - /opt/IamJava/opentelemetry-javaagent.jar:/opt/opentelemetry-javaagent.jar extra_hosts: - "otel-collector:169.254.169.254" restart: always network_mode: host working_dir: "/opt/IamJava" command: java -Xmx4g -Dfile.encoding=UTF-8 -Dclient.encoding.override=UTF-8 -javaagent:/opt/opentelemetry-javaagent.jar -jar /opt/IamJava/gugugaga.jar environment: ... - OTEL_SERVICE_NAME=IamJava - OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317 - OTEL_EXPORTER_OTLP_PROTOCOL=grpc - OTEL_PROPAGATORS=tracecontext,baggage - OTEL_LOGS_EXPORTER=none
最终效果
采样 todo
猜你需要 追踪 - envoy
上下文传播故障排除