源本科技 | 码上会

GitLab CI 准备篇之镜像构建

2026/05/15
61
0

GitLab Runner

GitLab Runner 是 GitLab CI/CD 自动化流水线的核心执行载体,是专门用来执行 GitLab 仓库中 CI/CD 任务的独立运行代理程序。

GitLab 本身仅负责流水线规则配置、任务调度、流程管理与版本代码托管,并不直接运行编译、打包、镜像构建、镜像推送、项目部署、自动化测试等实际作业;而 GitLab Runner 可部署在任意服务器、物理机、虚拟机或 Docker 容器中,主动向 GitLab 服务轮询待执行的流水线任务,按照预设的 .gitlab-ci.yml 脚本,自动完成代码拉取、项目编译、Docker 镜像构建、推送至 Harbor 私有仓库、应用容器化部署等全流程自动化工作。

它支持分布式部署、多运行器并行执行、多环境隔离,兼容 Shell、Docker、Kubernetes 等多种执行器模式,是打通 GitLab 代码提交到自动构建、自动打包、自动部署落地的关键基础设施,也是企业落地研发自动化、持续集成与持续交付的必备组件。

后端 Dockerfile

为了实现自动构建流水线,我们需要先将我们的项目构建成 Docker 镜像,需要编写 Dockerfile 文件

创建 Dockerfile 配置

  • 在项目根目录创建一个名为 devops 的目录,并创建两个配置文件

  • devops 目录下创建名为 Dockerfile 的文件

# ===================== 阶段 1:项目构建阶段(Maven 打包) =====================
# 基础镜像:Maven 3.9.15 + JDK21 环境(仅用于打包,最终不会打包进生产镜像)
# AS builder:给当前阶段命名,方便后续复制打包好的 jar 包
FROM maven:3.9.15-eclipse-temurin-21 AS builder

# 设置容器内的工作目录为 /app
# 后续所有复制、执行命令都基于这个目录,简化路径
WORKDIR /app

# 复制 Maven 私服配置文件
# 路径说明:构建上下文是项目根目录,所以文件路径为 devops/settings.xml
# 作用:让 Maven 从你的私有仓库 192.168.203.132 下载依赖
COPY devops/settings.xml /root/.m2/settings.xml

# 复制项目根目录下的所有文件
# 适配模块化项目:一次性复制所有子模块
COPY . .

# 执行 Maven 打包命令
# clean:清空旧的打包文件
# package:编译+打包项目
# -DskipTests:跳过单元测试(加速构建,避免测试报错)
# -Pprod:启用生产环境配置(加载 prod 环境参数)
RUN mvn clean package -DskipTests -Pprod

# ===================== 阶段 2:生产运行阶段(极小体积,仅含 JRE) =====================
# 基础镜像:eclipse-temurin-21-jre-alpine(轻量级生产运行环境,只有 JRE,无多余依赖)
# 多阶段构建核心优势:最终镜像只保留运行环境,体积减少 80% 以上
FROM eclipse-temurin:21-jre-alpine

# 容器工作目录,与构建阶段保持一致
WORKDIR /app

# 安全 + 权限核心配置(合并为一条 RUN,减少镜像层数)
# 1. addgroup -S app:创建系统用户组 app
# 2. adduser -S app -G app:创建系统用户 app 并加入用户组(禁止 root 运行,提升容器安全性)
# 3. mkdir -p /home/ruoyi/logs:手动创建框架默认的日志目录(解决日志目录不存在报错)
# 4. chown -R app:app /home/ruoyi:修改日志目录归属权(解决非 root 用户无权限写入日志)
RUN addgroup -S app && adduser -S app -G app && \
    mkdir -p /home/ruoyi/logs && \
    chown -R app:app /home/ruoyi

