☰
GitLab与Arbess构建Java多主机自动化部署流水线
2026/10/10 15:31:47 网站建设 项目流程

手工部署Java项目这事儿,干过的人都懂那套流程有多磨人:先登录堡垒机,找到目标服务器,上传新的jar包,再kill掉旧进程,nohup重启,最后还要盯着日志确认起来没有。一台机器这么搞勉强能忍,三台五台甚至更多主机的时候,脑子里那根弦基本就绷不住了——这台改了那台漏了、这台环境变量没配、那台端口被占了,每次发布就像开盲盒。

今天要聊的这套Arbess+GitLab方案,就是专门治这个毛病的。Arbess是我们内部对这套自动化流水线平台的统称,你可以把它理解为一套自建的精简CI/CD调度器,GitLab负责管代码和触发信号,Arbess负责把“拉代码、编译、打包、分发、滚动重启”这一整串动作串起来,最终把Java项目自动部署到多台主机上。它能解决的问题很明确:构建环境统一、发布流程标准化、多主机部署不再靠人肉逐台操作。适合被手工发布折磨过的小团队、想给项目搭正经CI/CD但还没动过手的后端开发,以及刚接手部署运维工作的DevOps新人。

这套东西搭完之后,日常发布就变成了一件事——把代码push到仓库,剩下的交给流水线。下面我把整个方案的架构思路、环境搭建、流水线设计和坑点排查完整拆开讲,照着做基本能落地。

1. 方案整体思路与架构设计:先想清楚再动手

1.1 为什么必须上自动化构建部署平台

先说说手工部署最隐蔽的成本。很多小团队的Java项目一开始只有一台服务器,开发自己打包自己传,出问题自己回滚,好像也没多大事。但项目一旦进入多人协作、多环境(测试、预发、线上)、多主机集群的阶段,手工部署的短板就会集中爆发。

最典型的场景是“构建环境不一致”。张三电脑上编出来的包和李四电脑上编出来的包,可能因为JDK小版本不一样、本地maven仓库里的依赖版本漂移,产生莫名其妙的行为差异。每次排查问题,最后发现是“我本地是好的啊”。这本质上不是代码问题,而是构建过程不可控。自动化构建平台把编译环境固定下来,跑出来的包就是标准化的产物,这种“在我机器上能用”的扯皮会直接消失。

另一个痛点是多主机部署的“时间错位”。集群里有五台机器,手工发布时先改1号机,再改2号机,中间隔着几分钟甚至十几分钟。这期间前两台机器跑的是新版本,后三台还在跑旧版本,如果负载均衡没有及时摘流量,用户请求一分散,就会出现“一会儿新版本一会儿旧版本”的体验,甚至因为新旧版本接口不兼容直接报错。手工操作很难做到让集群内所有节点在同一时间窗口内完成切换,而流水线把分发和重启做成标准动作,节奏可以压到很紧凑。

还有一层是“可回滚性”。手工部署时,旧包往往随手丢在某个临时目录里,真要回滚,经常连上一个版本的文件都找不到。自动化平台天然会保留历史产物,回滚就是选择版本再跑一遍部署流程,几分钟搞定。这一点在线上出故障的时候,价值怎么强调都不过分。

1.2 核心架构与流水线链路拆解

整个架构可以拆成四个角色:GitLab负责代码托管和事件触发;Arbess作为流水线调度中枢,负责拉取代码、执行构建、管理产物、下发部署指令;应用集群主机是最终的部署目标,负责运行Java进程;另外一般还会有一台负载均衡器在前面承接流量。

流水线的主链路非常直观:

  1. 开发把代码push到GitLab的某个分支;
  2. GitLab通过Webhook向Arbess发送“代码有更新”的HTTP回调;
  3. Arbess校验回调签名后,触发对应的构建任务;
  4. 构建任务先从GitLab拉代码,回到Arbess的工作目录;
  5. 执行Maven编译、测试、打包,生成带版本号的jar产物;
  6. 将jar产物分发给集群内每一台目标主机;
  7. 各主机按照滚动策略依次拉起新版本,做健康检查;
  8. 全部主机新版本正常后,流水线标记为成功;任一节点异常则触发回滚。

这里面最核心的设计思路是“控制与执行分离”。Arbess只做调度和下发指令,真正的软件安装、进程管理、健康检查脚本在目标主机上执行。这种设计和Kubernetes里控制面与节点分离的思路是相通的,好处是Arbess本身不会成为单点故障——就算它宕机了,已经在主机上运行的Java进程不受影响,最多只是不能继续发新版。

