ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SkyWalking环境搭建实战:从零部署到拓扑图生成

SkyWalking环境搭建实战:从零部署到拓扑图生成 1. 为什么是 SkyWalking不是 Zipkin也不是 JaegerSkyWalking 这个名字在分布式系统可观测性领域里已经不是新鲜词了。但真正把它从“听说过”变成“用得稳、看得懂、调得准”的人其实远比想象中少。我第一次在生产环境里部署 SkyWalking是在一个日均订单量 30 万 的电商履约系统里——当时团队刚把单体架构拆成 12 个微服务接口超时、链路断裂、慢 SQL 隐形埋伏排查一次线上抖动要拉三个人盯两小时 Grafana ELK 日志平台最后发现根源竟是一段被忽略的 Redis 连接池配置。那会儿我们试过 Zipkin也搭过 Jaeger但要么是 UI 太简陋查个跨服务异常得手动拼 traceId要么是存储层太重Elasticsearch 一升级就丢数据要么是 Java Agent 注入后 GC 频率翻倍业务 RT 直接涨 15%。直到把 SkyWalking 8.3.0 跑通在测试集群才真正体会到什么叫“开箱即用的观测力”。它不是单纯一个链路追踪工具而是一整套APM应用性能监控 服务网格可观测性 指标告警闭环的轻量级实现。核心优势很实在Java Agent 零代码侵入、UI 原生支持拓扑图/依赖分析/慢服务定位、后端存储可插拔H2 / Elasticsearch / MySQL / TiDB、告警规则支持表达式引擎、甚至能对接 Prometheus 指标。最关键的是——它对 JVM 的资源消耗控制得极好实测在 QPS 2000 的 Spring Boot 服务上Agent 内存占用稳定在 15MB 以内CPU 开销低于 3%远低于同类方案。这不是宣传口径是我拿压测脚本跑满 48 小时后截的 jstat 和 top 数据。所以当你看到“SkyWalking 环境搭建”这个标题别只当成装个软件它本质是在为整个微服务体系装上一套“数字听诊器”听得清心跳JVM 指标摸得到脉络服务拓扑查得出淤堵慢 SQL / HTTP 超时 / RPC 异常。适合谁不是只给运维看的而是给每个写接口、调远程、配中间件的开发同学准备的——你写的那段 Feign 调用到底卡在哪一层SkyWalking 会直接标红给你看。2. 整体架构设计与选型逻辑为什么必须分三块搭很多人以为 SkyWalking 就是个“下载解压启动”的工具点开 bin/startup.sh 就完事。结果一跑起来UI 打不开、Agent 不上报、Elasticsearch 报错 Connection refused然后开始疯狂搜“SkyWalking 启动失败”最后在 GitHub Issues 里翻三天没找到解法。问题出在哪根本没理解它的三层解耦架构OAPObservability Analysis Platform负责数据接收、分析、存储UI 是纯前端展示层Agent 是嵌入到业务进程里的探针。这三者不是“一个包打天下”而是各自独立部署、独立配置、独立扩缩容。我见过最典型的错误就是把 OAP 和 UI 打包进同一个 Docker 容器结果 UI 页面加载慢顺手把 OAP 的 JVM 参数也调低了导致指标聚合延迟飙升——这是把“医生”和“CT 机”塞进同一个诊室还让它们共用一台电脑。所以搭建的第一步永远是明确角色分工OAP Server核心大脑处理所有 Span 数据、计算 SLA、生成拓扑、触发告警。它需要稳定内存、可控 GC、可靠存储。我们线上用的是 4C8G 虚拟机 Elasticsearch 7.10注意SkyWalking 9.x 已弃用 ES 6.x但 8.x 兼容性仍有坑后面细说。UI Server静态资源服务器不处理任何业务逻辑只做数据请求代理。可以和 OAP 分离部署甚至用 Nginx 反向代理完全不用 Java 环境。Agent注入到业务 JVM 的字节码增强探针版本必须严格匹配 OAP 版本比如 OAP 是 9.4.0Agent 就不能用 9.3.0否则 span 丢失率超 30%。它不占业务线程但会增加类加载时间所以首次启动慢 2~3 秒是正常的。为什么强调“必须分三块”因为每一块的故障域完全不同OAP 挂了新链路数据停止采集但历史数据还在UI 挂了你看不到图表但告警照发Agent 挂了单个服务失联不影响其他服务。这种隔离性决定了排查路径必须清晰——看到 UI 空白先 curl http://oap-ip:12800/graphql 看是否返回 schema看到拓扑图没节点先检查业务日志里有没有 “SkyWalking agent start success”看到慢服务没标记先确认 Agent 的 config/agent.config 里plugin.spring-cloud-gateway是否开启Spring Cloud Gateway 场景下必须显式启用。这不是教条是踩过三次 OAP 内存溢出、两次 UI 跨域失败、五次 Agent 版本错配后的肌肉记忆。3. 核心细节解析与实操要点从 JDK 到存储的硬核校验清单搭建 SkyWalking 最容易栽跟头的地方从来不是命令敲错而是那些藏在文档角落、没人明说、但决定成败的“隐性前提”。我列一份真实环境验证过的校验清单每一条都对应一个曾让我重启三次集群的坑3.1 JDK 版本不是“支持”是“强绑定”SkyWalking OAP 官方说支持 JDK 8但实际生产中JDK 11 是唯一推荐版本。原因很现实JDK 8 的 G1 GC 在高吞吐场景下容易触发 Concurrent Mode FailureOAP 的 Metrics Collector 线程会卡住JDK 17 的 JFRJava Flight Recorder默认开启和 SkyWalking 的字节码增强有冲突导致部分 Span 丢失。我们实测过 JDK 8u292、JDK 11.0.15、JDK 17.0.2 三个版本在相同压力下JDK 11 的 OAP Full GC 频率比 JDK 8 低 67%平均响应延迟稳定在 8msJDK 8 是 22ms。所以别信“兼容”直接执行java -version # 必须输出类似 # openjdk version 11.0.15 2022-04-19 # OpenJDK Runtime Environment Temurin-11.0.1510 (build 11.0.1510) # OpenJDK 64-Bit Server VM Temurin-11.0.1510 (build 11.0.1510, mixed mode)提示如果公司强制用 JDK 8务必关闭 G1 GC改用-XX:UseParallelGC并在config/application.yml中将core.default下的bufferSize从 10000 降到 5000否则内存溢出概率极高。3.2 存储选型ES 不是唯一解MySQL 更适合中小团队官方文档大力推 Elasticsearch但真实场景里MySQL 5.7 是更稳妥的选择尤其对没有专职 ES 运维的团队。ES 的坑太具体SkyWalking 8.x 要求 ES 7.x但 7.10 之后的版本默认禁用script.max_compilations_rateOAP 启动时会报painless script too largeES 的_doc类型映射在 SkyWalking 9.x 里被废弃但旧索引不自动迁移导致新老数据混杂最致命的是ES 的refresh_interval如果设为 1s为了实时性磁盘 IO 会暴涨OAP 的storage.elasticsearch配置里clusterNodes必须写成http://es-host:9200漏掉http://就连不上——这种错误连日志都不报只在 UI 里显示“no data”。而 MySQL 方案只需确保字符集为utf8mb4避免 emoji 或特殊符号入库失败max_allowed_packet≥ 64MOAP 的 segment 表单条记录可能超 10MB创建数据库时指定COLLATEutf8mb4_unicode_ciconfig/application.yml中storage.mysql的url必须带useSSLfalseserverTimezoneUTC我们线上 MySQL 集群是 3 节点 MGROAP 并发写入 5000 TPS 下CPU 稳定在 45%远低于 ES 的 78%。而且备份恢复、权限管理、慢查询分析全是 DBA 熟悉的流程不用额外学 ES DSL。3.3 Agent 注入不是加个 -javaagent 就完事Agent 的agent.jar必须和 OAP 版本严格一致这点再强调也不为过。但更隐蔽的坑在于JVM 参数顺序。很多同学把-javaagent:/path/to/skywalking-agent.jar放在-jar app.jar后面结果 JVM 直接报错Unrecognized option: -javaagent。正确顺序是java -javaagent:/opt/skywalking/agent/skywalking-agent.jar \ -Dskywalking.agent.service_nameorder-service \ -Dskywalking.collector.backend_service10.10.1.100:11800 \ -jar order-service.jar注意三个关键点-javaagent必须在-jar之前skywalking.collector.backend_service的 IP 必须是 OAP 的gRPC 端口11800不是 HTTP 端口12800service_name不能含下划线或大写字母SkyWalking 内部用作 ES 索引名前缀非法字符会导致索引创建失败。实操心得Agent 启动后第一件事不是打开 UI而是看业务日志里有没有这行INFO org.apache.skywalking.apm.agent.SkyWalkingAgent - SkyWalking agent starting...INFO org.apache.skywalking.apm.agent.SkyWalkingAgent - SkyWalking agent boot success.如果只有第一行第二行缺失99% 是backend_service地址不通或端口错误。4. 实操过程与核心环节实现从零到拓扑图的完整流水线现在进入真正的“抄作业”环节。以下步骤基于 Ubuntu 20.04 JDK 11 MySQL 5.7 SkyWalking 9.4.0全程无跳步、无省略所有命令和配置都来自我们生产集群的部署脚本。4.1 OAP Server 部署配置文件逐行解读第一步下载并解压wget https://archive.apache.org/dist/skywalking/9.4.0/apache-skywalking-apm-9.4.0.tar.gz tar -xzf apache-skywalking-apm-9.4.0.tar.gz cd apache-skywalking-apm-bin关键在config/application.yml。不要直接改先备份cp config/application.yml config/application.yml.bak然后重点修改三处其他保持默认① 存储配置MySQLstorage: selector: ${SW_STORAGE:mysql} mysql: jdbc_url: ${SW_JDBC_URL:jdbc:mysql://10.10.1.100:3306/sw?useSSLfalseserverTimezoneUTC} user: ${SW_MYSQL_USER:sw_user} password: ${SW_MYSQL_PASSWORD:sw_pass} metadata_query_timeout: ${SW_STORAGE_MYSQL_QUERY_TIMEOUT:30}注意jdbc_url中的sw是数据库名必须提前创建好user和password对应 MySQL 账号权限该账号需有sw库的SELECT,INSERT,UPDATE,DELETE,CREATE,DROP,ALTER,INDEX全部权限。② gRPC 服务监听Agent 上报入口receiver-grpc: selector: ${SW_RECEIVER_GRPC_SELECTOR:default} default: host: ${SW_RECEIVER_GRPC_HOST:0.0.0.0} port: ${SW_RECEIVER_GRPC_PORT:11800} # 关键必须设为 0.0.0.0否则 Agent 无法跨主机连接③ UI 代理配置让 UI 能访问 OAPui: selector: ${SW_UI_SELECTOR:default} default: url: http://10.10.1.100:12800 # 这里填 OAP 的 HTTP 地址UI 会通过此地址拉取数据保存后启动 OAPbin/startup.sh # 查看日志 tail -f logs/skywalking-oap-server.log等待出现OAP started. Listening for gRPC on 0.0.0.0:11800和Started SkyWalkingOAPServerApplication即成功。4.2 UI Server 部署Nginx 代理的最小化配置UI 默认在webapp/目录但直接运行bin/startup.sh会启动一个内置 Jetty不稳定且难管理。强烈建议用 Nginxsudo apt install nginx -y sudo cp -r webapp/* /var/www/html/编辑/etc/nginx/sites-available/skywalking-uiserver { listen 80; server_name skywalking.example.com; location / { root /var/www/html; try_files $uri $uri/ /index.html; } # 关键反向代理到 OAP 的 GraphQL 接口 location /graphql { proxy_pass http://10.10.1.100:12800/graphql; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } # 静态资源缓存 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { expires 1y; add_header Cache-Control public, immutable; } }启用并重启sudo ln -sf /etc/nginx/sites-available/skywalking-ui /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl restart nginx此时访问http://your-server-ip应该能看到 SkyWalking 登录页。4.3 Agent 注入实战Spring Boot 服务的完整接入以一个标准的 Spring Boot 2.7.x 服务为例。假设 jar 包名为user-service.jarAgent 路径为/opt/skywalking/agent。① 创建启动脚本start.sh#!/bin/bash JAVA_HOME/usr/lib/jvm/java-11-openjdk-amd64 export JAVA_HOME # JVM 参数堆内存设为 2GGC 日志开启 JAVA_OPTS-Xms2g -Xmx2g -XX:UseG1GC -XX:MaxGCPauseMillis200 JAVA_OPTS$JAVA_OPTS -XX:PrintGCDetails -XX:PrintGCDateStamps -Xloggc:logs/gc.log # Agent 参数服务名、OAP 地址、采样率生产环境建议 1.0即全采样 AGENT_OPTS-javaagent:/opt/skywalking/agent/skywalking-agent.jar AGENT_OPTS$AGENT_OPTS -Dskywalking.agent.service_nameuser-service AGENT_OPTS$AGENT_OPTS -Dskywalking.collector.backend_service10.10.1.100:11800 AGENT_OPTS$AGENT_OPTS -Dskywalking.agent.sample_n_per_3_secs1000 # 启动 nohup $JAVA_HOME/bin/java $JAVA_OPTS $AGENT_OPTS -jar user-service.jar logs/app.log 21 echo $! logs/pid② 关键参数说明sample_n_per_3_secs1000每 3 秒采样 1000 条 trace避免高流量下 OAP 过载。如果 QPS 100可设为1.0全采样service_name必须小写、无符号多个单词用短横线连接如payment-gatewaybackend_service的 IP 必须能被业务服务器 ping 通端口 11800 必须开放ufw allow 11800。③ 验证 Agent 是否生效启动后立刻执行curl -X POST http://10.10.1.100:12800/graphql \ -H Content-Type: application/json \ -d {query:{ getGlobalTraceIds(timeRange: { startTime: \2023-01-01 00:00:00\, endTime: \2023-01-01 00:00:01\ }) }}如果返回非空数组说明 Agent 已上报数据。4.4 拓扑图生成第一个服务调用的完整链路现在模拟一次真实调用。假设user-service通过 Feign 调用order-service两者都已注入 Agent。在user-service中发起一次 HTTP 请求curl -X GET http://localhost:8080/user/1001等待 10 秒OAP 默认 10 秒聚合周期刷新 UI →Topology页面。你应该看到两个节点user-service和order-service中间有一条连线标注HTTP和FeignClient。点击连线右侧弹出详情avg response time: 124ms,success rate: 100%,calls per minute: 3.2。点击user-service节点选择Traces标签页筛选Service Instance为user-service时间范围设为最近 5 分钟。列表里会出现一条 trace点击进入看到完整的调用栈GET /user/1001入口FeignClient.orderService.getOrder(1001)远程调用Redis.get(user:1001)本地缓存MySQL.select from users where id 1001DB 查询每一层都有耗时、状态码、SQL 语句如果开启了 MyBatis 插件。这就是 SkyWalking 的价值不用改一行业务代码就能把一次请求的“生命旅程”完整还原。5. 常见问题与排查技巧实录那些文档不会写的真相以下是我在 12 个不同项目中遇到的 Top 5 问题附带真实日志、定位方法和根治方案。这些不是理论是凌晨三点盯着屏幕反复验证的结果。5.1 问题UI 显示 “No data in the selected time range”但 OAP 日志无报错现象UI 拓扑图空白Traces 列表为空Network 面板显示Failed to fetch但skywalking-oap-server.log里只有 INFO 级日志没有 ERROR。排查路径检查 OAP 的 gRPC 端口是否监听netstat -tuln | grep 11800 # 正常应输出tcp6 0 0 :::11800 :::* LISTEN从 Agent 所在服务器 telnet 测试telnet 10.10.1.100 11800 # 如果连接超时说明防火墙或安全组未放行查看 Agent 日志业务服务的 stdoutgrep reporter logs/app.log # 如果出现 GRPCChannelManager connect to 10.10.1.100:11800 failed就是网络问题根治方案在云服务器上除了ufw allow 11800还要检查云厂商安全组规则入方向规则必须包含 TCP:11800。很多团队只开了 12800HTTP忘了 11800gRPC。5.2 问题拓扑图有节点但连线断开显示 “Unknown” 服务现象user-service和order-service都出现在拓扑图但没有连线order-service节点下方标注Unknown。原因Agent 的service_name配置不一致。比如user-service的-Dskywalking.agent.service_nameuser-service而order-service写成了-Dskywalking.agent.service_nameOrderService首字母大写。验证方法在 MySQL 中查service表SELECT service_code, name FROM service WHERE name LIKE %order%; -- 如果返回 OrderService 和 order-service 两条说明命名不统一根治方案所有服务的service_name必须严格小写、短横线分隔。用 CI/CD 脚本强制校验if [[ $SERVICE_NAME ! ${SERVICE_NAME,,} ]] || [[ $SERVICE_NAME *_* ]]; then echo ERROR: service_name must be lowercase and use - not _ 2 exit 1 fi5.3 问题慢 SQL 没被识别只显示 “JDBC PreparedStatement.execute” 而非真实 SQL现象Traces 里看到JDBC PreparedStatement.execute耗时 800ms但点开详情看不到SELECT * FROM orders WHERE user_id ?这样的语句。原因Agent 默认不开启 MyBatis 插件且 JDBC URL 缺少profileSQLtrue参数。解决方案在agent/config/agent.config中启用插件plugin.mysql.jdbc.enabletrue plugin.mybatis.enabletrue在业务的 JDBC URL 里加参数jdbc:mysql://db-host:3306/mydb?useSSLfalseserverTimezoneUTCprofileSQLtrue重启服务。注意profileSQLtrue会略微增加 JDBC 开销生产环境建议仅在问题排查期开启。5.4 问题OAP 启动后内存持续增长3 小时后 OOM现象top显示java进程 RES 内存从 1.2G 涨到 7.8Gjstat -gc pid显示OUOld Gen Used持续上升Full GC 频繁。根因config/application.yml中core.default的bufferSize过大且downsampling未启用。SkyWalking 默认保留原始数据 7 天高流量下内存吃紧。修复配置core: default: downsampling: - Hour - Day bufferSize: 2000 # 从默认 10000 降到 2000 # 关键启用降采样Hour 级别保留 1 小时内所有数据Day 级别只存每日统计重启 OAP 后内存稳定在 2.1GFull GC 从每 15 分钟一次降到每天 2 次。5.5 问题告警规则配置后邮件不发送日志无报错现象在config/alarm-settings.yml里写了 CPU 使用率 80% 发邮件但阈值触发后logs/alarm.log为空。真相SkyWalking 9.x 的告警模块默认使用Webhook不是邮件。邮件发送需要额外集成SMTP且配置分散在两处。正确配置在config/application.yml中启用 SMTPalarm: selector: ${SW_ALARM_SELECTOR:webhook} webhook: # 保持默认 smtp: host: smtp.exmail.qq.com port: 465 username: alertcompany.com password: your-app-password # 注意不是邮箱密码是 QQ 邮箱的 SMTP 授权码 from: alertcompany.com to: ops-teamcompany.com在config/alarm-settings.yml中指定 SMTP 规则rules: - rule-name: service-percentage-rule expression: service_percent_success 80 message: Service {name} success rate less than 80% tags: {level: high} # 关键添加 notifyList notifyList: [smtp]实操心得QQ 邮箱的 SMTP 授权码必须在邮箱设置里单独生成不能用登录密码Outlook 邮箱需用smtp-mail.outlook.com:587且password是应用密码。6. 后续可扩展的方向从监控到 SRE 的能力跃迁搭好 SkyWalking 只是起点真正的价值在于把它变成 SRE站点可靠性工程工作流的一环。我们团队后续做了三件事让这套环境从“能看”升级为“能管”第一和 CI/CD 深度集成。每次 Jenkins 构建成功后自动调用 SkyWalking API获取该版本服务的 baseline 指标P95 响应时间、错误率、JVM GC 时间存入 InfluxDB。上线后 10 分钟如果新版本 P95 比 baseline 高 20%自动回滚并钉钉告警。第二构建故障知识库。把每次通过 SkyWalking 定位的典型问题如 Redis 连接池耗尽、MySQL 死锁、线程池满整理成 Markdown 模板嵌入到 UI 的“诊断建议”面板。运维同学点开慢服务右边直接弹出《Redis 连接池配置检查清单》和《一键检测脚本》。第三打通变更管理系统。当运维在 CMDB 里提交“升级 Nacos 配置中心”工单时SkyWalking 自动开启该服务的“变更观察模式”未来 2 小时内所有 trace 自动打标change_idCMDB-2023-001方便事后归因。这些不是 SkyWalking 内置功能但它的开放 APIGraphQL REST和插件机制让这一切成为可能。所以别再只把它当作一个“装完就完”的工具它是你构建可观测性文化的基石。我最后想说的是环境搭建的终点从来不是 UI 上出现那个绿色的拓扑图而是当你看到一个报警不用翻日志、不用问同事、不用开会议直接点开 SkyWalking30 秒内定位到是哪个服务、哪行代码、哪个配置出了问题——那一刻你才真正拥有了对系统的掌控力。
返回列表