Vue3 项目 GitLab CI/CD 自动构建并推送到 Harbor 教程

在前端项目交付中,如何保证构建一致性、镜像可追溯性、自动化交付,是团队必须解决的问题。参考 Spring Boot 项目 GitLab CI/CD 自动构建并推送到 Harbor 教程,本文将以 Vue3 项目为例,演示如何通过 GitLab CI/CD 自动构建并推送镜像到 Harbor。


一、环境准备

  • GitLab:14+,自建或 GitLab.com
  • GitLab Runner:Docker executor,Runner 节点需安装 Docker
  • Harbor :2.5+,已创建项目(如 ayy-admin)
  • CI/CD 变量配置 :
    • HARBOR_USER:Harbor 用户名(推荐使用 Robot 账号)
    • HARBOR_PASS:Harbor 密码或 token
    • (可选)SSH_PRIVATE_KEY:若需要远程部署

二、Dockerfile 示例

Vue3 项目通常需要先构建静态资源,再由 Nginx 提供服务:

dockerfile 复制代码
# -------------------------------
# 构建阶段:使用 Node 构建 Vue3 前端
# -------------------------------
FROM node:20-alpine AS build

WORKDIR /app

# 设置 npm 镜像源和 Node.js 内存限制(提前设置)
RUN npm config set registry https://registry.npmmirror.com/
ENV NODE_OPTIONS="--max-old-space-size=4096"

# 先复制依赖文件,利用 Docker 缓存(重要优化)
COPY package*.json ./

# 安装依赖
RUN npm install

# 再复制源码(不包括 node_modules,通过 .dockerignore 排除)
COPY . .

# 构建
RUN npm run build

# -------------------------------
# 运行阶段:使用 Nginx 提供静态资源
# -------------------------------
FROM nginx:1.28.0-alpine

WORKDIR /usr/share/nginx/html