用一个生活化的类比:GitLab是仓库管理员,负责登记货物入库;Arbess是调度台,负责排计划、派车、盯进度;每台主机像是独立的装配车间,收到货后自己完成组装和自检,自检结果再反馈给调度台。调度台不亲自去拧螺丝,但每一颗螺丝拧没拧到位它都知道。

1.3 技术选型背后的取舍逻辑

这套方案里有两个关键选型决定,值得多说几句。

第一个是“为什么用GitLab而不是其他代码平台”。GitLab是自托管、开箱即用的,它对Webhook、Deploy Token、分支保护这些CI/CD配套能力的支持非常成熟,而且私有化部署时数据不出内网,对很多公司来说这是硬性要求。相比GitHub的某些功能需要付费,GitLab Community Edition已经能把代码托管和触发通道这件事干得很完整。

第二个是“为什么构建和部署要分开,而不是让每台主机各自构建”。很多人一开始会想:既然每台主机都有JDK和Maven,那我让每台主机自己去GitLab拉代码、自己编译不就行了?省掉分发这一步。这个想法在几台机器的时候确实可行,但集群规模一大就出问题:每台机器都要一份完整的maven依赖库,磁盘占用翻倍;每台机器构建出来的包理论上一致,但实际会因为依赖缓存差异产生偶发的“灵异问题”;更关键的是,分发过程本身是一个天然的“发布前检查点”——在同一个地方集中构建、统一推送,方便做产物校验和版本管理。构建一次,分发多处,是性价比最高的方式。

还有一个容易被忽略的选型点:部署脚本不用做成Agent常驻进程,而是用Arbess通过SSH远程执行。Agent方案需要每台机器装额外的客户端,还要维护Agent自身的升级和心跳,复杂度提升不少。SSH方案简单直接,OpenSSH本身在所有Linux发行版上都有,一个免密配置就能打通,故障面小很多。对小团队和中等规模的集群来说,SSH执行脚本已经足够稳。

2. 环境准备与基础搭建:把GitLab和Arbess拉通

2.1 基础环境清单与网络规划

在动手之前,先把机器规划清楚,别等到装了一半发现内网端口互不相通,那才是最浪费时间的事。我这里给一套经过实测的参考配置,按最小规模来。

角色建议数量建议配置基础软件
GitLab服务器1台4核8G,100G磁盘GitLab CE、Docker(可选)
Arbess调度机1台4核8G,200G磁盘JDK17、Maven 3.8+、MySQL或H2数据库
应用集群主机初始3台2核4G起,按业务量加JDK17、OpenSSH、Java应用运行环境
负载均衡器1台2核4G起Nginx或HAProxy

网络规划上,重点关注三件事。

第一,GitLab和Arbess之间要能双向通信。Arbess要能访问GitLab的HTTP端口拉代码,GitLab要能回调Arbess暴露的Webhook端口。回调一般走HTTP 8080或者自定义端口,注意防火墙别把这个端口忘了。

第二,Arbess到各目标主机的22端口必须畅通。整个部署行为都依赖SSH,如果中间有防火墙策略,记得把Arbess这台机器的IP加白名单。

第三,目标主机之间最好也能互通,因为有些回滚脚本可能需要从别的节点拷贝产物,互不相通的话就得让Arbess中转,反而复杂。前期规划时把这些通道理清楚,后面会顺很多。

2.2 Arbess平台安装初始化

Arbess作为一套轻量的流水线调度平台,安装过程不复杂,核心是配好Java运行环境和数据库。

第一步先装JDK和Maven。JDK直接上17,这是目前Java生态里比较稳妥的长期支持版本,Maven用3.8以上。装完记得把Maven的settings.xml配好阿里云镜像或者公司内网私服地址,这一步非常关键,不然后面每次构建都去中央仓库下载,慢到你怀疑人生。

第二步把Arbess的服务端包解压到一个固定目录,比如/opt/arbess。它的启动方式就是一个Java进程:

java -jar arbess-server.jar --server.port=8080 \ --spring.datasource.url=jdbc:mysql://127.0.0.1:3306/arbess \ --spring.datasource.username=arbess \ --spring.datasource.password=你的密码

数据库可以用MySQL,也可以用轻量的H2。团队刚起步、不想单独维护数据库的话,H2更省事;如果后面要多人同时使用、保留大量历史记录,MySQL更靠谱。我建议一开始就用MySQL,免得日后迁移。

启动之后,打开Arbess的管理控制台,第一件事是配置一个“构建环境”。这个环境里指定Maven的路径、JDK的路径、默认的工作目录。Arbess执行构建时,会在这个环境里自动调用mvn命令,所以Maven和JDK版本一定要固定,不能今天17明天8,否则构建环境统一这件事就名存实亡了。

