源本科技 | 码上会

GitLab Runner 实现持续集成

2026/05/16
56
0

环境准备

  • Docker

  • Docker Compose v2

  • GitLab

  • Harbor

  • Nexus

  • 前后端项目已托管到 GitLab

  • 服务器配置:建议 4 核 8 GB 内存以上,确保构建性能

部署 GitLab Runner

  • 实际工作中通常会有一台专门用于部署的服务器,本次实验基于我们之前 Ubuntu Service 虚拟机

  • 创建 docker-compose.yml 文件

services:
  gitlab-runner:
    image: gitlab/gitlab-runner:ubuntu-v18.10.1
    container_name: gitlab-runner
    restart: always
    privileged: true  # 必须启用,允许 Docker 执行器构建镜像
    volumes:
      - ./config:/etc/gitlab-runner  # 配置持久化
      - /var/run/docker.sock:/var/run/docker.sock  # 共享 Docker 引擎
      - /usr/bin/docker:/usr/bin/docker  # 映射 Docker 命令
    environment:
      - TZ=Asia/Shanghai  # 时区设置

配置说明

  • privileged: true:启用特权模式,解决 Docker 执行器构建镜像权限问题

  • Docker 套接字映射:让 Runner 容器内的 Docker 命令能访问宿主机 Docker 引擎

  • 配置目录持久化:确保 Runner 配置不会因容器重启丢失

  • 时区设置:统一日志与构建时间显示

启动 Runner

# 启动服务
docker compose up -d

# 查看运行状态
docker compose logs -f

注册 GitLab Runner

获取注册凭证

  • 登录 GitLab,进入项目 → 设置CI/CDRunner

  • 展开更多,主要为了获取令牌

执行注册命令

# 进入 Runner 容器
docker exec -it gitlab-runner gitlab-runner register

# 按提示输入以下信息:
1. Enter the GitLab instance URL: http://192.168.203.132
2. Enter the registration token: 你 GitLab Runner 里显示的 Token
3. Enter a description for the runner: myproject-runner
4. Enter tags for the runner (comma-separated): myproject,backend,frontend,docker
5. Enter optional maintenance note for the runner: MyProject 项目专用 Runner
6. Enter an executor: docker  # 选择 Docker 执行器
7. Enter the default Docker image: docker:27.3.1  # 这里是 Runner 里调用的 Docker 版本

注意:Enter optional maintenance note for the runner: MyProject 项目专用 Runner 时回报警告

  • 下面的警告说的是现在使用的注册方式过时了,新的企业级方法会比较复杂,暂时不用。当前不影响使用

WARNING: Support for registration tokens and runner parameters in the 'register' command has been deprecated in GitLab Runner 15.6 and will be replaced with support for authentication tokens. For more information, see https://docs.gitlab.com/ci/runners/new_creation_workflow/

验证注册结果

  • 刷新 GitLab 页面 Runner 列表中显示新注册的 Runner,状态为【在线】

  • 查看 Runner 配置文件验证

cat config/config.toml

特别注意:观察下面的配置务必修改

  • privileged = true

  • volumes = ["/var/run/docker.sock:/var/run/docker.sock", "/cache"]

  • pull_policy = "if-not-present"

  • image = "docker:27.3.1"

concurrent = 1
check_interval = 0
shutdown_timeout = 0

[session_server]
  session_timeout = 1800

[[runners]]
  name = "myproject-runner"
  url = "http://192.168.203.134"
  id = 3
  token = "glrtr-CV4ArYNJR1_kxdaslVcc1G86MQpwOjEKdDozCw.01.1205xxv0a"
  token_obtained_at = 2026-05-16T05:32:45Z
  token_expires_at = 0001-01-01T00:00:00Z
  executor = "docker"
  [runners.cache]
    MaxUploadedArchiveSize = 0
    [runners.cache.s3]
    [runners.cache.gcs]
    [runners.cache.azure]
  [runners.docker]
    tls_verify = false
    # 确定这里的 image 版本
    image = "docker:27.3.1"
    # 请修改为 true
    privileged = true
    # 追加这行内容
    volumes = ["/var/run/docker.sock:/var/run/docker.sock", "/cache"]
    # 追加这行内容
    pull_policy = "if-not-present"
    disable_entrypoint_overwrite = false
    oom_kill_disable = false
    disable_cache = false
    volume_keep = false
    shm_size = 0
    network_mtu = 0
  • 配置修改完成后一定要重启容器,才能使新配置生效

