ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

GitLab + Arbess + OSS:构建可追溯的 Java 制品流水线

GitLab + Arbess + OSS:构建可追溯的 Java 制品流水线 1. 为什么要用 Arbess 把 GitLab 和 OSS 串起来1.1 从“构建靠人盯”到“流水线自动跑”的转变先交代背景。我们团队内部有大量 Java 服务代码都放在自建的 GitLab 上但很长一段时间里构建、打包、传服务器这些环节都靠开发自己手动执行。开发在本地跑mvn package再把 JAR 用 scp 传到服务器遇到多模块项目经常漏传依赖包构建机器上残留的历史包一多连哪次提交打出来的都不知道。后来我们引入了 GitLab Runner 做 CI虽然能解决一部分自动构建的问题但管理多个 Runner、维护.gitlab-ci.yml里的复杂规则、跨项目复用流水线成本都很高。真正让我下决心切换的是团队开始要求“所有制品必须进统一制品库”。GitLab 自带的 Package Registry 可以存但要接入现有的发布审批流程别扭而且运维想按目录、按版本、按分支精细控制保存周期Package Registry 的可见性模型不够灵活。这个时候我们开始试 Arbess。Arbess 可以简单理解成一个偏向“流程编排”的 DevOps 平台它和 GitLab 的集成非常直接你可以拉取 GitLab 仓库、读取提交事件、触发构建任务也能在同一个平台里把构建、测试、上传制品甚至后续的部署动作串成一条可视化流程。我这边把最终目标定为GitLab 代码提交后自动触发 Arbess 流水线流水线里完成 Java 项目构建再把 JAR 制品上传到阿里云 OSS 做统一归档。这样做的好处是后续部署系统直接从 OSS 下载指定版本即可彻底摆脱了“从某台构建机目录里翻包”的原始状态。1.2 对比 GitLab CI、Jenkins 之后为什么留了 Arbess我当时把三个方案放在一起比过原生 GitLab CI、公司已经在用的 Jenkins、还有 Arbess。这里贴一张我评估时用的对比能直观看出为什么最后留下 Arbess。对比项GitLab CIJenkinsArbess流水线定义方式YAML写起来灵活但规则一多容易失控页面配置 脚本自由度高但插件维护成本高可视化节点编排也支持脚本节点门槛最低跨项目复用需要模板继承与 include配置思维偏代码靠共享库初期搭建成本高节点模板可以直接复用适合多项目推广与 GitLab 交互原生不需要额外打通通过 GitLab Plugin 配置偶尔出现 token/权限问题内置 GitLab 集成Webhook 和 API 配置都比较直观制品上传 OSS要自己写 runner shell 脚本且对非 GitLab 仓库不友好脚本自由度最高但也最容易写得乱可以把上传封装成独立节点后续其他项目直接拖学习成本中高高低强调一下我并不是说 GitLab CI 或 Jenkins 不行而是我们团队需要一个“让运维能画流程、开发能点按钮”的平台。Arbess 的这种流程编排方式对非 CI 专职人员比较友好。另外一点很关键Arbess 的节点执行日志可以集中看到构建失败时不需要登录 GitLab 去翻 Runner 日志省了很多沟通成本。1.3 这条链路解决的核心问题制品可追溯很多团队对“构建成功”的理解就是看到日志里出现BUILD SUCCESS。但放到运维视角这远远不够。上传到 OSS 之后每个 JAR 都带上了分支名、提交号、构建序号我们在任意时刻都能回答三个问题这个制品是哪个分支、哪次 commit 构建出来的对应的源码在哪里可以看如果这个制品有问题当时用的是哪个 JDK、哪个 Maven 版本、哪份配置文件这就是“制品可追溯性”。把 GitLab 的提交信息作为变量的来源在 Arbess 里传递给构建和上传节点OSS 的对象命名里直接体现这些信息。后面一旦线上出了诡异问题我不需要再去问开发“你什么时候传的包”直接看 OSS 里对象的最后修改时间和元数据就能定位。2. 开工前的三样准备GitLab 令牌、Arbess 服务、OSS 权限2.1 GitLab 侧要准备什么Arbess 要读取 GitLab 项目、接收推送事件第一步就是在 GitLab 里创建 Access Token。这里我有过惨痛教训。最开始为了图省事建了一个read_user权限的 Token配置到 Arbess 后一直报错日志里就是那句经典的login failed. check api token or gitlab version. log in via git if the version...一开始以为 GitLab 版本太老排查了半天。后来用 curl 直接调 GitLab API 才发现是 Token 的 scope 权限不够。Arbess 拉取项目列表、拿分支信息、检查合并请求状态最少需要read_api或api权限如果还需要它帮你创建 Webhook那就必须给apiscope。建议直接建一个专用的 Bot 账号给它Maintainer级别Token 勾选api、read_repository两项。另外有个和热词相关的常见问题“your account is pending approval from your gitlab administrator”。自建 GitLab 如果开启了 LDAP 或新用户审核新建的 Bot 账号没有通过管理员审批时Arbess 连接时会一直失败。这个错比较隐蔽因为页面上不会指向 GitLab 账号状态。遇到这种情况先进 GitLab Admin Area 把账号激活再回 Arbess 重连。Token 创建好之后建议先用下面这条命令验证确认不会等到配置完 Arbess 才发现问题curl --header PRIVATE-TOKEN: your_access_token \ http://gitlab.example.com/api/v4/projects?per_page1simpletrue能正常返回 JSON就说明 API 通路没问题。2.2 Arbess 服务本身要注意的配置Arbess 的部署方式各家不一样我这里默认你已经有一套可用的 Arbess 服务。在 Arbess 里配置 GitLab 连接时有三项最容易踩坑GitLab URL必须填内网其他机器也能访问的地址不要填localhost或127.0.0.1。很多团队部署在容器里Arbess 服务容器里访问宿主机的 GitLab应该用宿主机局域网 IP。SSL 验证如果 GitLab 是自签名证书建议先在测试环境关掉验证否则会卡在证书信任上。内部使用没有太大风险但如果是公网环境还是建议开启并导入证书。API Version与 GitLab 版本的兼容性。Arbess 底层走的是 GitLab REST API v4如果你的 GitLab 还停留在 11.x 之前的老版本很多接口路径对不上登录时的报错会和 Token 错误混在一起非常误导人。至少升到 13.x 以上再排查其他问题。还有一点如果 Arbess 和 GitLab 在同一台机器上务必确认构建任务的临时工作目录权限。Arbess 运行用户必须对 workspace 目录有读写权限否则拉取代码时会报“Failed to create workspace”或者权限拒绝。2.3 OSS 侧Bucket 规划和最小权限OSS 这边不能上来就拿主账号 AccessKey 配置到流水线里。我的建议是创建一个独立的 Bucket例如devops-artifacts读写权限设为“私有”不对外公开。在 RAM 里创建一个子账号只授予 OSS 相关权限不要给 ECS 等资源权限。权限策略从最小开始一般流水线需要oss:PutObject、oss:GetObject、oss:ListObjects不需要 DeleteObject清理过期制品可以交给 OSS 生命周期规则而不是让流水线误删。以下是一份可以直接用的 RAM 策略示例{ Version: 1, Statement: [ { Effect: Allow, Action: [ oss:PutObject, oss:GetObject, oss:ListObjects ], Resource: [ acs:oss:*:*:devops-artifacts, acs:oss:*:*:devops-artifacts/* ] } ] }这里“Resource”里既写了 Bucket 本身也写了 Bucket 下所有对象。如果用了oss:ListObjects一定要把 Bucket 级别的 ARN 也加进来否则在控制台或工具里列举对象会失败。关于 Endpoint提醒一个容易忽略的点阿里云 OSS 的 Endpoint 分成公网和内网。如果 Arbess 构建节点部署在阿里云 ECS 上而且 ECS 与 OSS Bucket 在同一个地域应该用内网 Endpoint比如杭州是oss-cn-hangzhou-internal.aliyuncs.com速度快且不产生下行流量费。但如果你把流水线搭建在公司自家机房那就只能用公网 Endpoint。千万别为了“看着统一”把内网 Endpoint 写在本地构建环境里那样会一直连不上。3. Java 构建任务在 Arbess 里的落地步骤3.1 构建参数先想清楚别把乱写脚本当“灵活”很多人在 Arbess 里创建构建节点时图省事直接写一行mvn clean package看起来没问题但放到多模块 Java 项目上就会埋雷。比如你的仓库里有parent、common、service-a、service-b多个模块而这次 MR 只改了service-a全量构建所有模块不仅浪费时间还可能因为某些模块代码不规范导致整体失败。我的建议是在构建节点的脚本里用如下方式mvn clean package -pl service-a -am -DskipTestsfalse解释一下参数的含义-pl service-a表示只构建service-a模块-am表示“也构建依赖到的模块”比如common但不会构建无关模块-DskipTestsfalse表示测试照跑除非有特殊原因否则不要跳过。还有一个实际问题就是 Maven 仓库下载慢。国内环境建议在settings.xml里配置阿里云 Maven 镜像。在构建节点脚本里可以直接指定mvn clean package -pl service-a -am \ -s /opt/maven/conf/settings-aliyun.xml我在settings-aliyun.xml里配置了mirrorOf为central的maven.aliyun.com镜像仓库。这样即使不专门做二次缓存构建速度也比默认中央仓库快很多。3.2 在 Arbess 里编排节点每个节点只干一件事Arbess 的流程编排是节点制的我习惯把 Java 项目构建拆成几个独立节点而不是把所有命令堆在一个节点里。推荐的最小节点顺序是Git 拉取节点指定 GitLab 仓库地址、分支、以及要用的凭证。Maven 构建节点执行编译、单测、打包。信息提取节点从构建产物和 GitLab 提交记录中提取版本号、提交号、分支名。OSS 上传节点把 JAR 按照约定路径传到 OSS。通知节点构建完成后发钉钉或企业微信消息里面带上制品路径和下载链接。每个节点之间可以通过变量传递数据。比如 Git 节点拉取代码后会暴露GIT_COMMIT、GIT_BRANCH这样的变量上传节点读取这些变量来拼 OSS 对象名。不要在一个节点里既编译又把上传路径写死后面换分支策略或加版本规则时你会很想回去重做。如果你需要在构建节点里先看一次 Maven 全量依赖树可以用mvn dependency:tree -pl service-a -am确认依赖范围没有意外后再执行完整构建。这一条在从老 Jenkins 迁移项目时特别有用能快速看出某个模块是不是偷偷依赖了别的模块的 SNAPSHOT 版本。3.3 制品命名把可追溯性落实到文件名构建成功后的 JAR 默认名字一般是service-a-1.0.0-SNAPSHOT.jar这种命名在本地开发没问题但统一归档到 OSS 时遇到不同分支打出同一个版本号后上传的会覆盖先上传的。我的处理方式是重命名 JAR把分支和短提交号拼进去。可以在 Maven 构建之后、上传之前加一个 Shell 节点APP_NAMEservice-a VERSION1.0.0 BRANCH${GIT_BRANCH##*/} # 去掉 refs/heads/ 前缀 COMMIT${GIT_COMMIT:0:8} BUILD_NUM${BUILD_NUMBER:-unknown} JAR_FILEtarget/${APP_NAME}-${VERSION}.jar # 防止同一个 JAR 被重复构建时旧文件残留 ls -l ${JAR_FILE} OSS_KEYreleases/${APP_NAME}/${BRANCH}/${COMMIT}-${BUILD_NUM}/${APP_NAME}-${VERSION}-${BUILD_NUM}.jar echo OSS_KEY${OSS_KEY} build.properties为什么要把OSS_KEY写成变量写入临时文件而不是硬编码到下一个节点因为后续如果要在上传节点之外做审计我们需要一个统一的元数据来源。从build.properties里读取能保证上传脚本、通知消息里引用的路径完全一致不用维护两套。如果你用 Spring Boot 项目构建产物通常是target/*.jar但里面会有.jar.original这种文件上传前最好用find精确匹配find target -maxdepth 1 -name *.jar ! -name *.original避免把 Maven 插件生成的临时文件一起传上去。4. JAR 如何安全可靠地进 OSS4.1 上传方式选型脚本优先SSH 不参与OSS 上传有几种常见姿势阿里云 CLI、ossutil、各种 SDK。在 Arbess 的节点里我推荐ossutil命令行工具理由有三点。它只有一个二进制文件不依赖 Java 环境构建节点只要能用 Shell 就行。它原生支持断点续传、分片上传、并发控制传大 JAR 包很稳。配置简单可以通过环境变量或--access-key-id、--access-key-secret参数传凭证无需在机器上留下明文配置文件。安装非常直接curl -L -o /usr/local/bin/ossutil \ https://gosspublic.alicdn.com/ossutil/ossutil-v1.7.19-linux-amd64.zip 2/dev/null # 这里是简化示意实际可以解压后放置如果构建节点已经装了python也可以选择阿里云 OSS Python SDK但维护成本会高一层而且如果只有上传这一个诉求脚本方式更轻。方式适合场景缺点ossutil流水线内单文件/批量上传额外安装一个二进制阿里云 CLI同时操作多种阿里云资源依赖较大配置偏重Java SDK需要在代码里做复杂逻辑每次上传都要写不少代码OSS Browser人工传包不适合自动化4.2 编写一个可复用的上传脚本以下是我放在 Arbess OSS 上传节点里的脚本精简版。它的核心工作是读取上一个节点生成的变量文件检查 JAR 存在调用 ossutil 上传并输出对象的外链地址。#!/usr/bin/env bash set -euo pipefail source ./build.properties OSS_BUCKETdevops-artifacts OSS_ENDPOINToss-cn-hangzhou.aliyuncs.com # 根据实际区域修改 # 推荐通过 RAM 子账号的 AccessKey 环境变量传入 OSS_AK_ID${OSS_ACCESS_KEY_ID} OSS_AK_SECRET${OSS_ACCESS_KEY_SECRET} LOCAL_JAR$(find target -maxdepth 1 -name *.jar ! -name *.original | head -n 1) if [[ -z ${LOCAL_JAR} ]]; then echo [ERROR] 未找到可上传的 JAR 包构建产物可能未生成 exit 1 fi echo [INFO] 待上传文件${LOCAL_JAR} echo [INFO] OSS 目标路径${OSS_KEY} ossutil cp ${LOCAL_JAR} \ oss://${OSS_BUCKET}/${OSS_KEY} \ --access-key-id ${OSS_AK_ID} \ --access-key-secret ${OSS_AK_SECRET} \ --endpoint ${OSS_ENDPOINT} \ --content-type application/java-archive \ --force echo [INFO] 上传完成脚本里每个细节都可以展开说一说。set -euo pipefail保证任何一步失败都会让节点直接失败而不是“看似跑完但制品没传上去”。--force覆盖同名对象配合我们的唯一命名规则这个覆盖几乎不会误伤旧制品。--content-type不设置的话ossutil 可能会根据 JAR 的二进制内容识别成application/octet-stream。虽然不影响下载但后面如果接阿里云 CDN 或做 Head 请求时Content-Type 不对会带来小麻烦。4.3 我用过的几种上传失败的典型场景第一类Endpoint 和 Bucket 区域不匹配。报错类似NoSuchBucket或者InvalidAccessKeyId。以前我在北京区域的 ECS 上配了杭州区域的公网 Endpoint明明代码逻辑没错但就是传不上去。排查步骤很简单在 ECS 上用curl访问一次 OSS 域名看通不通再检查 Endpoint 属于哪个区域。第二类AccessKey 权限问题。报错AccessDenied时优先检查 RAM 策略里的 Resource 是否包含 Bucket 本身和/*对象。我见过有人只授权了devops-artifacts/*结果ossutil ls这个 Bucket 能列出来真正cp对象时却拒绝因为PutObject对单个对象的权限确实有了但某些版本的 SDK 或工具会先要求 List 权限。把策略改成我上面那个 JSON 示例就好。第三类上传路径中包含特殊字符。如果GIT_BRANCH带上了feature/xxx这种斜杠OSS 对象名里会出现子目录这没问题但如果你直接在 JAR 文件名里放中文或空格上传后 URL 访问时会遇到编码问题。我的习惯是所有路径统一使用小写英文、数字、斜杠和中划线分支名中的/保留作为目录层级但分支名里的其他非法字符要主动替换成-。4.4 生命周期清理制品不是传上去就一劳永逸制品库最怕无限膨胀。如果不做清理一次构建 500 MB 的胖 JAR全团队一天触发 20 次流水线一个月就是 300 GB。OSS 的存储费用虽然不贵但真没必要留那么多“垃圾”。建议在 OSS 控制台给 Bucket 配置生命周期规则releases/*/master/*保留 180 天releases/*/develop/*保留 60 天releases/*/feature/*保留 30 天。snapshots/目录直接 7 天过期。具体天数按团队需求来重点是不要所有目录一刀切。如果不小心把生产稳定版本也设成 30 天清理某天回滚时就会发现找不到旧包非常尴尬。5. 从“构建成功”到“制品可用”验证、排错与日常维护5.1 端到端验证怎么做得快又准流水线配置完第一件事不是直接改代码触发而是先在 Arbess 里手动跑一次。我一般按这个顺序验证Git 节点测试看能否按指定分支拉取到最新代码。构建节点测试确认 Maven 能正常编译环境变量GIT_COMMIT、GIT_BRANCH是否在日志里正确打印。上传节点测试上传完去 OSS 控制台刷新一下看对象名是否符合预期。下载验证用ossutil cp下载回来比对哈希值。这一步最容易被省略但非常重要。ossutil cp oss://${OSS_BUCKET}/${OSS_KEY} ./test.jar \ --access-key-id ${OSS_AK_ID} \ --access-key-secret ${OSS_AK_SECRET} \ --endpoint ${OSS_ENDPOINT} md5sum ./test.jar # 与构建产物 md5 对比 md5sum ${LOCAL_JAR}哈希一致才能确认上传过程没有损坏文件。之后再从 GitLab 侧手动触发一次 Webhook验证 MR 或 push 事件能不能自动拉起流程。5.2 日常维护中最容易出现的问题清单我整理了一份自己团队遇到过的报错对照表供你排查时参考。现象大概率原因处理方式Arbess 连接 GitLab 后登录失败报 check api token or gitlab versionToken scope 不足或 GitLab API 版本过低给 Token 加apiscope升级 GitLab 至 13账号提示 pending approval from administratorGitLab 启动了用户审批用管理员账号激活该用户Maven 构建卡在依赖下载中央仓库网络不稳定配置阿里云 Maven 镜像JAR 上传后 OSS 里大小是 0 字节脚本中JAR_FILE路径指向了空文件在脚本里加文件存在且非空判断上传报 InvalidAccessKeyIdAccessKey 写错或 RAM 子账号被禁用在目标机器用ossutil config交互验证其他机器无法访问 OSS 内网 Endpoint构建节点不在同一区域/VPC改用公网 Endpoint 或打通内网值得注意的是第一类报错我到现在还会在同事新搭环境时看到。很多教程只教你填 Token不提醒 scope。只要日志中出现login failed. check api token or gitlab version不要第一时间怀疑版本先用 curl 手动调 GitLab API 验证 Token 能力和网络连通性这一步能省下至少半小时。5.3 这个链路还能往哪里扩展当最基础的“GitLab 触发 → Arbess 构建 → OSS 归档”跑通之后我强烈建议做下面几件事把链路价值放大增加代码质量节点在 Maven 构建节点后面接一个 SonarQube 扫描节点扫描失败则流程中断让问题代码没有机会进入制品库。增加人工审批节点生产分支的构建上传不是上传完就结束而是先进“待发布”状态由相关负责人在 Arbess 里点击确认后才允许后续部署系统从 OSS 拉取该制品。增加镜像或部署节点如果 Java 服务最终要跑在 Kubernetes上传 JAR 之后还可以接一步“构建 Docker 镜像”把镜像推到私有镜像仓库形成从源码到环境的完整闭环。OSS 版本管理如果团队需要保留每次构建的历史不可变快照可以开启 Bucket 版本控制。但建议谨慎使用它会让存储量翻倍配合生命周期规则一起用才合理。还有一个我个人体会很深的小经验每次调整流水线前先在 Arbess 里手动跑一次空构建验证基础环境没有变化。我吃过一次亏因为构建节点所在机器被运维重装过JDK 从 11 换成了 17但我们流水线脚本里还写着source /etc/profile去加载老路径结果构建一直失败。后来我们在构建节点脚本开头统一打印 Java 和 Maven 版本一旦环境变更日志里立刻能看出来不必去翻系统变更记录。java -version mvn -version echo JAVA_HOME${JAVA_HOME:-} echo MAVEN_HOME${MAVEN_HOME:-}把这几行加到构建脚本最顶部排查环境问题会轻松很多。集成 Arbess、GitLab 和 OSS 这条链路本身并不复杂真正花时间的往往是这些容易忽略的细节。把基础打好后续无论是接质量门禁、推送镜像还是做更复杂的发布流程都会顺手很多。
返回列表