2.3 GitLab项目管理与令牌配置

GitLab这边要做的事,是把代码仓库准备好,并且给Arbess开一个“最小权限”的访问通道。

在GitLab上创建一个项目,比如叫做order-service,Java项目代码就推到这个仓库里。为了不让Arbess每次拉代码都输入账号密码,推荐用Deploy Token。在项目设置里找到Repository,新增一个Deploy Token,勾选read_repository权限即可。不要图方便去用个人Access Token,那玩意儿权限太大,而且绑定在某个人的账号下,人离职了整个流水线就没法拉代码了。Deploy Token是项目级的,权限收敛,生命周期独立,是正解。

拿到Token后,回到Arbess控制台,在“代码源管理”里把仓库地址填进去。地址格式类似http://gitlab服务器IP/group/order-service.git,认证方式选Token,把Deploy Token的用户名和密码填上。Arbess拉代码时用的就是这组凭据,平时它只在构建时访问代码,不需要任何写权限。

这个环节有一个容易踩的坑:GitLab默认配置下,请求频率限制可能比较保守,如果后面并发构建多了,偶尔会出现403。遇到这种情况,在GitLab的配置文件里把rate limit的阈值调大就行。先记着,后面排查章节再展开。

2.4 Webhook联动配置:从push到自动触发

流水线自动化的触发器是Webhook,配置时要把GitLab的“代码变化事件”和Arbess的“构建任务”绑定在一起。

登录GitLab项目,进入Settings → Webhooks,在URL里填Arbess接收回调的地址,一般长这样:

http://arbess服务器IP:8080/api/webhook/gitlab

Secret Token填一串自己随机生成的字符串,后面Arbess会用这个Token校验回调的真实性,防止别人伪造请求触发构建。触发事件勾选Push events即可,如果你希望合并请求也触发构建,可以额外勾选Merge request events,但初期建议保持简单,只保留Push。

保存之后,GitLab会提供一个Test按钮。点击Test,观察Arbess那边的日志,如果出现类似“webhook received, triggering pipeline”的记录,说明链路已经通了。这里注意,测试时要确认Arbess的8080端口对GitLab服务器是开放的,很多Webhook配了半天没反应,最后发现是防火墙把端口挡了。

Webhook通了之后,在Arbess里创建一个流水线任务,把代码源、分支名称(比如main或develop)、构建命令、部署目标等信息都填进去,然后手动执行一次,验证整条流水线能跑通。这一步先不追求多主机部署,只要能把代码拉下来、构建出产物,就算成功了。

3. 核心流水线实现:构建、分发与集群部署

3.1 流水线阶段拆解:拉取、编译、打包、分发、部署

Arbess里的一个完整流水线任务,通常拆成五个阶段。每个阶段之间是串行依赖关系,前一个失败,后面的不会执行。阶段划分如下:

阶段核心动作产出
1. Pull从GitLab指定分支拉取最新代码工作目录下的源码
2. Build执行Maven编译和打包target下的jar包
3. Test运行单元测试(可选但强烈建议)测试报告
4. Publish将jar包按版本号归档到产物库带版本标记的产物
5. Deploy分发jar包到各主机并滚动重启全部节点新版本就绪

这五个阶段里,Pull和Build是纯开发视角的,跟本地开发差不多;Publish和Deploy是运维视角的,约束了产物如何流转。把这五步串成流水线,本质上是把开发到上线之间的所有动作标准化。

阶段命名建议清晰直白,比如prepare-source、mvn-package、publish-artifact、deploy-all。Arbess的控制台里可以设置阶段超时时间,比如Pull阶段超过5分钟就失败,Deploy阶段超过10分钟就失败。超时失败是非常必要的保护措施,不然一个卡死的任务会一直占着构建节点。

3.2 Maven构建与产物管理的细节

构建这一步,命令本身不复杂,但有几个细节直接决定流水线好不好维护。

先看核心命令:

mvn clean package -DskipTests=false -Dmaven.test.failure.ignore=false

-DskipTests=false的意思是跳过测试的开关,把skipTests设为false,就是强制测试不跳过;-Dmaven.test.failure.ignore=false表示测试一旦失败,构建直接失败,整个流水线中止。很多团队为了“省事”把这两项设成了跳过,结果代码里全是红线自己还不知道,等线上炸了才后悔。测试阶段是流水线里成本最低的故障拦截点,千万别省。

为了保证产物可追溯,建议在构建命令前后把版本号算出来。Arbess环境变量里可以读取Git提交号(Commit SHA)和构建时间。用它们拼出产物文件名:

ARTIFACT_NAME="order-service-$(git rev-parse --short HEAD)-$(date +%Y%m%d%H%M%S).jar" cp target/original.jar /opt/arbess/release/${ARTIFACT_NAME}

这样产物库里的每个jar包都有唯一的标识,回滚时只要按名字找历史版本就行。

产物管理还要注意保留策略。Arbess的产物目录建议保留最近5~10个版本,超出后自动清理。线上出问题需要回滚时,找到上一版本或者上上版本就够了,保留太多历史包纯粹浪费磁盘。清理逻辑可以简单粗暴:保留最近N个文件,其余删除。用cron脚本定时跑一次即可。

3.3 多主机集群部署:SSH免密、分发策略与滚动发布

多主机部署是整个方案里最有技术含量、也最容易翻车的部分。先说SSH免密配置。

Arbess调度机要能以指定用户身份免密登录所有目标主机。在Arbess机器上执行:

ssh-keygen -t rsa -b 4096 -N "" -f ~/.ssh/id_rsa ssh-copy-id deploy_user@主机IP

每一台目标主机都执行一次ssh-copy-id,完成后测试:

ssh deploy_user@主机IP "hostname"

能正常返回主机名就说明通了。这里提醒一句:部署用的账号别用root,更别直接用root登录跑应用。建议每台机器创建一个专用用户,比如deploy,给予应用目录的读写权限,然后用sudo去执行受控的重启命令。权限收得越紧,线上出事故时的爆炸半径越小。

SSH通了之后,分发策略就简单了。Arbess把构建好的jar包用scp传到每台主机的固定目录,比如/app/order-service/releases/,这个目录下每个版本一个子目录,目录名就是版本号。然后执行滚动发布脚本。

滚动发布的核心思路是“逐台替换,全程不掉线”。具体步骤:

  1. 在负载均衡器上把当前要发布的主机从upstream里摘掉,让新流量不再进入这台机器;
  2. 检查这台机器上正在运行的旧版本进程,记录PID和当前启动命令;
  3. 停止旧进程,把旧jar包备份到/releases-backup目录;
  4. 把新jar包放到运行目录,用记录的启动参数启动新进程;
  5. 等待进程启动完成,执行健康检查(比如curl /actuator/health),确认返回UP;
  6. 把该主机重新挂回负载均衡;
  7. 重复上述动作,直到集群内所有主机都完成切换。

这里有个很容易被忽略的点:新版本启动后不要急着挂回负载均衡,一定要等健康检查过了再挂。如果新版本起不来,这台机器就一直摘着,等回滚到旧包后再挂回。这套滚动策略用一句话概括就是“先摘流量、再杀进程、换包重启、验活通过、恢复流量”。

3.4 健康检查与自动回滚机制

健康检查是整个部署链路里最不能省的一环,否则“自动化部署”就变成了“自动化制造故障”。Java项目如果是Spring Boot,最简单的健康检查就是Actuator接口:

curl -fsS http://127.0.0.1:8080/actuator/health

这个接口正常时返回{"status":"UP"}。在Arbess的部署脚本里,检查逻辑不只是一次curl,而是带重试的多次探活:

for i in {1..15}; do code=$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:8080/actuator/health) if [ "$code" == "200" ]; then echo "health check ok" exit 0 fi sleep 5 done echo "health check failed" exit 1

15次重试、每次间隔5秒,等于给新应用最多75秒的启动时间。Java进程在低配机器上启动确实可能慢,这个窗口要留足。

健康检查失败时,自动回滚的逻辑要接上。回滚的思路和发布是镜像的:把当前这台机器再次从负载均衡摘掉,停止新进程,从备份目录恢复上一个版本的jar包,用原来的启动参数启动,再次做健康检查,通过后挂回。整个过程在Arbess里可以封装成一个rollback任务,触发条件就是健康检查失败。

打个比方,这个过程就像换汽车轮胎:先把车稳稳架起来(摘流量),拆下旧胎(停旧进程),换上新胎(放新包),转动检查(健康检查),没问题了才落地放行(恢复流量)。万一新胎有问题,立刻换回旧胎,再检查一遍。发布的本质不是“换了新零件”,而是“保证行驶始终安全”。

4. 参数调优与问题排查:上线后更重要的那部分

4.1 关键配置项参考表

流水线能跑起来只是第一步,真正要长期稳定运行,有几个参数值得提前设置好。直接给一份参考值:

