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-fromBUILDKIT_INLINE_CACHE 提升构建速度
  • 环境变量注入VUE_APP_ENV=test/production 控制前端构建环境
  • 安全性:Harbor 凭据通过 CI/CD 变量注入,避免明文泄露
  • 清理策略after_script 清理 Runner 本地镜像,防止磁盘膨胀

五、总结

通过本文,你可以实现:

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

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

相关推荐
記億揺晃着的那天15 小时前
CI/CD 是如何改变软件世界的?
ci/cd
西部森林牧歌17 小时前
Arbess零基础学习 - 使用Arbess+GitLab实现Node.js项目自动化构建/主机部署
ci/cd·node.js·gitlab·arbess·tiklab devops
西部森林牧歌19 小时前
Arbess零基础学习 - 使用Arbess+GitLab实现 React.js 项目自动化构建/主机部署
react.js·ci/cd·arbess·tiklab devops
极小狐2 天前
极狐GitLab 18.5 正式发布,更新 Maven 虚拟仓库 UI(Beta)、全新个人主页、实例级合规与安全策略管理 以及 DAST 认证脚本 等
java·gitlab·maven
tnan25222 天前
基于阿里云效实现cicd记录
阿里云·ci/cd·docker·容器·自动化
fruge2 天前
GitHub Actions CI/CD 自动化部署完全指南
ci/cd·自动化·github
FJW0208143 天前
DevOps——CI/CD持续集成与持续交付/部署的理解与部署
运维·ci/cd·devops
我就是全世界3 天前
告别手动部署!GitHub Workflow与Action完全指南:从零构建自动化CI_CD流水线
ci/cd·自动化·github
维尔切3 天前
GitLab 版本控制系统
gitlab