- 教程
- 云原生
- 容器编排
【免费下载链接】kubernetes-handbook
Kubernetes 架构与生态:从云原生到 AI 原生基础设施的构建指南
在将单体应用拆分为微服务之后,服务可能分布在上千台服务器、不同的数据中心和可用区中,服务之间的调用关系错综复杂。本文以 kubernetes-handbook 仓库中 分布式追踪 一文为主线,系统讲解分布式追踪(Distributed Tracing)的核心概念、OpenTracing 标准与基本术语、业界主流实现(Jaeger、Zipkin、SkyWalking),并结合作者仓库中的 Istio 教程与 OpenTracing 文档,给出从本地启动 Jaeger、在 Spring Boot 中接入 OpenTracing 客户端、到 Kubernetes 集群中查看调用链的完整实战方案。读完本文,你将能够:理解 Trace/Span 等追踪模型,掌握分布式追踪系统的三类硬性要求,并能在自己的微服务与 Kubernetes 环境中独立部署和接入一套可用的追踪系统。
为什么微服务架构需要分布式追踪
当单体应用被拆分成多个微服务后,一次用户请求往往需要穿越多个服务、多台主机甚至多个数据中心。此时传统单机日志与监控手段会失效,因为:
- 难以定位故障环节:请求在哪一个服务环节失败、哪一次调用耗时最长,无法从单点日志中判断;
- 难以评估优化点:服务之间的依赖关系不透明,哪些调用链存在瓶颈、哪些服务可以拆分或合并缺乏数据支撑;
- 跨进程上下文丢失:单个服务内部的指标无法还原一次完整请求的端到端行为。
分布式追踪(Distributed Tracing)正是为解决上述问题而生。它通过为每次请求生成全局唯一的 Trace ID,并在请求流经的每个服务节点上记录 Span,从而还原出一次请求从入口到出口的完整调用链、每段调用的耗时与依赖关系。
在 kubernetes-handbook 的 可观察性 一文中,将可观察性定义为"使用指标、日志和追踪这些外部输出来理解系统的能力",并明确指出:追踪让你能够看到一个请求从开始到结束的过程,它是对事件行为的实时捕捉,可以帮助确定故障发生的位置,或确定引起当前示例性能问题的原因。在微服务环境中会产生大量事件,事件被定义为"从请求到达网络外围的那一刻起发生的一切"。分布式追踪正是还原这些事件的时空脉络的关键技术。
分布式追踪标准与主流实现
OpenTracing:厂商中立的追踪 API 标准
CNCF 提出了分布式追踪的标准OpenTracing,它提供厂商中立的 API,并提供了Go、Java、JavaScript、Python、Ruby、PHP、Objective-C、C++ 和 C#这九种语言的库。这意味着应用代码只需面向 OpenTracing API 编程,底层 Tracer 可以随时在 Zipkin、SkyWalking、Jaeger 等实现之间切换,从而避免被单一厂商绑定。
从仓库中的 OpenTracing 文档可知,目前支持 OpenTracing 的 Tracer 包括Zipkin、SkyWalking、Jaeger等,支持的框架包括gRPC、MOTAN、django、Flask、Sharding-JDBC等。在开发应用时,需要使用兼容 OpenTracing API 的 Tracing 实现库(例如 Jaeger)来实现自动的分布式追踪。
主流追踪系统:Jaeger、Zipkin 与 SkyWalking
大部分分布式追踪系统都是根据 Google 的Dapper 论文(《Dapper,大规模分布式系统的跟踪系统》)实现的。Dapper 论文提出了以 Trace 和 Span 为核心的数据模型、低消耗的埋点采样策略,以及"对应用透明"的追踪设计理念,成为后续所有主流追踪系统的理论基础。
业界常见的分布式追踪系统包括:
- Jaeger:CNCF 旗下的端到端分布式追踪项目,完整支持 OpenTracing API,提供查询 UI、依赖分析图与多后端存储(如 Elasticsearch、Cassandra)能力;
- Zipkin:Twitter 开源、由 Apache 基金会维护的分布式追踪系统,是 Istio 早期版本默认集成的追踪后端;
- Apache SkyWalking:Apache 基金会旗下的中国开源应用性能监控工具,不仅支持分布式追踪,还集成了应用性能管理(APM)与服务性能管理(SPM)能力。
在 kubernetes-handbook 的 Istio 教程 中,作者采用了"使用 Zipkin 做分布式追踪而不是 Jaeger"的方案,同时也在教程的本地运行环节演示了 Jaeger 的部署,说明这两个系统在实践中可以按需选用。
OpenTracing 核心术语:Trace、Span 与 SpanContext
要在实践中用好追踪系统,必须先理解 OpenTracing 定义的基本数据模型。以下是仓库 OpenTracing 文档中的核心术语。
Trace:一次完整的调用链
Trace通常指一次完整的调用链。例如在 Istio 官方提供的 Bookinfo 示例中,对productpage服务的一次访问,会在追踪系统中形成一条贯穿productpage → details等服务的完整调用链,这就是一个 Trace。
Span:Trace 中的一段调用
每个 Trace 都由一系列Span组成。一个 Span 可以理解为两个微服务之间的一次调用,如同 Chrome 开发者工具中查看网络访问瀑布图一样,每个请求占据一段横条,按时间顺序展开。根据 OpenTracing 的规格约定,每个 Span 都要包含以下状态:
| 状态字段 | 说明 | 必填性 | 示例 |
|---|---|---|---|
| 操作名称 | 可以是访问的一个 URL | 必填 | localhost:8808/ |
| 起/止时间戳 | 也可以使用起始时间和持续时间表示 | 必填 | 1540273832696773 |
| Tags | 一组键值对集合,OpenTracing 的 Semantic Conventions 有一些常用约定 | 必填 | http.protocol |
| Logs | 一组键值对集合,用于记录调用日志 | 可选填 | — |
| SpanContext | 在进程间通信时携带的 span 信息,指整个 trace | — | — |
其中SpanContext是整个追踪传播机制的关键:它随请求在进程间传递(通常通过 HTTP Header 或消息队列消息头),下游服务据此将自己产生的 Span 挂接到同一个 Trace 上,从而形成完整的调用链。
真实追踪数据示例
下面是仓库文档中记录的、由 Jaeger 收集的来自 Bookinfo 示例中productpage的调用链追踪数据:
{ "data": [ { "traceID": "aaccbe962478cf93", "spans": [ { "traceID": "aaccbe962478cf93", "spanID": "fa36a9cbd60b4ae5", "operationName": "details.default.svc.cluster.local:9080/*", "references": [ { "refType": "CHILD_OF", "traceID": "aaccbe962478cf93", "spanID": "2" } ], "startTime": 1540273832696773, "duration": 8171, "tags": [ { "key": "component", "type": "string", "value": "proxy" }, { "key": "node_id", "type": "string", "value": "sidecar~172.33.5.11~productpage-v1-8584c875d8-4jgwg.default~default.svc.cluster.local" } ], "logs": [], "processID": "p1", "warnings": null } ], "processes": { "p1": { "serviceName": "productpage", "tags": [ { "key": "ip", "type": "string", "value": "172.33.5.11" } ] } }, "warnings": null } ], "total": 0, "limit": 0, "offset": 0, "errors": null }这份真实数据揭示了几点实现事实:
- 每条 Trace 有全局唯一的
traceID,每个 Span 有本 Trace 内唯一的spanID; - Span 通过
references中的refType: "CHILD_OF"声明父子关系,即 Spanfa36a9cbd60b4ae5是 Span2的子调用; operationName直接反映了被调用的服务与接口(details.default.svc.cluster.local:9080/*),说明该调用链来自 Istio/Envoy 注入的 sidecar 代理(component: proxy);startTime单位为微秒,duration为该 Span 的耗时(8171 微秒),可用于定位耗时瓶颈;processes记录了每个 Span 归属的服务进程(serviceName: productpage)及其 IP 地址。
分布式追踪系统的设计要求
在对分布式追踪系统选型或自研评估时,kubernetes-handbook 的 分布式追踪 文档给出了三类硬性要求。
1. 对应用程序的消耗足够低
"消耗低"包含两层含义:
- 占用的系统资源要足够低:埋点、采样、上报过程不应显著增加服务的 CPU 与内存开销;
- 造成的延迟要足够低:追踪数据的产生与传播不能拖慢业务请求本身的响应时间。
这也是 Dapper 论文中强调的设计原则——埋点代码运行在请求的关键路径上,任何额外开销都会被放大,因此必须保证极低的性能损耗。
2. 对应用程序透明
为了做到 7x24 小时无所不在的部署,在向应用程序中集成分布式追踪系统时,要让程序员对程序的改动尽可能的小,这样才便于大范围、低成本地接入。对"透明性"的追求催生了两类实现路径:
- 通过服务网格实现零侵入追踪:在 Kubernetes 中由 Istio/Envoy sidecar 代理自动完成 Span 的创建、传播与上报,应用代码完全无感知(上文 JSON 数据中
component: proxy即为 sidecar 产生的 Span); - 通过 SDK 自动埋点实现低侵入接入:应用只需引入 OpenTracing 兼容的客户端库并配置 Tracer,即可自动捕获 HTTP/RPC 调用的追踪信息(下文 Istio 教程即采用此方式)。
3. 可扩展
为了将所有服务接入分布式追踪系统,该系统必须能够承载大规模服务。这要求追踪后端具备横向扩展能力,包括:
- 支持分布式采集与多后端存储(如 Elasticsearch、Cassandra);
- 能够应对高并发的 Span 写入;
- 查询与可视化组件与存储解耦,可按需扩容。
其他要求
除了以上三点,分布式追踪系统还应对产生的追踪数据处理得尽可能快,并且可以方便地对追踪结果进行查询和可视化。这体现在 Jaeger 的 Query UI、Zipkin 的依赖关系图、SkyWalking 的拓扑图等可视化能力上。
实战一:本地启动 Jaeger 并接入 Spring Boot 微服务
kubernetes-handbook 的 Istio 教程 提供了一个完整的分布式追踪实战案例:三个服务customer → preference → recommendation组成的 Java 微服务调用链,其中customer和preference基于 Spring Boot 构建,recommendation基于 vert.x 构建。
引入 OpenTracing 与 Jaeger 依赖
customer和preference微服务的pom.xml中都引入了 OpenTracing 和 Jaeger 的依赖:
<dependency> <groupId>io.opentracing.contrib</groupId> <artifactId>opentracing-spring-cloud-starter</artifactId> <version>0.1.7</version> </dependency> <dependency> <groupId>com.uber.jaeger</groupId> <artifactId>jaeger-tracerresolver</artifactId> <version>0.25.0</version> </dependency>其中opentracing-spring-cloud-starter提供 Spring Cloud 环境下基于 OpenTracing API 的自动埋点能力,jaeger-tracerresolver则负责将 Jaeger 实现注册为 OpenTracing 的全局 Tracer。二者配合即可做到"对应用程序透明":业务代码无需改动,框架自动捕获服务间 HTTP 调用并生成 Span。
用 Docker 启动 Jaeger all-in-one
在本地使用 Docker 运行 Jaeger 的单机一体化镜像:
docker run -d \ --rm \ -p5775:5775/udp \ -p6831:6831/udp \ -p6832:6832/udp \ -p16686:16686 \ -p14268:14268 \ jaegertracing/all-in-one:1.3端口说明:
5775/udp:兼容 Zipkin thrift 协议的 UDP 接收端口;6831/udp、6832/udp:Jaeger 原生 thrift compact/binary 协议的 UDP 接收端口;16686:Jaeger Query UI 的 HTTP 访问端口;14268:jaeger-collector 的 HTTP 接收端口。
启动后访问 http://localhost:16686 即可看到 Jaeger query UI。
启动微服务并查看调用链
在本地依次启动三个服务,并为 Jaeger 指定服务名:
cd customer/java/springboot JAEGER_SERVICE_NAME=customer mvn \ spring-boot:run \ -Drun.arguments="--spring.config.location=src/main/resources/application-local.properties"cd preference/java/springboot JAEGER_SERVICE_NAME=preference mvn \ spring-boot:run \ -Drun.arguments="--spring.config.location=src/main/resources/application-local.properties"cd recommendation/java/vertx mvn vertx:run三个服务分别监听 8280、8180、8080 端口。访问 http://localhost:8280 后将看到输出:
customer => preference => recommendation v1 from 'unknown': 1此时访问 http://localhost:16686,即可在 Jaeger UI 中搜索customer和preference服务的 Trace,查看每次请求的完整追踪信息。
实战二:将追踪系统部署到 Kubernetes
在 Istio 服务网格中集成 Zipkin
在 Istio 教程 的集群部署环节,作者使用 Zipkin 作为分布式追踪后端。仓库中的 manifests/istio/zipkin.yaml 提供了 Zipkin 在 Kubernetes 中的 Deployment 与 Service 定义:
--- apiVersion: extensions/v1beta1 kind: Deployment metadata: name: zipkin spec: replicas: 1 template: metadata: annotations: alpha.istio.io/sidecar: ignore labels: app: zipkin spec: containers: - name: zipkin image: harbor-001.jimmysong.io/library/zipkin:latest ports: - containerPort: 9411 env: - name: POD_NAMESPACE valueFrom: fieldRef: apiVersion: v1 fieldPath: metadata.namespace --- apiVersion: v1 kind: Service metadata: name: zipkin spec: #type: NodePort ports: - name: http port: 9411 #nodePort: 30411 selector: app: zipkin这个配置文件的实现细节值得注意:
- Deployment 的 Pod 注解
alpha.istio.io/sidecar: ignore表明追踪系统自身不注入 sidecar,避免追踪后端成为被追踪对象; - Zipkin 的 HTTP 端口为
9411,这也是 Istio/Envoy 默认上报追踪数据的端口; - Service 暴露
9411端口,通过 selectorapp: zipkin关联 Pod;配置中预留了 NodePort 与 nodePort 的注释项,说明可以按需改为type: NodePort以便在集群外部访问。
部署应用并查看分布式追踪与依赖关系
将三个微服务部署到 Kubernetes(使用istioctl kube-inject注入 sidecar):
kubectl create ns istio-tutorial kubectl apply -f <(istioctl kube-inject -f recommendation/kubernetes/Deployment.yml) -n istio-tutorial kubectl apply -f recommendation/kubernetes/Service.yml kubectl apply -f <(istioctl kube-inject -f preference/kubernetes/Deployment.yml) -n istio-tutorial kubectl apply -f preference/kubernetes/Service.yml kubectl apply -f <(istioctl kube-inject -f customer/kubernetes/Deployment.yml) -n istio-tutorial kubectl apply -f customer/kubernetes/Service.yml批量访问 customer 服务产生流量后,即可在追踪系统中查看调用链。下面两张图分别展示了服务间调用的分布式追踪视图与服务依赖关系图——这正是追踪系统"快速处理、方便查询与可视化"要求的直接体现。
从追踪数据反推流量分布
该教程还演示了如何利用追踪之外的信息辅助验证流量控制:为recommendation构建 v2 版本并扩容到 2 个实例后,持续访问 customer 服务,观察输出中 v1/v2 版本被访问的频次变化。结合追踪系统,开发者可以进一步确认每次请求实际命中哪个版本的服务、经过哪些节点,实现从"调用链可观测"到"流量行为可验证"的闭环。
小结
分布式追踪是微服务与 Kubernetes 环境下不可或缺的可观测性支柱。以 kubernetes-handbook 中的相关文档为骨架,可以梳理出完整的技术脉络:
- 为什么需要:微服务拆分后,跨主机、跨数据中心的调用链无法用单机日志还原,需要分布式追踪定位故障环节与优化点;
- 标准与模型:CNCF 的 OpenTracing 标准提供九种语言的厂商中立 API,以 Trace、Span、SpanContext、Tags、Logs 为核心数据模型;大部分实现源于 Google Dapper 论文;
- 主流实现:Jaeger(CNCF 项目,完整支持 OpenTracing)、Zipkin(Istio 早期默认后端)、Apache SkyWalking(集成 APM/SPM 能力);
- 设计要求:消耗足够低(资源 + 延迟)、对应用透明(低侵入接入)、可扩展(支撑大规模服务),并需支持快速处理、查询与可视化;
- 落地路径:本地可用
jaegertracing/all-in-one镜像快速启动 Jaeger,通过opentracing-spring-cloud-starter与jaeger-tracerresolver实现 Spring Boot 低侵入接入;在 Kubernetes 中可部署 Zipkin(参考 manifests/istio/zipkin.yaml),由 Istio sidecar 自动上报 Span,零侵入实现调用链还原与服务依赖可视化。
读者可继续深入仓库阅读 OpenTracing(术语与数据模型详解)、可观察性(指标、日志、追踪三支柱定位)以及 Istio 教程(完整 Java 微服务追踪实战)获取更多细节。
- 教程
- 云原生
- 容器编排
【免费下载链接】kubernetes-handbook
Kubernetes 架构与生态:从云原生到 AI 原生基础设施的构建指南
相关推荐
分布式追踪标准 OpenTracing 与 Jaeger 实践:从 Trace/Span 语义到 Kubernetes 微服务调用链分析
分布式追踪标准 OpenTracing 与 Jaeger 实践:从 Trace/Span 语义到 Kubernetes 微服务调用链分析 OpenTracing
教程云原生容器编排如何使用SwiftGodot快速构建跨平台游戏:iOS、Linux、macOS全指南
如何使用SwiftGodot快速构建跨平台游戏:iOS、Linux、macOS全指南 SwiftGodot是一套为Swift开发者设计的全新Godot绑定库,让
Bitnami Containers追踪系统:Jaeger+Zipkin分布式追踪
Bitnami Containers追踪系统:Jaeger+Zipkin分布式追踪 还在为微服务架构中的性能问题头疼吗?分布式系统调用链复杂,问题定位困难?一文
云原生供应链安全应用安全
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考