news 2026/9/17 6:10:43

Arbess+GitLab+Hadess:Java微服务自动化部署流水线实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Arbess+GitLab+Hadess:Java微服务自动化部署流水线实战

开头先亮个底:我最近把公司一套Java微服务项目的交付链路,从“开发自己打包、运维手动部署”的原始状态,改造成了基于Arbess、GitLab和Hadess三件套的自动化流水线。核心效果就一句话——开发把代码推到指定分支,剩下的编译、打包、推送制品、登录主机、停老进程、起新进程,全自动完成。这套方案折腾了大概两周,中间踩了不少坑,今天把完整思路和实操细节写出来,给正准备搞DevOps落地、尤其是预算有限不想上K8s的团队做个参考。

这套组合适合什么场景呢?我判断的标准很直接:你的Java项目还是以物理机或云主机为主要部署目标,团队已经有GitLab在管代码,但构建和发布还靠人肉;又或者你试过Jenkins,觉得维护插件、配置节点太麻烦,想要一条更轻的链路。只要符合其中一条,Arbess加Hadess这套玩法就比硬上Kubernetes划算得多,学习成本和运维成本都能压得很低。

1. 整体链路设计与选型思路

先说结论:Arbess在整个链路里相当于“总导演”,负责把GitLab的代码事件和Hadess的部署能力串成一条可编排的流水线;GitLab是代码仓库和触发源;Hadess则负责把构建好的Java制品分发到目标主机并执行部署动作。

1.1 为什么不直接全用GitLab CI或Jenkins

这个可能是大家第一个想问的。GitLab自带的CI/CD其实已经很强,单项目用完全够,但当我手上项目管理多了,每个项目都得写一份.gitlab-ci.yml,公共逻辑抽不干净,而且主机部署这种动作放在CI里总要靠ssh穿来穿去,权限和审计都很难受。Jenkins则是另一座大山,插件版本兼容性、Master-Slave节点管理、Pipeline脚本维护,动不动就给你个红色报错。项目多了之后,维护成本直接起飞。

Arbess强悍在它把“事件”—“编排”—“执行”拆成了三层:GitLab只负责产生push事件,Arbess负责定义“事件来了之后该怎么走”,Hadess负责真正到主机上干活。这样好处很明显,代码仓库不必再跟某个CI系统强绑定,不同项目可以用同一套编排模板,Java、Python、Node项目都能共存,以后想迁移到K8s,只需要把“主机部署”这个步骤换成“镜像部署”就行,编排层不用动。

1.2 三件套各自扮演什么角色

我类比一下:GitLab是前台的“收件柜”,开发把代码往那一放,系统就知道有新需求了;Arbess是中间那层“调度室”,它收到收件柜的通知,不会自己跑去拆快递,而是翻开自己的流程手册,把任务一个一个派下去;Hadess就是“执行专员”,专门负责到目标机器上把老服务停掉、新包放上去、把服务拉起来。

这个拆法的价值在于可替换性。我今天可以用Hadess做主机部署,明天想换成容器编排,只要在Arbess里把执行节点换掉就行;我也可以把Arbess背后的代码托管从GitLab换成其他平台,触发源调整一下,其他环节完全不受影响。这种解耦思路,是纯用Jenkins或纯用GitLab CI很难做到的。

1.3 我们最终跑通的链路全景

我画一条文字版的链路,大家先建立全局印象,后面每一段都会细拆。

  • 开发提交代码到GitLab的release分支。
  • GitLab通过Webhook把push事件推送给Arbess。
  • Arbess根据项目绑定的流水线模板,启动一次自动化流程。
  • 流程第一步调用构建节点,执行Maven编译打包。
  • 产物被推送到Hadess的制品库模块集中存储,每次构建都有唯一版本号。
  • 部署阶段,Hadess的Agent登录目标主机,执行备份、停服、更新、重启等动作。
  • 完成后Arbess汇总日志,把结果回写到GitLab的Commit状态上。

我之所以把“制品集中管理”这一步单独拎出来,而不是构建完直接scp上服务器,是因为生产环境出问题时你要能快速回滚到上一个版本,如果没有历史制品,回滚就等于重新构建,时间根本等不起。这条链路看着简单,真正落地要考虑的细节其实非常多。

2. 环境准备与组件部署