# 从构建阶段(builder)复制打包好的 jar 包到当前生产镜像
# 路径固定:启动模块为 ruoyi-admin/target/*.jar
COPY --from=builder /app/ruoyi-admin/target/*.jar app.jar

# 声明容器暴露的端口(Spring Boot 默认 8080,仅声明,不实际发布端口)
EXPOSE 8080

# 容器启动命令:固定格式运行 Spring Boot jar 包
ENTRYPOINT ["java", "-jar", "app.jar"]

创建 Maven 配置

  • 还需要创建 settings.xml 配置文件,用于构建时 Maven 连接私服

<settings>
    <servers>
        <server>
            <id>maven-public</id>
            <username>admin</username>
            <password>12345678</password>
        </server>
        <server>
            <id>maven-release</id>
            <username>admin</username>
            <password>12345678</password>
        </server>
        <server>
            <id>maven-snapshots</id>
            <username>admin</username>
            <password>12345678</password>
        </server>
    </servers>
    <mirrors>
        <mirror>
            <!-- 注意这里的 ID 需要匹配上面 Server 元素的 ID,用于配置 Nexus 的账号密码 -->
            <id>maven-public</id>
            <url>http://192.168.203.132:8081/repository/maven-public/</url>
            <mirrorOf>*</mirrorOf>
        </mirror>
    </mirrors>
</settings>

构建镜像

  • 修改完成后提交代码,进入远程服务器的项目目录,使用 git pull 命令拉取最新代码

  • 在项目根目录执行构建命令

# -t 192.168.203.133/library/myproject:latest 作用:构建后可直接推送到私有仓库
# -t:给镜像打标签(名字 + 版本)
# 192.168.203.133:你的 Harbor 私有仓库地址
# library/myproject:镜像名称
# latest:镜像版本(最新版)
docker build --network host -f devops/Dockerfile -t 192.168.203.133/library/myproject:latest .

命令最后一个 . 的作用

  • 当前执行命令的目录(项目根目录)

  • 告诉 Docker 从项目根目录读取代码

  • 必须用根目录作为上下文

命令片段

作用

解释

docker build

构建自定义镜像

开始执行镜像构建流程

--network host

使用宿主机网络模式

容器共享宿主机网络,访问局域网私服

-f devops/Dockerfile

指定 Dockerfile 路径

Dockerfile 文件在 devops 文件夹,-f 指定位置

  • 构建成功会看到如下内容

推送镜像

  • 最后推送镜像到私服

  • 第一次推送需要先信任私有仓库:vi /etc/docker/daemon.json

{
  "insecure-registries": ["192.168.203.133"]
}
  • 配置完成后重启 Docker

systemctl daemon-reload 
systemctl restart docker
  • 再执行以下操作

# 1. 登录 Harbor
docker login 192.168.203.133 -u admin -p bitnami

# 2. 推送镜像
docker push 192.168.203.133/library/myproject:latest

# 3. 退出登录(可选,生产环境建议退出)
docker logout 192.168.203.133
  • 登录 Harbor 查看已推送的镜像

启动容器

  • 主要修改容器脚本的镜像

services:
  myproject:
    image: 192.168.203.133/library/myproject:latest
    container_name: myproject
    ports:
      - "8080:8080"
    environment:
      - TZ=Asia/Shanghai
      - REDIS_HOST=192.168.203.129
      - REDIS_PORT=6379
      - REDIS_PASSWORD=123456
      - MYSQL_HOST=192.168.203.129
      - MYSQL_PORT=3306
      - MYSQL_DATABASE=ry-vue
      - MYSQL_USERNAME=root
      - MYSQL_PASSWORD=123456
    restart: always
    volumes:
      # 映射日志目录
      - ./logs:/home/ruoyi/logs

前端 Dockerfile

创建 Dockerfile 配置

  • 进入 ruoyi-ui 目前创建 Dockerfile 配置文件

# ===================== 阶段 1:前端构建阶段 =====================
# 基础镜像:Node 22 轻量版
FROM node:22-alpine AS builder

# 设置容器工作目录
WORKDIR /app

# ===================== 国内镜像加速 =====================
# Yarn 依赖镜像:阿里云
ENV YARN_REGISTRY=https://registry.npmmirror.com
# Node-Sass 二进制镜像:阿里云
ENV SASS_BINARY_SITE=https://npmmirror.com/mirrors/node-sass/

# 复制依赖文件
COPY package.json yarn.lock ./

# 安装项目依赖
RUN yarn install --frozen-lockfile

# 复制前端所有源代码
COPY . .

# 执行前端生产打包命令
RUN yarn build:prod

# ===================== 阶段 2:生产运行阶段 =====================
# 基础镜像:Nginx
FROM nginx:1.26.2-alpine

# 删除 Nginx 默认配置,避免冲突
RUN rm -rf /etc/nginx/conf.d/default.conf

# 复制构建好的前端静态资源(dist目录)到 Nginx 访问目录
COPY --from=builder /app/dist /usr/share/nginx/html

# 复制自定义 Nginx 配置
COPY nginx.conf /etc/nginx/conf.d/ui.conf

# 声明前端访问端口
EXPOSE 80

# 后台启动 Nginx
CMD ["nginx", "-g", "daemon off;"]

创建 Nginx 配置

  • 创建 nginx.conf 配置,需要特别注意 location /prod-api/ 部分的配置

server {
    listen       80;
    server_name  localhost;
    root   /usr/share/nginx/html;
    index  index.html;

    # 若依前端路由模式:解决刷新 404 问题
    location / {
        try_files $uri $uri/ /index.html;
    }

    # 接口反向代理(根据后端地址配置)
    location /prod-api/ {
        # 注意:这里反代的是容器脚本里服务的名字
        proxy_pass http://myproject:8080/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    # 静态资源缓存
    location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
        expires 7d;
    }
}

构建镜像

  • 修改完成后提交代码,进入远程服务器的项目目录,使用 git pull 命令拉取最新代码

  • ruoyi-ui 目录执行构建命令

docker build --network host -t 192.168.203.133/library/myproject-nginx:latest .

推送镜像

# 1. 登录 Harbor
docker login 192.168.203.133 -u admin -p bitnami

# 2. 推送镜像
docker push 192.168.203.133/library/myproject-nginx:latest

# 3. 退出登录(可选,生产环境建议退出)
docker logout 192.168.203.133

启动容器

services:
  myproject:
    image: 192.168.203.133/library/myproject:latest
    container_name: myproject
    ports:
      - "8080:8080"
    environment:
      - TZ=Asia/Shanghai
      - REDIS_HOST=192.168.203.129
      - REDIS_PORT=6379
      - REDIS_PASSWORD=123456
      - MYSQL_HOST=192.168.203.129
      - MYSQL_PORT=3306
      - MYSQL_DATABASE=ry-vue
      - MYSQL_USERNAME=root
      - MYSQL_PASSWORD=123456
    restart: always
    volumes:
      # 映射日志目录
      - ./logs:/home/ruoyi/logs
  
  myproject-nginx:
    image: 192.168.203.133/library/myproject-nginx:latest
    container_name: myproject-nginx
    ports:
      - "80:80"
    restart: always