基于Jenkins+Gitlab的自动化部署实战
Contents
故事背景
一个中小型企业,是典型的互联网公司,当初期的时候可能运维只能标配到2~3人,此时随着公司的发展,项目会逐渐增多。前期部署项目可能都是手动的,
俗称“人肉部署”,这简直是无比的痛苦,不能忍受的。这样开发的时间也会耽误,运维的时间也会耽误,全都浪费在这些重复性的工作上面,毫无价值可言,
这时候运维终于忍受不了,上了脚本。但是慢慢的发现项目依旧在增长,脚本每次还要更改给开发,效率低下,后来测试环境以及开发环境直接上了jenkins,
每台开发机器是jenkins agent端,自此,开发环境运维终于解脱了出去。但是线上上线运维依旧、所以得定制一套线上上线的流程标准,然后上jenkins自动化。
技术实现原理
为什么要自动化:人肉部署的问题不是慢,是不可重复
手动上线每次执行的人、顺序、环境都不完全一样,出了错只能靠回忆排查;脚本化之后每次上线都是同一串命令——出错可复现、过程可审计。这是“人肉→脚本→平台”这条演进路线的内在逻辑:脚本解决可重复,Jenkins 解决“谁来触发、在哪执行、给谁看”。
为什么标准化是自动化的前提(本文的关键论点)
脚本要能通吃所有项目,前提是所有项目长得一样:目录、包名、分支、日志路径全部统一。存在一个特例,脚本就要多一个分支判断,特例多了自动化反而比人工更累——这就是前文“严格标准并落地执行”的因果。也是为什么 PHP/Python 项目在这个体系里更简单(代码即成品,git pull 即发布),Java 要多出“编译打包”这一环。
Jenkins 在这套系统里的角色
Jenkins 服务器自己是自己的 agent(master 直接执行任务),它的价值不在算力而在:把“上线”收敛成一个可点击的 job(服务名+IP 命名)、按权限分发给运维/开发、留下每次执行的日志。真正干活的是 job 调用的 shell 脚本——Jenkins 只是那根“手指”。
优雅上线的完整闭环
下线负载 → 更新 → 健康检查 → 重新上线,四步里最容易被省略的是第一和第三步:不摘流量直接重启,用户会撞上 502;不检查就挂回负载,坏版本直接面向全网。脚本示例里省略的 5/7/8 步(作者已注明)正是这两环。
前提标准
想要实现自动化的前提是标准化,例如程序的日志目录、程序目录、程序目录命名、代码分支、代码命名规则、程序高可用. 针对以上内容我们给开发做了
严格的标准并落地执行。
在此我会以Java程序为例子,因为我见到的最多的就是java程序比较麻烦,而php或者python可能只需要在服务器上git pull更新一下代码就可以了。
tomcat规则: 每台服务器放置一个tomcat,tomcat使用ROOT.war,并配置日志切割
程序目录:统一使用tomcat进行管理,所有的项目统一打出war包,放置tomcat下面命名为ROOT.war
程序日志:统一放置在规定的目录,例如: /apps/logs/$app.log
代码分支:不同的环境使用不同的分支,开发 dev分支, 测试 test 分支, 预发布 pre分支, 生产线上 master分支。分支隔离,不同环境取不同环境的配置
代码打包: 因为是java的代码,我们选择的是使用maven进行打包,开发只需要关心代码层即可
高可用: 每个程序必须支持多节点部署,不可出现单点故障的情况,否则不予上线.
自动化部署系统
因为中型公司不可能配置运维开发,而开发只管开发的,所以运维只能是通过使用开源工具的方式来搭建自动化部署系统,组件如下图:

上图的Jenkins服务器即自己是自己的agent,gitlab服务器是代码仓库服务器,Jenkins服务器会调用脚本,脚本做一些相应的动作进行自动化上线。
自动化上线流程:

下面进行步骤的分解:
-
运维人员登录jenkins,在jenkins界面点击相应的job,每个job是更新一个主机,job以服务名+ip组成,点击后jenkins会调用Shell发布脚本,下面这些都是脚本完成
-
发布脚本所做的第一步就是获取此项目的最新的代码版本
-
发布脚本所做的第二步是使用maven进行打包,每个maven打包的参数都统一
-
将打包好的war包拷贝到目标服务器上面
-
将需要上线的主机在前端负载haproxy上面进行下线(针对核心业务,建议这么做,比较优雅,不太暴力)
方法参考: <http://www.cnblogs.com/topicjie/p/7106860.html>
6.重启目标主机的tomcat服务
7.测试url访问,返回是否正常
8.在haproxy上线该主机
脚本实例
下面是一个线上使用的上线脚本,scp是通过ssh免密的方式,并且部署tomcat是java用户. tomcat路径是/usr/local/tomcat,每台主机一个tomcat
脚本必须指定要更新的主机,上线代码路径
分支线上默认master ,mvn配置为product参数
注意: 此脚本中不包含 5,7,8 步骤,如果需要,请自行补充。
|
|
脚本说明
- main 逻辑前有三道保险:check_user 强制 java 用户运行(防 root 手滑,脚本里有 rm -rf);usage 处理参数个数与 -h;check_host 用 inc_host 白名单核对目标 IP——IP 打错也碰不到清单外的机器;
- 三个函数即流水线:code_pull 拉最新代码(git pull 失败立即退出)→ mvn_build 以 product 配置打包(编译失败立即退出)→ push_remote 推送发布。每步失败即停,坏代码推不到线上;
- push_remote 的细节:先
rm -rf ROOT*清旧包再 scp 新 ROOT.war(防新旧包并存时 tomcat 解压出两个应用);先 stop、sleep 3 再 start,是给 tomcat 留出释放端口的时间; - 首次运行前:代码要手动 clone 到 CODE_PATH(脚本注释已说明),java 用户的 ssh 免密也要提前配好。
自此,以上可以实现在jenkins点击一下,服务一会自己就上好了,虽然说还有很多地方需要改进,但是一般中小型公司采用这种方式则是足够了,
只能持续的进行优化,当然,再厉害一点的公司可以自己开发运维平台。
踩坑记录与注意事项
- rm -rf 的路径来自变量:Tomcat_PATH、CODE_PATH 定义错,清的就是错的目录,脚本上生产前先 echo 一遍路径人工核对;
- sleep 3 不是精确等待:大应用停机可能超过 3 秒,稳妥做法是循环检测端口/进程消失再启动,否则双进程抢端口;
- 健康检查别省:脚本没做第 7 步,意味着坏包也会被挂回负载;上线后先 curl 探活再上线到 haproxy 才是完整闭环(原理见上文);
- 滚动发布一台一台来:脚本单次只接受一个 IP 正是为此,多节点同时重启等于自己制造停机;
- Jenkins 自身是单点:job 配置、凭据都在它身上,定期备份 JENKINS_HOME;凭据用 Jenkins 的 Credentials 存,别明文写进脚本再进 git;
- master 直发生产是简化模型:更严格的流水线会加 tag 发版与审批,规模到了再演进不迟。
Author 软件开发大郭
LastMod 2021-11-03