这套方案里GitLab和Arbess我用Docker部署,Hadess的Agent单独安装到目标主机,服务端走容器化。如果团队没有Docker基础,强烈建议先补一补,因为GitLab的依赖项很多,容器化能省掉大量系统兼容性折磨。

2.1 主机资源与网络规划

先算资源。我们公司的项目规模不大不小,30个Java服务、10人研发团队,我准备了两台8核16G的服务器,一台放在内网作为“管理中心”,跑GitLab、Arbess、Hadess服务端;另一台作为“构建执行机”,跑编译任务和Hadess Agent。两台机器都装了Docker和Docker Compose。

内存分配上有个小建议:GitLab比较吃内存,尤其执行代码质量检查时,给它留足6G左右;Arbess和Hadess服务端都是Java写的,各给2G就够;构建执行机上Maven构建非常吃内存,8G给构建JVM进程和Docker守护进程分。实际用下来,这套配置并发跑3个Java项目的构建不会卡。

端口规划也要提前想清楚,我列一下常用的:

  • GitLab Web端口:映射为80和443。
  • GitLab SSH端口:映射为222,避免跟宿主机SSH冲突。
  • Arbess服务端口:8000。
  • Hadess服务端端口:8081。
  • Hadess Agent通信端口:8082。

注意:如果服务器有防火墙,记得把这些端口加白名单。我在测试环境吃过亏,忘了放行Agent的8082端口,导致构建成功但部署永远卡在“等待Agent连接”。

2.2 用Docker快速拉起GitLab

GitLab的容器化部署很成熟,直接用一个docker-compose文件管理。我的docker-compose.yml大概长这样:

version: '3' services: gitlab: image: gitlab/gitlab-ce:16.4.1-ce.0 container_name: gitlab restart: always hostname: gitlab.internal environment: GITLAB_OMNIBUS_CONFIG: | external_url 'http://gitlab.internal' gitlab_rails['gitlab_shell_ssh_port'] = 222 prometheus_monitoring['enable'] = false ports: - '80:80' - '443:443' - '222:22' volumes: - /data/gitlab/config:/etc/gitlab - /data/gitlab/logs:/var/log/gitlab - /data/gitlab/data:/var/opt/gitlab shm_size: '256m'

首次启动大概需要等两三分钟,可以用docker logs -f gitlab观察初始化进度。等看到“gitlab Reconfigured!”的日志后,访问服务器IP,初次进入会让设置root密码。这里有个经验:如果想让研发直接通过域名访问,要把external_url设成你规划的域名,同时在内网DNS做好解析。如果暂时没有域名,先用IP访问也行,但后续配置Webhook时回调地址也得填IP,容易出乱子。

2.3 Arbess与Hadess的最小化部署

Arbess和Hadess都提供了官方Docker镜像,部署命令其实就一条docker run的事。我当时图省事,把两个服务写在同一个Compose文件里:

version: '3' services: arbess: image: arbess/arbess-server:latest container_name: arbess restart: always ports: - '8000:8000' volumes: - /data/arbess/data:/data environment: DB_HOST: mysql DB_PORT: 3306 DB_NAME: arbess DB_USER: arbess DB_PASSWORD: 'yourpassword' hadess-server: image: hadess/hadess-server:latest container_name: hadess-server restart: always ports: - '8081:8081' volumes: - /data/hadess/data:/data

Arbess依赖MySQL做元数据存储,先用Docker单独起一个MySQL实例,再启动Arbess。我最初图简单直接用H2内嵌数据库,结果重启几次后流水线历史记录丢了一部分,排查半天才发现是文件权限的问题。这里建议一步到位,测试环境也别省,直接上MySQL。

Hadess设计上更轻一些,服务端负责管理Agent的注册和制品索引,Agent装在每一台目标部署机上,负责执行实际的部署脚本。Agent安装包里就一个可执行文件加一份配置文件,改一下Server地址和认证Token,然后跑hadess-agent install就能注册成系统服务。

2.4 GitLab Runner注册与凭证配置

虽然前文说不想过度依赖GitLab CI,但构建动作还是要有人去跑的。我选择的方式是:Arbess触发构建时,调用的是独立构建机上的GitLab Runner,让Runner执行Maven命令。原因不复杂:GitLab Runner跟GitLab代码仓库之间拉代码最顺畅,权限模型也是现成的。

Runner注册主要记住几个参数:

sudo gitlab-runner register \ --url http://gitlab.internal/ \ --token glrt-xxxxxxxx \ --executor shell \ --description "java-build-runner" \ --tag-list "java,maven,build" \ --run-untagged=false \ --locked=false

我用的是shell执行器,因为Maven构建直接在本机跑最直接。如果想隔离得更干净,可以用docker执行器,让每个构建跑在独立容器里,但多了一层镜像管理的成本。注册完Runner后,把构建机的Java环境、Maven环境、私有制品库的settings.xml都配好,这部分属于“一次配置、长期受益”,做规范一点后面省心。

3. Java项目接入流水线的实操细节

环境搭好只是第一步,真正的重头戏是让Java项目跑通整条自动化流水线。这一章我按“构建—存储—部署”三段来写,每个环节都放关键配置和脚本。

3.1 Maven多模块项目的构建设计

现在Java后端项目基本没有单模块的,大多是多模块结构:父pom统一管依赖版本,下面挂common、service、web等子模块。构建时如果无脑mvn clean package,会浪费大量时间在重复打包不相关的模块上。

我在Arbess里为Java项目设计了两种构建模式,通过项目参数控制:

  • 全量构建:执行mvn clean package -DskipTests,产物为一个zip包,包含所有模块的Jar。
  • 增量构建:通过-pl参数指定需要构建的模块,例如mvn clean package -pl service-web -am -DskipTests,适合改动只涉及个别模块时的快速部署。

增量构建能把一个30个模块项目的构建时间从8分钟压到2分钟以内,这个优化对研发体验提升特别明显。但要注意,增量构建产出的制品是单个Jar,部署脚本里要对“本次是哪个Jar”有明确的标识,不然容易部署错模块。

还有几个构建参数需要统一管理:JDK版本必须跟项目要求一致,现在公司新项目基本都用JDK17,老项目还停在JDK8,所以构建机上要同时装两个JDK,通过JAVA_HOME变量切换;Maven的settings.xml里要配好私有仓库地址和认证信息,否则构建时候会卡在下载依赖。

3.2 流水线配置:从代码提交到制品归档的串联

Arbess里创建一条“Java主机部署流水线”模板,我这边配了4个阶段:拉取代码、编译打包、制品归档、主机部署。每个阶段之间可以设置“失败自动跳过后续阶段”,这个一定要开,避免代码编译失败后还在那傻傻执行部署。

日志里曾经出现过一出很尴尬的事:编译明明失败了,流水线居然继续往下跑,把上一次构建的旧包部署到了生产。查下来发现是Arbess的阶段状态判断没有配置好,失败条件没勾上“异常结束”。这种事故一旦发生,团队对这套系统的信任直接就崩了,所以阶段失败策略务必仔细检查。

流水线触发方式我强烈建议用“分支过滤+Tag驱动”:普通分支的push只触发构建并推送制品到测试环境,只有打上v*格式的Tag才触发生产部署。这是在安全性和便利性之间比较平衡的选择。直接在release分支上push就触发生产部署,听起来很爽,但研发提交一个临时彩色日志代码都可能发到生产,时间长了总会出事。

3.3 制品推送与版本管理

Hadess的制品库在阶段设计上很像一个简化版Nexus,支持按“项目—环境—版本号”三层结构来组织软件包。Arbess在构建完成后会调用Hadess的开放接口,把产物打成.tar.gz包推上去,版本号默认用时间戳-短GitCommit号

举个例子,这次构建的提交号是7f2c91e,那版本号大概是20241011-1530-7f2c91e。这个命名规则建议写死在流水线模板里,不要让人手工填。出问题时,凭版本号就能知道是哪天构建的、对应哪次代码提交,排查起来效率高很多。

制品归档时还有个小细节:要把Maven构建生成的build-info.properties或类似的信息一起归档进去,里面记录Git提交时间、分支、构建机的hostname等。这些在出事的时候就是“最后一根救命稻草”。

3.4 主机部署与优雅重启脚本

部署动作是整套链路里最容易出问题的一环。我先梳理一下正常流程:

  • Hadess Agent收到部署指令后,从制品库下载对应版本的tar包。
  • Agent把包解压到发布目录,例如/data/apps/yourproject/versions/20241011-1530-7f2c91e
  • 备份当前运行版本,备份到/data/apps/yourproject/backups
  • current软链接切换到新版本目录。
  • 执行停止命令、启动命令、健康检查。

