news 2026/4/14 16:57:13

devops系列(四) Jenkins Pipeline:搭建你的第一条 CI/CD 流水线

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
devops系列(四) Jenkins Pipeline:搭建你的第一条 CI/CD 流水线

Jenkins Pipeline:搭建你的第一条 CI/CD 流水线

最近有个读者私信我:“哥,我们公司发版还是手动mvn package完用 FTP 传服务器,再 SSH 上去kill -9重启,发一次版熬到凌晨两点,还总出错。有没有救?”

我说:“兄弟,你这哪是发版,这是渡劫啊。”

今天咱们就来聊聊,怎么用 Jenkins Pipeline 把这套"人肉 CI/CD"给自动化了。读完这篇文章,你至少能搭出一条像模像样的流水线,告别凌晨两点的人工操作。


一、问题引入:那些年,我们手动发过的版

先回忆一下传统发版的"标准流程":

  1. 开发在本地打好包(mvn packagenpm run build
  2. 打开 FTP/WinSCP,把 jar 包或 dist 文件夹拖到服务器上
  3. SSH 登录服务器,找到旧进程,kill -9干掉
  4. 启动新服务,祈祷不要报错
  5. 打开浏览器手动点两下,验证"应该没问题"
  6. 如果有问题,手忙脚乱回滚…

这套流程的问题,咱们心里都门儿清:

  • 容易出错:人不是机器,步骤一多就容易漏。jar 包传错了目录?配置文件忘了覆盖?服务启动参数写岔了?随便一个失误就是生产事故。
  • 不可追溯:谁发的版?什么时候发的?发了什么版本?全凭微信群里的"我发完了"和运维小哥的记忆力。
  • 效率低下:发一次版少则半小时,多则几个小时。要是多环境(测试、预发、生产)各来一遍,一晚上就没了。
  • 回滚困难:一旦出问题,想回滚到上一个版本?不好意思,你可能连上个版本的包都找不到。

说白了,发版这件事,就不该靠人手动去做。机器能干的活,为什么要让人熬夜干?


二、方案分析:为什么选 Jenkins Pipeline?

解决这个问题的方案其实不少,咱们简单盘一盘:

方案优点缺点
GitLab CI/CD和代码仓库深度集成,配置简单对 Jenkins 生态已有的插件和存量项目迁移成本高
GitHub Actions云端即用,社区 Action 丰富企业内网或私有部署场景下不太方便
Jenkins + Pipeline生态最成熟,插件丰富,自由度高,企业用得最多界面有点复古,配置稍显复杂

如果你的公司已经在用 Jenkins,或者你想学一套"通用性最强、找工作最吃香"的 CI/CD 方案,那Jenkins Pipeline几乎是不二之选。

而且 Pipeline 相比传统的 Jenkins Job(在 Web 界面上点点点配置),有个巨大的优势:Pipeline as Code。整个流水线写在一个Jenkinsfile里,跟代码一起放在 Git 仓库里。版本可控、可 review、可复用,谁改了流水线一目了然。

Jenkins Pipeline 有两种语法:

  • Scripted Pipeline:基于 Groovy 脚本,灵活度高,但写起来像代码,对新手不太友好。
  • Declarative Pipeline:声明式语法,结构清晰,像写 YAML 一样规整,推荐新手和大多数场景使用。

我的建议:除非你有特别复杂的动态逻辑,否则一律用 Declarative。好读、好维护、团队协作成本低。


三、实现过程:Step by Step 搭一条流水线

好了,说了这么多,咱们开始动手。先看一下这条流水线的完整流程:

┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ 拉取 │ -> │ 编译 │ -> │ 单元测试│ -> │ 构建镜像│ -> │ 推送镜像│ -> │ 部署应用│ │ 代码 │ │ 打包 │ │ │ │ │ │ 仓库 │ │ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘

这就是一条标准的 CI/CD 流水线。下面咱们逐个 stage 拆解。

3.1 前置准备:Jenkins 和核心插件

Jenkins 的安装就不手把手教了,网上教程一大把。不管你是用 Docker 起一台,还是在服务器上直接装,核心思路都一样。

装完之后,记得装这几个必备插件

  • Pipeline:没有它,Jenkinsfile 跑不起来。
  • Git:从 Git 仓库拉代码。
  • Docker Pipeline:在 Pipeline 里执行 Docker 命令。
  • Kubernetes CLI(可选):如果你要部署到 K8s,这个插件能帮你方便地操作kubectl

3.2 编写 Jenkinsfile(Declarative 语法)

下面是一份精简但可直接用的 Jenkinsfile 模板。我加了详细注释,你可以直接复制到项目里改改参数就能跑。

pipeline{// 指定在哪台 agent 上运行,any 表示任意可用节点agent any// 定义环境变量,方便后面复用environment{IMAGE_NAME="myapp"IMAGE_TAG="${BUILD_NUMBER}"// 用 Jenkins 构建号做镜像标签REGISTRY="registry.example.com"KUBE_CONFIG=credentials('kubeconfig-id')// 从 Jenkins 凭据里取 kubeconfig}stages{// Stage 1: 拉取代码stage('Checkout'){steps{// 从 Git 仓库拉代码,分支参数化,默认 mastergit branch:'${BRANCH_NAME}',url:'https://github.com/your-repo.git'echo"代码拉取完成,当前分支:${env.BRANCH_NAME}"}}// Stage 2: 编译打包stage('Build'){steps{// 以 Maven 项目为例,执行编译并跳过测试(测试单独跑)sh'mvn clean package -DskipTests'echo"编译完成,产物在 target/ 目录下"}}// Stage 3: 单元测试stage('Test'){steps{sh'mvn test'}post{always{// 收集测试报告,Jenkins 界面上能看到junit'target/surefire-reports/*.xml'}}}// Stage 4: 构建 Docker 镜像stage('Build Docker Image'){steps{script{// 用当前目录的 Dockerfile 构建镜像docker.build("${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}")}}}// Stage 5: 推送镜像到仓库stage('Push Image'){steps{script{// 登录镜像仓库并推送docker.withRegistry("https://${REGISTRY}",'registry-credentials-id'){docker.image("${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}").push()// 同时推一个 latest 标签,方便快速拉取docker.image("${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}").push('latest')}}}}// Stage 6: 部署到 K8sstage('Deploy'){steps{// 用 sed 替换 deployment.yaml 里的镜像标签sh""" sed -i 's|IMAGE_TAG|${IMAGE_TAG}|g' k8s/deployment.yaml kubectl --kubeconfig=${KUBE_CONFIG}apply -f k8s/ """echo"部署完成,镜像版本:${IMAGE_TAG}"}}}// 流水线跑完后的收尾post{success{echo"🎉 流水线执行成功!可以去喝杯咖啡了。"}failure{echo"❌ 流水线挂了,快去查日志吧..."}always{// 清理工作区,避免磁盘爆掉cleanWs()}}}

关键点解读:

  • agent any:让 Jenkins 自己挑一台空闲的节点跑任务。如果你的编译节点有标签(比如label 'maven'),可以改成agent { label 'maven' }
  • environment:把镜像名、仓库地址等抽成变量,后面改起来方便。
  • post里的always:不管成功失败都执行,适合放清理、发通知这类操作。
  • cleanWs():很多新手会漏掉这步,结果 Jenkins 服务器磁盘被旧构建占满,别问我怎么知道的。

3.3 蓝绿部署 vs 滚动更新:部署的时候怎么保证不断服?

代码编译完了、镜像也推了,最后一步部署可不能简单粗暴地kill -9重启。生产环境讲究的是零停机发布。这里介绍两个最常见的思路:

滚动更新(Rolling Update)

这是 K8s 的默认策略。简单说就是:先起几个新 Pod,等它们健康了,再逐个干掉旧 Pod。整个过程中,服务的总实例数基本保持不变,用户无感知。

优点是简单、省资源;缺点是如果新版本有问题,回滚需要时间,而且更新期间新旧版本会共存一小段时间。

蓝绿部署(Blue-Green Deployment)

同时维护两套环境:蓝色(当前线上版本)和绿色(新版本)。部署时,先把绿色环境全部启动并验证通过,然后把流量一次性从蓝色切到绿色。如果出问题,秒切回蓝色。

优点是回滚快、风险低;缺点是需要双倍资源。

对于刚入门 CI/CD 的同学,我的建议是:先用 K8s 自带的滚动更新,配置简单、成本低。等团队成熟了,再考虑上蓝绿部署或金丝雀发布。


四、踩坑记录:我踩过的那些 Jenkins 的坑

理论很美好,实操起来总有些意想不到的坑。这里分享两个我实实在在踩过的,希望你别重蹈覆辙。

坑 1:Jenkins 容器里执行不了 Docker 命令

很多同学习惯用 Docker 跑 Jenkins,结果在 Pipeline 里一执行docker build,报错:

docker: not found

或者权限不足:

Got permission denied while trying to connect to the Docker daemon socket

原因:Jenkins 容器本身没有 Docker 环境,或者容器里的jenkins用户没有权限访问宿主机的 Docker socket。

解决方案

启动 Jenkins 容器时,把宿主机的 Docker socket 挂载进去:

dockerrun-d\-v/var/run/docker.sock:/var/run/docker.sock\-v/usr/bin/docker:/usr/bin/docker\jenkins/jenkins:lts

同时,给jenkins用户加到docker组:

# 进入 Jenkins 容器usermod-aGdockerjenkins

注意:挂载 Docker socket 有一定安全风险,但在内网 CI 场景下基本够用。如果要求严格,可以考虑用 Docker in Docker(DinD),配置会复杂一些。

坑 2:Pipeline 里的环境变量传不到 Shell 脚本里

我在environment里定义了一个变量:

environment{MY_VAR="hello"}

然后在sh步骤里用$MY_VAR,结果为空。

原因:Declarative Pipeline 的sh步骤里,Groovy 变量和 Shell 环境变量的作用域是两套系统。直接用$MY_VAR有时候能行,有时候不行,取决于 Jenkins 版本和上下文。

解决方案:用双引号包裹sh,并显式用${env.MY_VAR}${MY_VAR}插值:

sh""" echo${env.MY_VAR}echo${MY_VAR}"""

如果你用的是单引号sh '...',Groovy 不会进行变量插值,Shell 里自然拿不到值。这个细节坑了我整整一个下午。

坑 3:构建缓存导致依赖没更新( bonus 坑)

有时候明明改了pom.xml里的依赖版本,Jenkins 构建出来的包还是老的。

原因:Maven 的本地仓库缓存(~/.m2/repository)或者 Docker 的 layer 缓存没清。

解决方案

  • Maven 构建加-U参数强制更新快照依赖:mvn clean package -U
  • Docker 构建加--no-cache参数,或者在 Dockerfile 里把pom.xml和源码分层 COPY,避免不必要的缓存

五、验证与总结

按照上面的 Jenkinsfile 配置好流水线之后,你可以这样做验证:

  1. 在代码仓库里提交一个改动,推送到 Git。
  2. 打开 Jenkins,点击"立即构建",或者配置 Webhook 让它自动触发。
  3. 看着流水线一个 stage 一个 stage 地变绿,最后显示"🎉 流水线执行成功!"。
  4. 登录 K8s Dashboard 或服务器,确认新版本已经跑起来了。

这时候你会发现,发版从原来的人工两小时,变成了点一下按钮(甚至自动触发)的十分钟。而且全程有日志、有版本、可追溯,出了问题也知道从哪查。


六、写在最后

CI/CD 这件事,说起来高大上,本质上就是把"人重复做的事"交给机器去做。Jenkins Pipeline 只是工具之一,更重要的是流水线思维——代码提交后应该自动编译、自动测试、自动部署,人只负责写代码和做决策。

当然,Jenkins 也不是完美的。界面老旧、插件兼容性偶尔抽风、Groovy 语法对 Java 程序员还算友好但对前端同学可能有点劝退。如果你已经在用 GitLab 或 GitHub,也可以试试它们原生的 CI/CD。工具没有绝对的好坏,适合团队现状的才是最好的。

最后,想问问你:

  • 你们公司现在是怎么发版的?还是手动 FTP 吗?
  • 你在搭 Jenkins Pipeline 的时候踩过哪些坑?
  • 有没有比 Jenkins 更好用的 CI/CD 工具推荐?

欢迎在评论区交流,咱们一起进步!


如果这篇文章对你有帮助,点个赞或者转发给还在手动发版的兄弟,救救他吧。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/14 16:56:14

Phi-4-mini-reasoning行业方案:专利权利要求逻辑覆盖度分析工具

Phi-4-mini-reasoning行业方案:专利权利要求逻辑覆盖度分析工具 1. 项目背景与价值 在知识产权领域,专利权利要求书的逻辑覆盖度分析是一项关键但耗时的工作。传统方法依赖人工阅读和比对,效率低下且容易遗漏关键点。Phi-4-mini-reasoning作…

作者头像 李华
网站建设 2026/4/14 16:55:15

Java据结构深度解析:AVL 树与红黑树

AVL树二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年 发明了一种解决上述问题…

作者头像 李华
网站建设 2026/4/14 16:54:18

软件测试工程师转型AI全栈实战指南

测试工程师的AI转型机遇在AI重构软件工程体系的浪潮中,软件测试人员凭借业务场景理解力、异常检测敏感度和质量保障思维三大核心优势,成为AI落地关键角色。本文基于测试工程师的知识结构,设计分阶段转型路径,提供可落地的技术栈与…

作者头像 李华
网站建设 2026/4/14 16:53:31

5分钟完成视频字幕提取:Video-subtitle-extractor完整使用指南

5分钟完成视频字幕提取:Video-subtitle-extractor完整使用指南 【免费下载链接】video-subtitle-extractor 视频硬字幕提取,生成srt文件。无需申请第三方API,本地实现文本识别。基于深度学习的视频字幕提取框架,包含字幕区域检测、…

作者头像 李华
网站建设 2026/4/14 16:51:01

Springboot 实现多数据源(PostgreSL 和 SL Server)连接

7.1 初识三维模型 7.1.1 三维模型的数据载体 随着计算机图形技术的发展,我们或多或少都会见过或者听说过三维模型。笔者始终记得小时候第一次在电视上看到三维动画《变形金刚:超能勇士》的震撼感受;而现在我们已经可以在手机上玩三维游戏《王…

作者头像 李华