在现代前端工程化与微服务架构中,容器化 与 CI/CD 自动化 已经成为必备技能。本文将带你完成两个关键步骤:
- Vue3 项目容器化 ------ 使用多阶段 Dockerfile 构建轻量、安全的运行镜像。
- CI/CD 自动化 ------ 借助 GitLab CI/CD,将镜像自动构建并推送到 Harbor 私有仓库。
参考阅读:
一、Vue3 项目容器化
好的 ✅ 我来帮你把之前的 Vue3 容器化 + CI/CD 自动化教程 补充上 流程图 和 项目目录结构图,让读者更直观地理解整个流水线。
项目目录结构图
一个典型的 Vue3 + Docker + CI/CD 项目目录如下:
vue3-demo/
├── .gitlab-ci.yml # GitLab CI/CD 配置文件
├── Dockerfile # 多阶段构建 Dockerfile
├── deploy/
│ └── nginx.conf # Nginx 配置文件
├── package.json
├── package-lock.json
├── public/ # 静态资源
├── src/ # Vue3 源码
│ ├── assets/
│ ├── components/
│ ├── router/
│ ├── store/
│ └── App.vue
└── dist/ # 构建产物(CI/CD 中自动生成)
说明:
.gitlab-ci.yml:定义流水线流程(构建、推送)Dockerfile:定义容器镜像构建方式.dockerignoredocker 忽略文件deploy/nginx.conf:自定义前端路由支持(如 history 模式)dist/:构建产物,CI/CD 中由npm run build自动生成
1. Dockerfile 多阶段构建
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;"]
关键点:
- 多阶段构建:减少最终镜像体积
- Nginx 托管静态资源:性能更优
- 配置文件挂载:方便后续环境差异化
2. 本地验证
bash
docker build -t vue3-demo:1.0 .
docker run -d -p 8080:80 vue3-demo:1.0
访问 http://localhost:8080 验证页面是否正常。
二、GitLab CI/CD 自动化构建与推送 Harbor
1. 环境准备
- GitLab Runner(Docker executor,挂载宿主机 Docker)
- Harbor 私有仓库(已创建项目与机器人账号)
- GitLab CI/CD 变量配置:
HARBOR_USER/HARBOR_PASSWORDCI_REGISTRY=harbor.example.com
2. .gitlab-ci.yml 核心配置
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 分支推送 latest,其他分支可推送测试镜像
- 版本可追溯 :使用
commit SHA作为镜像标签 - 安全性:Harbor 账号通过 GitLab CI/CD 变量注入
三、完整流程回顾
- 编写 Dockerfile ------ 多阶段构建、轻量可移植
- 本地验证 ------
docker build + docker run - 配置 GitLab Runner ------ 挂载宿主机 Docker,信任 Harbor 证书
- 编写 CI/CD 流水线 ------ 自动构建、推送 Harbor
- 验证与上线 ------ Harbor 中查看镜像,服务器拉取并运行
四、总结
通过结合 容器化部署教程 与 CI/CD 自动化推送教程,我们实现了 从代码提交 → 自动构建 → 镜像推送 → 部署上线 的完整闭环。
- 容器化:解决环境一致性问题
- CI/CD + Harbor:实现自动化与可追溯的镜像管理
- 最终上线:只需拉取镜像并运行容器,极大提升交付效率