SkyWalking链路追踪从零搭建:架构、部署与生产调优实战 1. 为什么我们最终选了SkyWalking这套链路追踪方案第一次接触SkyWalking是在一个微服务拆分到二十多个模块的项目里那会儿排查一个跨服务调用超时的问题基本靠人肉串日志一个请求经过网关、用户服务、订单服务、库存服务每个服务日志格式还不一样时间戳对不齐查一次问题少说半小时起步。后来团队里有人提议上链路追踪对比了几套方案之后最终落地了SkyWalking。这篇文章就把我当时从零搭建SkyWalking的完整过程、踩过的坑、以及后续调优的经验原原本本梳理一遍。SkyWalking本质上是一套分布式系统的应用性能监控APM与链路追踪平台它最核心的能力是把一次用户请求在多个服务之间的完整调用链路串起来让你能直观看到每个环节的耗时、状态、异常。它支持Java、Go、Python、Node.js、.NET等多种语言的探针接入对业务代码基本零侵入这一点是它相比很多同类方案最大的优势。适合谁看如果你是后端开发、运维、SRE或者正在做微服务架构落地需要一套能快速定位性能瓶颈的工具那这篇内容基本可以照着抄。我搭建的环境是Linux服务器CentOS 7SkyWalking版本用的是8.x系列后端存储选的是Elasticsearch 7.x。为什么不用H2或者MySQLH2是内存数据库重启数据就没了只适合本地跑DemoMySQL在数据量大了之后查询性能会明显下降而Elasticsearch天生适合时序数据的存储和聚合查询这是生产环境的标配选择。下面我会把整个搭建过程拆成几个部分从架构理解到环境准备再到具体部署和验证最后是常见问题排查。2. SkyWalking整体架构与核心概念拆解2.1 四大核心组件到底各干什么很多人第一次看SkyWalking的架构图会觉得组件有点多其实拆开看就四个东西各司其职。探针Agent挂在你的业务应用上负责采集调用数据。Java应用就是在启动参数里加一个-javaagent指向探针jar包不需要改一行业务代码。探针会把采集到的数据通过gRPC协议上报给后端。后端OAP Server全称Observability Analysis Platform是整个系统的核心。它接收探针上报的数据做流式分析、聚合计算然后把结果写入存储。OAP Server可以集群部署多个节点之间通过协调机制保证数据不重复处理。存储Storage负责持久化数据。支持Elasticsearch、H2、MySQL、TiDB等多种后端。生产环境强烈建议用Elasticsearch原因前面说过了。界面UI就是那个Web控制台默认端口8080用来展示拓扑图、链路详情、指标仪表盘等。UI本身不存数据它通过GraphQL查询OAP Server拿数据。提示探针和后端之间是gRPC通信默认端口11800后端和UI之间是HTTP通信默认端口12800。这两个端口别搞混防火墙规则要分别放行。2.2 数据是怎么流转的理解数据流转对后面排查问题特别重要。一次完整的链路数据流转是这样的用户发起请求请求经过服务A服务A的探针生成一个TraceContext包含TraceId和SpanId当服务A调用服务B时探针会把TraceContext通过请求头传递过去服务B的探针接收到之后继续在这个TraceId下生成新的Span。每个Span记录了操作的开始时间、结束时间、状态、标签等信息。这些Span数据被探针批量上报到OAP ServerOAP Server按照不同的维度做聚合比如按服务维度算平均响应时间、按端点维度算P99耗时、按服务间调用关系构建拓扑图。聚合后的数据写入ElasticsearchUI查询时直接从ES读取聚合结果所以UI的响应速度取决于ES的查询性能。这里有个关键点SkyWalking默认会对数据进行采样不是每一个请求都会完整记录链路。采样率通过探针配置里的agent.sample_n_per_3_secs参数控制默认是-1表示全量采集。生产环境如果流量很大建议设置一个合理的采样率否则存储压力会很大。2.3 为什么选Elasticsearch做存储这个问题值得单独说一下。SkyWalking的存储选型直接决定了整套系统的稳定性和查询性能。我整理了一个对比表格是我当时选型时的实际考量存储类型适用场景优点缺点H2本地Demo、功能验证零配置开箱即用内存存储重启丢数据不支持集群MySQL小规模测试运维熟悉部署简单大数据量下查询慢聚合能力弱Elasticsearch生产环境分布式、高可用、聚合查询快需要额外维护ES集群资源占用较高TiDB大规模生产水平扩展能力强运维复杂度高社区案例相对少我当时选ES的理由很直接链路数据本质上是时序数据写入量大、查询以聚合为主ES在这方面的能力是经过大量生产验证的。而且SkyWalking对ES的版本兼容性做得不错7.x系列基本都能用。3. 搭建前的环境准备与规划3.1 服务器资源怎么估算这一步很多人容易拍脑袋我当时的经验是按数据量倒推。假设你的系统每天有100万次请求每次请求平均产生5个Span每个Span大约1KB数据那一天的数据量大概是5GB。ES默认会保留一份副本所以实际存储需求翻倍就是10GB/天。如果你要保留30天的数据那就是300GB。这还没算索引开销和ES自身的元数据。基于这个估算我当时的配置是OAP Server单独一台机器4核8GES集群三台机器每台8核16GSSD硬盘500G。UI可以和OAP Server部署在一起因为它本身不怎么吃资源。当然这是中等规模的配置如果你的请求量更大ES集群的节点数和配置都要相应往上加。注意ES对内存特别敏感JVM堆内存建议设置为物理内存的一半但不要超过31GB超过之后JVM会关闭指针压缩反而浪费内存。剩下的内存留给操作系统的文件缓存这对ES的查询性能至关重要。3.2 软件版本兼容性确认SkyWalking的版本和ES版本之间有兼容性要求不是随便哪个版本都能搭配。我整理了一下常见的对应关系SkyWalking版本推荐ES版本JDK要求8.9.x7.10 - 7.17JDK 8/119.x7.17 - 8.xJDK 11/1710.x8.xJDK 17我当时用的是SkyWalking 8.9.1配ES 7.17.0JDK用的11。这里要特别提醒OAP Server和探针的JDK要求可能不一样。OAP Server从9.x开始要求JDK 11以上但探针支持JDK 8的应用这个不冲突因为探针是跑在业务应用进程里的用的是业务应用的JDK。3.3 端口规划与防火墙配置SkyWalking涉及的端口比较多提前规划好能省很多事。默认端口清单如下11800探针gRPC上报端口12800UI查询OAP的HTTP端口8080UI Web访问端口9200ES HTTP端口9300ES集群内部通信端口如果服务器开了防火墙这些端口都要放行。我当时的做法是只对内部网段开放11800和92008080和12800通过反向代理对外暴露这样安全性更好。# 查看端口是否被占用 netstat -tlnp | grep -E 11800|12800|8080|9200 # 开放防火墙端口以firewalld为例 firewall-cmd --zonepublic --add-port11800/tcp --permanent firewall-cmd --zonepublic --add-port12800/tcp --permanent firewall-cmd --zonepublic --add-port8080/tcp --permanent firewall-cmd --reload4. 分步实操从零把SkyWalking跑起来4.1 Elasticsearch的安装与关键配置ES是整套系统的地基地基不稳后面全是问题。我当时的安装步骤是这样的# 下载ES 7.17.0 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 -C /opt/ # 创建专用用户ES不允许root用户启动 useradd esuser chown -R esuser:esuser /opt/elasticsearch-7.17.0/接下来是配置文件config/elasticsearch.yml的关键修改cluster.name: skywalking-es node.name: node-1 path.data: /data/es/data path.logs: /data/es/logs network.host: 0.0.0.0 http.port: 9200 discovery.seed_hosts: [10.0.0.1, 10.0.0.2, 10.0.0.3] cluster.initial_master_nodes: [node-1, node-2, node-3]JVM配置在config/jvm.options里我设置的是-Xms8g -Xmx8g为什么是8G因为机器是16G内存按一半给堆剩下一半留给文件缓存。这个比例在实际使用中表现最稳。注意ES启动前一定要检查vm.max_map_count参数默认值太小会导致ES启动失败。执行sysctl -w vm.max_map_count262144并写入/etc/sysctl.conf永久生效。这个坑我踩过当时ES日志里报的是max virtual memory areas vm.max_map_count [65530] is too low找了半天才发现是这个参数的问题。4.2 SkyWalking OAP Server的部署OAP Server的部署相对简单下载解压之后主要改一个配置文件。# 下载SkyWalking 8.9.1 wget https://archive.apache.org/dist/skywalking/8.9.1/apache-skywalking-apm-8.9.1.tar.gz # 解压 tar -zxvf apache-skywalking-apm-8.9.1.tar.gz -C /opt/ cd /opt/apache-skywalking-apm-bin/核心配置文件是config/application.yml需要修改存储部分storage: selector: ${SW_STORAGE:elasticsearch} elasticsearch: nameSpace: ${SW_NAMESPACE:} clusterNodes: ${SW_STORAGE_ES_CLUSTER_NODES:10.0.0.1:9200,10.0.0.2:9200,10.0.0.3:9200} protocol: ${SW_STORAGE_ES_HTTP_PROTOCOL:http} user: ${SW_ES_USER:} password: ${SW_ES_PASSWORD:} indexShardsNumber: ${SW_STORAGE_ES_INDEX_SHARDS_NUMBER:2} indexReplicasNumber: ${SW_STORAGE_ES_INDEX_REPLICAS_NUMBER:1} bulkActions: ${SW_STORAGE_ES_BULK_ACTIONS:2000} flushInterval: ${SW_STORAGE_ES_FLUSH_INTERVAL:10} concurrentRequests: ${SW_STORAGE_ES_CONCURRENT_REQUESTS:2}这里有几个参数值得解释一下。indexShardsNumber是索引分片数默认是1我改成了2因为分片数一旦设定就不能改除非重建索引所以要根据数据量提前规划。bulkActions是批量写入的条数2000是一个比较均衡的值太小会导致写入频繁太大则内存占用高。flushInterval是刷新间隔单位秒10秒意味着数据最多延迟10秒可见。启动OAP Server# 启动 bin/oapService.sh start # 查看日志确认启动成功 tail -f logs/oap.log日志里看到OAP starts up successfully就说明起来了。如果启动失败大概率是ES连接问题先检查ES是否正常、网络是否通、认证信息是否正确。4.3 UI界面的启动与访问UI的配置在webapp/webapp.yml里主要改OAP的地址server: port: 8080 spring: cloud: gateway: routes: - id: oap-route uri: lb://oap-service predicates: - Path/graphql/**如果OAP和UI在同一台机器默认配置基本不用改。启动UIbin/webappService.sh start浏览器访问http://服务器IP:8080能看到SkyWalking的界面就说明搭建成功了。默认没有登录认证生产环境建议在前面加一层Nginx做Basic Auth或者接入统一的认证网关。4.4 业务应用接入探针的完整步骤后端跑起来了接下来就是让业务应用把数据上报上来。以Java应用为例步骤是这样的第一步把探针目录拷贝到业务服务器上。探针在SkyWalking包的agent/目录下整个目录拷过去就行。第二步修改探针配置agent/config/agent.config# 服务名这个很重要UI上就是按这个名来区分服务的 agent.service_name${SW_AGENT_NAME:user-service} # OAP Server地址 collector.backend_service${SW_AGENT_COLLECTOR_BACKEND_SERVICES:10.0.0.1:11800} # 采样率-1表示全量采集 agent.sample_n_per_3_secs${SW_AGENT_SAMPLE:-1} # 忽略特定后缀的请求 agent.ignore_suffix${SW_AGENT_IGNORE_SUFFIX:.jpg,.jpeg,.png,.css,.js}第三步在应用启动参数里加上javaagentjava -javaagent:/opt/skywalking-agent/skywalking-agent.jar \ -Dskywalking.agent.service_nameuser-service \ -Dskywalking.collector.backend_service10.0.0.1:11800 \ -jar user-service.jar这里有个小技巧service_name和backend_service既可以在配置文件里写也可以通过-D参数传-D参数的优先级更高。我习惯用-D参数因为这样不同环境可以用同一份探针配置启动脚本里区分就行。提示探针的service_name命名要有规范建议用应用名-模块名的格式比如order-service-api、order-service-job。如果命名太随意UI上的拓扑图会乱成一团后期维护很痛苦。5. 搭建后的验证与核心功能体验5.1 怎么确认数据真的上报成功了应用启动之后访问几个接口产生一些流量然后打开UI界面。如果搭建正确你会在几个地方看到数据拓扑图Topology能看到服务之间的调用关系节点之间的连线代表调用连线上会显示平均响应时间。如果拓扑图是空的说明探针没有上报数据先检查探针日志agent/logs/skywalking-api.log里有没有报错。追踪Trace能看到具体的调用链路每个Span的耗时、状态、标签都列得很清楚。我一般会按耗时排序快速找到最慢的那个Span。仪表盘Dashboard有服务、端点、实例等多个维度的指标包括响应时间、吞吐量、成功率等。这些指标是OAP聚合计算出来的不是原始数据。5.2 链路追踪界面到底怎么看链路追踪界面是平时用得最多的。一次请求的链路会以树形结构展示根Span是入口子Span是下游调用。每个Span上会显示几个关键信息Duration这个Span的耗时单位是毫秒Status成功还是失败失败会标红Span Type是本地方法调用还是远程调用Tags附加信息比如HTTP状态码、数据库语句等我排查问题时有个习惯先看根Span的总耗时然后找耗时占比最大的子Span一层层往下钻基本能定位到瓶颈在哪个服务、哪个方法、甚至哪条SQL。这个效率比翻日志高太多了。5.3 告警功能的基本配置SkyWalking内置了告警功能可以基于指标设置规则。配置文件在config/alarm-settings.yml我配置了几条常用的规则rules: service_resp_time_rule: metrics-name: service_resp_time op: threshold: 1000 period: 10 count: 3 silence-period: 5 message: 服务 {name} 响应时间超过1秒连续3次 service_sla_rule: metrics-name: service_sla op: threshold: 9500 period: 10 count: 2 silence-period: 5 message: 服务 {name} 成功率低于95%这个配置的意思是服务响应时间连续3次每次统计周期10秒超过1秒就告警告警后静默5分钟避免重复告警。告警可以通过Webhook推送到钉钉、企业微信等平台配置在alarm-settings.yml的webhooks部分。6. 常见问题排查与避坑经验实录6.1 探针不上报数据的排查思路这是搭建过程中最高频的问题。我的排查顺序是这样的第一步看探针日志。探针的日志在agent/logs/目录下重点看skywalking-api.log。如果里面有connect refused之类的错误说明探针连不上OAP Server检查网络和端口。第二步确认backend_service配置是否正确。这个地址是OAP Server的gRPC端口默认11800不是UI的8080也不是HTTP的12800。我见过有人填错端口排查了半天的。第三步确认服务名是否配置。如果service_name没配或者配成了默认值数据可能上报了但UI上找不到。第四步检查OAP Server日志。如果探针显示发送成功但UI没数据问题可能在OAP这边看oap.log里有没有写入ES失败的报错。6.2 ES索引膨胀导致磁盘打满这个问题在生产环境很常见。SkyWalking会在ES里创建大量索引按天分索引如果没设置生命周期管理索引会一直堆积。我当时的解决方案是配置ES的ILMIndex Lifecycle Management策略或者用SkyWalking自带的TTL配置。在application.yml里可以设置数据保留天数storage: elasticsearch: recordDataTTL: ${SW_STORAGE_ES_RECORD_DATA_TTL:3} metricsDataTTL: ${SW_STORAGE_ES_METRICS_DATA_TTL:7}recordDataTTL是链路明细数据的保留天数metricsDataTTL是指标数据的保留天数。明细数据量大但查询频率低保留3天就够了指标数据量小但需要看趋势保留7天。这个要根据实际磁盘容量来调。6.3 OAP Server启动慢或频繁GCOAP Server默认的JVM堆是2G如果数据量大或者ES响应慢很容易出现GC频繁甚至OOM。调整方式是在bin/oapService.sh里修改JAVA_OPTSJAVA_OPTS -Xms4g -Xmx4g -XX:UseG1GC -XX:MaxGCPauseMillis200 用G1垃圾回收器并且设置最大GC停顿时间200毫秒这对在线服务的响应时间影响最小。堆大小根据实际负载调整一般4G到8G之间。6.4 常见问题速查表问题现象可能原因排查方向解决方案UI拓扑图空白探针未上报查探针日志、网络连通性检查backend_service配置和端口链路数据不完整采样率设置过低查agent.sample配置调高采样率或设为-1OAP启动报ES连接失败ES地址或认证错误查oap.log核对clusterNodes和账号密码查询界面很慢ES查询性能差查ES集群状态和索引分片增加ES节点、优化分片数磁盘快速增长数据保留策略未配置查ES索引列表配置TTL或ILM策略探针导致应用启动变慢探针初始化阻塞查应用启动日志检查探针版本兼容性6.5 几个我踩过的坑坑一探针版本和OAP版本不匹配。有一次升级了OAP Server但没升级探针结果数据上报格式对不上UI上数据错乱。后来我养成了习惯升级时探针和后端一起升版本号保持一致。坑二ES的indexReplicasNumber设成了0。当时为了省磁盘把副本数设成0结果ES节点挂了一个之后数据直接不可用。生产环境副本数至少是1这是底线。坑三忽略了时钟同步。链路追踪依赖时间戳来串联Span如果服务器之间时钟不同步链路会错乱甚至断裂。所有节点都要配NTP同步这个在分布式系统里是基本要求。坑四探针的ignore_suffix没配。静态资源请求也被采集了导致大量无意义的Span既浪费存储又干扰排查。把图片、CSS、JS这些后缀加到忽略列表里数据量能降不少。7. 生产环境的一些调优建议7.1 采样策略怎么定全量采集只适合流量小的场景。流量大了之后采样是必须的。SkyWalking支持两种采样方式一种是固定采样率通过agent.sample_n_per_3_secs设置每3秒采集的请求数另一种是自定义采样通过实现SamplingService接口来做更精细的控制。我的经验是核心链路比如下单、支付全量采集非核心链路比如查询、列表按10%采样。这样既保证了关键业务的可观测性又控制了整体数据量。自定义采样需要写一点代码但收益很明显。7.2 OAP Server集群部署要点单节点OAP Server存在单点故障风险生产环境建议至少两个节点。集群部署时多个OAP节点之间需要通过协调机制来避免重复处理数据。SkyWalking支持多种协调方式默认是Standalone单机模式集群模式需要配置为Kubernetes或者Zookeeper。cluster: selector: ${SW_CLUSTER:zookeeper} zookeeper: hostPort: ${SW_CLUSTER_ZK_HOST_PORT:10.0.0.1:2181} sessionTimeout: 100000集群部署之后探针可以配置多个OAP地址用逗号分隔探针会自动做负载均衡和故障转移。7.3 数据备份与恢复ES里的数据是链路追踪的全部家当备份很重要。ES提供了Snapshot API可以把索引快照到对象存储或者共享文件系统。我当时的做法是每天凌晨做一次增量快照保留最近7天的快照。恢复的时候通过Snapshot API恢复指定索引。这个操作虽然不复杂但一定要提前演练。我见过有团队配了快照但从来没验证过恢复流程真出问题的时候发现快照是坏的那就尴尬了。8. 写在最后的一点个人体会SkyWalking这套东西搭建本身其实不难官方文档写得也算清楚真正花时间的是调优和排错。我最大的体会是监控系统本身也需要被监控。OAP Server的JVM状态、ES集群的健康度、探针的上报成功率这些指标都要纳入监控否则监控系统自己挂了你还不知道。另外一点链路追踪的价值不在于搭建而在于团队真的用它来排查问题。我见过不少团队搭了SkyWalking但没人看出了问题还是翻日志。工具再好不用就是摆设。建议搭建完成之后组织一次团队内的分享把常用的查询技巧、排查思路讲清楚让大家都用起来这套系统的价值才能真正发挥出来。最后分享一个实用小技巧SkyWalking的UI支持自定义仪表盘可以把团队最关心的几个指标比如核心接口的P99耗时、错误率做成一个面板每天早会的时候看一眼有异常能第一时间发现。这个习惯坚持下来很多问题在用户反馈之前就能被主动发现。