微服务分布式链路追踪:SkyWalking单点服务搭建指南
微服务分布式链路追踪:SkyWalking 单点服务搭建指南
在微服务架构下,应用被拆分成多个独立的服务,如何在分布式环境中快速定位调用链路、诊断性能瓶颈,成为了运维与开发的核心难题。Apache SkyWalking 是一款开源的分布式链路追踪、性能监控与可观测性平台,能够采集多种语言与框架的调用数据,汇总在一个可视化界面中进行分析。本指南将聚焦单点部署(一台机器上同时运行 OAP、存储与 UI)的场景,详细讲解如何快速搭建 SkyWalking 并在一个简单的 Spring Boot 微服务中接入 Tracing Agent,帮助你快速上手链路追踪。
目录
- 引言:为什么需要分布式链路追踪
- SkyWalking 简介与核心组件
- 单点部署架构设计
- 环境准备
- 步骤一:安装与配置 Elasticsearch(可选存储)
- 步骤二:下载并启动 SkyWalking OAP 与 UI
- 步骤三:微服务接入 SkyWalking Agent 示例(Spring Boot)
7.1. 引入 Maven 依赖
7.2. 配置 Agent 启动参数
7.3. 样例代码:两个简单微服务间的调用 - 步骤四:验证链路追踪效果
- 常见问题与优化建议
- 总结
1. 引言:为什么需要分布式链路追踪
在传统单体应用中,遇到性能问题时,通过阅读日志、打点或 APM 工具往往就能快速定位瓶颈。但在微服务架构下,业务请求往往需要跨越多个服务节点(Service A → Service B → Service C),每个服务在不同进程、不同机器或容器中运行,甚至使用不同的语言栈,日志难以串联、调用链难以重现,常见痛点包括:
- 跨服务请求耗时不明:难以知道某次请求在每个服务上花费了多少时间。
- 复杂的依赖树:多个子服务并发调用,调用顺序、并发关系比较复杂。
- 异常链追踪:异常抛出后,需要快速定位是哪个服务、哪段代码引发的问题。
- 动态扩缩容场景:服务实例按需自动伸缩,IP/端口会变化,不便人工维护调用链。
分布式链路追踪(Distributed Tracing)能够在请求跨服务调用时,向每个调用节点注入唯一的 Trace Context,将所有 span(调用片段)通过一个全局 Trace ID 串联起来,最终在一个可视化面板中完整呈现请求在各服务的调用路径与耗时。Apache SkyWalking 就是其中一款成熟的链路追踪与可 observability 平台,支持多语言、多框架和可扩展的插件体系,适合快速构建全链路可观测体系。
2. SkyWalking 简介与核心组件
SkyWalking 的核心组件大致可分为以下几部分:
Agent
- 部署在应用服务所在的 JVM(或其他语言运行时)中,负责拦截入口/出口调用(如 Spring MVC、gRPC、Dubbo、JDBC、Redis 等),并将 Trace 与时序指标数据上报到 OAP。
- 支持 Java、C#、Node.js、PHP、Go、Python 等多种语言,通过自动探针(ByteBuddy、ASM、eBPF)或手动埋点接入。
OAP Server(Observability Analysis Platform)
- SkyWalking 的核心后端服务,接收并解析来自 Agent 上报的链路与指标数据,对数据进行聚合、存储与分析。
- 包含多种模块:Receiver(接收各协议数据)、Analysis(拓扑计算、调用时序存储)、Storage(存储引擎接口)、Alarm(告警规则)、Profile(性能分析)等。
- 支持插件化存储:可以将时序数据与 Trace 数据存入 Elasticsearch、H2、MySQL、TiDB、InfluxDB、CLICKHOUSE 等后端存储。
存储(Storage)
- SkyWalking 本身并不内置完整的数据库,而是通过 Storage 插件将数据写入后端存储系统。
- 对于单点部署,最常见的选择是 Elasticsearch(便于在 UI 中进行 Trace 搜索和拓扑查询);也可以使用 H2 内存数据库做轻量化测试。
UI(Web UI)
- 提供可视化界面,用于展示服务拓扑图、调用链详情、时序监控图表、实例列表、告警管理等功能。
- 在单点部署下,OAP 与 UI 通常在同一台机器的不同进程中运行,默认端口为 12800(OAP gRPC)、12800(HTTP)、8080(UI)。
Agent → OAP 通信协议
- Java Agent 默认使用 gRPC 协议(在 8.x 及更高版本)或 HTTP/Jetty。
- 非 Java 语言 Agent(如 Node.js、PHP)也有各自的插件,使用 HTTP 协议上报。
3. 单点部署架构设计
本文所讲“单点部署”指在同一台物理机/虚拟机/容器中,同时部署:
- 后端存储(以 Elasticsearch 为例);
- SkyWalking OAP Server(负责数据接收、分析、写入);
- SkyWalking UI(负责可视化展示)。
整体架构示意(ASCII 图)如下:
┌────────────────────────────────────────────────────────────────┐
│ 单点部署服务器(Host) │
│ │
│ ┌───────────────┐ ┌───────────────┐ ┌─────────────┐ │
│ │ Elasticsearch │ │ OAP Server │ │ UI Server │ │
│ │ (单节点集群) │◀────▶│ (12800 gRPC/HTTP)│◀──▶│ (端口 8080) │ │
│ │ 端口: 9200 │ │ 存储适配 ES │ │ │ │
│ └───────────────┘ └───────┬───────┘ └─────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────┐ │
│ │ 多个微服务实例(Java/Spring Boot) │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ │ ServiceA│ │ ServiceB│ │ ServiceC│ │ ServiceD│ │
│ │ │ (8081) │ │ (8082) │ │ (8083) │ │ (8084) │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
│ │ │ │ │ │ │
│ │ Agent Agent Agent Agent │
│ │ │ │ │ │ │
│ │ ▼ ▼ ▼ ▼ │
│ │ (数据上报 gRPC/HTTP) (数据上报 ...) (数据上报 ...) (数据上报 ...) │ │
│ └───────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────────┘
- Elasticsearch:用于存储 Trace、拓扑与监控指标,单节点即可完成链路查询与可视化。
- OAP Server:接收 Agent 上报的数据,进行分析并写入 Elasticsearch。
- UI Server:展示拓扑图、调用链、服务实例列表、指标图表等。
- 微服务实例:示例中采用 Spring Boot 服务,分别运行在不同端口(8081、8082、8083、8084)上,通过挂载 SkyWalking Java Agent 自动采集链路数据。
4. 环境准备
- 操作系统:Linux(如 CentOS 7/8、Ubuntu 18.04/20.04 均可)。
- Java 版本:Java 8 或更高(建议 OpenJDK 8/11)。
- Elasticsearch:7.x 系列(与 SkyWalking 版本兼容,本文以 ES 7.17 为例)。
- SkyWalking 版本:本文以 SkyWalking 8.8.0 为示例。
磁盘与内存:
- Elasticsearch:至少 4GB 内存,20GB 可用磁盘;
- OAP+UI:至少 2GB 内存;
- 微服务(每个实例)约 512MB 内存。
网络端口:
- Elasticsearch: 9200(HTTP)、9300(集群通信);
- SkyWalking OAP: 12800(gRPC)、12800(HTTP/Rest);
- UI: 8080;
- 微服务:8081、8082、8083、8084。
注意:如果在同一台机器上运行所有组件,建议确保硬件资源充足,避免资源争抢导致性能瓶颈。
5. 步骤一:安装与配置 Elasticsearch(可选存储)
5.1. 下载与解压 Elasticsearch
以 Elasticsearch 7.17.0 为例:
# 进入 /opt 目录(或其他任意目录)
cd /opt
wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.17.0-linux-x86_64.tar.gz
tar -zxvf elasticsearch-7.17.0-linux-x86_64.tar.gz
mv elasticsearch-7.17.0 elasticsearch
5.2. 修改配置(单节点模式)
编辑 /opt/elasticsearch/config/elasticsearch.yml
,确保以下几项(最小化单节点部署):
cluster.name: skywalking-cluster
node.name: es-node-1
path.data: /opt/elasticsearch/data
path.logs: /opt/elasticsearch/logs
# 单机模式关闭集群发现
discovery.type: single-node
# 根据主机内存调整 JVM Heap
# 编辑 /opt/elasticsearch/config/jvm.options,将 -Xms4g -Xmx4g(根据实际调整)
默认情况下,ES 会自动分配单节点集群。确保 discovery.type: single-node
,避免待集群中只有一个节点时无法组网。
5.3. 启动 Elasticsearch
# 创建 data 和 logs 目录
mkdir -p /opt/elasticsearch/data /opt/elasticsearch/logs
# 启动脚本
cd /opt/elasticsearch
bin/elasticsearch -d # -d 表示后台启动
启动成功后,访问
http://localhost:9200/
,应显示 Elasticsearch 集群信息:{ "name" : "es-node-1", "cluster_name" : "skywalking-cluster", "cluster_uuid" : "xxxxxxxxxxxx", "version" : { "number" : "7.17.0", ... }, "tagline" : "You Know, for Search" }
6. 步骤二:下载并启动 SkyWalking OAP 与 UI
6.1. 下载 SkyWalking
以 SkyWalking 8.8.0 为例:
cd /opt
wget https://archive.apache.org/dist/skywalking/8.8.0/apache-skywalking-apm-8.8.0.tar.gz
tar -zxvf apache-skywalking-apm-8.8.0.tar.gz
mv apache-skywalking-apm-bin apache-skywalking
解压后目录为 /opt/apache-skywalking
,结构如下:
/opt/apache-skywalking
├── agent/ # Java Agent
├── config/ # 默认配置文件
│ ├── application.yml # OAP/Storage 配置
│ └── webapp.yml # UI 配置
├── bin/
│ ├── oapService.sh # 启动 OAP Server 脚本
│ └── webappService.sh # 启动 UI Server 脚本
└── oap-libs/ # OAP 依赖库
6.2. 配置 application.yml
编辑 /opt/apache-skywalking/config/application.yml
,在 storage
部分将存储类型改为 Elasticsearch:
storage:
elasticsearch:
# 指定 Elasticsearch 存储类型
# 兼容 ES 6.x/7.x 版本
nameSpace: ${SW_NAMESPACE:"default"}
clusterNodes: ${SW_STORAGE_ES_CLUSTER_NODES:localhost:9200}
# 集群模式、多节点可写为 node1:9200,node2:9200
protocol: ${SW_STORAGE_ES_HTTP_PROTOCOL:http}
user: ${SW_ES_USER:} # 如果无权限可留空
password: ${SW_ES_PASSWORD:} # 如果无密码可留空
trustCertsPath: ${SW_ES_TRUST_CERT_PATH:} # TLS 情况可指定证书
# 索引截断保留时间(天),超过将删除
indexShardsNumber: ${SW_ES_INDEX_SHARDS_NUMBER:1}
indexReplicasNumber: ${SW_ES_INDEX_REPLICAS_NUMBER:0}
clusterNodes
指向运行在本机的 Elasticsearch 实例(localhost:9200
)。- 默认设置索引分片为
1
、副本为0
(单节点无需副本)。
6.3. 启动 OAP Server
cd /opt/apache-skywalking/bin
# 给脚本赋可执行权限(如果需要)
chmod +x oapService.sh
./oapService.sh
- 启动过程中,OAP 会尝试连接 Elasticsearch 并自动创建所需索引(如
skywalking*
)。 - 日志默认输出在
/opt/apache-skywalking/logs/oap.log
,可观察初始化情况。
6.4. 启动 UI Server
在 OAP 启动并运行正常后,再启动前端 UI:
cd /opt/apache-skywalking/bin
chmod +x webappService.sh
./webappService.sh
- 默认 UI 监听端口 8080,启动后访问
http://localhost:8080/
,可看到 SkyWalking Web 界面登录页。 - 默认用户名/密码:admin/admin。首次登录后建议修改密码。
7. 步骤三:微服务接入 SkyWalking Agent 示例(Spring Boot)
以下示例将演示如何在一个简单的 Spring Boot 微服务项目中接入 SkyWalking Java Agent,实现链路采集。
7.1. 引入 Maven 依赖
在 ServiceA 与 ServiceB 的 pom.xml
中,添加 spring-boot-starter-web
和其他业务依赖。注意:Agent 本身不需要在 pom.xml
中声明 SkyWalking 依赖,只需将 Agent Jar 放在本地即可。示例 pom.xml
片段:
<dependencies>
<!-- Spring Boot Web Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 如果使用 RestTemplate 或 Feign 调用下游服务,可添加对应依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>3.1.2</version>
</dependency>
<!-- 其他自定义业务依赖 -->
</dependencies>
7.2. 配置 Agent 启动参数
- 下载 Agent:在
/opt/apache-skywalking/agent/
目录中已有skywalking-agent.jar
。 在启动 Spring Boot 应用时,增加如下 JVM 参数(以 Linux shell 为例):
# 启动 ServiceA export SW_AGENT_NAME=ServiceA # 在 UI 中的服务名称 export SW_AGENT_COLLECTOR_BACKEND_SERVICES=localhost:12800 # OAP 地址 java -javaagent:/opt/apache-skywalking/agent/skywalking-agent.jar \ -Dskywalking.agent.service_name=$SW_AGENT_NAME \ -Dskywalking.collector.backend_service=$SW_AGENT_COLLECTOR_BACKEND_SERVICES \ -jar serviceA.jar --server.port=8081
在 ServiceB 中类似配置:
export SW_AGENT_NAME=ServiceB export SW_AGENT_COLLECTOR_BACKEND_SERVICES=localhost:12800 java -javaagent:/opt/apache-skywalking/agent/skywalking-agent.jar \ -Dskywalking.agent.service_name=$SW_AGENT_NAME \ -Dskywalking.collector.backend_service=$SW_AGENT_COLLECTOR_BACKEND_SERVICES \ -jar serviceB.jar --server.port=8082
-javaagent
:指定 SkyWalking Java Agent 的 Jar 包路径;-Dskywalking.agent.service_name
:在 SkyWalking UI 中显示的服务名称;-Dskywalking.collector.backend_service
:OAP Server 地址,默认端口 12800。
7.3. 样例代码:两个简单微服务间的调用
假设有 ServiceA 和 ServiceB,其中 ServiceA 提供一个接口 /api/a
,调用 ServiceB 的 /api/b
后返回结果,示例代码如下。
7.3.1. ServiceB
项目结构:
serviceB/ ├── src/main/java/com/example/serviceb/ServiceBApplication.java └── src/main/java/com/example/serviceb/controller/BController.java
ServiceBApplication.java
:package com.example.serviceb; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ServiceBApplication { public static void main(String[] args) { SpringApplication.run(ServiceBApplication.class, args); } }
BController.java
:package com.example.serviceb.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class BController { @GetMapping("/api/b") public String helloB() { // 模拟业务逻辑耗时 try { Thread.sleep(50); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } return "Hello from ServiceB"; } }
7.3.2. ServiceA
项目结构:
serviceA/ ├── src/main/java/com/example/servicea/ServiceAApplication.java └── src/main/java/com/example/servicea/controller/AController.java
ServiceAApplication.java
:package com.example.servicea; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ServiceAApplication { public static void main(String[] args) { SpringApplication.run(ServiceAApplication.class, args); } }
AController.java
:package com.example.servicea.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController public class AController { private final RestTemplate restTemplate; @Autowired public AController(RestTemplate restTemplate) { this.restTemplate = restTemplate; } @GetMapping("/api/a") public String helloA() { // 调用 ServiceB 的 /api/b 接口 String bResponse = restTemplate.getForObject("http://localhost:8082/api/b", String.class); return "ServiceA calls -> [" + bResponse + "]"; } }
在
ServiceAApplication.java
中定义RestTemplate
Bean:@SpringBootApplication public class ServiceAApplication { public static void main(String[] args) { SpringApplication.run(ServiceAApplication.class, args); } @Bean public RestTemplate restTemplate() { return new RestTemplate(); } }
7.3.3. 启动顺序
- 启动 Elasticsearch(请确保已启动并可访问
http://localhost:9200
)。 - 启动 SkyWalking OAP Server:
./oapService.sh
。 - 启动 SkyWalking UI:
./webappService.sh
,访问http://localhost:8080/
,确认 UI 可访问。 启动 ServiceB(带 Agent):
export SW_AGENT_NAME=ServiceB export SW_AGENT_COLLECTOR_BACKEND_SERVICES=localhost:12800 java -javaagent:/opt/apache-skywalking/agent/skywalking-agent.jar \ -Dskywalking.agent.service_name=$SW_AGENT_NAME \ -Dskywalking.collector.backend_service=$SW_AGENT_COLLECTOR_BACKEND_SERVICES \ -jar serviceB/target/serviceB.jar --server.port=8082
启动 ServiceA(带 Agent):
export SW_AGENT_NAME=ServiceA export SW_AGENT_COLLECTOR_BACKEND_SERVICES=localhost:12800 java -javaagent:/opt/apache-skywalking/agent/skywalking-agent.jar \ -Dskywalking.agent.service_name=$SW_AGENT_NAME \ -Dskywalking.collector.backend_service=$SW_AGENT_COLLECTOR_BACKEND_SERVICES \ -jar serviceA/target/serviceA.jar --server.port=8081
8. 步骤四:验证链路追踪效果
访问 ServiceA 接口
在浏览器或命令行中执行:curl http://localhost:8081/api/a
应返回:
ServiceA calls -> [Hello from ServiceB]
在 SkyWalking UI 中查看 Trace
- 打开浏览器,访问
http://localhost:8080/
; - 登录后,点击顶部导航的 “Trace” → “Trace List”;
- 默认会显示最近产生的 Trace,找到服务名称为
ServiceA
的 Trace,点击进入详情。 在 Trace 树状图中,可以看到:
ServiceA: /api/a → 调用耗时 ~50ms → 下游 ServiceB: /api/b
- 点击 Span 详情可展开每个调用的时间戳、耗时、标签(如 HTTP Status、Method、URL)等信息。
- 打开浏览器,访问
8.1. 链路调用示意图
┌─────────┐ ┌─────────┐
│ Client │── HTTP GET /api/a ──────────▶│ ServiceA│
└─────────┘ └────┬────┘
│
(SkyWalking Agent 拦截 /api/a)
│
↓ 调用下游 (RestTemplate)
│
HTTP GET /api/b
│
┌───▼──────┐
│ ServiceB │
└──────────┘
│
(SkyWalking Agent 拦截 /api/b)
│
┌───▼────────┐
│ 返回 "Hello"│
└────────────┘
│
(SkyWalking Agent 在返回时上报 Span 结束)
│
┌─────────┐ ┌────▼────┐
│ SkyWalking OAP Server (收集) │ SkyWalking UI │
└─────────┘ └─────────────┘
- 每个服务的 Agent 都会在方法入口处创建一个 Span,调用外部调用器(如 RestTemplate)时创建子 Span,并最终向 OAP Server 报送数据;
- 最终在 UI 中可以看到 ServiceA 的入口 Span 和 ServiceB 的子 Span,形成完整的调用链。
9. 常见问题与优化建议
Agent 无数据上报
- 确认 JVM 启动参数中
-javaagent
路径是否正确; - 检查
-Dskywalking.collector.backend_service
配置的地址和端口是否能访问到 OAP Server; - 确认 OAP 日志中没有报错(查看
/opt/apache-skywalking/logs/oap.log
); - 确认服务端口、URL 与实际接口路径是否正确,Agent 默认只能拦截常见框架(Spring MVC、Dubbo、gRPC 等)。
- 确认 JVM 启动参数中
UI 无法访问或登录失败
- 检查 UI Server 是否启动、日志中有无报错;
- 确认 OAP Server 与 Elasticsearch 是否都处于运行状态;
- 确认 UI 与 OAP 版本兼容(同一 SkyWalking 发行版自带的版本应当一致)。
链路不完整或时间跨度过长
- 可能是下游服务没有配置 Agent,导致无法链到子 Span;
- 检查 Agent 的采样率(默认是 100%,可通过
application.yml
中的agent.sample_n_per_3_secs
等参数调整); - 对于高并发场景,可调整
agent.buffered_span_limit
、agent.async_nanos_threshold
等参数,避免 Agent 过载。
ES 存储性能不足
- 单节点 ES 默认 Heap 是半机内存,可在
/opt/elasticsearch/config/jvm.options
中调整; - 如果链路数据增多,可考虑扩展为 ES 集群或使用更轻量化的 H2(仅做测试)。
- 定期清理过期索引:在
application.yml
中调整indexShardsNumber
、indexReplicasNumber
和indexTTL
(以天为单位)。
- 单节点 ES 默认 Heap 是半机内存,可在
跨语言服务链路追踪
- SkyWalking 支持多语言 Agent,比如 Node.js、Go、PHP 等;
- 只需在各语言服务中接入对应版本的 Agent,即可将链路数据统一汇总到同一个 OAP。
10. 总结
本文从单点部署的视角,详细介绍了如何在一台服务器上完成 SkyWalking 的完整搭建及微服务接入,包括:
- 概念梳理:为什么需要分布式链路追踪,以及 SkyWalking 的核心组件与作用;
- 单点部署架构:OAP、UI 与 Elasticsearch 在一台机器上的部署架构示意;
- 环境准备与安装:如何下载、解压并配置 Elasticsearch,启动 SkyWalking OAP 与 UI;
- 微服务接入示例:以两个简单的 Spring Boot 服务为例,演示引入 SkyWalking Java Agent 的方法与注意事项;
- 验证链路追踪效果:在 UI 中查看 Trace,理解 Span 之间的调用关系;
- 常见问题与优化:排查 Agent 无上报、UI 无法访问、链路断裂、ES 性能瓶颈等常见场景,并给出优化建议。
通过本文的步骤,即可在短时间内完成一个可用的链路追踪平台,实现微服务间的分布式调用可视化与诊断。在生产环境中,可将该单点部署方案扩展为多节点集群(OAP、Elasticsearch、UI 分布式部署),满足高并发与高可用需求。
评论已关闭