ARTICLE DETAIL

资讯详情

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

SpringBoot+Docker+Jenkins自动化部署流水线实战

SpringBoot+Docker+Jenkins自动化部署流水线实战 做后端开发这些年我最怕的不是写业务代码而是“部署”这两个字。SpringBoot 项目虽然打出来就是一个 jar但真正上线的时候环境不一致、依赖缺失、端口冲突、忘记重启服务哪一件都能让人当场崩溃。后来我把 SpringBoot Docker Jenkins 连成一条流水线代码 push 到仓库之后单元测试、打包、镜像构建、服务器部署全部自动跑完我只需要在 Jenkins 页面上看日志就行。这篇文章打算把这条流水线从设计思路、环境搭建、配置文件、完整实战到避坑经验全部讲清楚正在折腾 CI/CD 的同学可以直接照着往下走。1. 这套组合拳到底解决了什么问题1.1 先回忆一下没有流水线时的发布流程我最早带一个订单服务项目时团队就三个人发布全靠手搓。每次发版的固定动作是本地跑一遍mvn clean package然后 scp 把 jar 包传到测试服务器再用ps -ef | grep java找端口kill 掉旧进程最后 nohup 启动新包。听起来简单实际上一周来几次问题就全出来了。最常见的是“我本地是好的”。本地 JDK 8服务器 JDK 11本地内存充足服务器是 2G 小机器启动参数没调一压测就 OOM。还有一次同事把配置文件里某个密码改成自己本地的打包后直接传上去结果连不上生产数据库服务起不来。当时排查了半天最后发现是“本地环境”和“测试环境”的差异在作怪。这些痛点的本质是发布流程没有标准化每一步都依赖人的记忆和操作习惯。哪怕你写一个 deploy.sh它也只是把一部分操作脚本化测试、构建、镜像管理这些环节仍然是割裂的。所以当时我定的目标状态是开发者把代码推到仓库后续动作全部自动化。仓库收到新代码 - 自动拉取 - 跑单元测试 - 用 Maven 打包 - 构建 Docker 镜像 - 推送到镜像仓库 - 目标服务器拉取新镜像并启动容器。如果哪一步挂了能在网页上直接看到日志不用 SSH 上去翻文件。这其实就是今天要讲的完整流水线。1.2 为什么偏偏是 SpringBoot Docker Jenkins先说 SpringBoot。它不是流水线的一部分但它是整个流程的“产物主体”。SpringBoot 应用打包成可执行 jar内嵌 Tomcat不需要单独安装 Web 容器天然适合容器化。如果你换成传统 WAR 包项目构建和部署方式会复杂一些但 SpringBoot 这种“拿起来就能跑”的特点让流水线可以很干净。再说 Docker。我要解决的核心问题是环境一致性。把 jar 包丢进一个 Docker 镜像里等于把“应用 运行环境 启动命令”一起固化下来。本地能跑的镜像生产上大概率也能跑因为你交给部署目标的不再是半成品 jar而是完整的运行时。这一点是手动 scp 永远比不了的。最后是 Jenkins。你可能想为什么不直接写个 Shell 脚本加 cron说实话小项目确实可以但我选择 Jenkins 有几个实际理由第一有任务历史和构建记录出问题能对比上一次成功和这次失败的日志第二有权限控制和凭证管理不用把服务器密码写在脚本里第三插件生态非常全Docker、Git、通知、Blue Ocean 这些都能直接接第四后续如果接 Kubernetes 或作为分布式构建调度Jenkins 无缝衔接。用一张表格来对比手动发布和流水线发布会更直观。环节手动发布流水线发布代码拉取本地手动 pull 或直接不管Jenkins 自动拉取指定分支和 commit单元测试经常跳过构建前强制跑失败终止构建打包本地执行 mvn package构建机执行环境固定镜像制作无直接传 jar多阶段构建生成轻量镜像部署方式手动 kill nohupDocker 容器化秒级启停回滚方式找上一个包用上一个镜像 tag 直接恢复过程审计口口相传日志全在 Jenkins 里这套组合选型不算什么黑科技但它把一个中小团队最缺的“发布确定性”给补上了。1.3 流水线从头到尾要经过哪些节点在开始动手之前我习惯先把整条链路在纸上画一遍。虽然不建议用太复杂的架构图但每个阶段的输入输出必须清楚。整个流水线大致分七个节点代码提交。开发者把代码推送到 Git 仓库这一步是触发源头。拉取代码。Jenkins 从仓库拉取指定分支的最新代码。单元测试。在构建机上执行mvn test测试失败直接终止。打包构建。执行mvn clean package -DskipTests生成可执行 jar。构建镜像。根据 Dockerfile 将 jar 打成 Docker 镜像。推送镜像。把镜像推到镜像仓库并打上版本标签。部署上线。目标服务器拉取新镜像停止旧容器启动新容器。每个节点的产物分别是什么也要心里有数代码提交的产物是 commit测试的产物是测试报告打包的产物是 jar镜像构建的产物是带 tag 的镜像部署的产物是运行中的容器。搞清楚这些之后接下来就是逐个把环境搭起来。2. 环境准备把四件套装好2.1 版本选型要先想清楚不然坑在后面很多同学铆足劲安装结果第一条命令就跑不通原因多半是版本不匹配。我的建议是先定版本再动手。组件建议版本备注JDK17对应 SpringBoot 3.xSpringBoot 2.x 用 8/11但新项目直接上 17 更省心Maven3.8 或 3.9兼容 JDK 17SpringBoot3.2.x / 3.3.x新项目选 3.xDocker24 以及 Docker Compose v2构建机和部署机都需要Jenkins官方 lts 长期支持版不用刻意追最新版这里要特别提醒一个“springboot 版本太高”的坑。如果你以前写的是 SpringBoot 2.x升级到 3.x 以后javax.*相关包会变成jakarta.*很多老配置和代码要跟着改。比如spring.factories的自动配置方式也被AutoConfiguration.imports替代。这意味着你不能拿一套旧的 SpringBoot 2.x 项目代码直接放到新流水线里跑需要先保证项目本身能在 JDK 17 下构建通过。我自己建项目时统一用 JDK 17因为它是长期支持版本Text Blocks、Switch 表达式这些语法能让代码写起来舒服不少而且 Docker 生态里对应的基础镜像非常成熟。2.2 Docker 环境安装与初始化构建机和部署机建议都用标准 Linux 发行版。Windows 或 Mac 上的 Docker Desktop 适合开发时用但不建议拿来做 Jenkins 构建节点原因有两个一是文件路径和权限模型跟 Linux 有差异二是资源占用太高。Linux 上安装 Docker 很简单我以 Debian/Ubuntu 为例核心步骤如下# 安装依赖 sudo apt-get update sudo apt-get install -y ca-certificates curl gnupg # 添加 Docker 官方 GPG 密钥和仓库 sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod ar /etc/apt/keyrings/docker.gpg echo deb [arch$(dpkg --print-architecture) signed-by/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release echo $VERSION_CODENAME) stable | sudo tee /etc/apt/sources.list.d/docker.list /dev/null # 安装 Docker Engine 和 compose 插件 sudo apt-get update sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin装完以后记得把当前用户加进 docker 组否则每一条 docker 命令都要加 sudo。执行之后重新登录一次生效。sudo usermod -aG docker $USER开发机上如果用 Windows 的 Docker Desktop偶尔会遇到启动时提示 “virtualization support not detected”这个我会在后面的排查章节详细讲这里先提一句本质是 BIOS 的虚拟化没开或者 Windows 的 Hyper-V/WSL2 功能没启用不是 Docker 本身的问题。2.3 Jenkins 怎么装容器方式还是 war 包方式这是个老问题了。我两种都试过最终长期用的是“容器方式安装 Jenkins”。因为 Jenkins 本身也是一个 Java 应用升级、迁移、备份用容器管理最方便。但有一个关键细节我希望 Jenkins 容器内部能直接调用宿主机的 docker 命令。所以启动 Jenkins 容器时必须把宿主机的 Docker socket 和 docker 客户端二进制挂载进去。docker run -d \ --name jenkins \ -p 8080:8080 \ -p 50000:50000 \ -v jenkins_home:/var/jenkins_home \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /usr/bin/docker:/usr/bin/docker \ -e TZAsia/Shanghai \ jenkins/jenkins:lts解释一下为什么这样挂载。Jenkins 容器内的 jenkins 用户通过访问宿主机的/var/run/docker.sock可以和宿主机上的 Docker 守护进程通信这样我们在流水线里执行docker build、docker push实际是在调度宿主机的 Docker。这种模式叫 docker-outside-of-docker和传统的 Docker-in-Docker 相比不需要启动一个嵌套的 Docker 服务资源开销小也更稳定。第一次启动后容器日志或宿主机docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword可以拿到管理员密码。初始化时插件不要全装按需来就好我装的基础插件是这些PipelineGitDocker PipelineDocker pluginCredentials BindingTimestamper时间戳看日志舒服很多Workspace Cleanup构建前清理工作区LocaleJenkins 汉化用提到汉化顺便说一句装 Locale 插件后还要在系统设置里把语言改成 zh_CN并勾选强制使用否则中英文会混杂。2.4 凭证管理Git 和镜像仓库的钥匙自动化流水线里最避讳的一件事就是把账号密码写在 Jenkinsfile 里。Jenkins 的凭证管理就是干这个的。进入 系统管理 - Credentials - System - Global credentials - Add Credentials我建议至少准备三组Git 凭证。如果仓库是 GitLab用用户名密码或 Access Token 都行如果是 GitHub用 Personal Access Token。我推荐用 token因为 token 可以精确控制权限范围和有效期。镜像仓库凭证。Docker Hub、Harbor 或私有 Registry都需要一个账号密码用来执行docker push。部署服务器 SSH 凭证。如果目标是远程服务器通过 SSH 执行部署命令需要上传私钥或密码。这三组凭证会分别绑定到 Jenkinsfile 中的不同步骤。凭证 ID 自己命名清晰一点比如gitlab-cred、docker-registry-cred、deploy-server-cred后续写流水线时不容易记混。3. 应用容器化Dockerfile 与镜像构建3.1 多阶段构建让最终镜像瘦身SpringBoot 项目写 Dockerfile我强烈推荐多阶段构建。简单说第一阶段用一个带 Maven 和 JDK 的镜像去编译第二阶段只拷贝编译好的 jar配上 JRE 运行环境。这样最终镜像里没有 Maven、没有源码、没有临时文件体积小安全性也高。下面是我常用的模板以 SpringBoot 3.x 为例。# 第一阶段构建 FROM maven:3.9.6-eclipse-temurin-17 AS builder WORKDIR /workspace COPY pom.xml . RUN mvn dependency:go-offline COPY src ./src RUN mvn clean package -DskipTests # 第二阶段运行 FROM eclipse-temurin:17-jre RUN useradd -m appuser WORKDIR /app COPY --frombuilder /workspace/target/order-service.jar app.jar EXPOSE 8080 USER appuser ENTRYPOINT [sh, -c, java $JAVA_OPTS -jar /app/app.jar]这个文件的第一阶段有一行RUN mvn dependency:go-offline很多人会忽略它的价值。Docker 构建镜像时是有层缓存机制的只要pom.xml没变这一层就不会重新执行依赖会被完整缓存下来。如果把它去掉直接COPY . .那你每次改一行代码Maven 都要重新下载所有依赖构建时间翻几倍都不止。第二阶段用eclipse-temurin:17-jre而不是继续用maven镜像是因为运行阶段根本不需要 Maven。我见过有人偷懒一个镜像从头跑到尾最后镜像体积 800M。用多阶段构建之后SpringBoot 镜像一般能控制在 250M 以内如果再用jlink裁剪 JRE能压到 150M 左右。还有一个细节ENTRYPOINT我写成了 shell 形式而不是 exec 形式原因是为了能接收JAVA_OPTS环境变量。这样部署时如果想调整 JVM 堆大小直接在容器环境变量里传即可不用改镜像。3.2 镜像仓库与版本标签镜像构建出来之后不能只留在构建机上需要推到一个镜像仓库里。常见选择有三个Docker Hub、简单私有 Registry、Harbor。各有各的适用场景。仓库适用场景缺点Docker Hub个人项目、公开 demo私有仓库要收费拉取有频率限制Registry内网自建够用没有 Web UI权限管理弱Harbor企业生产环境部署和运维成本高一些我个人的习惯是团队不大时直接用带 Web UI 的轻量 Registry 或 Harbor重点是要支持镜像推送时的凭证认证。镜像的 tag 必须包含可追溯的信息比如构建号或 Git commit 短哈希。举例来说order-service:20250112-18或者order-service:1.2.3-a1b2c3d。千万不要所有人都在用同一个latest标签否则生产上一旦拉错镜像排查起来很痛苦。给每个构建一个唯一 tag回滚时直接docker run上一个 tag 就行这是成本最低的回滚方案。3.3 构建完先本地验证一把在接流水线之前我总会先在构建机上手动把镜像构建一遍验证能不能启动。docker build -t order-service:test . docker run --rm -p 8080:8080 -e SPRING_PROFILES_ACTIVEdev order-service:test curl http://localhost:8080/actuator/health有的项目一启动就要连 MySQL、Redis这时候只用docker run拉一个应用容器是不够的。可以把 MySQL 和 Redis 也用 Docker Compose 编排在同一套网络里本地一把拉起。简单示例services: mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: order_db ports: - 3306:3306 order-service: image: order-service:test environment: SPRING_PROFILES_ACTIVE: dev SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/order_db ports: - 8080:8080这里的关键点是 SpringBoot 连接数据库的地址不要写localhost要写服务名mysql因为同一个 Compose 网络里服务名就是 DNS 名。如果这一步验证通过说明镜像本身没问题就可以放心交给 Jenkins。4. Jenkins 流水线脚本设计与参数化4.1 声明式流水线的基本骨架Jenkins 的 Pipeline 有两种写法声明式和脚本式。我推荐声明式它把结构固定下来可读性更强适合团队里其他人接手维护。管线常见格式是这样。pipeline { agent any stages { stage(阶段名) { steps { // 具体命令 } } } post { success { /* 成功处理 */ } failure { /* 失败处理 */ } } }一个容易混淆的细节是steps块里可以直接写sh xxx也可以写echo、script等步骤。当需要执行 Groovy 逻辑时就得包一层script { }。比如我要动态决定镜像名就会在script块里处理变量。4.2 从拉代码到部署的完整 Jenkinsfile我把前面提到的七个节点串成 Jenkinsfile直接贴出来参考pipeline { agent any triggers { // 这里放置 webhook 或轮询的触发配置 } parameters { string(name: BRANCH, defaultValue: main, description: 要构建的分支) string(name: APP_NAME, defaultValue: order-service, description: 应用名) string(name: DEPLOY_PORT, defaultValue: 8081, description: 宿主机映射端口) } environment { REGISTRY registry.example.com IMAGE ${REGISTRY}/${params.APP_NAME} } stages { stage(拉取代码) { steps { cleanWs() git branch: ${params.BRANCH}, credentialsId: gitlab-cred, url: http://gitlab.example.com/backend/order-service.git } } stage(单元测试) { steps { sh mvn test } } stage(打包) { steps { sh mvn clean package -DskipTests } } stage(构建镜像) { steps { script { docker.build(${IMAGE}:${BUILD_NUMBER}, .) } } } stage(推送镜像) { steps { script { docker.withRegistry(https://${REGISTRY}, docker-registry-cred) { docker.image(${IMAGE}:${BUILD_NUMBER}).push() docker.image(${IMAGE}:${BUILD_NUMBER}).push(latest) } } } } stage(远程部署) { steps { sshagent([deploy-server-cred]) { sh ssh deployprod-server docker pull ${IMAGE}:${BUILD_NUMBER} docker stop ${params.APP_NAME} || true docker rm ${params.APP_NAME} || true docker run -d --name ${params.APP_NAME} \ -p ${params.DEPLOY_PORT}:8080 \ -e SPRING_PROFILES_ACTIVEprod \ ${IMAGE}:${BUILD_NUMBER} } } } } post { failure { echo 构建失败请检查日志 } success { echo 部署完成${IMAGE}:${BUILD_NUMBER} } } }这个文件有几个值得注意的地方。BUILD_NUMBER是 Jenkins 自带的环境变量代表当前构建的序号每次递增。用它做镜像 tag可以保证每次构建的镜像都是唯一的不会和旧版本冲突。类似的环境变量还有很多比如GIT_COMMIT、GIT_BRANCH、WORKSPACE看时间段可以用 Timestamper 插件在日志上加时间戳。远程部署这一步我采用 SSH 直接连目标服务器执行命令。这里建议不要把所有部署逻辑都写成 ssh 后面的一大串而是先在目标服务器上放一个deploy.sh脚本Jenkins 只负责调用脚本。这样既能减少 SSH 传输复杂命令的出错概率也让运维同学能在服务器上看到清晰的部署日志。另外注意docker stop后面加了|| true。原因是第一次部署时容器不存在stop 返回非 0如果不用|| true整个部署阶段会被判定为失败。4.3 参数化构建与触发方式流程跑通之后还要让开发者按需选择部署环境。我开启参数化构建后手动触发 Jenkins 任务时可以填分支名、应用名、部署端口。比如测试环境填BRANCHdevelop生产环境填BRANCHrelease在同一个 Jenkins job 里切换非常方便。自动触发的常见方式有两种。一是 GitLab/GitHub 的 Webhook仓库收到 push 事件后请求 Jenkins 的一个远程 URL让构建自动开始。二是在triggers块配轮询比如每五分钟检查一次仓库是否有新提交。Webhook 更实时但需要 Jenkins 对公网或内网网络可达轮询实现简单最坏情况延迟几分钟。我推荐团队里把 Webhook 和分支过滤配合起来只在 push 到 main 或 develop 分支时触发其他临时分支不触发不然每个功能分支都会跑一次构建浪费资源。4.4 构建结果通知不能少日志不是所有人都看得及时所以通知要主动。最简单的方案是 Jenkins 邮件通知但我用下来更推荐钉钉或企业微信机器人。机器人本质是往一个 webhook 地址 POST 一段 JSON在post块里写sh curl ...即可。post { failure { sh curl -H Content-Type: application/json \ -d {msgtype:text,text:{content:构建失败order-service}} \ http://your-dingtalk-webhook } }这样构建一挂手机立刻收到消息不用一直盯着 Jenkins 页面。5. 从代码提交到服务上线完整跑一遍流水线5.1 提交代码触发流水线环境都配好了我用一次真实的发布过程来演示。假设我在本地修复了一个订单超时问题提交信息是fix: adjust order timeout logic然后执行git push origin mainGitLab 收到 push 后Webhook 通知 Jenkins任务列表里立刻出现一个新的构建记录。状态从排队开始很快就进入“拉取代码”阶段。这个触发链路一开始配置时最容易出问题前后端网络不通、token 配错都会导致没反应这些后面再讲。5.2 在构建日志里看每一步的产出点进构建控制台看到的日志大概是这样的片段[Pipeline] stage [Pipeline] { (拉取代码) [Pipeline] git Checking out Revision a1b2c3d4... [Pipeline] { (单元测试) ... Tests run: 42, Failures: 0, Errors: 0, Skipped: 0 [Pipeline] { (打包) ... Building jar: target/order-service.jar [Pipeline] { (构建镜像) Step 1/9 : FROM maven:3.9.6-eclipse-temurin-17 AS builder --- Using cache ... Successfully tagged registry.example.com/order-service:18 [Pipeline] { (推送镜像) The push refers to repository [registry.example.com/order-service] 18: digest: sha256:... [Pipeline] { (远程部署) ... docker run -d --name order-service -p 8081:8080 registry.example.com/order-service:18日志其实是很好的“证据链”。哪一步耗时多少、有没有命中缓存、镜像 digest 是什么全都看得到。比如Using cache出现说明依赖层没有重新构建这验证了我前面说的分层缓存策略。如果看到构建失败第一时间不要看完整输出而是顺着阶段名往下找。Jenkins 的 Stage View 插件会把每个阶段用色块显示红色挂点就是问题点效率比我以前滚动态日志高得多。5.3 部署后的检查与回滚流水线显示成功后我建议不要直接认为万事大吉还要验证服务真的可用。目标服务器上执行docker ps | grep order-service docker logs --tail 200 order-service curl http://localhost:8081/actuator/health这三条命令分别确认容器状态、应用启动日志、健康检查结果。如果 health 接口返回UP才算真正部署完成。万一新版本有问题回滚也很简单。假设当前线上是构建号 18要回退到 17就执行docker stop order-service docker rm order-service docker run -d --name order-service -p 8081:8080 registry.example.com/order-service:17之所以能这样流畅就是因为每个构建号的镜像都是独立存在的。如果一开始偷懒都用 latest回滚就会变成一场噩梦。6. 常见问题排查与避坑实录6.1 高频问题速查表下面这张表是我在搭建和日常使用过程中踩过的坑按出现频率排了个序。问题现象常见原因解决方案Maven 下载依赖很慢网络波动、本地仓库未预热配置镜像源Dockerfile 里先 COPY pom.xml 再 go-offlineDocker 构建缓存不生效COPY . .放太靠前代码一变全部层失效依赖层和代码层分离COPY 顺序调整容器启动后立刻退出端口被占用、数据库连接不上、启动参数问题docker logs看具体报错检查环境变量Jenkins 容器内运行 docker 报 command not found没有挂载 docker CLI 或 socket引导启动命令挂载/usr/bin/docker和 socket镜像推送到仓库失败凭证失效、仓库地址写错检查 credentialsId 和 withRegistry 地址远程部署命令卡死SSH 连接超时、命令交互部署脚本化加 timeout优化 ssh 参数构建任务频发互相干扰Webhook 没做分支过滤限制触发条件必要时禁止并发构建6.2 Jenkins 容器内调用 Docker 的权限坑这个坑我保证你会遇到所以单独拿出来说。容器方式启动 Jenkins 后流水线里执行docker build时最容易报两类错。一类是docker: command not found。原因是容器里根本没有 docker 客户端二进制启动时没挂载/usr/bin/docker。解决方法是启动 Jenkins 容器时挂载宿主机 docker 客户端并确认版本和 socket 匹配。另一类是Permission denied while trying to connect to the Docker daemon socket。原因是/var/run/docker.sock的属主是宿主机的 root 用户或 docker 组而 Jenkins 容器内的 jenkins 用户没有权限访问。简单来说jenkins 用户在宿主机上并不存在所以 socket 的权限校验会走一遍“other”权限默认就是拒绝。我的处理方式是把 Jenkins 容器内的 jenkins 用户加到宿主机的 docker 组里。但由于容器和宿主机的用户体系并不完全打通更稳妥的一种做法是修改 socket 的属组或者把 docker CLI 和 socket 的权限放宽到orw。需要说明的是这种做法的安全边界是能访问 Docker socket 就等同于能控制宿主机。所以我一般建议只用在内部开发/测试构建节点上生产环境要谨慎条件允许的时候应该用 Kubernetes 动态 agent 或特权模式做隔离。6.3 并发构建冲突与版本回滚策略流水线跑起来之后第二次坑大概率出现在“并发”上。场景是我同时推了两个分支到仓库GitLab webhook 连续触发 Jenkins结果两个构建同时在“远程部署”阶段去操作同一台服务器一个docker stop把另一个刚启动的容器给停了场面一度很混乱。解决办法有两个层面。第一给任务加并发限制让同一时间只有一个构建在跑。properties([ disableConcurrentBuilds(abortPrevious: true) ])这一段要放在 pipeline 块最前面。abortPrevious: true表示新的构建开始后直接中断正在跑的那个适合解决连续 push 触发多次的冗余问题。第二在部署层面不要让latest成为唯一确定版本。刚才部署阶段一直是按BUILD_NUMBER来部署的这一点务必保持。如果为了图省事统一用 latest并发部署时可能两个构建各自拉取结果最后一个 push 的人赢但谁是最后日志上很难快速定位。我实际用的版本策略是Docker tag 对应 Git commit 短哈希同时打上BUILD_NUMBER的标签。回滚时直接切回上一个 commit 的镜像即可既准确又可审计。6.4 Windows 环境下 Docker Desktop 的启动问题虽然不是 Linux 部署机的必备环节但很多同学开发机是 Windows会碰到 Docker Desktop 启动时报virtualization support not detected。这个错误字面意思很清楚宿主机没有开启虚拟化支持Docker Desktop 无法创建 Linux 虚拟机。排查步骤就三步开机进 BIOS确认 CPU 虚拟化选项Intel VT-x或AMD-V已开启。在 Windows 功能里勾选“适用于 Linux 的 Windows 子系统”和“虚拟机平台”然后重启。确认 Windows 版 Docker Desktop 的运行模式是 WSL2而不是旧版 Hyper-V有时 Hyper-V 和第三方虚拟机软件冲突。这个坑和流水线本身关系不大但会影响你在本地的验证环节。如果开发机起不来 Docker就只能在 Linux 构建机上完成镜像验证体验会差一些。最后再分享一个我自己的操作习惯。流水线全部跑通之后并不是说就一劳永逸了我仍然会在每次大版本发布前手动在构建机上用新镜像跑一遍 smoke testcurl 一下核心接口再让 Jenkins 自动部署到生产。这个动作看着多余但可以拦截掉那些“镜像构建成功但业务接口 500”的问题。自动化解决的是重复劳动不等于放弃最终把关。搭建流水线的过程本质上是一件“花时间省时间”的事情前期把环境、脚本、权限这些磨顺了后面的每一次发布都会变得轻松可靠。
返回列表