Jenkins自动化发布
手工发布做多了,每次打包、传包、重启一套流程下来既耗时间又容易出错。这篇记录我用 Jenkins 把整个发布过程串成流水线的配置步骤。
前言
在没有流水线之前,一次发布大概是这样:本地打包、scp 传到服务器、登上去停服务、替换 jar 包、再启动。步骤不复杂,但全靠人肉执行,环境配置改漏一个、包传错一台机器,都是线上事故的常见来源。把这些动作交给 Jenkins,一方面是省事,更重要的是让发布过程可重复、可追溯——每次构建都有记录,出问题能回滚到指定版本的镜像。
记录一下最近配置Jenkins的发布流程的部分步骤
整体流程:使用 Jenkins 进行线上发布并打包docker镜像上传至私有docker镜像仓库、并配置 docker login 线上推送/拉取镜像发布运行。
简单拆开看,这条流水线做了四件事:从 git 拉代码并替换环境配置、maven 构建出 jar 包、用 docker-compose 打成镜像推到私有仓库、最后远程通知目标服务器拉取新镜像运行。下面按这个顺序展开。
安装docker 拉取 Jenkins 镜像就不再描述,有很多现成的文章。
插件集成
Jenkins 本体只是一个调度器,构建能力基本都靠全局工具配置和插件补齐。所以第一步是在「全局工具配置」里把构建要用到的组件配好,Jenkins 支持自动下载安装,也可以指向宿主机上已有的安装路径。
配置并安装maven、jdk、git、nodeJs、docker等基本组件

工具配好之后,再到插件中心装对接外部系统的插件。这里的原则是按需安装:代码托管在 gitlab 就装 gitlab 插件,它能接收 gitlab 的 webhook 推送,实现提交代码后自动触发构建。
安装对应各种支持插件、如gitlab。

nodejs 插件前端发布使用
前后端可以走同一套 Jenkins,只是构建工具不同——后端用 maven,前端任务里指定 nodeJs 版本跑 npm build 即可。
最后还需要一个能连接远程服务器执行命令的插件。Jenkins 所在机器和线上运行的机器通常不是同一台,构建产物推到镜像仓库之后,得有办法通知目标服务器去拉取并重启,这一步就靠它通过 SSH 在远端执行脚本完成。
连接远程服务端并执行命令插件
创建流水线任务
插件就绪后开始建任务。任务的本质是把一串构建步骤按顺序编排起来,每个步骤指定用哪个组件、执行什么命令,上一步的产物就是下一步的输入。
创建任务、并配置任务各个环节执行命令与使用组件
首先配置拉取git库地址与项目代码
后续替换源码中的环境值、如dev环境替换为pro nacos 连接地址与命令空间等

替换环境值这一步值得多说一句。开发环境和生产环境的配置中心地址、命名空间往往不同,如果代码仓库里默认写的是 dev 配置,构建生产包之前就要把它替换掉,否则服务上线后会连到测试环境的 nacos。
我使用的是最简单的sed -i 命令,该命令可以匹配正则表达式从而替换文本字符串。
# -i 表示直接修改文件本身;s/旧值/新值/g 为正则替换,g 表示替换所有匹配
sed -i 's/dev-nacos-addr/pro-nacos-addr/g' src/main/resources/bootstrap.yml
sed 的好处是零依赖、脚本里一行就能解决;缺点是替换关系散落在 Jenkins 任务配置里,配置项多了不好维护。项目规模上来之后,可以考虑用 maven profile 或配置中心的多环境能力来做,把环境差异收敛到一处。
资源打包与推送
使用maven 进行jdk build,构建项目jar包

打包完成之后使用shell命令把target包下的jar包cp到对应docker-compose文件下,此处需要提前写好docker-compose文件与对应服务的docker-file文件。
之所以要提前写好这两个文件:Dockerfile 描述单个服务怎么打成镜像(基础镜像、拷贝 jar、启动命令),docker-compose 则把多个服务的构建声明汇总在一起,这样一条命令就能批量出镜像,不用每个服务单独 docker build。
执行docker-compose进行批量build镜像
build完成之后执行docker tag 对镜像进行版本打标
打标是为了让每次发布的镜像都有唯一版本号,而不是全部叫 latest。用 Jenkins 内置的构建号做 tag 是个省事的办法,回滚时直接指定历史版本号即可。
在镜像打标完成后docker push上传到镜像仓库
# 用构建号给镜像打版本标,再推送到私有仓库
docker tag myapp:latest registry.example.com/myapp:${BUILD_NUMBER}
docker push registry.example.com/myapp:${BUILD_NUMBER}
推送前记得 Jenkins 所在机器和目标服务器都要先 docker login 私有仓库,否则 push/pull 都会因为鉴权失败中断。
远程连接目标服务器或者k8s master 节点 执行job命令并给予镜像下载地址。
到这一步,构建机的工作就结束了。远程命令里只需要告诉目标服务器新镜像的完整地址,由它自己 pull 镜像并重启容器——构建和运行彻底分离,目标服务器上不需要装任何构建工具。
历史资源删除
打包之后上传到私服之后别忘记清空本地工作空间中无用的资源,以及docker镜像等资源,避免多次发布后出现资源占用过高问题。这样就可以形成一个闭环操作,可以多次发布项目版本。
清理可以直接作为流水线的最后一个步骤:删掉工作空间里的构建产物,再清掉本次构建出来的本地镜像。docker 的镜像是分层存储的,旧版本镜像不清理会一直堆积,几十次发布之后磁盘被占满,构建就会莫名失败。
踩坑与注意
-
Jenkins 跑在容器里时,想在任务里执行 docker 命令,需要把宿主机的 docker.sock 挂载进容器,否则容器内找不到 docker 守护进程。
-
私有仓库如果走 http 而不是 https,docker 默认会拒绝连接,需要在推拉两端的 docker 配置里把仓库地址加入 insecure-registries。
-
sed 替换配置时注意特殊字符转义,替换内容里带
/时可以换用#之类的分隔符,避免和 sed 自身的分隔符冲突。 -
远程执行命令的凭据(SSH 私钥、仓库账号密码)用 Jenkins 的凭据管理来存,不要明文写在任务的 shell 脚本里。
镜像 tag 尽量和 git 提交或构建号关联起来,排查线上问题时能快速对应到具体一次代码变更。
小结
整条流水线跑通之后,发布就变成了点一次构建按钮(或提交代码自动触发):拉代码、替换配置、maven 打包、docker-compose 出镜像、打标推送、远程拉起新版本、清理现场,全程无人工介入。相比手工发布,收益不只是快,更在于每一步都固化在任务配置里,发布结果稳定可预期,回滚也只是换个镜像 tag 的事。
评论 / COMMENTS