这个顺序非常关键。很多团队部署失败是因为先停服务再解压文件,一旦新包有问题,旧服务已经起不来了。用软链接这个方案,整个切换过程是原子操作,新包启动失败时,只需要一条命令把current指回旧版本,服务就回来了。

我用的Java服务启停脚本核心部分如下:

#!/bin/bash APP_NAME="yourproject" BASE_DIR="/data/apps/yourproject" CURRENT_DIR="${BASE_DIR}/current" LOG_DIR="${BASE_DIR}/logs" # 停止旧进程 if [ -f "${CURRENT_DIR}/app.pid" ]; then OLD_PID=$(cat "${CURRENT_DIR}/app.pid") if kill -0 "$OLD_PID" 2>/dev/null; then kill "$OLD_PID" for i in {1..30}; do if ! kill -0 "$OLD_PID" 2>/dev/null; then break fi sleep 1 done # 如果还没有退出,强杀 if kill -0 "$OLD_PID" 2>/dev/null; then kill -9 "$OLD_PID" fi fi fi # 启动新进程 nohup java -Xms512m -Xmx1024m \ -jar "${CURRENT_DIR}/lib/service-web.jar" \ --spring.profiles.active=prod \ --server.port=8080 \ > "${LOG_DIR}/app.log" 2>&1 & echo $! > "${CURRENT_DIR}/app.pid" # 健康检查 for i in {1..60}; do if curl -f http://127.0.0.1:8080/actuator/health; then echo "Health check passed" exit 0 fi sleep 2 done echo "Health check failed" exit 1

这个脚本的细节值得说道说道:等老进程退出最多给30秒,超时就强杀,避免线程卡住拖着不退出;健康检查循环60次、每次间隔2秒,总共120秒,给足Spring Boot冷启动时间。如果项目比较大启动慢,这个时间还要调大。

4. 常见问题与排查技巧实录

这一章记录的每一个问题都是我在真实的落地过程中碰到的,不是网上抄来的。我把它们整理成速查表,再挑几个典型的展开讲讲排查思路。

问题现象可能原因解决办法
构建卡在下载依赖Maven私服地址配置错误或私服宕机配置settings.xml镜像为内网私服,添加健康检查
构建成功但部署超时Hadess Agent与Server网络不通检查Agent心跳,telnet测试8082端口
部署后服务频繁重启健康检查命令错误或端口配置不符确认服务实际端口,用curl的返回值作为判断
回滚后配置丢失配置文件包含在制品包外的路径共享配置放置到固定路径,不放进版本目录
Webhook触发不生效GitLab网络与Arbess网络隔离,URL填了localhost使用双方可达的内网域名或IP,看Arbess访问日志
并发构建互相影响多个构建共用同一个workspace目录配置Runner每次build前清理workspace,用日期子目录隔离
4.1 场景一:GitLab Webhook触发不稳定

这个问题困扰了我差不多一天。明明在GitLab后台测试推送事件时Arbess都能收到,但开发真正push代码后,流水线十次里只有七八次能跑起来。后来仔细排查发现,GitLab后台测试Webhook是直接发出请求的,而实际push事件触发时,GitLab会带一个token签名字段,Arbess验签失败的事件直接丢弃了。原因就在于我重新部署Arbess后,系统自动生成了一组新的Webhook密钥,而GitLab那边还留着老的配置。

解决办法:在GitLab项目设置里打开Webhook配置,把Secret Token重新填成Arbess界面里显示的最新token,然后点“Push events”旁边的Test按钮,确认返回200。这个坑的教训是,所有外部集成的凭据类配置,一定要纳入配置管理,别只存在部署人脑子里。

4.2 场景二:主机部署时老进程总是停不干净

用PID文件判断进程是否存在的方法,理论上没问题,但实际会碰到两种情况:一是应用进程被守护进程自动拉起,kill后又起来了;二是Spring Boot启动时PID文件里写的是Shell进程号,而不是Java进程号。前一种需要排查是否有systemd或Supervisor类守护工具,后一种要在启动命令里用exec java或在脚本里正确获取子进程PID。

还有一个比较隐蔽的场景:同一个服务端口被另一个进程占用,健康检查明明探测到了8080端口有响应,但那其实是老进程还没死透或别的服务。我现在的部署脚本里会先执行ss -lntp | grep 8080确认到底哪个进程在监听,避免把“端口通”当成“服务就绪”。

4.3 场景三:构建并发高时Maven内存溢出

