上一篇文章讲完了SkyWalking的基础概念和整体架构,评论区好几个人在问Docker方式具体怎么落地。这篇就是接着上篇来的:SkyWalking 10.3.0,Docker模式,保姆级部署教程(二)。我把整个部署过程从头到尾走了一遍,把踩过的坑、容易忽略的细节全部记录下来了,照着操作基本能一步到位。
我这次的部署环境是Windows宿主机 + Docker Desktop,然后CentOS 7.9服务器上用Docker Compose编排的方式跑SkyWalking OAP Server和UI。为什么要专门用Docker而不是传统的tar包部署?原因很简单:SkyWalking 10.x是一个前后端分离的架构,OAP Server负责数据处理和存储,UI是独立的前端工程。如果手动部署,得分别处理Java环境、配置修改、进程守护、日志轮转这一堆杂事。用Docker Compose两条服务就能把整个链路跑起来,升级回滚都方便得多。
1. 为什么第10.3.0版本选择Docker模式部署
1.1 三种部署方式的对比
我在实际项目里把SkyWalking的三种主流部署方式都试过,各有各的适用场景。
| 部署方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 源码编译 | 可以二次开发,定制化强 | 编译耗时长,新手容易卡在依赖上 | 二次开发者 |
| Tar包手动部署 | 配置透明,容易排查问题 | 环境差异多,升级麻烦 | 服务器数量少的老项目 |
| Docker模式 | 环境一致,启动快,易扩展 | 需要熟悉Docker基本操作 | 大多数生产环境 |
做完这个对比,我强烈建议第一次接触SkyWalking的朋友直接选Docker模式。原因特别实在:Tar包部署最大的坑是环境不一致。同一个配置文件,放在不同的JDK版本或者操作系统上,表现可能完全不一样。而Docker把OAP Server和UI一起打包,环境统一了,很多奇奇怪怪的问题从一开始就避免掉。
1.2 Docker模式的核心优势
Docker模式带来的好处远不止部署快。最明显的是资源隔离。OAP Server本质上是Java应用,默认的JVM参数可能会去扫宿主机全部内存,在物理机或者云服务器上容易把内存吃满。在Docker容器里跑,我们可以通过-Xmx和-Xms参数配合cgroup限制,把内存精确控制在需要的范围内。
另外一个隐蔽的好处:
提示:SkyWalking的UI和OAP Server是有版本对应关系的。Docker镜像的tag就代表了完整的版本组合,不需要像手动部署那样分别下载、比对版本。这是很多新手栽跟头的地方。
还有快速扩缩容。某天线上业务量突然上来了,需要加一个OAP节点分担压力,Docker模式下用Compose的scale命令几秒钟就能搞定。要是手动部署,得重新准备一台机器、再走一遍安装流程,半小时都算快的。
1.3 版本与镜像的对应关系
部署之前,先确认版本。SkyWalking 10.3.0对应的Docker镜像tag是10.3.0。我用的镜像是:
apache/skywalking-oap-server:10.3.0:OAP Server,负责数据收集、分析和存储apache/skywalking-ui:10.3.0:UI界面,负责数据展示
这两个镜像在Docker Hub上都能直接拉取。如果网络条件不好,可以配镜像加速器,我后边会讲到具体操作。
2. 部署前的环境准备与版本选型
2.1 Docker环境安装:Windows便携版方案
因为我这台Windows机器上经常要切换不同项目,装Docker Desktop有时候会跟已有软件冲突,所以我采用的是Docker便携版方案。这个方案最大的好处就是绿色、解压就能用。
准备步骤就两步:
# 1. 双击运行 portable-docker 启动脚本 # 2. 等待docker客户端连接成功,验证环境 docker version启动脚本的核心逻辑其实很简单,就是把Docker的客户端和服务端路径加到系统环境变量里,然后启动后台服务。第一次启动的时候会稍微慢一点,因为要初始化虚拟机。如果遇到Docker Desktop failed to start这种报错,大概率是Windows虚拟化没开。
Virtualization support not detected这个问题我见过很多人问。处理方式很直接:进BIOS把Intel VT-x或者AMD-V打开,然后在Windows功能里启用“虚拟机平台”和“适用于Linux的Windows子系统”。这一步不做,Docker Desktop怎么都起不来。
2.2 CentOS服务器上的Docker准备
CentOS服务器上部署的话,先把Docker装好:
# 1. 移除旧版本 yum remove docker docker-common docker-selinux docker-engine -y # 2. 安装依赖包 yum install -y yum-utils device-mapper-persistent-data lvm2 # 3. 配置阿里云镜像源 yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo # 4. 安装并启动 yum install -y docker-ce docker-ce-cli containerd.io systemctl start docker systemctl enable docker这套操作下来,Docker环境就绪了。接下来设置镜像加速器,不然直接从Docker Hub拉SkyWalking的镜像,速度会让人崩溃:
# 编辑daemon.json vim /etc/docker/daemon.json # 粘贴以下内容,哪家加速器快就用哪个 { "registry-mirrors": [ "https://docker.mirrors.ustc.edu.cn", "https://hub-mirror.c.163.com" ] } # 重启Docker systemctl daemon-reload systemctl restart docker2.3 版本选型建议和兼容性分析
10.3.0这个版本要注意存储后端的版本兼容性。SkyWalking 10.x默认的存储是H2,但如果要上生产,建议还是用Elasticsearch。我这边测试环境用H2,生产环境接的是Elasticsearch 7.17.x。
选型的时候有两点参考:
JDK版本:OAP Server的Docker镜像内置了JDK,不需要额外在宿主机装。但Java Agent探针这一侧需要关注,后面接应用的时候会专门说。
存储选型:H2适合个人学习、小规模验证;Elasticsearch适合日志量大、需要长期保存的场景;MySQL也是支持的,看团队的运维习惯。
3. 核心组件拆解:OAP与UI的协作机制
3.1 OAP Server到底做了什么
OAP Server全称是Observability Analysis Platform,它做的是整个链路里最核心的数据处理工作。它接收探针上报的Trace数据,进行拓扑分析、指标聚合、告警判断,然后把结果写入存储后端。UI界面上看到的服务拓扑图、调用链路详情、性能指标曲线,数据都来自OAP的加工。
OAP暴露了两个端口:
| 端口 | 用途 | 协议 | 谁连它 |
|---|---|---|---|
| 11800 | gRPC数据上报 | gRPC | Java Agent等探针 |
| 12800 | HTTP数据查询 | HTTP/REST | SkyWalking UI |
部署的时候这两个端口要映射到宿主机,不然探针连不上、UI也打不开。
3.2 UI就是数据的“翻译官”
SkyWalking UI本身不存数据,它做的事情是从OAP Server的12800端口调用查询接口,把数据进行可视化展示。服务拓扑图用到了OAP分析好的拓扑关系数据,链路追踪模块展示的是每个Trace的调用明细。
UI和OAP之间如果配置不一致,最常见的表现就是UI能打开,但任何数据都显示不出来。这里需要重点检查两件事:一个是UI配置里指向的OAP地址是否正确,另一个是服务端OAP设置的命名空间和Agent探针的命名空间是否一致。
3.3 数据从探针到界面的完整链路
一条请求从用户发起到UI界面看到数据,经过的路径是这样的:
业务应用(Java Agent埋点) → gRPC上报(11800端口) → OAP Server分析聚合 → 写入存储(H2/Elasticsearch) → UI查询(12800端口) → 可视化展示如果任何一个环节断了,最终界面上的表现就是服务列表为空。排查的时候按这个链路顺序来,先看Agent日志有没有报错,再看OAP接收日志有没有异常,最后确认存储是否写入成功。
4. 完整部署过程:一步步实操
4.1 docker-compose.yml配置全解读
在实际部署时,我强烈建议用Docker Compose而不是直接写docker run命令。因为两三条服务用Compose管理,配置清晰、启动停止命令统一,后续维护省心太多。下面是我修改后的完整配置文件:
version: '3.8' services: oap: image: apache/skywalking-oap-server:10.3.0 container_name: skywalking-oap environment: - SW_STORAGE=h2 - SW_NAMESPACE=skywalking-prod ports: - "11800:11800" - "12800:12800" restart: always volumes: - ./oap-data:/opt/skywalking/data ui: image: apache/skywalking-ui:10.3.0 container_name: skywalking-ui environment: - SW_OAP_ADDRESS=http://oap:12800 - SW_NAMESPACE=skywalking-prod ports: - "8080:8080" restart: always depends_on: - oap这个配置里有几个关键点要说明清楚:
SW_STORAGE:存储类型选择器。测试环境我用的h2。如果要接Elasticsearch,改成elasticsearch,同时要加SW_STORAGE_ES_CLUSTER_NODES指定ES地址。
SW_NAMESPACE:命名空间。这个参数特别容易被忽略。如果OAP设置了命名空间,那么所有连接它的Agent也必须配置完全相同的命名空间,否则数据上报直接被丢弃。UI侧的命名空间同样要保持一致,不然查不到数据。
SW_OAP_ADDRESS:UI连接OAP的地址。因为UI和OAP在同一个Docker网络里,所以直接用服务名oap加端口12800。
depends_on:指定UI容器在OAP容器之后启动。但这个只是启动顺序的控制,不保证OAP内部服务已经就绪。所以启动后要等个几十秒让OAP完全初始化,再访问UI。
volumes:数据卷。默认的H2数据文件存在容器里,容器删了数据就全没了。所以把数据目录挂载到宿主机,这样升级或者重建容器时数据不丢。
4.2 启动与验证:踩过的坑都写在这里
配置文件准备好之后,执行启动命令:
# 在docker-compose.yml所在目录下执行 docker-compose up -d第一次启动会拉镜像,时间取决于网络情况。镜像拉完后,用下面的命令查看容器状态:
docker-compose ps我遇到的一个典型坑就是:OAP容器启动后又退出了。排查方法很直接:
# 查看容器日志 docker logs -f skywalking-oap日志里如果出现类似Error creating bean with name 'storageModuleH2Provider'这样的报错,大概率是H2存储目录的权限问题。解决办法就是给挂载目录放开权限:
chmod -R 777 ./oap-data还有个坑是8080端口被占。我测试服务器上跑了一堆别的服务,8080早就被占了。这种时候把UI的宿主机端口改掉就行:
ports: - "18080:8080"改完之后访问地址就变成了http://服务器IP:18080,里面容器端口不用动。
4.3 访问UI界面:第一步该看什么
等容器状态变为Up之后,浏览器访问UI地址。打开界面第一眼会看到服务列表是空的,这一瞬间很多人会慌,觉得自己哪里没配好。
放心,这是正常现象。SkyWalking不会自己产生数据,必须有探针接入后上报数据,UI上才会展示。这时候要做的是去验证OAP是否正常:
# 检查11800端口和12800端口是否监听 netstat -tlnp | grep -E "11800|12800" # 检查OAP日志是否有异常 docker logs skywalking-oap --tail 100确认端口监听、日志没有Error级别报错,那部署就算成功了。接下来就是接业务应用的数据。
5. Java Agent探针接入:让数据“跑”起来
5.1 下载与放置:版本必须对应
OAP部署好了只是空房子,Java Agent探针就是把业务应用的数据搬进这个房子的搬运工。SkyWalking的Java Agent和主程序是分开打包的,需要去官网或者GitHub Releases页面下载。
这里特别强调一个版本匹配问题:Agent的版本最好和OAP Server的版本一致或者接近。我用OAP 10.3.0,对应的Java Agent应该下载9.x版本。版本差距过大会导致gRPC通信协议不兼容,探针上报数据OAP根本不认。
下载后用tar命令解压:
wget https://archive.apache.org/dist/skywalking/java-agent/9.3.0/apache-skywalking-java-agent-9.3.0.tgz tar -zxvf apache-skywalking-java-agent-9.3.0.tgz mv apache-skywalking-java-agent-9.3.0 skywalking-agent解压出来目录里最重要的就是skywalking-agent.jar,以及config/agent.config配置文件。
5.2 JVM参数挂载:三种方式任选
Agent接入方式是在启动Java应用的时候,通过-javaagent参数挂载探针jar包。这就是Java Agent的典型机制:在JVM启动早期,通过Instrumentation API改写字节码,从而实现无侵入式埋点。
最简单的接入方式:
java -javaagent:/opt/skywalking-agent/skywalking-agent.jar \ -Dskywalking.agent.service_name=my-demo-service \ -Dskywalking.collector.backend_service=127.0.0.1:11800 \ -jar my-demo-app.jar如果是Spring Boot应用用java -jar启动,就是这个命令。参数解释一下:
skywalking.agent.service_name:应用在SkyWalking UI上显示的服务名,一定要起一个容易识别的名字skywalking.collector.backend_service:OAP Server的gRPC地址和端口,格式是IP:11800
除了命令行参数,还有一种方式就是修改agent/config/agent.config配置文件:
agent.service_name=${SW_AGENT_NAME:my-demo-service} collector.backend_service=${SW_AGENT_SERVER:127.0.0.1:11800}这样做的好处是Agent配置和应用启动命令解耦,同一套Agent可以复制到多台机器,只需要单独配置环境变量。
5.3 验证探针是否生效:日志与界面双重确认
接入后重启应用,观察应用日志。如果看到类似下面的输出,说明探针加载成功:
DEBUG 2025-01-01 12:00:00.001 [main] org.apache.skywalking.apm.agent.core.boot.AgentPackagePath - The beacon class location is jar:file:/opt/skywalking-agent/skywalking-agent.jar!这时候去UI刷新页面,等一两分钟,服务列表中就会出现my-demo-service。如果服务一直不出来,按下面的清单排查:
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| Agent日志显示无法连接 | OAP地址配错或端口不通 | telnet测试11800端口 |
| 有服务但数据为空 | 命名空间不一致 | 确认Agent和OAP的namespace一致 |
| 请求完成后UI无拓扑 | 没有持续产生流量 | 多跑几次测试接口 |
| 探针版本和OAP不匹配 | 日志有gRPC通信报错 | 升级Agent到与OAP匹配的版本 |
排查的时候顺着链路一层层来,先确保Agent访问得到OAP,再确认数据已写入,最后看UI查询是否正常。这个思维模式比死记命令管用得多。
5.4 实际案例:一个Spring Boot应用的完整接入过程
光说不练假把式。我拿一个简单的Spring Boot服务做个完整演示。
# 1. 假设应用包在/opt/app目录 cd /opt/app # 2. 启动应用并挂载Agent java -javaagent:/opt/skywalking-agent/skywalking-agent.jar \ -Dskywalking.agent.service_name=order-service \ -Dskywalking.collector.backend_service=192.168.1.100:11800 \ -jar order-service-1.0.0.jar &应用启动后,我模拟了一些请求。在UI的“服务”页面能看到order-service显示出来,并且有实时的CPM、响应时间等指标曲线。在“拓扑”页面能看到服务之间调用的可视化关系,在“追踪”页面能看到每条请求的完整调用链。
有个小技巧:压测的时候用一些带参数的查询接口,生成的Trace会更有对比价值,可以顺便验证SkyWalking对不同响应时间的请求是否有准确的耗时统计。
6. 后续运营:告警配置与扩展实践
6.1 告警规则怎么配置
SkyWalking 10.3.0内置了一套告警规则,但生产环境用下来,我觉得最好还是按照业务实际情况来调整。告警配置文件在OAP容器的/opt/skywalking/config/alarm-settings.yml。
自定义告警的做法有两种。简单的方式是进入容器修改文件,但容器重建会丢。更推荐用Docker挂载的方式:
volumes: - ./alarm-settings.yml:/opt/skywalking/config/alarm-settings.yml我在配置里加了一个比较实用的规则——慢查询告警:
rules: - rule-name: endpoint_slow metric-name: endpoint_avg op: ">" threshold: 1000 period: 10 count: 3 message: 接口响应时间超过1秒,请及时排查 tags: level: WARNING这段配置的含义是:任意接口在10分钟周期内的平均响应时间超过1000毫秒,并且触发3次,就会产生一条告警。告警支持对接webhook、钉钉、Slack等通知渠道,在alarm-settings.yml的hooks类目下面配置即可。
6.2 命名空间与多环境隔离
前面提到的SW_NAMESPACE不仅能用来防止Agent连错OAP,还能做多环境隔离。比如开发环境和测试环境共用一套SkyWalking集群的话,可以分别设置dev和test命名空间,这样两个环境的数据互不干扰。
不过要注意的是,命名空间一旦设置,UI查询的时候也会默认带上这个namespace。如果你想在UI上同时查看多个命名空间的数据,那不建议在生产环境用这个名字空间做环境隔离,更好的方式还是拆分独立部署。
6.3 与日志系统对接的思路
Trace数据解决了“请求走过了哪些服务”的问题,但如果我们还想知道某个环节的日志详情,最好把TraceId和日志系统关联起来。
SkyWalking提供了日志增强的扩展能力:在日志中自动注入TraceId。Java Agent里开启相关配置后,日志框架输出的内容会自动带上一个tid字段。这个TraceId和SkyWalking链路追踪的TraceId是同一个值,这样就可以实现“日志平台里看到TraceId,复制到SkyWalking里直接跳转到这条请求的完整调用链”的联调体验。
这一步做完了,整个可观测体系才算真正闭环:指标看趋势、链路看关系、日志看细节。
6.4 升级与备份的惯用操作
Docker模式下升级SkyWalking相对简单。常规操作流程:
# 1. 备份数据卷 cp -r ./oap-data ./oap-data-backup-$(date +%Y%m%d) # 2. 拉取新版本镜像 docker-compose pull # 3. 重启容器 docker-compose up -d有个细节特别提醒:升级前仔细阅读官方的升级说明。SkyWalking大版本之间存储结构可能会有变化,有时候需要执行特定的数据迁移脚本。直接拉新版镜像启动,老数据可能读不出来。我见过有人从9.x直接跳到10.3.0,结果存储数据不兼容,被迫重新搭了一套。稳妥起见,生产环境升级前先在测试环境跑一遍全流程。
结语:几点没有人写在文档里的体会
整套部署走完,最大的感受是SkyWalking 10.3.0的Docker化已经做得相当成熟,只要你理解了OAP和UI的关系、端口的作用、命名空间的约束,基本不会再遇到什么拦路虎。比起早期版本,10.x在性能上明显优化过一轮,UI的流畅度和数据聚合的响应都强了不少。
最后分享一个小经验:把docker-compose.yml和agent配置放到一个统一的目录管理,比如/data/skywalking/下,不要散落在各处。这样无论是升级、备份还是迁移服务器,直接打包整个目录就能带走。第一次部署的时候多花五分钟做这个规范,后面维护能省下大量时间。
如果照着这套流程操作过程中还有卡住的地方,大概率是环境差异导致的问题。排查的时候记得抓住两个核心:网络通不通、版本配不配。解决这两个问题,别的都是细枝末节。有具体报错也欢迎留言交流,我看到会回复。