编辑
2026-10-24
微服务
00

目录

SkyWalking 11 从零搭建到实战排障:一份给后端开发的使用手册
目录
一、先搞清楚四个角色
端口一览
二、服务端部署
2.1 前置:JDK 17
2.2 下载并解压
2.3 配置存储(以 Elasticsearch 为例)
2.4 调整内存
2.5 systemd 托管
2.6 装 UI:注意 11.0 换了新 UI
2.7 验证
三、接入你的项目
3.1 下载 Agent
3.2 IDEA 里加启动参数
3.3 命令行启动
3.4 验证接入成功
四、部署时踩的六个坑
坑 3 的细节:为什么 Node 装不上
五、日常排障:三步定位法
第一步:从服务仪表板看宏观
第二步:在追踪列表里找慢请求
第三步:打开单条 Trace 看瀑布图
现象 → 结论对照表
关于「空白」的进一步判断
六、七个高频功能详解
1. 追踪(Trace)—— 用得最多
2. 服务仪表板 —— 看趋势
3. 拓扑图 —— 看依赖关系
4. 实例 —— 排查单机问题
5. 日志 —— 关联排查
6. 告警 —— 被动发现问题
7. Trace 剖析 / Profiling —— 找 CPU 热点
七、真实案例拆解
现场
排查过程
从案例里能学到的
八、进阶:让排查更省力
8.1 打通 Trace ID 和日志
8.2 采样率
8.3 忽略不需要的端点
8.4 多环境隔离
九、速查表
服务端
验证
Agent 侧
关键路径
写在最后

SkyWalking 11 从零搭建到实战排障:一份给后端开发的使用手册

本文记录一次完整的 SkyWalking 落地过程:服务端部署(11.0.0)、本地项目接入(JDK 8~21)、以及真正天天用得到的排障方法。

环境:CentOS 7 + JDK 17(服务端)+ Elasticsearch 7.9.3 + SkyWalking 11.0.0 + Java Agent 9.8.0(本地 Java 21 项目)


目录


一、先搞清楚四个角色

很多人装了 SkyWalking 却用不起来,是因为没分清这几个东西各自在哪、干什么:

┌─────────────────────────┐ ┌──────────────────────────┐ ┌──────────────┐ │ 被监控的应用 │ 11800 │ OAP Server │ 9200 │ Storage │ │ JVM + Java Agent │ ──────> │ (接收/解析/聚合) │ ─────> │ ES/BanyanDB │ └─────────────────────────┘ └──────────────────────────┘ └──────────────┘ │ 12800 ▼ ┌──────────────────────────┐ │ UI (Horizon, 8080) │ └──────────────────────────┘
角色位置作用必须吗
Java Agent被监控应用内字节码增强,采集链路数据✅ 必须
OAP Server服务端接收数据、解析、聚合、写存储✅ 必须
StorageES / BanyanDB / H2存 traces、metrics、logs✅ 必须(可换)
UI服务端浏览器看的页面✅ 必须

三个关键认知:

  1. Agent 跑在你应用进程里,不是跑在服务器上。所以监控本地项目完全可行——本地主动往外推数据,服务器不需要反向连你。
  2. OAP 自己不存数据,必须挂一个存储。存储不是只能选 ES,BanyanDB / H2 / MySQL 都行。
  3. Agent 和 OAP 是两个独立安装包。下载页里 apache-skywalking-apm-bin 只有服务端,Agent 要单独下。

端口一览

端口用途需要公网暴露吗
11800Agent 上报(gRPC)✅ 要(给你的应用所在机器)
11800内部通信—
12800GraphQL 查询❌ UI 内部调用
17128Admin API❌ UI 内部调用
8080UI 页面✅ 要

只需要对公网开 11800(上报) 和 8080(看页面),其余走内网。


二、服务端部署

2.1 前置:JDK 17

SkyWalking 11 要求 Java 11+,官方推荐 17。

这是第一个坑:用 JDK 8 启动会得到 UnsupportedClassVersionError: class file version 55.0。 class 55 = Java 11 编译,52 = Java 8。