团队规模一上来,并发构建的数量自然就上去了。两台构建机跑八个项目的构建,终于有一天Jenkins任务列表变成一片红色——哦不,是Arbess流水线列表全是失败。登录构建机一看,Maven编译进程表现得很不正常,GC日志频繁。原因很直接:Maven构建时默认的MAVEN_OPTS给的堆内存太小,多个构建同时跑直接内存溢出。

我的调整方案是在构建机的/etc/profile里加上export MAVEN_OPTS="-Xms1024m -Xmx2048m",同时给Arbess配置每个项目的并发上限为2个构建。并发拉的太高没有意义,构建主要瓶颈在CPU和网络,内存给够了就稳。

4.4 场景四:GitLab Runner创建不了构建日志文件

shell执行器的时候,Runner默认会以gitlab-runner用户的身份执行命令,但这个用户在很多系统上权限有限,连在项目目录创建文件都可能没有权限。我的处理方式比较直接,注册Runner时指定使用root用户执行,同时在Runner配置里把shell改成bash

需要说明,这样确实存在一定安全隐患,构建脚本里如果被塞了恶意命令,能拿到root权限。但在内网开发环境,团队代码本身就是可信的,权衡便利和风险后我选择了root。如果公司要求更严格,建议单独建一个构建账号,把需要用到的目录权限精确分配好。

5. 上线后的运维沉淀与心得

流水线跑通只是开始,真正难的是让它稳定地跑下去。这个阶段我主要补了三件事:回滚策略、日志监控、模板固化。

5.1 回滚策略怎么设计才不慌

我刚才提到用软链接的方式部署,天然就支持秒级回滚。实际做法是:部署成功后在versions目录下保留最近5个版本包,回滚时把current软链接指到目标版本,再执行一次重启脚本。因为制品和配置是分离的,回滚不会把环境配置带回旧状态,出问题的概率很低。

需要注意的是,回滚操作本身也应该走Arbess的流水线,而不是人肉登录服务器去改软链接。我给回滚做了一条专属流水线,参数是“项目名”和“目标版本号”,这样回滚动作也有审计日志,出了问题能追到人。

5.2 告警与通知别偷懒

自动化程度越高,告警就越重要。以前的模式是“发布失败有人立刻知道”,现在的模式是“发布流程完全无人值守”,那就必须让系统在关键节点主动通知到人。我用的是“企业微信机器人”配合Arbess的Webhook通知:流水线执行到“构建完成”和“部署成功”时各发一条消息到技术群,失败则带上具体阶段和日志摘要,@对应的负责人。

通知消息里一定要带上版本号和提交号,不然群里刷屏后根本不知道在说哪个项目。我曾经因为通知里没带项目名,导致同事在群里反复问“这是哪个服务的构建消息”,体验很差。格式上可以参考“服务名|环境|版本号|状态|耗时”。

5.3 把最佳实践固化成模板

到了这一步,我不建议每个项目各自为战地配置流水线,否则维护成本会重新把人拖垮。正确的做法是把最标准的那条链路——拉代码、构建、归档、部署、健康检查、通知——固化成Arbess的模板,新项目接入时只要填项目名、Git地址、部署主机、启动参数这四个字段就能跑起来,分钟级接入。

我复盘整套改造时有个体会:DevOps工具链不是越复杂越好,关键是切中团队的痛点。Arbess加GitLab加Hadess这套组合,避开了Kubernetes的高门槛,把Java主机部署这件事做到了自动化、标准化、可回滚,对我们这种几十个服务的团队来说性价比极高。如果你现在正被手动部署折磨,不妨按这套思路搭一遍,链路跑通的那个瞬间,你会觉得之前踩的那些坑都值了。

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

小型PLC通信全链路:RS-485、Modbus RTU/TCP与排错调优

简介:《小型可编程序控制器通信技术的问答(六)》是一份面向通信技术、通信工程及工业自动化技术开发人员的问答式技术参考文档,聚焦欧姆龙小型PLC在实际应用中的通信疑难。内容围绕P型机、CPM型机、CQ1V/H型机、CJ1V型机四大产品分类与结构特性展开&…

作者头像 李华
网站建设 2026/9/17 6:05:43

YOLOv8-Pose实战:从零构建实时人体姿态检测与动作计数系统

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/17 6:05:40

RAG知识库问答系统实操指南:从文档解析到检索生成的完整链路

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华