尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Jenkins自动化部署实战:从Pipeline到CI/CD全流程解析

Jenkins自动化部署实战:从Pipeline到CI/CD全流程解析 JenkinsDevOps 自动化中枢聊到DevOpsJenkins是一个绕不开的名字。哪怕现在市面上有GitHub Actions、GitLab CI、Drone这些新工具绝大多数从零开始做自动化交付的团队第一套真正跑起来的CI/CD系统还是它。原因很简单插件生态足够大、调度模型足够通用不管是Java、前端、Python还是容器化部署它都能把你那些“本来靠手工点击和复制粘贴”的环节串成一条自动执行的流水线。这篇内容不打算重复官方文档的安装步骤而是按照我从零搭建、日常维护Jenkins的经验把“自动化中枢”这件事讲透怎么部署、怎么跟GitLab打通、怎么写一份能真正用于生产发布的Pipeline以及那些构建报错到底怎么排查。尤其是如果你正在Windows上用Docker跑Jenkins或者卡在GitLab连接、自动部署Java Web应用、用户权限这些环节这篇文章应该能帮你省下一整天的排查时间。1. 把Jenkins摆进DevOps这个盘子它究竟解决什么问题1.1 “构建工具”只是入门印象从定时任务到流水线调度很多新人第一次接触Jenkins看的教程都是“新建一个自由风格任务定时执行一段shell脚本”于是容易把它理解成一个高级版crontab。这个理解不能说错但格局小了。Jenkins真正的价值在于它把“代码提交”到“生产可用”这条链路里的所有动作统一编排成一个有状态、可观测、可回滚的过程。比如一次发布往往要经历拉取指定分支的代码执行单元测试和静态检查用Maven/Gradle编译打包构建Docker镜像并推送镜像仓库登录目标服务器拉取新镜像并重启服务执行健康检查通知相关人员这些动作如果靠人肉操作任何一个环节出错都得从头再来而且没有日志可以追溯。Jenkins做的事情就是把上面每一环拆成Pipeline里的Stage哪个阶段挂了界面上直接标红日志点开就能看。这才是“自动化中枢”的定位——它不只是“跑一跑命令”而是整个发布过程的调度中心和状态记录仪。1.2 一套典型DevOps链路里Jenkins站在哪个位置我们不妨画一张典型的链路图用文字描述开发者把代码推到GitLabGitLab通过Webhook通知JenkinsJenkins执行Pipeline拉代码 → 编译 → 测试 → 构建镜像 → 推送镜像库 → 在目标服务器部署部署完成后Jenkins把结果推送给钉钉或邮件在这条链路里GitLab负责“代码托管”镜像仓库负责“制品托管”目标服务器负责“运行”而Jenkins是那个把大家串起来的“编排者”。它不需要自己完成所有事情但每一个环节的启动、执行、状态判断都由它来统一调度。这也解释了为什么它叫“中枢”而不是“引擎”。真正干活的其实是Maven、Docker、Shell脚本这些工具Jenkins更像是一个认真负责的项目经理把任务拆解好、交接清楚、盯紧结果。2. 先跑起来Windows和Linux下的部署路径2.1 部署前必须先想清楚的三件事直接说结论Jenkins的部署方式主要就三种——原生安装包、war包手动启动、Docker容器化。选哪种取决于你后续打算怎么维护它。原生安装包Linux用apt install jenkins或yum install jenkinsWindows直接装msi。优点是省事服务由系统托管缺点是Jenkins自身的版本升级有点被动。war包启动Java环境里java -jar jenkins.warJDK、Jenkins版本完全可控升级就是换个war包。适合喜欢掌控一切的工程师。Docker容器化隔离最彻底宿主机不会因为装个Jenkins污染环境但数据卷、网络、Docker Socket这些概念不熟的人踩坑概率比较大。我个人的建议是如果是公司正式环境优先用war包或Linux原生包维护简单、查日志方便如果是个人电脑上临时体验或者你本身就在Windows上开发直接Docker Desktop跑一个容器也很舒服。2.2 Linux下用war包直接部署假设你有一台Linux服务器已经装好了JDK 17Jenkins 2.361以后的版本要求Java 11或17老版本才是Java 8装之前务必确认版本对应关系否则启动直接报错# 创建专用目录 mkdir -p /opt/jenkins cd /opt/jenkins # 下载指定版本的war包这里以2.440.3为例 wget https://get.jenkins.io/war-stable/2.440.3/jenkins.war # 设置JENKINS_HOME所有配置、构建记录、插件都放这里 # 这是Jenkins最重要的环境变量后续备份、迁移都靠它 export JENKINS_HOME/var/jenkins_home mkdir -p $JENKINS_HOME # 前台启动先跑起来看日志 java -jar jenkins.war --httpPort8080启动日志里会出现一个随机密码位于/var/jenkins_home/secrets/initialAdminPassword这是首次解锁用的。等看到Jenkins is fully up and running浏览器访问http://服务器IP:8080输入密码就进入初始化界面了。生产环境建议设置一个systemd服务把JENKINS_HOME写进配置文件里这样重启机器后Jenkins能自动恢复。真实环境里我见过有人图省事直接nohup java -jar挂着跑机器一重启忘了启服务整个发布流程全断那个教训挺深刻的。2.3 在Windows上用Docker跑Jenkins的完整过程Windows下没有systemd原生安装Jenkins服务虽然可行但Jenkins自身依赖的很多工具JDK、Git、Maven都得在你Windows上单独配环境变量比较麻烦。用Docker Desktop隔离一套环境反而是我更推荐的方式。前提是你电脑上装好了Docker Desktop并且把它切换成Linux容器模式。接着打开PowerShell# 创建一个目录用于存放jenkins数据注意Windows路径写法 mkdir C:\docker\jenkins_home # 拉镜像并启动 docker run -d --name jenkins -p 8080:8080 -p 50000:50000 -v C:\docker\jenkins_home:/var/jenkins_home jenkins/jenkins:lts-jdk17这里有几个容易踩的坑我必须单独拿出来说。第一务必确认Docker Desktop的Settings → Resources → File Sharing里把C:\docker加进去了否则挂载目录不生效容器里创建的文件只写在容器层容器一删全没了。第二-v宿主机路径不能写C:\docker\jenkins_home这种反斜杠格式实际上新版Docker Desktop已经能识别但最稳妥的写法是C://docker//jenkins_home或/c/docker/jenkins_home这种避免路径解析出错。第三容器内的Jenkins默认用户是jenkinsUID是1000而Windows挂载目录的所有者映射比较特殊经常出现workspace目录无法写入的问题。我的解决办法是在启动命令后手动执行docker exec -u root jenkins chown -R jenkins:jenkins /var/jenkins_home一次性解决权限问题。容器启动后打开http://localhost:8080首次解锁密码通过下面命令获取docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword后续管理Jenkins比如重启、看日志直接用docker restart jenkins和docker logs -f jenkins生活成本很低。2.4 初始化配置插件源、管理员账号、JDK与Git首次进入界面会引导你安装插件。这里建议选“安装推荐的插件”然后去系统设置里把插件源换成国内镜像比如清华源或华为云源否则默认从Jenkins官方源拉插件那个速度在部分网络环境下能让你等到怀疑人生。如果你是离线环境装Jenkins最痛苦的就是插件装不上。我有一个经验在能联网的机器上把需要的插件.hpi文件下载好传到服务器的JENKINS_HOME/plugins目录下然后重启Jenkins。虽然土但在内网环境实测最可靠。紧接着创建管理员账号然后进入Manage Jenkins → Tools配置JDK、Git、Maven。这些工具如果宿主机已经装好就填本机路径如果是Docker容器方式建议直接在容器内apt install git maven或者使用Jenkins自带的“自动安装”功能它会帮你下载并管理工具版本。在这里我要强调一个容易被忽略的细节Pipeline里agent any指的就是Jenkins节点默认节点上必须能执行git、mvn这些命令否则你在Pipeline里写了半天的构建步骤全都会挂在“command not found”上。3. 代码仓库接入GitLab连接与凭据配置3.1 Jenkins的凭据体系值得先花十分钟搞懂Jenkins要拉取GitLab私有仓库的代码肯定逃不开认证。它的凭据存储位置一般在Manage Jenkins → Credentials → System → Global credentials支持的凭据类型很多但跟GitLab打交道最常见的是两种Username with password直接把GitLab账号密码或Access Token存在Jenkins里。优点是配置简单缺点是密码一旦变更所有关联任务全部失效。SSH key在Jenkins服务器上生成一对密钥把公钥加到GitLab用户的SSH Keys里Jenkins用私钥认证。这个方式更推荐一是密钥可以单独管控二是不依赖账号密码的时效性。选型建议如果你只是想让Jenkins拉代码SSH Key最省心一次配置长期有效如果你想调用GitLab API比如创建MR、修改仓库设置那就用Access Token权限范围按需勾选。3.2 配置GitLab连接的完整步骤实操过程是这样的第一步在Jenkins服务器或容器里生成SSH密钥# 生成密钥邮箱随便填用于标识 ssh-keygen -t rsa -b 4096 -C jenkinsexample.com -f ~/.ssh/jenkins_rsa第二步查看公钥并复制cat ~/.ssh/jenkins_rsa.pub第三步登录GitLab进入用户设置 → SSH Keys把公钥粘贴进去。第四步回到Jenkins进入Manage Jenkins → Credentials → Global选择类型为“SSH Username with private key”Username填你自己的GitLab用户名Private key把~/.ssh/jenkins_rsa的内容粘贴进去保存。第五步在Pipeline或自由风格任务的源码管理里选择GitRepository URL填gitgitlab.com:yourgroup/yourproject.gitCredentials选择刚才添加的凭据。这里有个巨坑我遇到过不止一次Jenkins服务器首次连接某个GitLab域名SSH会弹出“Are you sure you want to continue connecting (yes/no)”但Jenkins是非交互环境这个提示会让clone直接超时失败。解决办法是提前手动信任主机指纹ssh-keyscan -t rsa gitlab.com ~/.ssh/known_hosts如果你用的是Docker容器记得要进入容器执行这条命令写的是容器内的~/.ssh/known_hosts别在宿主机上刷了半天没效果。3.3 Webhook触发构建与Secret Token配置除了Jenkins主动拉取代码更常见的模式是GitLab主动触发构建。流程是开发push代码 → GitLab发一个HTTP请求给Jenkins → Jenkins执行对应Job。配置方法在Jenkins的Job页面找到“构建触发器”勾选“触发远程构建”填一个Token比如deploy-web-token。记下触发URL格式通常是http://jenkins服务器IP:8080/job/你的Job名称/build?tokendeploy-web-token在GitLab的项目设置 → Webhooks里填上URL再加一个Secret Token这个Secret Token是GitLab和Jenkins之间的校验码。触发事件里通常勾选“Push events”和“Merge request events”即可。配置完你可以先在GitLab后台点击“Test”按钮如果Jenkins对应Job立刻开始构建说明链路已通。如果点击测试没反应95%的可能不是URL错了而是网络不通或者过滤了请求。内网环境尤其常见Jenkins服务器防火墙要放行GitLab服务器访问8080端口的流量这个排查方向比检查Webhook内容更有效。4. Java Web应用的自动化部署实战4.1 自由风格任务 vs 声明式Pipeline很多人第一次做Jenkins自动部署Java Web应用用的都是“自由风格项目”界面上填Git地址、填构建命令、填构建后操作。这种方式对单个简单项目来说很直观但一旦项目变多或者构建步骤稍稍复杂一点自由风格任务的问题就暴露了配置分散在界面各个角落没法版本化你根本说不清上一个版本是怎么构建的。所以我建议从一开始就用声明式Pipeline把整个流程写成Jenkinsfile放到代码仓库根目录。收益有三个构建流程跟随代码仓库一起版本化代码回溯到哪个版本构建方式就回到哪个版本代码评审时评审人能看到构建流程的变更换一台Jenkins机器只要把Pipeline脚本拉下来流程一秒复现4.2 一份可直接落地的Jenkinsfile下面这份是我实际在用的一个Java Web项目的Pipeline包含拉代码、Maven打包、构建Docker镜像、远程部署这一整套pipeline { agent any environment { // 镜像仓库地址按你的实际环境修改 REGISTRY registry.example.com // 镜像名称这里用项目名 IMAGE_NAME my-java-web // 通过BUILD_NUMBER给镜像打唯一tag便于追溯和回滚 IMAGE_TAG ${BUILD_NUMBER} } stages { stage(拉取代码) { steps { checkout scm } } stage(单元测试与编译打包) { steps { // 如果你的pom.xml在子目录先加dir(xxx) sh mvn clean package -DskipTestsfalse } } stage(构建Docker镜像) { steps { sh docker build -t ${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} . } } stage(推送镜像到仓库) { steps { sh docker login ${REGISTRY} -u ${REGISTRY_USER} -p ${REGISTRY_PASS} docker push ${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} } } stage(远程服务器部署) { steps { sh ssh deployyour-server docker pull ${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} docker stop my-java-web || true docker rm my-java-web || true docker run -d --name my-java-web -p 8081:8080 ${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} } } } post { success { echo 构建部署成功镜像Tag为 ${IMAGE_TAG} } failure { echo 构建失败请查看Jenkins控制台日志 } } }肯定有朋友要问REGISTRY_USER和REGISTRY_PASS这种密码写在哪里绝对不能硬编码在Jenkinsfile里。正确的做法是在Jenkins里配置“凭据”然后在Pipeline中引用environment { REGISTRY_CRED credentials(registry-account) } // 使用方式 sh docker login ${REGISTRY} -u ${REGISTRY_CRED_USR} -p ${REGISTRY_CRED_PSW}credentials(registry-account)是Jenkins的凭据绑定语法自动生成两个环境变量xxx_USR和xxx_PSW拿到的就是你在凭据里填的用户名和密码妥妥地避免了明文密码入库。4.3 Jenkins内置环境变量把编译产物和版本号串起来这部分是“jenkins可用环境变量”这个热搜点里大家最常搜的。Jenkins内置了一批环境变量Pipeline和Shell脚本里都能直接使用我把最常用的几个列出来环境变量含义典型使用场景${BUILD_NUMBER}每次构建的唯一编号镜像Tag、版本号后缀${JOB_NAME}任务名称通知消息里标识哪个项目${WORKSPACE}当前构建的工作目录Shell脚本里拼接路径${GIT_COMMIT}当前构建对应的Git提交哈希追溯代码版本${GIT_BRANCH}当前构建的分支判断环境master分支发布生产${JENKINS_URL}Jenkins服务地址通知消息附上控制台链接${BUILD_URL}当前构建的完整地址邮件/钉钉通知里直达构建页我最常用的组合是${JOB_NAME}_${BUILD_NUMBER}当版本号。比如我的Java应用用Maven打包默认版本是1.0-SNAPSHOT但每次构建都应该能区分开。我一般会把产物重命名一下cp target/my-app.war target/my-app-${BUILD_NUMBER}.war这样归档到Nexus或传到服务器上时文件名带上构建号部署人员一眼能看出当前跑的是第几次构建的产物。镜像Tag用BUILD_NUMBER也是同样的理由永远不要用latest做生产镜像Tag你根本不知道latest指向哪一次构建一旦需要回滚就会对着镜像列表发呆。5. 团队协作与资源监控权限和可视化5.1 用基于角色的策略管住不同用户如果你的Jenkins只有一个人用那权限随便设置都行。但一旦团队超过三个人尤其是有了“谁都能点构建”、“谁都能删任务”的场景就必须上权限控制了否则早晚有人点错按钮导致生产环境被误发布。Jenkins默认的权限模型只有“管理员”和“登录用户可操作一切”太粗糙了。我一般都会装Role-based Authorization Strategy插件然后按下面这三步做第一步Manage Jenkins → Security授权策略改成“Role-Based Strategy”。第二步进入“Manage and Assign Roles”里创建一个Global Role比如developer角色只给Overall/Read权限。第三步在“Assign Roles”里把用户分配进去。如果还要控制某个用户只能看特定Job可以在Manage Roles的Item roles里新建角色然后用正则表达式匹配Job名比如给web-.*这个模式配置查看和构建权限用户就只能在web-开头的任务上操作。这套方案我在十几人的开发团队里用过最大的感受是权限收敛之后误操作的概率明显下降而且每个操作都能从Jenkins日志里追溯到具体用户出了问题不再需要互相猜疑。这里有个细节新建的普通用户初始状态下是看不到任何Job的很多人配置完权限后发现界面空空如也以为Jenkins坏了。其实只是没把Job的查看权限分配给他回到Item roles里补上对应权限就行。5.2 可视化界面查看内存和使用情况有朋友问“jenkins可视化界面怎么查看使用内存情况”这里我多说几句。Jenkins的仪表盘默认是看不到内存和CPU占用曲线的因为它是Java应用运行状态都在JVM里。想看有两条路。一条是系统自带的监控页。访问http://你的Jenkins地址:/monitoring如果安装了Metrics插件能看到JVM堆内存、CPU使用率、线程数的实时图表。这个地址不常被人提但排查性能问题非常好用。另一条是看宿主机资源。如果Jenkins是Docker方式部署直接一条命令docker stats jenkins就能实时看到容器占用的内存和CPU。Linux直装方式就用top或free -h重点看Java进程的RES内存是不是持续上涨。如果发现内存只增不减大概率是构建任务太多导致堆内存不够了去Manage Jenkins里调高JVM参数java -Xms1024m -Xmx2048m -jar jenkins.warWindows上部署的Docker容器对应调整的是容器内存限制docker run -m 2g这种。6. 构建报错排查实录与避坑清单6.1 高频报错无法访问Docker daemon或镜像仓库有不少安装了Docker插件的朋友在Jenkins里跑构建控制台报错长这样docker: error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting for connection这条报错看起来像Docker daemon出了问题但翻译成人话是执行docker pull时Docker daemon访问registry-1.docker.ioDocker Hub的默认仓库地址超时了。国内网络环境下非常常见不是你Jenkins配置错了而是默认镜像源不通。解决办法有两种第一种给宿主机Docker配置国内镜像加速器。以Linux为例编辑/etc/docker/daemon.json{ registry-mirrors: [https://docker.mirrors.ustc.edu.cn] }然后systemctl restart docker。第二种如果你用的是自己的私有镜像仓库那就在Pipeline里每个docker pull前先指定地址。比如你的仓库在registry.example.com镜像名写全registry.example.com/myapp:v1就不会去Docker Hub找自然也不受网络影响。排查这类问题有个顺序先在你构建节点上手动执行同样的docker pull命令如果宿主机上能成功说明问题出在Jenkins环境如果宿主机上也超时就是网络层面的事跟Jenkins无关。先划清责任边界再下手修效率最高。6.2 其他高频问题速查表下面这些是我在自动部署过程中遇到过的真实问题顺手整理成一个速查表症状可能原因排查方法构建执行到git clone时报Permission denied (publickey)凭据配置错误或SSH公钥没加到GitLab检查Credentials里的私钥是否完整GitLab端公钥是否存在Git clone报Host key verification failedJenkins首次连接GitLab没有记录known_hosts执行ssh-keyscan把主机指纹写入known_hostsMaven命令执行报command not foundJenkins节点PATH里没有Maven去Manage Jenkins → Tools配置Maven自动安装Docker命令执行报权限不足当前用户不在docker组sudo usermod -aG docker jenkins后重启Jenkins构建成功但部署没生效SSH目标服务器命令执行失败被吞掉在Pipeline里ssh后加-o StrictHostKeyCheckingno并把输出回显出来Jenkins磁盘使用率飙高历史构建产物和日志堆积配置“丢弃旧构建”保留最近10次构建有一个我强烈建议新手先做的优化在Job配置里打开“丢弃旧构建”设成保留最近5天或最近10个构建记录。Jenkins跑半年workspace和归档的构建产物就能吃掉几十GB磁盘不清理的话某天磁盘写满整个Jenkins直接假死连登录页面都刷不出来。真到了那一步恢复的功夫可比配这个参数麻烦多了。还有个小技巧是写脚本定期清JENKINS_HOME下的workspace和jobs/*/builds目录但既然界面里就有配置项建议优先用配置项解决别轻易动脚本搞不好把构建历史删错了又得来一次崩溃恢复。结尾说点实际体会Jenkins这套东西我刚接触时也觉得很麻烦JDK版本、插件兼容性、凭据配置、Pipeline语法每一环都有新坑。但真正把它当成一个基础设施来维护把流程沉淀成Jenkinsfile之后你会发现团队发布一次应用的步骤从半小时的半手动操作压缩成了点一下按钮、等两分钟、看个结果的事。这个回报远比当初搭环境花的时间值。我个人最深的体会是Jenkins的部署方式、插件选型都可以按自己习惯调整但“流程必须持续可复用”这一点不能妥协。构建脚本、部署脚本一旦只能跑在某个同事的电脑上就说明你的“自动化”还没落地。把流程写进代码仓库让任何一个新同事都能通过Jenkins独立完成发布这才算真正建设好了团队的自动化中枢。希望这篇文章能让你少走一些我走过的弯路。
返回列表