diff --git a/docs/CI-CD部署文档.md b/docs/CI-CD部署文档.md index 7366d41..e727d09 100644 --- a/docs/CI-CD部署文档.md +++ b/docs/CI-CD部署文档.md @@ -1,6 +1,6 @@ # CI/CD 部署文档 -本文档描述 MiAssessment 学业邑规划项目的 CI/CD 流水线配置,基于 Drone CI + Docker + Harbor 私有镜像仓库(内网)。 +本文档描述基于 Drone CI + Docker + Harbor 私有镜像仓库(内网)的 CI/CD 流水线配置。其他项目可参考本文档搭建自己的 CI/CD 流程。 ## 一、整体架构 @@ -8,15 +8,183 @@ 代码推送 (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` | +| 部署服务器 | `192.168.195.15`(可按项目分配不同服务器) | | 触发条件 | `master` 分支 push 事件 | -## 二、构建的镜像 +## 二、快速开始(新项目接入指南) + +### 2.1 在 Harbor 创建项目 + +1. 访问 Harbor 管理界面:`http://192.168.195.25:19900` +2. 创建一个新项目,名称使用项目标识(如 `my-project`) +3. 设置为私有项目(推送需要认证) + +### 2.2 在 Drone CI 激活仓库 + +1. 访问 Drone CI:`http://192.168.195.25:13080` +2. 使用 Gitea/Gogs 账号登录(Drone 与代码仓库联动) +3. 在仓库列表中找到你的项目,点击 **Activate** +4. 进入仓库设置 → **Secrets**,添加以下密钥: + +| Secret 名称 | 说明 | 示例值 | +|-------------|------|--------| +| `harbor_username` | Harbor 仓库用户名 | `admin` | +| `harbor_password` | Harbor 仓库密码 | `Harbor12345` | +| `ssh_username` | 部署服务器 SSH 用户名 | `root` | +| `ssh_password` | 部署服务器 SSH 密码 | `your-password` | + +### 2.3 编写 `.drone.yml` + +在项目根目录创建 `.drone.yml`,参考下方模板。 + +### 2.4 编写 Dockerfile + +为每个需要构建的服务编写 Dockerfile。 + +### 2.5 编写 `docker-compose.yml` + +在部署服务器上创建 docker-compose 配置。 + +### 2.6 推送代码触发流水线 + +推送代码到 `master` 分支,Drone 会自动触发构建和部署。 + +## 三、`.drone.yml` 配置模板 + +### 3.1 单服务项目模板 + +适用于只有一个服务需要构建和部署的项目: + +```yaml +--- +kind: pipeline +type: docker +name: my-project # ← 改为你的项目名 + +trigger: + branch: + - master # ← 触发分支,可改为 main + event: + - push + +steps: + # ==================== 构建并推送镜像 ==================== + - name: build + image: plugins/docker + settings: + registry: 192.168.195.25:19900 + repo: 192.168.195.25:19900/my-project/app # ← 改为 Harbor项目名/镜像名 + dockerfile: Dockerfile # ← Dockerfile 路径 + context: . # ← 构建上下文目录 + tags: + - latest + - ${DRONE_COMMIT_SHA:0:8} # commit SHA 前8位,用于回滚 + username: + from_secret: harbor_username + password: + from_secret: harbor_password + insecure: true # Harbor 使用 HTTP 时必须 + + # ==================== 部署到服务器 ==================== + - name: deploy + image: appleboy/drone-ssh + settings: + host: 192.168.195.15 # ← 改为你的部署服务器 IP + username: + from_secret: ssh_username + password: + from_secret: ssh_password + port: 22 + script: + - cd /disk/docker-compose/my-project # ← 改为服务器上的部署目录 + - docker compose pull + - docker compose up -d + depends_on: + - build +``` + +### 3.2 多服务项目模板 + +适用于有多个服务(如 API + Admin)需要分别构建的项目: + +```yaml +--- +kind: pipeline +type: docker +name: my-project + +trigger: + branch: + - master + event: + - push + +steps: + # ==================== 构建服务 A ==================== + - name: build-service-a + image: plugins/docker + settings: + registry: 192.168.195.25:19900 + repo: 192.168.195.25:19900/my-project/service-a + dockerfile: src/ServiceA/Dockerfile + context: src + tags: + - latest + - ${DRONE_COMMIT_SHA:0:8} + username: + from_secret: harbor_username + password: + from_secret: harbor_password + insecure: true + + # ==================== 构建服务 B ==================== + - name: build-service-b + image: plugins/docker + settings: + registry: 192.168.195.25:19900 + repo: 192.168.195.25:19900/my-project/service-b + dockerfile: src/ServiceB/Dockerfile + context: src + tags: + - latest + - ${DRONE_COMMIT_SHA:0:8} + username: + from_secret: harbor_username + password: + from_secret: harbor_password + insecure: true + + # ==================== 部署到服务器 ==================== + - name: deploy + image: appleboy/drone-ssh + settings: + host: 192.168.195.15 + username: + from_secret: ssh_username + password: + from_secret: ssh_password + port: 22 + script: + - cd /disk/docker-compose/my-project + - docker compose pull + - docker compose up -d + depends_on: + - build-service-a # 等待所有构建完成 + - build-service-b +``` + +> 多个 build 步骤默认并行执行,deploy 通过 `depends_on` 等待全部完成后执行。 + +## 四、MiAssessment 项目实际配置 + +本项目(学业邑规划)的实际配置如下,供参考: + +### 4.1 构建的镜像 | 镜像 | 说明 | Dockerfile | 构建上下文 | |------|------|-----------|-----------| @@ -25,69 +193,145 @@ 每个镜像打两个标签:`latest` 和 commit SHA 前 8 位(用于回滚)。 -## 三、流水线步骤 +### 4.2 流水线步骤 `.drone.yml` 定义了 3 个步骤: 1. `build-api` — 构建小程序 API 镜像 -2. `build-admin` — 构建后台管理 API 镜像 +2. `build-admin` — 构建后台管理 API 镜像(并行) 3. `deploy` — SSH 部署(等待前两步完成) -> 两个构建步骤默认并行执行,deploy 步骤通过 `depends_on` 等待全部完成后执行。 +### 4.3 完整 `.drone.yml` -## 四、基础镜像(内网 Harbor) +```yaml +--- +kind: pipeline +type: docker +name: mi-assessment -所有 Dockerfile 的基础镜像已切换到内网 Harbor,避免依赖外网: +trigger: + branch: + - master + event: + - push -| 基础镜像 | 内网地址 | 用途 | -|---------|---------|------| -| `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 前端构建 | +steps: + - name: build-api + image: plugins/docker + settings: + 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: + - latest + - ${DRONE_COMMIT_SHA:0:8} + username: + from_secret: harbor_username + password: + from_secret: harbor_password + insecure: true -### 一键推送基础镜像 + - name: build-admin + image: plugins/docker + settings: + 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: + - latest + - ${DRONE_COMMIT_SHA:0:8} + username: + from_secret: harbor_username + password: + from_secret: harbor_password + insecure: true -在能访问外网的机器上执行脚本,将基础镜像推送到内网 Harbor: - -```bash -bash scripts/push-base-images.sh + - name: deploy + image: appleboy/drone-ssh + settings: + host: 192.168.195.15 + username: + from_secret: ssh_username + password: + from_secret: ssh_password + port: 22 + script: + - cd /disk/docker-compose/mi-assessment + - docker compose pull + - docker compose up -d + depends_on: + - build-api + - build-admin ``` -如需手动推送单个镜像: +## 五、基础镜像管理 + +### 5.1 为什么需要内网基础镜像 + +内网环境无法直接拉取 Docker Hub / MCR 的镜像,需要提前将基础镜像推送到内网 Harbor 的 `library` 项目中。 + +### 5.2 推送基础镜像 + +在能访问外网的机器上执行: ```bash +# 一键推送(如果项目提供了脚本) +bash scripts/push-base-images.sh + +# 手动推送单个镜像 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.3 常用基础镜像 -### 5.1 Drone CI Secrets +| 基础镜像 | 内网地址 | 用途 | +|---------|---------|------| +| `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` | Node.js 前端构建 | +| `python:3.12-slim` | `192.168.195.25:19900/library/python:3.12-slim` | Python 项目 | +| `golang:1.22-alpine` | `192.168.195.25:19900/library/golang:1.22-alpine` | Go 项目 | +| `nginx:alpine` | `192.168.195.25:19900/library/nginx:alpine` | 静态文件服务 | -在 Drone 仓库设置 → Secrets 中配置: +### 5.4 Dockerfile 中使用内网镜像 -| Secret 名称 | 说明 | -|-------------|------| -| `harbor_username` | Harbor 仓库用户名 | -| `harbor_password` | Harbor 仓库密码 | -| `ssh_username` | 部署服务器 SSH 用户名 | -| `ssh_password` | 部署服务器 SSH 密码 | +```dockerfile +# 使用内网 Harbor 的基础镜像 +FROM 192.168.195.25:19900/library/node:20-alpine AS build +WORKDIR /app +COPY . . +RUN npm install && npm run build -### 5.2 Harbor 镜像仓库 +FROM 192.168.195.25:19900/library/nginx:alpine +COPY --from=build /app/dist /usr/share/nginx/html +``` -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): +### 6.1 安装 Docker 和 Docker Compose ```bash -# /etc/docker/daemon.json +# CentOS / RHEL +yum install -y docker-ce docker-compose-plugin + +# Ubuntu / Debian +apt-get install -y docker-ce docker-compose-plugin + +# 启动 Docker +systemctl enable docker +systemctl start docker +``` + +### 6.2 配置信任内网 Harbor(HTTP) + +由于 Harbor 使用 HTTP 而非 HTTPS,需要配置 Docker 信任该仓库: + +```bash +# 编辑 /etc/docker/daemon.json { "insecure-registries": ["192.168.195.25:19900"] } @@ -96,19 +340,213 @@ docker push 192.168.195.25:19900/library/dotnet/aspnet:10.0-preview systemctl restart docker ``` -3. 登录 Harbor: +### 6.3 登录 Harbor ```bash docker login 192.168.195.25:19900 +# 输入用户名和密码 ``` -4. 部署目录:`/disk/docker-compose/mi-assessment` +### 6.4 创建部署目录 -## 六、常见问题 +```bash +mkdir -p /disk/docker-compose/my-project +cd /disk/docker-compose/my-project +``` + +### 6.5 编写 `docker-compose.yml` + +示例(单服务): + +```yaml +version: "3.8" + +services: + app: + image: 192.168.195.25:19900/my-project/app:latest + container_name: my-project-app + ports: + - "8080:8080" + environment: + - ASPNETCORE_ENVIRONMENT=Production + restart: unless-stopped +``` + +示例(多服务,参考 MiAssessment): + +```yaml +version: "3.8" + +services: + api: + image: 192.168.195.25:19900/mi-assessment/api:latest + container_name: mi-assessment-api + ports: + - "5000:8080" + environment: + - ASPNETCORE_ENVIRONMENT=Production + volumes: + - ./appsettings.api.json:/app/appsettings.Production.json + restart: unless-stopped + + admin: + image: 192.168.195.25:19900/mi-assessment/admin:latest + container_name: mi-assessment-admin + ports: + - "5001:8080" + environment: + - ASPNETCORE_ENVIRONMENT=Production + volumes: + - ./appsettings.admin.json:/app/appsettings.Production.json + restart: unless-stopped +``` + +## 七、Drone CI 配置详解 + +### 7.1 关键字段说明 + +| 字段 | 说明 | +|------|------| +| `kind: pipeline` | 流水线类型 | +| `type: docker` | 使用 Docker 执行器 | +| `trigger.branch` | 触发分支 | +| `trigger.event` | 触发事件(push / pull_request / tag) | +| `settings.registry` | Harbor 仓库地址 | +| `settings.repo` | 镜像完整路径(含 registry) | +| `settings.dockerfile` | Dockerfile 相对路径 | +| `settings.context` | Docker 构建上下文目录 | +| `settings.tags` | 镜像标签列表 | +| `settings.insecure` | 允许 HTTP 推送(Harbor 非 HTTPS 时必须) | +| `from_secret` | 引用 Drone Secrets 中的密钥 | +| `depends_on` | 步骤依赖,等待指定步骤完成后执行 | + +### 7.2 常用 Drone 变量 + +| 变量 | 说明 | 示例值 | +|------|------|--------| +| `${DRONE_COMMIT_SHA}` | 完整 commit SHA | `a1b2c3d4e5f6...` | +| `${DRONE_COMMIT_SHA:0:8}` | commit SHA 前 8 位 | `a1b2c3d4` | +| `${DRONE_BRANCH}` | 当前分支名 | `master` | +| `${DRONE_TAG}` | Git 标签(tag 事件时) | `v1.0.0` | +| `${DRONE_BUILD_NUMBER}` | 构建编号 | `42` | +| `${DRONE_REPO_NAME}` | 仓库名 | `mi-assessment` | + +### 7.3 高级配置示例 + +#### 按分支部署到不同环境 + +```yaml +--- +kind: pipeline +type: docker +name: deploy-staging + +trigger: + branch: + - develop + event: + - push + +steps: + - name: build + image: plugins/docker + settings: + registry: 192.168.195.25:19900 + repo: 192.168.195.25:19900/my-project/app + tags: + - staging + - ${DRONE_COMMIT_SHA:0:8} + # ... 其他配置同上 + + - name: deploy-staging + image: appleboy/drone-ssh + settings: + host: 192.168.195.20 # 测试服务器 + username: + from_secret: ssh_username + password: + from_secret: ssh_password + port: 22 + script: + - cd /disk/docker-compose/my-project-staging + - docker compose pull + - docker compose up -d + depends_on: + - build + +--- +kind: pipeline +type: docker +name: deploy-production + +trigger: + branch: + - master + event: + - push + +steps: + - name: build + image: plugins/docker + settings: + registry: 192.168.195.25:19900 + repo: 192.168.195.25:19900/my-project/app + tags: + - latest + - ${DRONE_COMMIT_SHA:0:8} + # ... 其他配置同上 + + - name: deploy-production + image: appleboy/drone-ssh + settings: + host: 192.168.195.15 # 生产服务器 + # ... 其他配置同上 +``` + +#### 添加构建通知(钉钉/企业微信) + +```yaml + - name: notify + image: plugins/webhook + settings: + urls: https://oapi.dingtalk.com/robot/send?access_token=xxx + content_type: application/json + template: | + { + "msgtype": "text", + "text": { + "content": "✅ {{repo.name}} 部署成功\n分支: {{build.branch}}\n提交: {{build.commit}}" + } + } + depends_on: + - deploy + when: + status: + - success +``` + +## 八、新项目接入清单 + +按以下清单逐项完成,即可为新项目接入 CI/CD: + +| # | 步骤 | 操作位置 | 说明 | +|---|------|---------|------| +| 1 | 创建 Harbor 项目 | Harbor Web UI | 项目名与代码仓库名一致 | +| 2 | 推送基础镜像 | 外网机器 | 将 Dockerfile 中用到的基础镜像推送到 `library` | +| 3 | 编写 Dockerfile | 代码仓库 | 使用内网基础镜像地址 | +| 4 | 编写 `.drone.yml` | 代码仓库根目录 | 参考上方模板 | +| 5 | 激活 Drone 仓库 | Drone Web UI | 点击 Activate | +| 6 | 配置 Drone Secrets | Drone Web UI | 添加 harbor / ssh 凭证 | +| 7 | 创建部署目录 | 部署服务器 | `/disk/docker-compose/{项目名}` | +| 8 | 编写 `docker-compose.yml` | 部署服务器 | 配置镜像、端口、环境变量 | +| 9 | 配置 `insecure-registries` | 部署服务器 | Docker daemon 信任 Harbor | +| 10 | 推送代码触发 | 代码仓库 | push 到 master 分支 | + +## 九、常见问题 ### Q: 构建时拉取基础镜像失败? -基础镜像需要提前推送到内网 Harbor。运行 `bash scripts/push-base-images.sh` 一键推送。Drone CI 的 Docker daemon 也需要配置 `insecure-registries`(在 `.drone.yml` 中通过 `insecure: true` 处理推送,但 Dockerfile 中的 FROM 拉取需要 runner 层面配置)。 +基础镜像需要提前推送到内网 Harbor。Drone Runner 的 Docker daemon 也需要配置 `insecure-registries`。`.drone.yml` 中的 `insecure: true` 只处理推送,Dockerfile 中 `FROM` 拉取需要 runner 层面配置。 ### Q: Harbor 认证失败(unauthorized)? @@ -120,16 +558,46 @@ docker login 192.168.195.25:19900 2. 确认服务器 Docker 已配置 `insecure-registries` 3. 检查容器状态:`docker compose ps` / `docker compose logs` -### Q: 如何回滚? +### Q: 如何回滚到指定版本? 使用 commit SHA 标签: ```bash -# 在服务器上修改 docker-compose.yml 中的镜像标签 -# 例如将 :latest 改为 :a1b2c3d4 +cd /disk/docker-compose/my-project + +# 修改 docker-compose.yml 中的镜像标签 +# 将 :latest 改为 :a1b2c3d4(对应 commit SHA 前8位) +# 然后重新部署 docker compose up -d ``` ### Q: 如何手动触发构建? -在 Drone 界面找到对应仓库,点击 "New Build",选择分支即可。 +在 Drone 界面找到对应仓库,点击 **New Build**,选择分支即可。 + +### Q: 如何只部署不重新构建? + +直接在服务器上操作: + +```bash +cd /disk/docker-compose/my-project +docker compose pull +docker compose up -d +``` + +### Q: 构建步骤超时? + +在 `.drone.yml` 的 step 中添加超时配置(Drone 默认 60 分钟): + +```yaml + - name: build + image: plugins/docker + settings: + # ... + # Drone 2.x 不直接支持 step 级别 timeout, + # 可在仓库设置中调整全局超时时间 +``` + +### Q: 如何查看构建日志? + +访问 Drone CI 界面 `http://192.168.195.25:13080`,找到对应仓库和构建编号,点击查看每个步骤的日志。