From 2b10624c2171bfb0e5556f341775cd8bb6c686d0 Mon Sep 17 00:00:00 2001 From: zpc Date: Tue, 24 Mar 2026 15:10:19 +0800 Subject: [PATCH] =?UTF-8?q?chore(ci):=20=E8=BF=81=E7=A7=BBCI/CD=E5=88=B0?= =?UTF-8?q?=E5=86=85=E7=BD=91Harbor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Dockerfile基础镜像切换到内网Harbor (192.168.195.25:19900) - .drone.yml registry从外网域名改为内网Harbor - Admin Dockerfile增加前端构建阶段(多阶段构建) - 新增基础镜像推送脚本 (push-base-images.ps1/sh) - 更新CI-CD部署文档 --- .drone.yml | 10 +- docs/CI-CD部署文档.md | 135 ++++++++++++++++++ scripts/push-base-images.ps1 | 62 ++++++++ scripts/push-base-images.sh | 62 ++++++++ .../src/MiAssessment.Admin/Dockerfile | 15 +- .../MiAssessment.Admin/admin-web/Dockerfile | 4 +- .../src/MiAssessment.Api/Dockerfile | 4 +- 7 files changed, 279 insertions(+), 13 deletions(-) create mode 100644 docs/CI-CD部署文档.md create mode 100644 scripts/push-base-images.ps1 create mode 100644 scripts/push-base-images.sh diff --git a/.drone.yml b/.drone.yml index f8d912d..bc4119c 100644 --- a/.drone.yml +++ b/.drone.yml @@ -14,8 +14,8 @@ steps: - name: build-api image: plugins/docker settings: - registry: docker.shhmkjgs.cn - repo: docker.shhmkjgs.cn/mi-assessment/api + registry: 192.168.195.25:19900 + repo: 192.168.195.25:19900/mi-assessment/api dockerfile: server/MiAssessment/src/MiAssessment.Api/Dockerfile context: server/MiAssessment tags: @@ -27,12 +27,12 @@ steps: from_secret: harbor_password insecure: true - # ==================== 构建并推送 Admin 镜像 ==================== + # ==================== 构建并推送 Admin API 镜像 ==================== - name: build-admin image: plugins/docker settings: - registry: docker.shhmkjgs.cn - repo: docker.shhmkjgs.cn/mi-assessment/admin + registry: 192.168.195.25:19900 + repo: 192.168.195.25:19900/mi-assessment/admin dockerfile: server/MiAssessment/src/MiAssessment.Admin/Dockerfile context: server/MiAssessment tags: diff --git a/docs/CI-CD部署文档.md b/docs/CI-CD部署文档.md new file mode 100644 index 0000000..7366d41 --- /dev/null +++ b/docs/CI-CD部署文档.md @@ -0,0 +1,135 @@ +# CI/CD 部署文档 + +本文档描述 MiAssessment 学业邑规划项目的 CI/CD 流水线配置,基于 Drone CI + Docker + Harbor 私有镜像仓库(内网)。 + +## 一、整体架构 + +``` +代码推送 (master) → Drone CI 触发 → 构建 Docker 镜像 → 推送内网 Harbor → SSH 部署到服务器 +``` + +| 组件 | 说明 | +|------|------| +| CI 平台 | Drone CI (`192.168.195.25:13080`) | +| 镜像仓库 | Harbor (`192.168.195.25:19900`,HTTP) | +| 部署方式 | Docker Compose | +| 部署服务器 | `192.168.195.15` | +| 触发条件 | `master` 分支 push 事件 | + +## 二、构建的镜像 + +| 镜像 | 说明 | Dockerfile | 构建上下文 | +|------|------|-----------|-----------| +| `mi-assessment/api` | 小程序 API(.NET 10) | `server/MiAssessment/src/MiAssessment.Api/Dockerfile` | `server/MiAssessment` | +| `mi-assessment/admin` | 后台管理 API + 前端(.NET 10,内含 admin-web 构建产物) | `server/MiAssessment/src/MiAssessment.Admin/Dockerfile` | `server/MiAssessment` | + +每个镜像打两个标签:`latest` 和 commit SHA 前 8 位(用于回滚)。 + +## 三、流水线步骤 + +`.drone.yml` 定义了 3 个步骤: + +1. `build-api` — 构建小程序 API 镜像 +2. `build-admin` — 构建后台管理 API 镜像 +3. `deploy` — SSH 部署(等待前两步完成) + +> 两个构建步骤默认并行执行,deploy 步骤通过 `depends_on` 等待全部完成后执行。 + +## 四、基础镜像(内网 Harbor) + +所有 Dockerfile 的基础镜像已切换到内网 Harbor,避免依赖外网: + +| 基础镜像 | 内网地址 | 用途 | +|---------|---------|------| +| `dotnet/aspnet:10.0-preview` | `192.168.195.25:19900/library/dotnet/aspnet:10.0-preview` | .NET 运行时 | +| `dotnet/sdk:10.0-preview` | `192.168.195.25:19900/library/dotnet/sdk:10.0-preview` | .NET 构建 | +| `node:20-alpine` | `192.168.195.25:19900/library/node:20-alpine` | Admin 前端构建 | + +### 一键推送基础镜像 + +在能访问外网的机器上执行脚本,将基础镜像推送到内网 Harbor: + +```bash +bash scripts/push-base-images.sh +``` + +如需手动推送单个镜像: + +```bash +docker pull mcr.microsoft.com/dotnet/aspnet:10.0-preview +docker tag mcr.microsoft.com/dotnet/aspnet:10.0-preview 192.168.195.25:19900/library/dotnet/aspnet:10.0-preview +docker push 192.168.195.25:19900/library/dotnet/aspnet:10.0-preview +``` + +## 五、前置条件 + +### 5.1 Drone CI Secrets + +在 Drone 仓库设置 → Secrets 中配置: + +| Secret 名称 | 说明 | +|-------------|------| +| `harbor_username` | Harbor 仓库用户名 | +| `harbor_password` | Harbor 仓库密码 | +| `ssh_username` | 部署服务器 SSH 用户名 | +| `ssh_password` | 部署服务器 SSH 密码 | + +### 5.2 Harbor 镜像仓库 + +1. 在 Harbor 中创建项目 `mi-assessment` 和 `library` +2. `library` 为公开项目,存放基础镜像(pull 不需要登录) +3. `mi-assessment` 存放业务镜像,推送需要认证 +4. 当前使用 HTTP(`insecure: true`) + +### 5.3 部署服务器(192.168.195.15) + +1. 安装 Docker 和 Docker Compose +2. 配置 Docker 信任 Harbor(HTTP): + +```bash +# /etc/docker/daemon.json +{ + "insecure-registries": ["192.168.195.25:19900"] +} + +# 重启 Docker +systemctl restart docker +``` + +3. 登录 Harbor: + +```bash +docker login 192.168.195.25:19900 +``` + +4. 部署目录:`/disk/docker-compose/mi-assessment` + +## 六、常见问题 + +### Q: 构建时拉取基础镜像失败? + +基础镜像需要提前推送到内网 Harbor。运行 `bash scripts/push-base-images.sh` 一键推送。Drone CI 的 Docker daemon 也需要配置 `insecure-registries`(在 `.drone.yml` 中通过 `insecure: true` 处理推送,但 Dockerfile 中的 FROM 拉取需要 runner 层面配置)。 + +### Q: Harbor 认证失败(unauthorized)? + +检查 Drone Secrets 中的 `harbor_username` / `harbor_password` 是否正确且未过期。 + +### Q: 部署后服务没更新? + +1. 检查 `docker compose pull` 是否拉到了新镜像 +2. 确认服务器 Docker 已配置 `insecure-registries` +3. 检查容器状态:`docker compose ps` / `docker compose logs` + +### Q: 如何回滚? + +使用 commit SHA 标签: + +```bash +# 在服务器上修改 docker-compose.yml 中的镜像标签 +# 例如将 :latest 改为 :a1b2c3d4 +docker compose up -d +``` + +### Q: 如何手动触发构建? + +在 Drone 界面找到对应仓库,点击 "New Build",选择分支即可。 diff --git a/scripts/push-base-images.ps1 b/scripts/push-base-images.ps1 new file mode 100644 index 0000000..13dc27e --- /dev/null +++ b/scripts/push-base-images.ps1 @@ -0,0 +1,62 @@ +# ============================================================ +# 一键推送基础镜像到内网 Harbor (Windows PowerShell) +# 用法: powershell -ExecutionPolicy Bypass -File scripts/push-base-images.ps1 +# ============================================================ + +$HarborHost = "192.168.195.25:19900" +$HarborProject = "library" + +# 需要推送的基础镜像: 外网地址 -> 内网路径 +$Images = @( + @{ Src = "mcr.microsoft.com/dotnet/aspnet:10.0-preview"; Dst = "dotnet/aspnet:10.0-preview" }, + @{ Src = "mcr.microsoft.com/dotnet/sdk:10.0-preview"; Dst = "dotnet/sdk:10.0-preview" }, + @{ Src = "node:20-alpine"; Dst = "node:20-alpine" } +) + +Write-Host "==========================================" -ForegroundColor Cyan +Write-Host " 推送基础镜像到内网 Harbor" -ForegroundColor Cyan +Write-Host " Harbor: $HarborHost" -ForegroundColor Cyan +Write-Host "==========================================" -ForegroundColor Cyan + +# 登录 Harbor +Write-Host "`n[登录] Harbor $HarborHost" -ForegroundColor Yellow +docker login $HarborHost +if ($LASTEXITCODE -ne 0) { + Write-Host "登录失败,请检查用户名密码" -ForegroundColor Red + exit 1 +} + +$Success = 0 +$Fail = 0 + +foreach ($img in $Images) { + $src = $img.Src + $target = "$HarborHost/$HarborProject/$($img.Dst)" + + Write-Host "`n------------------------------------------" -ForegroundColor Gray + Write-Host "[拉取] $src" -ForegroundColor Yellow + docker pull $src + + if ($LASTEXITCODE -eq 0) { + Write-Host "[标签] $target" -ForegroundColor Yellow + docker tag $src $target + + Write-Host "[推送] $target" -ForegroundColor Yellow + docker push $target + + if ($LASTEXITCODE -eq 0) { + Write-Host "[完成] $($img.Dst)" -ForegroundColor Green + $Success++ + } else { + Write-Host "[失败] 推送失败: $($img.Dst)" -ForegroundColor Red + $Fail++ + } + } else { + Write-Host "[失败] 拉取失败: $src" -ForegroundColor Red + $Fail++ + } +} + +Write-Host "`n==========================================" -ForegroundColor Cyan +Write-Host " 完成: 成功 $Success, 失败 $Fail" -ForegroundColor Cyan +Write-Host "==========================================" -ForegroundColor Cyan diff --git a/scripts/push-base-images.sh b/scripts/push-base-images.sh new file mode 100644 index 0000000..c32f0b1 --- /dev/null +++ b/scripts/push-base-images.sh @@ -0,0 +1,62 @@ +#!/bin/bash +# ============================================================ +# 一键推送基础镜像到内网 Harbor +# 在能访问外网的机器上执行 +# 用法: bash scripts/push-base-images.sh +# ============================================================ + +set -e + +HARBOR_HOST="192.168.195.25:19900" +HARBOR_PROJECT="library" + +# 需要推送的基础镜像映射: "外网镜像|内网路径" +IMAGES=( + "mcr.microsoft.com/dotnet/aspnet:10.0-preview|dotnet/aspnet:10.0-preview" + "mcr.microsoft.com/dotnet/sdk:10.0-preview|dotnet/sdk:10.0-preview" + "node:20-alpine|node:20-alpine" +) + +echo "==========================================" +echo " 推送基础镜像到内网 Harbor" +echo " Harbor: ${HARBOR_HOST}" +echo "==========================================" + +# 登录 Harbor +echo "" +echo "[登录] Harbor ${HARBOR_HOST}" +docker login "${HARBOR_HOST}" + +SUCCESS=0 +FAIL=0 + +for item in "${IMAGES[@]}"; do + SRC="${item%%|*}" + DST="${item##*|}" + TARGET="${HARBOR_HOST}/${HARBOR_PROJECT}/${DST}" + + echo "" + echo "------------------------------------------" + echo "[拉取] ${SRC}" + if docker pull "${SRC}"; then + echo "[标签] ${TARGET}" + docker tag "${SRC}" "${TARGET}" + + echo "[推送] ${TARGET}" + if docker push "${TARGET}"; then + echo "[完成] ✓ ${DST}" + ((SUCCESS++)) + else + echo "[失败] ✗ 推送失败: ${DST}" + ((FAIL++)) + fi + else + echo "[失败] ✗ 拉取失败: ${SRC}" + ((FAIL++)) + fi +done + +echo "" +echo "==========================================" +echo " 完成: 成功 ${SUCCESS}, 失败 ${FAIL}" +echo "==========================================" diff --git a/server/MiAssessment/src/MiAssessment.Admin/Dockerfile b/server/MiAssessment/src/MiAssessment.Admin/Dockerfile index cb174b7..eee1c02 100644 --- a/server/MiAssessment/src/MiAssessment.Admin/Dockerfile +++ b/server/MiAssessment/src/MiAssessment.Admin/Dockerfile @@ -1,8 +1,13 @@ -FROM mcr.microsoft.com/dotnet/aspnet:10.0-preview AS base +# ==================== 前端构建阶段 ==================== +FROM 192.168.195.25:19900/library/node:20-alpine AS frontend WORKDIR /app -EXPOSE 8080 +COPY src/MiAssessment.Admin/admin-web/package*.json ./ +RUN rm -f package-lock.json && npm install +COPY src/MiAssessment.Admin/admin-web/ . +RUN npx vite build --outDir dist -FROM mcr.microsoft.com/dotnet/sdk:10.0-preview AS build +# ==================== 后端构建阶段 ==================== +FROM 192.168.195.25:19900/library/dotnet/sdk:10.0-preview AS build WORKDIR /src COPY ["src/MiAssessment.Admin/MiAssessment.Admin.csproj", "src/MiAssessment.Admin/"] COPY ["src/MiAssessment.Admin.Business/MiAssessment.Admin.Business.csproj", "src/MiAssessment.Admin.Business/"] @@ -13,7 +18,9 @@ RUN dotnet restore "src/MiAssessment.Admin/MiAssessment.Admin.csproj" COPY . . RUN dotnet publish "src/MiAssessment.Admin/MiAssessment.Admin.csproj" -c Release -o /app/publish --no-restore -FROM base AS final +# ==================== 最终运行阶段 ==================== +FROM 192.168.195.25:19900/library/dotnet/aspnet:10.0-preview AS final WORKDIR /app COPY --from=build /app/publish . +COPY --from=frontend /app/dist ./wwwroot ENTRYPOINT ["dotnet", "MiAssessment.Admin.dll"] diff --git a/server/MiAssessment/src/MiAssessment.Admin/admin-web/Dockerfile b/server/MiAssessment/src/MiAssessment.Admin/admin-web/Dockerfile index e86b65c..2b3227b 100644 --- a/server/MiAssessment/src/MiAssessment.Admin/admin-web/Dockerfile +++ b/server/MiAssessment/src/MiAssessment.Admin/admin-web/Dockerfile @@ -1,5 +1,5 @@ # 构建阶段 -FROM node:20-alpine AS build +FROM 192.168.195.25:19900/library/node:20-alpine AS build WORKDIR /app COPY package*.json ./ RUN rm -f package-lock.json && npm install @@ -7,7 +7,7 @@ COPY . . RUN npm run build # 生产阶段 -FROM nginx:alpine +FROM 192.168.195.25:19900/library/nginx:alpine COPY --from=build /wwwroot /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 diff --git a/server/MiAssessment/src/MiAssessment.Api/Dockerfile b/server/MiAssessment/src/MiAssessment.Api/Dockerfile index 125da91..be9777a 100644 --- a/server/MiAssessment/src/MiAssessment.Api/Dockerfile +++ b/server/MiAssessment/src/MiAssessment.Api/Dockerfile @@ -1,8 +1,8 @@ -FROM mcr.microsoft.com/dotnet/aspnet:10.0-preview AS base +FROM 192.168.195.25:19900/library/dotnet/aspnet:10.0-preview AS base WORKDIR /app EXPOSE 5238 -FROM mcr.microsoft.com/dotnet/sdk:10.0-preview AS build +FROM 192.168.195.25:19900/library/dotnet/sdk:10.0-preview AS build WORKDIR /src COPY ["src/MiAssessment.Api/MiAssessment.Api.csproj", "src/MiAssessment.Api/"] COPY ["src/MiAssessment.Model/MiAssessment.Model.csproj", "src/MiAssessment.Model/"]