如果服务器上已经有 JDK 8 跑着别的服务,不要动它,装一个独立的 JDK 17:

bash
mkdir -p /usr/local/java tar -xzf jdk-17.0.2_linux-x64_bin.tar.gz -C /usr/local/java /usr/local/java/jdk-17.0.2/bin/java -version

只需要在启动 SkyWalking 时指定 JAVA_HOME,不影响全局。

2.2 下载并解压

bash
mkdir -p /opt/skywalking && cd /opt/skywalking tar -xzf apache-skywalking-apm-11.0.0-bin.tar.gz cd apache-skywalking-apm-bin

目录结构:

bin/ # oapService.sh(启动脚本) config/ # application.yml(核心配置) oap-libs/ # 依赖 jar logs/ # 日志 ← 排错看这里

2.3 配置存储(以 Elasticsearch 为例)

编辑 config/application.yml,找到 storage 段:

yaml
storage: selector: ${SW_STORAGE:elasticsearch} # 默认是 banyandb,改成自己的 elasticsearch: clusterNodes: ${SW_STORAGE_ES_CLUSTER_NODES:127.0.0.1:9200} protocol: ${SW_STORAGE_ES_HTTP_PROTOCOL:"http"} user: ${SW_ES_USER:"elastic"} password: ${SW_ES_PASSWORD:"<你的密码>"} indexShardsNumber: ${SW_STORAGE_ES_INDEX_SHARDS_NUMBER:1} indexReplicasNumber: ${SW_STORAGE_ES_INDEX_REPLICAS_NUMBER:0} # 单机建议改 0

两个建议:

  • 单机环境把 indexReplicasNumber 改成 0。ES 单节点下副本会一直 yellow,浪费空间还告警。
  • 密码里有特殊字符(*、!)时用双引号包住。

2.4 调整内存

默认 -Xms256M -Xmx4096M,小机器扛不住。用环境变量覆盖:

bash
export JAVA_HOME=/usr/local/java/jdk-17.0.2 export JAVA_OPTS="-Xms512M -Xmx1024M" ./bin/oapService.sh

注意:oapService.sh 是前台 exec 启动的(不是后台),所以要用 systemd 或 nohup,否则终端一断进程就没了。

2.5 systemd 托管

/etc/systemd/system/skywalking-oap.service:

ini
[Unit] Description=SkyWalking OAP 11.0.0 After=network-online.target docker.service Wants=network-online.target docker.service [Service] Type=simple WorkingDirectory=/opt/skywalking/apache-skywalking-apm-bin Environment=JAVA_HOME=/usr/local/java/jdk-17.0.2 Environment=JAVA_OPTS=-Xms512M -Xmx1024M Environment=OAP_LOG_DIR=/opt/skywalking/apache-skywalking-apm-bin/logs ExecStart=/opt/skywalking/apache-skywalking-apm-bin/bin/oapService.sh Restart=on-failure RestartSec=15 LimitNOFILE=65535 [Install] WantedBy=multi-user.target
bash
systemctl daemon-reload systemctl enable --now skywalking-oap

2.6 装 UI:注意 11.0 换了新 UI

这是第二个坑:apache-skywalking-apm-bin 里没有 webapp 目录。

从 10.x 开始官方把 UI 拆出去了,到 11.0 直接换成了全新的 Horizon UI(独立项目,Node.js 写的)。旧的 booster-ui 不随包发布了。

下载:

bash
curl -LO https://downloads.apache.org/skywalking/horizon-ui/1.0.1/apache-skywalking-horizon-ui-1.0.1-bin.tar.gz mkdir -p /opt/skywalking/horizon tar -xzf apache-skywalking-horizon-ui-1.0.1-bin.tar.gz -C /opt/skywalking/horizon --strip-components=1

改 horizon.yaml:

yaml
server: host: "${HORIZON_SERVER_HOST:0.0.0.0}" # 默认 127.0.0.1,外部访问不了 port: ${HORIZON_SERVER_PORT:8080} # 默认 8081 oap: queryUrl: "${HORIZON_OAP_QUERY_URL:http://127.0.0.1:12800}" adminUrl: "${HORIZON_OAP_ADMIN_URL:http://127.0.0.1:17128}" zipkinUrl: "${HORIZON_OAP_ZIPKIN_URL:http://127.0.0.1:12800/zipkin}"

它需要 Node 20+。CentOS 7 上装不了(glibc 2.17 < 2.28),用 Docker 最省事:

bash
docker run -d --name skywalking-horizon --restart=always \ --network host \ -v /opt/skywalking/horizon:/app -w /app \ --env-file /opt/skywalking/horizon/horizon.env \ node:22-alpine node server.js

必须创建登录账号,否则页面能打开但登录一定失败:

bash
# 生成 argon2 哈希(用容器里的 argon2 库) docker exec skywalking-horizon node -e \ 'const a=require("/app/node_modules/argon2"); a.hash("你的密码").then(console.log)'

把输出写进 horizon.env:

HORIZON_AUTH_LOCAL_USERS=[{"username":"admin","passwordHash":"$argon2id$v=19$m=65536,t=3,p=4$...","roles":["admin"]}]

horizon.yaml 里其实早就写了:With local and no users, the BFF boots but no login succeeds —— 没有默认的 admin/admin,必须自己生成。

2.7 验证

bash
systemctl is-active skywalking-oap # active ss -lntp | grep -E ':(11800|12800|17128)' # 都在监听 curl -s -o /dev/null -w '%{http_code}\n' -X POST \ -H 'Content-Type: application/json' \ -d '{"query":"{version}"}' http://127.0.0.1:12800/graphql # 200 curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/ # 200

在 ES 里能看到 sw_* 索引,说明存储也通了:

bash
curl -u 'elastic:xxx' 'http://127.0.0.1:9200/_cat/indices' | grep sw_ # sw_segment-20261024 sw_metrics-all-20261024 sw_log-20261024 sw_management

三、接入你的项目

3.1 下载 Agent

Agent 是独立包,不在 apm-bin 里:

https://skywalking.apache.org/downloads/ → Java Agent

结构:

skywalking-agent/ ├── skywalking-agent.jar ← 就是这个 ├── config/agent.config ← 也可以把参数写这里 ├── plugins/ ← 框架插件,按需加载 ├── optional-plugins/ ← 需要手动拷进 plugins 才生效 └── logs/ ← agent 自己的日志

3.2 IDEA 里加启动参数

在 Run/Debug Configurations → VM options:

-javaagent:D:\skywalking\skywalking-agent\skywalking-agent.jar -Dskywalking.agent.service_name=nft-turbo-business -Dskywalking.collector.backend_service=<OAP_IP>:11800
参数含义常见错误
-javaagent探针 jar 路径指到 .tgz 或目录 → Error opening zip file
service_name页面显示的服务名多实例用同一个名字才会聚合
backend_serviceOAP 地址,端口是 11800写成 8080 / 12800 → 连不上

3.3 命令行启动

bash
java -javaagent:/opt/skywalking-agent/skywalking-agent.jar \ -Dskywalking.agent.service_name=my-app \ -Dskywalking.collector.backend_service=<OAP_IP>:11800 \ -jar app.jar

-javaagent 必须在 -jar 之前,否则会被当成应用参数。

3.4 验证接入成功

控制台出现:

INFO SkyWalkingAgent - SkyWalking agent started

agent 日志 skywalking-agent/logs/skywalking-api.log 里会打印连接结果。30 秒内页面「服务」列表出现你的服务名,就成功了。


四、部署时踩的六个坑

