告别手工发版:用 GitLab CI/CD 打通前后端自动化部署的"任督二脉"
想想你平时是怎么发版的?本地跑 mvn clean package,打开 Xftp 把几十兆的 Jar 包慢吞吞地传到服务器,再 SSH 连上去敲 docker build 和 restart。如果只是个单体项目还能忍,但如果是微服务架构(十几个服务排队发版),发一次版简直让人头皮发麻。
既然代码都在 GitLab 上,为什么不让它帮我们把活全干了?今天我们不扯复杂的理论,直接来实战:如何配置一套秒级出包、自动部署的 CI/CD 流水线,并且一次性把前端和后端的坑全部填平。
1. 核心概念:一句话听懂 CI/CD
不要被高大上的名词吓到,只要搞懂这三个"角色",你就能玩转自动化:
- GitLab: 是个"老板",负责管理代码和发号施令。
- GitLab Runner: 是个"打工人",它是需要安装在你目标服务器上的一个客户端程序,专门负责干活。
.gitlab-ci.yml: 是你给打工人写的"SOP(标准作业程序)说明书"。老板一看到代码有变动,就把说明书扔给打工人照着做。
2. 后端实战篇:Java 微服务极速流水线
对于后端(特别是 Spring Boot 微服务)来说,打包慢的核心痛点在于每次都要重新下载满屏的 Maven 依赖,以及在网络上传输打好的 Jar 包。
为了追求极致速度,我们直接放弃官方推荐的网络缓存玩法,采用直接挂载物理机本地目录的霸道操作,外加 Docker 进程直连,实现秒级发版!
后端完整 .gitlab-ci.yml 模板:
yaml
# 定义流水线的两个阶段:先打包,后部署
stages:
- build
- deploy
# 全局变量定义
variables:
# 统一时区,防止跑出来的日志时间少8小时
TZ: "Asia/Shanghai"
# 灵魂配置1:强制 Maven 使用宿主机的物理目录作为本地仓库
MAVEN_OPTS: "-Dmaven.repo.local=/root/.m2/repository"
SERVICE_NAME: "my-springboot-app"
SERVICE_PORT: "8080"
# ==========================================
# 1. 编译构建阶段
# ==========================================
build-job:
stage: build
image: maven:3.9-eclipse-temurin-17 # 使用含JDK17的Maven官方镜像
script:
- echo "开始编译后端代码..."
# 打包,跳过烦人的单元测试
- mvn clean package -DskipTests
# 灵魂配置2:不走网络传包,直接把打好的 jar 包丢进宿主机的物理共享目录
- mkdir -p /artifacts/${SERVICE_NAME}
- cp target/*.jar /artifacts/${SERVICE_NAME}/
tags:
# 强绑定:必须在一台名叫 test-runner 的特定服务器上执行
- test-runner
# ==========================================
# 2. 部署运行阶段
# ==========================================
deploy-job:
stage: deploy
image: docker:latest
variables:
# 灵魂配置3:让容器内的 docker 客户端直连宿主机的 docker 服务进程
DOCKER_HOST: unix:///var/run/docker.sock
script:
- echo "开始部署微服务: ${SERVICE_NAME}"
# 把刚才放在物理机里的 Jar 包拿回来,放到当前目录下供 Dockerfile 使用
- mkdir -p target
- cp /artifacts/${SERVICE_NAME}/*.jar target/
# 就地构建镜像并替换容器
- docker build -t ${SERVICE_NAME}:latest .
- docker rm -f ${SERVICE_NAME}-container || true
- docker run -d --name ${SERVICE_NAME}-container -p ${SERVICE_PORT}:${SERVICE_PORT} -e SPRING_PROFILES_ACTIVE=test ${SERVICE_NAME}:latest
tags:
# 必须和 build 阶段保持在同一台机器上,否则去哪找物理机里的 jar 包?
- test-runner
💡 核心防坑解读:
这套配置的精髓在于抛弃网络传输 。很多新手的流水线卡,是因为每次都在向 GitLab 服务器上传、下载几十兆的 Jar 包。我们利用 /artifacts/ 目录和 unix:///var/run/docker.sock,让代码下载、打包、出镜像、运行容器这一整套动作,全都在业务服务器自己的硬盘和内存里就地解决,速度快到起飞。
3. 前端实战篇:Vue / React 丝滑部署
前端没有像后端那样复杂的本地环境依赖,最推荐的做法是利用 GitLab 原生的 artifacts 功能传递编译后的静态文件,然后用基于 Nginx 的 Docker 容器跑起来。
前提准备: 在你的前端项目根目录,放一个极其简单的 Dockerfile:
dockerfile
# 第一步:准备一个极简的 Nginx 镜像
FROM nginx:alpine
# 第二步:把打好的 dist 文件夹丢到 Nginx 的默认静态资源目录
COPY dist/ /usr/share/nginx/html/
EXPOSE 80
前端完整 .gitlab-ci.yml 模板:
yaml
stages:
- build
- deploy
variables:
PROJECT_NAME: "my-vue-frontend"
PORT: "80"
# ==========================================
# 1. 前端 Node 编译阶段
# ==========================================
build-frontend:
stage: build
image: node:18-alpine
script:
- echo "开始安装前端依赖并打包..."
# 换上国内淘宝镜像,拯救可怜的拉取速度
- npm install --registry=https://registry.npmmirror.com
- npm run build
# 灵魂配置:把打包出来的 dist 文件夹打包存到 GitLab 上,供下个阶段使用
artifacts:
paths:
- dist/
expire_in: 1 hour # 存1个小时就够了,省点磁盘空间
tags:
- test-runner
# ==========================================
# 2. 前端 Nginx 部署阶段
# ==========================================
deploy-frontend:
stage: deploy
image: docker:latest
variables:
# 同样打通宿主机 Docker 进程
DOCKER_HOST: unix:///var/run/docker.sock
script:
- echo "开始将 dist 文件夹打包进 Nginx 镜像并运行..."
# 注意:此时当前目录下已经自动存在了上个阶段传过来的 dist 文件夹
- docker build -t ${PROJECT_NAME}:latest .
- docker rm -f ${PROJECT_NAME}-container || true
- docker run -d --name ${PROJECT_NAME}-container -p ${PORT}:80 ${PROJECT_NAME}:latest
tags:
- test-runner
💡 核心防坑解读:
前端发版的精髓在于环境干净 。我们使用 Node 镜像临时起一个容器去执行打包,拿到 dist 文件夹后,这个 Node 容器就销毁了。最后真正在服务器上跑的,只有那个纯净的 Nginx 容器,毫无环境污染。
4. 高阶防坑指南:那些让你痛不欲生的报错
打通了上面的流程还不算完,在实际团队配合中,你大概率还会遇到下面这几个坑:
坑位 1:"为什么我合并的是生产分支,代码却部署到了测试服务器上?"
原因与解法: 很多团队有服务器 A(测试)和服务器 B(生产)。如果你在两台机器上都装了 Runner,并且没有区分标签,GitLab 就会像无头苍蝇一样随机派单。
- 必须强隔离: 给测试机的 Runner 打上
test-runner标签,生产机打上prod-runner标签。 - Yaml 里对号入座: 测试分支的任务必须写
tags: - test-runner,死死钉在这台机器上跑,绝不串线。
坑位 2:微服务启动顺序错乱,网关疯狂报错
原因与解法: 微服务之间有依赖关系(比如网关必须等基础日志服务、鉴权服务起来了才能正常工作)。如果在多台微服务的并发流水线里一股脑启动,大概率全盘崩溃。
- 加个探针脚本: 在部署网关或上层业务服务的
script里,写个 Shell 循环检测基础容器的健康状态(Health Check),等基础服务状态变为healthy了,再放行后续的部署动作。
坑位 3:流水线一直卡在 Pending 状态不动
原因与解法: 别慌,这不是你代码写错了,是"打工人"失联了。去 GitLab 的 Settings -> CI/CD -> Runners 看一眼,如果对应的 Runner 是灰色的,登录目标服务器敲一句 gitlab-runner restart 踢它一脚就能满血复活。