# 开放权限,让 Runner 容器可以访问
chmod 666 /var/run/docker.sock

# 重启容器
docker compose down
docker compose up -d

项目 CI/CD 配置

配置 GitLab CI/CD 变量

(可选配置)进入项目设置CI/CD变量,添加以下关键变量(全部勾选 保护掩码

变量名

说明

HARBOR_URL

192.168.203.133

Harbor 仓库地址

HARBOR_USER

admin

Harbor 账号

HARBOR_PASSWORD

bitnami

Harbor 密码

MAVEN_SETTINGS

(settings.xml 内容)

Maven 私服配置

  • 注意:如果出现错误提示,可以先选择可见的,目前主要是做测试;如果有需要后期再改

修改项目 Dockerfile

由于 GitLab Runner 流水线会帮我们做一部分工作,原来的 Dockerfile 需要删除冗余的操作,请严格按照步骤执行

  • 删除项目根目录下 devops 中的 settings.xml

  • 修改 Dockerfile

FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
RUN mkdir -p /home/ruoyi/logs
COPY ruoyi-admin/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
  • 修改 ruoyi-ui/Dockerfile 之前创建的 nginx.conf 请保留

FROM nginx:1.26.2-alpine
RUN rm -rf /etc/nginx/conf.d/default.conf
COPY dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/ui.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

说明:主要是去掉了构建阶段,继续下面的步骤你会发现构建脚本写到了流水线中

编写流水线脚本

  • 注意:执行流水线之前需要预先下载 GitLab 的辅助镜像,这个镜像是必须的

docker pull docker:27.3.1
docker pull registry.gitlab.com/gitlab-org/gitlab-runner/gitlab-runner-helper:x86_64-v18.10.1
  • 但是这个镜像下载的非常慢,有可能还下载不了,请同学们通过网盘下载并导入

docker load -i gitlab-runner-helper-x86_64-v18.10.1.tar
  • 项目根目录创建名为 .gitlab-ci.yml 流水线脚本,整合前后端构建、镜像推送、部署全流程

stages:
  - build-backend
  - build-frontend
  - package-images
  - push-images
  - deploy

variables:
  HARBOR_URL: "192.168.203.133"
  HARBOR_USER: "admin"
  HARBOR_PASSWORD: "bitnami"
  BACKEND_IMAGE: "$HARBOR_URL/library/myproject:latest"
  FRONTEND_IMAGE: "$HARBOR_URL/library/myproject-nginx:latest"
  MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"
  DEPLOY_HOST: "192.168.203.129"
  DEPLOY_USER: "root"
  DEPLOY_PWD: "123456"
  DEPLOY_DIR: "/usr/local/docker/myproject"

cache:
  key: "$CI_COMMIT_REF_SLUG"
  paths:
    - .m2/repository/
    - ruoyi-ui/node_modules/

build-backend:
  stage: build-backend
  tags: [myproject, docker]
  image: maven:3.9.15-eclipse-temurin-21
  script:
    - echo "=== 开始构建后端项目 ==="
    - mkdir -p .m2
    - echo "$MAVEN_SETTINGS" > .m2/settings.xml
    - mvn -s .m2/settings.xml clean package -DskipTests -Pprod
  artifacts:
    paths:
      - ruoyi-admin/target/*.jar
    expire_in: 1 day

build-frontend:
  stage: build-frontend
  tags: [myproject, docker]
  image: node:22-alpine
  script:
    - echo "=== 开始构建前端项目 ==="
    - cd ruoyi-ui
    - yarn config set registry https://registry.npmmirror.com
    - yarn config set sass_binary_site https://npmmirror.com/mirrors/node-sass
    - yarn install --frozen-lockfile
    - yarn build:prod
  artifacts:
    paths:
      - ruoyi-ui/dist/
    expire_in: 1 day

package-images:
  stage: package-images
  tags: [myproject, docker]
  image: docker:27.3.1
  variables:
    DOCKER_HOST: unix:///var/run/docker.sock
  dependencies:
    - build-backend
    - build-frontend
  script:
    - echo "=== Runner 构建后端镜像 ==="
    - docker build --network host -f devops/Dockerfile -t $BACKEND_IMAGE .
    - echo "=== Runner 构建前端镜像 ==="
    - cd ruoyi-ui
    - docker build --network host -t $FRONTEND_IMAGE .

push-images:
  stage: push-images
  tags: [myproject, docker]
  image: docker:27.3.1
  variables:
    DOCKER_HOST: unix:///var/run/docker.sock
  script:
    - echo "=== 登录 Harbor ==="
    - docker login $HARBOR_URL -u $HARBOR_USER -p $HARBOR_PASSWORD
    - docker push $BACKEND_IMAGE
    - docker push $FRONTEND_IMAGE

deploy:
  stage: deploy
  tags: [myproject, docker]
  image: alpine:latest
  before_script:
    - apk add --no-cache openssh sshpass
  script:
    - echo "=== 远程服务器拉取镜像并重启容器 ==="
    - sshpass -p $DEPLOY_PWD ssh -o StrictHostKeyChecking=no $DEPLOY_USER@$DEPLOY_HOST "
      cd $DEPLOY_DIR &&
      docker login $HARBOR_URL -u $HARBOR_USER -p $HARBOR_PASSWORD &&
      docker compose pull &&
      docker compose up -d --force-recreate &&
      docker image prune -f
      "
    - echo "=== 部署成功 ==="
  only:
    - main

验证持续集成

  • 执行成功的效果图如下

image-xRsp.webp
  • 注意,首次编写 CI 脚本肯定会遇到各种各样的错误,同学们一定要有耐心,慢慢处理

  • 成功后尝试修改一下代码并提交,等待持续集成完成后上线看效果吧

附:CI 脚本注释

# 核心流程:代码提交 → 后端打包 → 前端打包 → 构建 Docker 镜像 → 推送 Harbor → 远程服务器更新服务

# ===================== 1. 流水线阶段定义 =====================
# 按从上到下的顺序执行,上一个阶段成功,才会执行下一个阶段
stages:
  - build-backend    # 阶段1:后端项目 Maven 编译打包(生成jar包)
  - build-frontend   # 阶段2:前端项目 Vue 编译打包(生成 dist 静态文件)
  - package-images    # 阶段3:在 GitLab Runner 中构建前后端 Docker 镜像
  - push-images       # 阶段4:将构建好的镜像推送至 Harbor 私有仓库
  - deploy            # 阶段5:远程连接服务器,拉取最新镜像并重启服务

# ===================== 2. 全局变量定义 =====================
# 全流水线通用的配置变量,统一管理,方便修改
variables:
  # Harbor 私有仓库地址
  HARBOR_URL: "192.168.203.133"
  # Harbor 登录账号
  HARBOR_USER: "admin"
  # Harbor 登录密码
  HARBOR_PASSWORD: "bitnami"
  # 后端镜像完整名称(仓库地址/项目名/镜像名:版本)
  BACKEND_IMAGE: "$HARBOR_URL/library/myproject:latest"
  # 前端镜像完整名称
  FRONTEND_IMAGE: "$HARBOR_URL/library/myproject-nginx:latest"
  # Maven配置参数:指定本地依赖仓库路径,加速构建
  MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"
  # 远程部署服务器 IP(目标生产服务器)
  DEPLOY_HOST: "192.168.203.129"
  # 远程服务器登录用户名
  DEPLOY_USER: "root"
  # 远程服务器登录密码
  DEPLOY_PWD: "123456"
  # 远程服务器上项目部署的目录(docker-compose.yml 所在路径)
  DEPLOY_DIR: "/usr/local/docker/myproject"

# ===================== 3. 缓存配置 =====================
# 缓存 Maven/前端 依赖,避免每次构建都重新下载,大幅提升速度
cache:
  # 缓存唯一标识(按分支区分,不同分支缓存独立)
  key: "$CI_COMMIT_REF_SLUG"
  # 需要缓存的文件/目录
  paths:
    - .m2/repository/       # 缓存 Maven 所有依赖包
    - ruoyi-ui/node_modules/ # 缓存前端 Node 依赖包

# ===================== 4. 后端项目构建任务 =====================
build-backend:
  # 指定当前任务归属的阶段
  stage: build-backend
  # 匹配 GitLab Runner 的标签,确保由指定的 Runner 执行
  tags: [myproject, docker]
  # 使用的构建镜像(内置 Maven3.9.15 + JDK21,服务器已预下载)
  image: maven:3.9.15-eclipse-temurin-21
  # 构建执行的核心命令
  script:
    - echo "=== 开始构建后端项目 ==="
    - mkdir -p .m2  # 创建 Maven 配置目录
    - echo "$MAVEN_SETTINGS" > .m2/settings.xml  # 写入私有仓库配置
    # Maven 打包命令:清理旧包 → 打包 → 跳过测试 → 使用生产环境配置
    - mvn -s .m2/settings.xml clean package -DskipTests -Pprod
  # 构建产物:将生成的 jar 包保存,供后续构建镜像使用
  artifacts:
    paths:
      - ruoyi-admin/target/*.jar
    # 产物有效期:1天(超时自动清理)
    expire_in: 1 day

# ===================== 5. 前端项目构建任务 =====================
build-frontend:
  stage: build-frontend
  tags: [myproject, docker]
  # 使用的构建镜像
  image: node:22-alpine
  script:
    - echo "=== 开始构建前端项目 ==="
    - cd ruoyi-ui  # 进入前端项目目录
    - yarn config set registry https://registry.npmmirror.com  # 设置淘宝镜像源
    - yarn config set sass_binary_site https://npmmirror.com/mirrors/node-sass  # Sass 镜像加速
    - yarn install --frozen-lockfile  # 安装依赖(严格按照 lock 文件版本)
    - yarn build:prod  # 生产环境打包,生成 dist 静态文件
  # 保存构建好的 dist 目录,供后续构建镜像使用
  artifacts:
    paths:
      - ruoyi-ui/dist/
    expire_in: 1 day

# ===================== 6. Docker 镜像构建任务 =====================
package-images:
  stage: package-images
  tags: [myproject, docker]
  # Docker 客户端镜像(用于执行 docker build 命令)
  image: docker:27.3.1
  # 核心配置:指定连接宿主机的 Docker 引擎(非容器内 Docker)
  variables:
    DOCKER_HOST: unix:///var/run/docker.sock
  # 依赖前两个阶段的产物(jar包 + dist文件)
  dependencies:
    - build-backend
    - build-frontend
  script:
    - echo "=== Runner 构建后端镜像 ==="
    # 构建后端镜像:指定 Dockerfile 路径 → 打标签
    - docker build --network host -f devops/Dockerfile -t $BACKEND_IMAGE .
    - echo "=== Runner 构建前端镜像 ==="
    - cd ruoyi-ui  # 进入前端目录
    # 构建前端镜像
    - docker build --network host -t $FRONTEND_IMAGE .

# ===================== 7. 镜像推送至 Harbor 仓库任务 =====================
push-images:
  stage: push-images
  tags: [myproject, docker]
  image: docker:27.3.1
  variables:
    DOCKER_HOST: unix:///var/run/docker.sock
  script:
    - echo "=== 登录 Harbor 私有仓库 ==="
    - docker login $HARBOR_URL -u $HARBOR_USER -p $HARBOR_PASSWORD
    - echo "=== 推送后端镜像至 Harbor ==="
    - docker push $BACKEND_IMAGE
    - echo "=== 推送前端镜像至 Harbor ==="
    - docker push $FRONTEND_IMAGE

# ===================== 8. 远程服务器部署任务 =====================
deploy:
  stage: deploy
  tags: [myproject, docker]
  # 极简 Linux 镜像,用于执行 SSH 远程连接
  image: alpine:latest
  # 前置脚本:安装 SSH 工具(每次构建都安装,容器为临时环境)
  before_script:
    - apk add --no-cache openssh sshpass  # openssh=远程连接工具 sshpass=免密输入密码
  script:
    - echo "=== 远程服务器拉取镜像并重启容器 ==="
    # SSH远程连接服务器,执行部署命令
    - sshpass -p $DEPLOY_PWD ssh -o StrictHostKeyChecking=no $DEPLOY_USER@$DEPLOY_HOST "
      cd $DEPLOY_DIR &&  # 进入项目部署目录
      docker login $HARBOR_URL -u $HARBOR_USER -p $HARBOR_PASSWORD &&  # 服务器登录 Harbor
      docker compose pull &&  # 拉取最新镜像
      docker compose up -d --force-recreate &&  # 强制重建容器,确保使用最新镜像
      docker image prune -f  # 仅清理虚悬镜像(无标签的垃圾镜像,安全清理)
      "
    - echo "=== 部署成功 ==="
  # 限制:仅 main/master 分支提交代码时,自动执行部署
  only:
    - main