#现象根因解法
1UnsupportedClassVersionError: class file version 55.0用 JDK 8 跑 JDK 11 编译的 class装 JDK 17,独立于已有 JDK 8
2解压后找不到 webapp 目录10.x 起 UI 从主包剥离,11.0 换成 Horizon UI单独下载 Horizon UI
3GLIBC_2.28 not found(装 Node 20)CentOS 7 是 glibc 2.17用 Docker 跑 node:22-alpine
4UI 能打开但登录失败auth.local.users 为空,没有默认账号生成 argon2 哈希写进配置
5Error opening zip file-javaagent 指向了 .tgz 压缩包解压,指向 skywalking-agent.jar
6Agent 启动正常但页面没数据11800 端口没放行 / 写成了 8080安全组放行 11800,参数确认

坑 3 的细节:为什么 Node 装不上

CentOS 7 的 glibc 是 2.17,而 Node 18+ 的官方二进制要求 glibc 2.28+。硬装会报:

node: /lib64/libm.so.6: version `GLIBC_2.27' not found

升级 glibc 风险极高(容易把系统搞崩),用 Docker 是最优解。Horizon 的依赖里有 argon2 这个 native 模块,但包内自带了 linux-x64/argon2.musl.node,所以 alpine 镜像能用。


五、日常排障:三步定位法

SkyWalking 的价值不在"有链路图",而在于能回答"慢在哪"。

第一步:从服务仪表板看宏观

打开「服务仪表板」,看三个核心指标:

指标含义异常表现
吞吐量 (CPM)每分钟调用次数突降 → 上游挂了 / 流量没了
响应时间P50 / P75 / P90 / P99P99 飙升但 P50 正常 → 长尾问题
SLA成功率跌破阈值 → 有异常

判断技巧:

  • P99 高、P50 正常 → 少数请求慢,通常是特定参数/特定数据触发(慢 SQL、大 key、缓存击穿)
  • P50 也高 → 整体变慢,通常是资源问题(GC、连接池、CPU 打满、下游整体慢)
  • SLA 掉 → 直接去「追踪」里按「错误」过滤

这一步的作用是把问题缩小到某个服务、某个时间段。

第二步:在追踪列表里找慢请求

进入「追踪」,两个关键操作:

  1. 按耗时排序(列表右侧的毫秒数),找出最慢的几条
  2. 用过滤条件缩小范围:时间范围、状态(全部/错误/慢)、服务名、Endpoint

这一步的作用是拿到一条具体的慢请求。

第三步:打开单条 Trace 看瀑布图

这是最关键的一步,也是新手最容易看错的地方。

打开后你会看到一棵 Span 树,每个节点有:操作名、耗时、开始偏移。

正确的看法:找"自身耗时"最大的,不是"总耗时"最大的。

GET /user/getUserInfo 442ms ← 总耗时 ├─ Redisson/GET 0ms ├─ Redisson/GET 0ms ├─ ShardingSphere/routeSQL 43ms ├─ Druid/Connection/getConnection 0ms ← 连接池健康 ├─ ShardingSphere/executeSQL 43ms ├─ MySQL/PreparedStatement/execute 42ms └─ Redisson/PSETEX 0ms

所有子 Span 加起来只有 128ms,剩下 314ms 是"空白" —— 这部分是应用自身逻辑(GC、序列化、锁等待、CPU 计算)。

空白大 ≠ 一定有问题,但它是排查的起点。

现象 → 结论对照表

这是我总结的最实用的一张表:

现象大概率原因下一步
DB Span 耗时高,且 SQL 相同慢 SQL / 缺索引看 Span 标签里的 SQL,EXPLAIN
同一 SQL 出现 N 次N+1 查询改成批量查询 / join
Druid/getConnection 耗时高连接池不够 / 有慢查询占着连接调大 maxActive,查慢 SQL
Redis Span 耗时高大 key / 热 key / 网络往返多看 key 大小,考虑 pipeline
两个 Span 之间有大片空白GC / 线程阻塞 / 锁竞争 / 序列化开 Profiling 看 CPU 火焰图
外部 HTTP Span 耗时高下游服务慢看下游自己的 Trace
Span 上有红色 Error 标记抛异常了点开看异常堆栈
同一个服务不同实例耗时差异大负载不均 / 某台机器有问题切到「实例」维度对比

关于「空白」的进一步判断

空白大时,按这个顺序排除:

  1. GC:加个 GC 日志或用 jstat -gc 看,Full GC 会造成明显停顿
  2. 锁竞争:线程 dump 看 BLOCKED
  3. 序列化:返回体特别大时,Jackson 序列化能吃掉几百毫秒
  4. 业务逻辑:真正的计算密集代码

六、七个高频功能详解

按日常使用频率排序。

1. 追踪(Trace)—— 用得最多

场景:某个接口慢、某个请求报错、想看清楚一次调用经过哪些组件。

关键操作:

  • 按耗时倒序找慢请求
  • 点进瀑布图看 Span 层级
  • 看 Span 的 标签(Tags):里面通常有 SQL 语句、Redis key、HTTP URL、状态码
  • 复制 Trace ID(页面右上角),可以拿去日志系统里搜

Trace ID 是打通"链路"和"日志"的钥匙。后面第八节讲怎么配。

2. 服务仪表板 —— 看趋势

场景:线上告警了、想确认发版后性能有没有退化、想对比昨天和今天。

看 P99 曲线的拐点,和发版时间对齐,能快速判断"是不是这次发布引入的"。

3. 拓扑图 —— 看依赖关系

场景:梳理系统调用关系、排查"到底是谁在调我"。

节点上的红色/黄色代表健康度。当你发现一个自己都不知道的服务在调你,这里能看出来。

4. 实例 —— 排查单机问题

场景:服务有 3 个实例,整体指标正常但偶发超时。

切到「实例」维度,对比各实例的响应时间。如果只有一台高,大概率是那台机器的问题(CPU、内存、网络、磁盘)。

5. 日志 —— 关联排查

场景:Trace 里看到报错,想看当时的业务日志。

前提是把日志接进来(用 log4j2/logback 的 SkyWalking appender,或用 traceId 关联 ELK)。没接入的话,这一项是空的。

6. 告警 —— 被动发现问题

场景:不想一直盯着页面,让系统自己报警。

在「告警规则」里配置,典型规则:

  • 服务 SLA < 95%,持续 3 分钟
  • 响应时间 P99 > 1000ms
  • 错误率 > 5%

SkyWalking 的告警是基于规则引擎的,可以配 webhook 推到钉钉/企业微信。

7. Trace 剖析 / Profiling —— 找 CPU 热点

场景:Trace 里看到大片空白,但不知道代码卡在哪。

可以对某个端点做线程级采样,生成火焰图。这是定位"没有 Span 覆盖的耗时"的终极手段。

注意:Profiling 对性能有影响,不要在生产高峰期对高频接口开。


七、真实案例拆解

拿一条真实 Trace 演示完整排查过程。

现场

GET /collection/collectionList 8144ms ← 明显异常 GET /user/getUserInfo 442ms cn.hollis.nft.turbo.api.user... 698ms

排查过程

第 1 步:先看 8.1 秒那条

点进去看瀑布图,重点找两样东西:

  • 有没有一个 Span 独占 8 秒(比如一条慢 SQL)
  • 还是由几百个小 Span 累加(比如循环查 Redis)

如果是几百个小 Span → 典型的 N+1 问题。在 Trace 详情里上下滚动,看同一个操作重复出现的次数。

如果是一个 MySQL Span 8 秒 → 点开标签看具体 SQL,去数据库 EXPLAIN。

第 2 步:再看 442ms 那条

这条很有意思,是个典型的"教科书案例":

Span 总和 ≈ 128ms,总耗时 442ms → 空白 314ms

子 Span 里最慢的是:

  • ShardingSphere/routeSQL 43ms —— SQL 解析路由,分库分表框架的固定开销
  • MySQL/PreparedStatement/execute 42ms —— 真正的数据库执行
  • Druid/Connection/getConnection 0ms —— 连接池健康,可以直接排除

所以这条链路里,数据库不是主要问题,314ms 的空白才是。往 GC / 序列化 / 业务逻辑方向查。

第 3 步:验证结论

  • 空白大 → 开 Profiling 看火焰图,或看 GC 日志
  • 如果确认是序列化 → 检查返回体大小、有没有循环引用
  • 如果确认是 GC → 看堆大小、对象创建速率

从案例里能学到的

  1. 总耗时最大的 Span 不一定是瓶颈,要看自身耗时
  2. getConnection 耗时为 0 是很强的信号:连接池没问题,别再往这个方向查
  3. 框架开销要心里有数:ShardingSphere 每条 SQL 有 ~40ms 解析开销,高频接口下会积少成多
  4. 空白区间不要忽略,它往往藏着真正的问题

八、进阶:让排查更省力

8.1 打通 Trace ID 和日志

这是投入产出比最高的一步。配置方式(logback 示例):

xml
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} [%X{tid}] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender>

%X{tid} 会从 SkyWalking 的上下文里取 Trace ID。

之后在日志里搜 Trace ID,就能把"链路"和"业务日志"对上。

8.2 采样率

默认采样率 100%,高并发下会拖慢应用、撑爆存储。在 agent.config 里:

properties
agent.sample_n_per_3_secs=100

意思是每 3 秒最多采样 100 条。

排查偶发问题时可以临时调到 100%,平时调低。 注意:采样率低的时候,不是每个请求都有 Trace,别因为搜不到就以为没上报。

8.3 忽略不需要的端点

健康检查、监控探针这类接口会产生大量垃圾数据:

properties
agent.ignore_suffix=.jpg,.jpeg,.js,.css,.png,.bmp,.gif,.ico,.mp3,.mp4,.svg

8.4 多环境隔离

用 namespace 区分环境,避免测试环境和生产环境的数据混在一起:

yaml
# OAP 侧 storage: elasticsearch: namespace: ${SW_NAMESPACE:"prod"}
properties
# Agent 侧 agent.namespace=prod

九、速查表

服务端

bash
systemctl status skywalking-oap systemctl restart skywalking-oap tail -f /opt/skywalking/apache-skywalking-apm-bin/logs/skywalking-oap-server.log docker logs -f skywalking-horizon # UI 日志 docker restart skywalking-horizon

验证

bash
ss -lntp | grep -E ':(11800|12800|17128|8080)' curl -s -X POST -H 'Content-Type: application/json' \ -d '{"query":"{version}"}' http://127.0.0.1:12800/graphql

Agent 侧

-javaagent:<path>/skywalking-agent.jar -Dskywalking.agent.service_name=<服务名> -Dskywalking.collector.backend_service=<OAP_IP>:11800
bash
tail -f skywalking-agent/logs/skywalking-api.log # agent 日志

关键路径

内容路径
OAP 配置config/application.yml
OAP 日志logs/skywalking-oap-server.log
UI 配置horizon.yaml
UI 登录用户horizon.env
Agent 配置skywalking-agent/config/agent.config
Agent 日志skywalking-agent/logs/skywalking-api.log

写在最后

SkyWalking 这类 APM 工具,装起来只是 20% 的工作量,剩下 80% 是"看到数据之后怎么用"。

很多团队装了之后,只在出故障时打开看一眼拓扑图,然后继续 grep 日志——这就浪费了它最大的价值:把"用户反馈慢"这种模糊描述,精确到某一层、某一条 SQL、某一毫秒。

我自己的使用习惯是:

  • 平时:每周看一次服务仪表板的 P99 趋势,发现缓慢劣化
  • 发版后:对比发版前后的响应时间曲线
  • 出问题时:三步法(仪表板 → 找慢请求 → 看瀑布图空白)
  • 优化后:用同一条 Trace 对比优化前后的 Span 分布

最后一句:别只盯着最慢的那个 Span。总耗时 442ms 的请求里,最慢的子 Span 只有 43ms,真正的问题藏在 314ms 的空白里——这个反直觉的点,是新手和老手最大的差别。


如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay

本文作者:章鱼哥

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!