RUN rm -rf ./*

COPY --from=build /app/dist/ ./
COPY deploy/nginx.conf /etc/nginx/nginx.conf

EXPOSE 8080

HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
  CMD wget -qO- http://localhost:8080/ || exit 1

CMD ["nginx", "-g", "daemon off;"]

三、GitLab CI/CD 配置

以下 .gitlab-ci.yml 实现了 main 分支(测试环境) 与 tag 发布(生产环境) 的自动构建与推送。

yaml 复制代码
stages:
  - build
  - push

variables:
  DOCKER_HOST: unix:///var/run/docker.sock
  REGISTRY: 192.168.0.12:5080
  PROJECT: ayy-admin
  IMAGE_NAME: ayy-admin-vue3
  # 禁用 Git LFS
  GIT_LFS_SKIP_SMUDGE: "1"

# ==================== 锚点定义 ====================
.docker-base: &docker-base
  image: docker:20.10.24
  before_script:
    # 验证 Docker 可用(使用宿主机 Docker)
    - echo "使用宿主机 Docker"
    - docker info
    - docker version
    # 登录 Harbor
    - echo "$HARBOR_PASS" | docker login -u "$HARBOR_USER" --password-stdin $REGISTRY

.ssh-base: &ssh-base
  image: alpine:latest
  before_script:
    - apk add --no-cache openssh-client curl
    - eval $(ssh-agent -s)
    - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh

# ==================== 测试环境 ====================
build-test:
  <<: *docker-base
  stage: build
  script:
    - export IMAGE_TAG="test-${CI_COMMIT_SHORT_SHA}"
    - export CACHE_IMAGE="$REGISTRY/$PROJECT/$IMAGE_NAME:test-cache"
    - echo "开始构建测试镜像 $IMAGE_TAG"

    # 尝试拉取缓存镜像
    - docker pull $CACHE_IMAGE || echo "缓存镜像不存在,将从头构建"

    # 构建镜像
    - |
      docker build \
        --cache-from $CACHE_IMAGE \
        --build-arg BUILDKIT_INLINE_CACHE=1 \
        --build-arg NODE_ENV=production \
        --build-arg VUE_APP_ENV=test \
        --build-arg BUILD_TIME=$(date -u +'%Y-%m-%dT%H:%M:%SZ') \
        --build-arg GIT_COMMIT=$CI_COMMIT_SHORT_SHA \
        -t $REGISTRY/$PROJECT/$IMAGE_NAME:$IMAGE_TAG \
        -t $REGISTRY/$PROJECT/$IMAGE_NAME:test-latest \
        -t $CACHE_IMAGE \
        .

    - echo "✓ 镜像构建成功"
    - docker images | grep $IMAGE_NAME

    # 保存构建信息
    - echo "IMAGE_TAG=$IMAGE_TAG" > build.env
    - echo "ENVIRONMENT=test" >> build.env
  artifacts:
    reports:
      dotenv: build.env
    expire_in: 1 day
  only:
    - main
  tags:
    - frontend
  retry:
    max: 2
    when:
      - runner_system_failure
      - stuck_or_timeout_failure

push-test:
  <<: *docker-base
  stage: push
  dependencies:
    - build-test
  script:
    - echo "开始推送测试镜像 $IMAGE_TAG"
    - docker push $REGISTRY/$PROJECT/$IMAGE_NAME:$IMAGE_TAG
    - docker push $REGISTRY/$PROJECT/$IMAGE_NAME:test-latest
    - docker push $REGISTRY/$PROJECT/$IMAGE_NAME:test-cache
    - echo "✓ 镜像推送成功"

    # 显示推送的镜像信息
    - echo "推送的镜像:"
    - echo "  - $REGISTRY/$PROJECT/$IMAGE_NAME:$IMAGE_TAG"
    - echo "  - $REGISTRY/$PROJECT/$IMAGE_NAME:test-latest"
  after_script:
    # 清理本地镜像
    - docker rmi $REGISTRY/$PROJECT/$IMAGE_NAME:$IMAGE_TAG || true
    - docker rmi $REGISTRY/$PROJECT/$IMAGE_NAME:test-latest || true
    - docker system prune -f || true
  only:
    - main
  tags:
    - frontend
  retry:
    max: 2
    when:
      - runner_system_failure

# ==================== 生产环境 ====================
build-prod:
  <<: *docker-base
  stage: build
  script:
    - export IMAGE_TAG="${CI_COMMIT_TAG}"
    - export CACHE_IMAGE="$REGISTRY/$PROJECT/$IMAGE_NAME:prod-cache"
    - echo "开始构建生产镜像 $IMAGE_TAG"

    # 验证标签格式 (v1.0.0)
    - |
      if ! echo "$IMAGE_TAG" | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+$'; then
        echo "❌ 错误: 标签格式必须为 vX.Y.Z (例如: v1.0.0)"
        echo "当前标签: $IMAGE_TAG"
        exit 1
      fi
    - echo "✓ 标签格式验证通过 $IMAGE_TAG"

    # 尝试拉取缓存镜像
    - docker pull $CACHE_IMAGE || echo "缓存镜像不存在,将从头构建"

    # 构建镜像
    - |
      docker build \
        --cache-from $CACHE_IMAGE \
        --build-arg BUILDKIT_INLINE_CACHE=1 \
        --build-arg NODE_ENV=production \
        --build-arg VUE_APP_ENV=production \
        --build-arg BUILD_TIME=$(date -u +'%Y-%m-%dT%H:%M:%SZ') \
        --build-arg GIT_TAG=$CI_COMMIT_TAG \
        -t $REGISTRY/$PROJECT/$IMAGE_NAME:$IMAGE_TAG \
        -t $REGISTRY/$PROJECT/$IMAGE_NAME:prod-latest \
        -t $CACHE_IMAGE \
        .

    - echo "✓ 镜像构建成功"
    - docker images | grep $IMAGE_NAME

    # 保存构建信息
    - echo "IMAGE_TAG=$IMAGE_TAG" > build.env
    - echo "ENVIRONMENT=production" >> build.env
  artifacts:
    reports:
      dotenv: build.env
    expire_in: 30 days
  only:
    - tags
  tags:
    - frontend
  retry:
    max: 2
    when:
      - runner_system_failure
      - stuck_or_timeout_failure

push-prod:
  <<: *docker-base
  stage: push
  dependencies:
    - build-prod
  script:
    - echo "开始推送生产镜像 $IMAGE_TAG"
    - docker push $REGISTRY/$PROJECT/$IMAGE_NAME:$IMAGE_TAG
    - docker push $REGISTRY/$PROJECT/$IMAGE_NAME:prod-latest
    - docker push $REGISTRY/$PROJECT/$IMAGE_NAME:prod-cache
    - echo "✓ 镜像推送成功"

    # 显示推送的镜像信息
    - echo "推送的镜像:"
    - echo "  - $REGISTRY/$PROJECT/$IMAGE_NAME:$IMAGE_TAG"
    - echo "  - $REGISTRY/$PROJECT/$IMAGE_NAME:prod-latest"
  after_script:
    # 清理本地镜像
    - docker rmi $REGISTRY/$PROJECT/$IMAGE_NAME:$IMAGE_TAG || true
    - docker rmi $REGISTRY/$PROJECT/$IMAGE_NAME:prod-latest || true
    - docker system prune -f || true
  only:
    - tags
  tags:
    - frontend
  retry:
    max: 2
    when:
      - runner_system_failure

四、关键点解析

  • 分支策略 :main → 测试镜像;tag → 生产镜像
  • 缓存加速 :利用 --cache-from 与 BUILDKIT_INLINE_CACHE 提升构建速度
  • 环境变量注入 :VUE_APP_ENV=test/production 控制前端构建环境
  • 安全性:Harbor 凭据通过 CI/CD 变量注入,避免明文泄露
  • 清理策略 :after_script 清理 Runner 本地镜像,防止磁盘膨胀

五、总结

通过本文,你可以实现:

  1. Vue3 项目容器化(Dockerfile 构建静态资源 + Nginx 托管)
  2. GitLab CI/CD 自动化(分支/标签策略、缓存加速、推送 Harbor)
  3. 可追溯与可维护(镜像标签规范、构建信息存档、日志清理)

最终达到 代码提交 → 自动构建 → 镜像推送 → 部署上线 的完整闭环。

相关推荐
蒸鱼Yuzheng15 小时前
Shader 指令成本回归:离线编译、设备权重与误报控制
ci/cd·性能优化·gpu·shader·vulkan
极小狐18 小时前
MR 里测试摘要空白、覆盖率不显示?JUnit 与覆盖率配置 9 步排查
数据库·ci/cd·junit·单元测试·mr·devops·代码覆盖率
Linux实战笔记19 小时前
Docker多阶段构建优化生产配置手记:镜像瘦身最佳实践
ci/cd·docker·生产环境·多阶段构建·镜像瘦身
GoodStudyAndDayDayUp2 天前
gitlab私有化跑CICD
gitlab·gitlab私有化跑cicd
成为你的宁宁2 天前
【Gitlab测试项目】
gitlab·devops
川石课堂软件测试2 天前
自动化测试|持续集成Git使用详解
git·python·jmeter·mysql·ci/cd·容器·kubernetes
陈聪.2 天前
CI/CD 与 Git 工具使用知识总结
git·ci/cd
7177772 天前
中小团队 CI/CD 工具选哪个:2026 年主流平台对比与 Gitee 方案解析
ci/cd·gitee
空空dream3 天前
04 | 用 Jenkins 打通 CI自动构建镜像并推送到本地 Harbor
运维·ci/cd·jenkins
smartpi_ai3 天前
离线语音识别模块选型指南:CI-03T、SU-32T、SU-03T 等模块在不同场景下的对比与选择
人工智能·ci/cd·语音识别