配置项建议值说明
流水线超时时间30分钟超过认为失败,避免任务卡死
构建阶段超时10分钟Maven第一次构建依赖较多,预留宽裕窗口
部署阶段超时15分钟多主机+健康检查,单台预留3~5分钟
SSH连接超时10秒防止主机失联时脚本长时间挂起
健康检查重试次数15次结合5秒间隔,覆盖应用启动慢的情况
日志保留天数30天过期日志自动清理,防磁盘满
构建产物保留版本数10个兼顾回滚余地和磁盘成本
并发构建数2避免同时多个构建把机器资源打满

4.2 常见坑点与排查实录

这套方案跑下来,遇到的问题是扎扎实实的,我挑几个最常见的写在这里,基本囊括了前期最可能踩的坑。

第一个坑是Webhook触发不成功。表现是push了代码,Arbess任务列表里没有任何反应。排查顺序先看GitLab的Webhook历史记录,里面有每次回调的请求和响应详情。如果显示连接超时或者拒绝连接,基本就是防火墙或者Arbess端口没开;如果显示能连上但返回非200,检查Secret Token是否一致。签名校验那步别嫌麻烦,一定要做,否则内网里随便一个请求都能触发你的构建任务,万一被恶意刷构建,机器直接被打爆。

第二个坑是Maven构建时依赖下载超时。这个在首次构建时尤其常见。解决思路有两个:一个是在maven的settings.xml里配置镜像仓库,把中央仓库的访问走国内镜像或者公司私服;另一个是在Arbess的构建环境里把本地仓库目录固定下来,比如/opt/maven-repo,并设为共享路径,这样第二次构建时大部分依赖都命中了缓存,速度能有数量级的提升。

第三个坑是SSH执行部署命令时提示Host key verification failed。这是因为Arbess机器的~/.ssh/known_hosts里没有目标主机的指纹。自动化的场景下,如果不想每次交互确认,可以给SSH配置加一行:

StrictHostKeyChecking no

放在Arbess机器的~/.ssh/config里,限定只对集群网段生效。安全洁癖者可能觉得放宽不好,但内网环境里这算务实的选择,把账号权限管住就好。

第四个坑是发布后一部分机器服务异常但没被发现。很多新人会把健康检查配置成只检查端口通不通,比如nc -vz 127.0.0.1 8080,结果应用进程虽然起来了,但内部状态根本不可用,端口照样通,等于白查。健康检查一定要打到业务接口上,Spring Boot的actuator/health是底线,更严格一点可以检查一个只读的DB查询接口,确保“依赖的资源也正常”。

第五个坑是滚动发布过程中负载均衡出现5xx。前面说了要先摘流量再发布,但很多人第一次操作时容易忘记在负载均衡上摘节点,或者摘了但没有等正在处理的请求结束就直接kill进程。建议摘节点后等待30~60秒(根据你的请求平均耗时来定),再停止旧进程,这样才能最大限度避免5xx。

4.3 性能调优与日常维护建议

流水线稳定运行一段时间后,关注的焦点会从“能不能跑”变成“跑得好不好”。这里有几个日常维护的点,亲测有效。

构建缓存是最值得投入的优化点。Arbess的构建环境要尽量固定,本地maven仓库不要动不动就clean。只要代码依赖不出现大的版本变更,第二次起的构建速度基本能控制在两分钟左右。定期给仓库做个瘦身,比如每周清理一次未使用的快照依赖。

Arbess调度机上的日志要配置滚动切分。Java进程的输出、部署脚本的执行日志、Webhook的访问日志,都按天归档,保留30天就好。不然跑三五个月,磁盘被日志塞满,Arbess自己先挂掉,那就尴尬了。我给每个部署脚本开头都加上时间戳,方便后续按时间线排查问题。

还有一个小建议:每台目标主机上的旧版本进程,别急着删。保留最近两个版本,配合负载均衡摘节点的方式,一旦线上需要临时切回旧包,手动操作也能在几分钟内完成。自动回滚脚本毕竟是程序,也会遇到程序解决不了的场景(比如数据库迁移不兼容),留一手总没错。

写在最后的一点经验

这套Arbess+GitLab的自动化部署方案,前前后后我调整过很多次,最大的体会是:自动化的价值不在于省去敲那几行ssh命令,而在于把“发布”从碰运气式的操作变成可重复、可预期、可回滚的流程。流水线跑通的那天确实很爽,但真正让我睡踏实的是健康检查和回滚脚本写好之后——那意味着就算新版本上线出问题,系统也知道怎么自己“退回来”。

最后再分享一个小技巧:部署脚本里的版本号、端口、健康检查地址这些常量,建议全部抽到Arbess的任务变量里,不要硬编码在脚本中。这样以后改端口或者加主机,只需要在控制台改配置,不需要动脚本本身。别小看这一步,等你维护了五六个项目的流水线之后,就知道这有多重要了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询