结论:用 GitLab Runner + 分支条件判断 + 环境变量控制部署目标更可靠可维护;CI_COMMIT_BRANCH 是稳定可靠的分支识别方式,应结合项目级变量(如 PROD_SERVER)和动态 Maven profile 激活(-P$DEPLOY_ENV)实现多环境自动化部署,并通过 rsync 差量同步提升效率。

直接说结论:用 GitLab Runner + 分支条件判断 + 环境变量控制部署目标,比写死 IP 或手动切换分支更可靠、可维护。
GitLab CI 中根据 CI_COMMIT_BRANCH 决定部署目标
部署逻辑不能靠人肉判断分支名再选服务器,必须由 CI 自动识别。GitLab 提供了稳定的环境变量 CI_COMMIT_BRANCH,它在 pipeline 启动时就已确定,且不会被后续操作污染。
- 不要用
git rev-parse --abbrev-ref HEAD—— 在 Runner 的 clean 模式下,工作目录可能没检出任何分支,命令会失败或返回HEAD - 推荐写法:
if [[ "$CI_COMMIT_BRANCH" == "master" ]]; then deploy_to 192.168.1.30; fi - 注意区分大小写:
main和master是两个不同值,需按实际仓库默认分支名匹配 - 避免硬编码 IP:把服务器地址定义为项目级变量(如
PROD_SERVER),在 GitLab Settings → CI/CD → Variables 里配置,方便统一管理
Spring Boot 应用多环境打包时避免 profile 冲突
同一个代码库打不同环境的包,关键在 Maven 的 profiles 和 resources 过滤机制。如果只靠 application.yml 里的 spring.profiles.active 切换,容易漏掉数据库 URL、Redis 地址等环境敏感项。
- 每个环境应有独立的
src/main/resources/application-{env}.yml,比如application-prod.yml、application-test.yml - Maven 打包命令必须显式激活 profile:
mvn clean package -Pprod -Dmaven.test.skip=true - CI 脚本中要根据分支动态传参:
mvn clean package -P$DEPLOY_ENV -Dmaven.test.skip=true,其中DEPLOY_ENV是通过case $CI_COMMIT_BRANCH设置的变量 - 别依赖 IDE 自动生成的
target/*.jar—— CI 里必须重新构建,否则 profile 可能没生效,启动时报java.lang.IllegalArgumentException: Could not resolve placeholder
部署脚本里用 rsync 替代 scp 减少重复传输
每次部署都全量传整个 JAR 包,既慢又占带宽。Spring Boot 的 fat jar 实际变化很小,rsync 能基于差异同步,实测可减少 70%+ 传输时间。
- 基础命令:
rsync -avz --delete target/app.jar user@${DEPLOY_HOST}:/opt/app/ - 加
--delete是为了清理旧版本残留文件(比如上一次部署失败留下的临时文件) - 务必加
-z压缩传输,尤其跨公网时;内网可去掉以降低 CPU 开销 - 别用
scp -r推整个target/目录 —— 里面包含classes、maven-archiver等无用子目录,浪费 IO
GitLab Runner 的 tags 和 only 配置容易漏掉的关键点
Runner 不是“注册了就能用”,必须和 .gitlab-ci.yml 中的约束条件精确匹配,否则 pipeline 会卡在 “waiting for a runner” 状态。
-
tags必须完全一致:Runner 注册时指定的 tag(如deploy-prod)和 job 里的tags: [deploy-prod]字母、大小写、空格都不能差 -
only规则不支持通配符匹配分支名:only: ["/^feature.*$/"]是无效的,得用only: [/^feature\/.*$/](注意斜杠转义) - 如果用了
rules(推荐),记得加上changes条件,否则即使改了非 Java 文件也会触发部署:changes: ["pom.xml", "src/**/*"] - 别忽略
interruptible: true—— 对部署类 job 没用,但对测试 job 加上它,可以避免 cancel 时还占用 Runner 资源
最常被跳过的其实是权限校验:部署用户在目标服务器上是否真有 /opt/app/ 的写权限?systemctl 是否允许非 root 用户 reload service?这些不在 Git 配置里,但一出错就卡住整个流程。


















