From 8819a2f91e33a378399000880884bbd901629c62 Mon Sep 17 00:00:00 2001 From: zfc Date: Mon, 2 Mar 2026 10:47:58 +0800 Subject: [PATCH] feat: Add /deploy skill for Drone CI/CD full workflow guidance Co-Authored-By: Claude Opus 4.6 --- .claude/skills/deploy/SKILL.md | 316 +++++++++++++++++++++++++++++++++ 1 file changed, 316 insertions(+) create mode 100644 .claude/skills/deploy/SKILL.md diff --git a/.claude/skills/deploy/SKILL.md b/.claude/skills/deploy/SKILL.md new file mode 100644 index 0000000..b99810c --- /dev/null +++ b/.claude/skills/deploy/SKILL.md @@ -0,0 +1,316 @@ +--- +name: deploy +description: Drone CI + 服务器 CD 全流程引导:从基础设施检查到生成配置文件到验证部署,交互式完成。 +--- + +# Deploy - CI/CD 全流程部署引导 + +> **定位**:基于公司 Drone CI + 私有 Docker Registry + Docker Compose 的自动化部署方案,交互式引导用户从零完成 CI/CD 接入。 + +当用户调用 `/deploy` 或 `/deploy <指令>` 时,执行以下步骤: + +## 1. 收集项目信息 + +快速了解项目情况(已知的不重复问): + +| 项目 | 说明 | 示例 | +|------|------|------| +| 项目名称 | 用于镜像命名 | `douyin`, `crm`, `blog` | +| 需构建的服务 | 每个服务对应一个镜像 | `backend`, `frontend` | +| 各服务的 Dockerfile 路径 | Docker build context | `./backend`, `./frontend` | +| 生产服务器 SSH 端口 | 默认 22 | `22`, `3141` | +| 部署目录 | 生产服务器上的路径 | `/opt/docker/myproject` | +| 数据库迁移命令 | 如有 | `alembic upgrade head`, `npx prisma migrate deploy` | +| 健康检查方式 | 三选一 | python / curl / host | +| 健康检查 URL | 容器内地址 | `http://127.0.0.1:8000/health` | +| 通知 Webhook | 可选,不配则跳过 | 企业微信/钉钉/飞书 | + +确认后进入下一步。 + +## 2. 基础设施检查 + +输出 Checklist,让用户逐项确认(首次接入需全部完成,后续项目可跳过): + +``` +□ 基础设施(一次性,已完成则跳过) + □ Drone CI Server + Runner 已部署运行 + □ 私有 Docker Registry 已运行(默认 :5000) + □ insecure-registries 已配置(Drone CI 服务器 + 生产服务器) + □ SSH 密钥已配置(Drone CI 服务器 → 生产服务器免密登录) + □ 生产服务器用户已加入 docker 组 +``` + +如果用户表示基础设施未就绪,输出对应的一次性搭建指引(参见下方「附录:基础设施搭建」)。 + +## 3. 生成配置文件 + +基于收集到的信息,生成以下文件: + +### 3.1 `.drone.yml` + +核心原则(踩坑总结,不可违反): +1. 使用 `docker:27-cli` + 宿主机 Docker socket,**不用** `plugins/docker` DinD +2. 使用 `environment: { VAR: { from_secret: name } }` 注入密钥,**不用** `secrets:` 字段 +3. 使用 `${DRONE_TAG:-latest}` 作为镜像 tag,**不自定义中间变量**(Drone 变量替换冲突) +4. 触发条件只用 `event: [tag, cron]`,**不叠加** `cron: [name]`(AND 运算陷阱) + +生成内容包括: +- `trigger`: tag + cron +- `volumes`: 挂载宿主机 Docker socket +- 每个服务的 `build-` step +- `deploy` step(使用 `appleboy/drone-ssh`) +- `notify-success` / `notify-failure` step(如配置了 Webhook) + +### 3.2 `scripts/deploy-remote.sh` + +部署脚本要点: +- `set -euo pipefail` 严格模式 +- 部署锁(PID 文件防并发) +- 同时 export `IMAGE_TAG` 和 `VERSION`(兼容不同 compose 变量命名) +- 按顺序:pull → 停 beat → 更新核心服务 → 健康检查 → 数据库迁移 → 启动剩余服务 → 最终健康检查 +- 健康检查根据用户选择的方式生成(python / curl / host) + +### 3.3 生成后展示 + +``` +已生成: + 📄 .drone.yml — Drone CI 流水线配置 + 📄 scripts/deploy-remote.sh — 远程部署脚本 + +确认写入?[Y/n] +``` + +用户确认后写入文件。 + +## 4. Drone 面板配置引导 + +生成文件后,输出需要在 Drone 面板手动配置的清单: + +### 4.1 仓库设置 + +``` +在 Drone 面板完成以下配置: + +1. 激活仓库:SYNC → 找到仓库 → ACTIVATE +2. 开启 Trusted:Settings → General → Project Settings → 勾选 Trusted +``` + +### 4.2 Secrets 配置 + +根据收集到的信息,输出具体的 Secret 列表: + +``` +在 Drone 面板 → 仓库 Settings → Secrets 添加: + +| Secret 名称 | 填写内容 | +|-------------------|----------------------------------------------------| +| backend_repo | docker.internal.intelligrow.cn:5000/{project}-backend | +| frontend_repo | docker.internal.intelligrow.cn:5000/{project}-frontend | +| deploy_host | {生产服务器 IP} | +| deploy_user | {SSH 用户} | +| deploy_ssh_key | cat ~/.ssh/drone_deploy 的完整内容 | +| deploy_path | {部署目录} | +| wecom_webhook | {Webhook URL}(如已配置) | +``` + +### 4.3 Cron 配置(可选) + +``` +如需定时构建,在 Settings → Cron Jobs 添加: + +| 字段 | 值 | 说明 | +|----------|------------------|-------------------------| +| Name | nightly-build | 任务名称 | +| Branch | main | 构建分支 | +| Schedule | 0 16 * * * | UTC 16:00 = 北京 00:00 | +``` + +## 5. 生产服务器配置引导 + +``` +在生产服务器上确认: + +1. 部署目录结构: + {deploy_path}/ + ├── docker-compose.prod.yml + ├── .env + └── scripts/ + └── deploy-remote.sh ← 需从代码仓库复制 + +2. .env 至少包含: + DOCKER_REGISTRY=docker.internal.intelligrow.cn:5000 + (其他数据库密码等生产配置) + +3. docker-compose.prod.yml 中镜像引用格式: + image: ${DOCKER_REGISTRY}/{project}-backend:${VERSION:-latest} + +⚠️ 变量一致性:Drone Secret 的镜像地址前缀 = .env 的 DOCKER_REGISTRY = compose 中的镜像名拼接结果 +``` + +## 6. 验证 + +自己执行可执行的验证,不能远程执行的给出命令让用户确认结果: + +### 6.1 本地验证(自己执行) + +```bash +# 检查 .drone.yml 语法合法性(YAML 解析) +# 检查 deploy-remote.sh 语法(bash -n) +# 检查文件是否已正确写入 +``` + +### 6.2 远程验证引导(输出命令,让用户在服务器上执行并反馈结果) + +```bash +# 推送 Tag 触发首次构建 +git tag v{version} +git push origin v{version} + +# 观察 Drone 面板 pipeline 状态 + +# 生产服务器检查 +ssh -p {port} {user}@{host} "cd {deploy_path} && docker compose -f docker-compose.prod.yml ps" +``` + +## 7. 完成输出 + +``` +✅ CI/CD 接入完成! + +📄 生成的文件: + - .drone.yml + - scripts/deploy-remote.sh + +🔧 Drone 面板配置(需手动): + - [x] 仓库已激活 + - [x] Trusted 已开启 + - [x] Secrets 已添加 + - [ ] Cron Job(可选) + +🖥️ 生产服务器: + - [ ] .env 已配置 + - [ ] deploy-remote.sh 已复制 + - [ ] 首次部署成功 + +📖 回滚方案: + 方式1: ssh 到生产服务器执行 bash scripts/deploy-remote.sh {旧版本tag} + 方式2: git tag {旧版本}-rollback {旧版本} && git push origin {旧版本}-rollback + +主人,用不用我沉淀 or git 提交? +``` + +--- + +## 附录:基础设施搭建 + +当用户表示基础设施未就绪时,按需输出以下指引: + +### A. Drone CI 部署 + +在 Drone CI 服务器创建 `~/drone/docker-compose.yml`: + +```yaml +services: + drone-server: + image: drone/drone:2 + container_name: drone-server + restart: always + ports: + - "3080:80" + environment: + - DRONE_GITEA_SERVER=https:// + - DRONE_GITEA_CLIENT_ID= + - DRONE_GITEA_CLIENT_SECRET= + - DRONE_SERVER_HOST= + - DRONE_SERVER_PROTO=https + - DRONE_RPC_SECRET= + - DRONE_USER_CREATE=username:,admin:true + volumes: + - ./data:/data + + drone-runner: + image: drone/drone-runner-docker:1 + container_name: drone-runner + restart: always + depends_on: + - drone-server + environment: + - DRONE_RPC_PROTO=http + - DRONE_RPC_HOST=drone-server + - DRONE_RPC_SECRET=<与 server 相同> + - DRONE_RUNNER_CAPACITY=2 + - DRONE_RUNNER_NAME=drone-runner-1 + volumes: + - /var/run/docker.sock:/var/run/docker.sock +``` + +关键注意: +- `DRONE_RPC_PROTO=http`:Runner 走 Docker 内网直连,不走 HTTPS +- `DRONE_USER_CREATE` 的 username 必须与 Gitea **登录用户名**完全一致(不是邮箱) + +### B. 私有 Registry + +```bash +docker run -d --name registry \ + -p 5000:5000 \ + -v /opt/registry-data:/var/lib/registry \ + --restart always \ + registry:2 +``` + +### C. insecure-registries 配置 + +在 Drone CI 服务器和生产服务器的 `/etc/docker/daemon.json` 添加: + +```json +{ + "insecure-registries": [":5000"] +} +``` + +**不要带 `http://` 前缀**,直接写 `host:port`。修改后 `sudo systemctl restart docker`。 + +### D. SSH 免密 + +```bash +# Drone CI 服务器上生成密钥 +ssh-keygen -t ed25519 -C "drone-ci-deploy" -f ~/.ssh/drone_deploy -N "" + +# 将公钥添加到生产服务器 +ssh-copy-id -i ~/.ssh/drone_deploy.pub -p @ + +# 验证 +ssh -i ~/.ssh/drone_deploy -p @ "echo ok" +``` + +--- + +## 踩坑清单(生成配置时必须规避) + +| # | 坑 | 正确做法 | +|---|-----|---------| +| 1 | `insecure-registries` 带 `http://` 前缀 | 直接写 `host:port` | +| 2 | Drone `${VAR}` 与 shell 变量冲突 | 直接用 `${DRONE_TAG:-latest}`,不赋中间变量 | +| 3 | 用 `secrets:` 字段注入 secret | 用 `environment: { VAR: { from_secret: name } }` | +| 4 | `plugins/docker` DinD 启动失败 | 用 `docker:27-cli` + 挂载 Docker socket | +| 5 | `DRONE_USER_CREATE` 填邮箱 | 必须填 Gitea 登录用户名 | +| 6 | `event + cron` 触发条件互斥 | 只用 `event: [tag, cron]`,不加 `cron:` 过滤 | +| 7 | Registry 地址不一致(IP vs 域名) | Drone Secret、`.env`、compose 三处统一 | +| 8 | SSH 端口不对 | `appleboy/drone-ssh` 显式指定 `port` | +| 9 | Docker 权限不足 | `sudo usermod -aG docker ` 后重新登录 | +| 10 | `daemon.json` 被覆盖 | 修改前先 cat 查看,合并内容 | + +--- + +## 故障排查速查表 + +| 现象 | 检查方向 | +|------|---------| +| Pipeline 不触发 | Gitea Webhook 是否勾选"创建"事件;`.drone.yml` trigger | +| Step 一直 pending | Runner 是否连通 Server;仓库是否 Trusted | +| 构建报 secret 为空 | `environment: from_secret` 而非 `secrets:` | +| Docker push 失败 (HTTPS) | 两台服务器 `insecure-registries` 配置 | +| SSH 部署超时 | 密钥是否正确;端口是否匹配;Docker 权限 | +| 镜像名 invalid reference | `.env` 的 `DOCKER_REGISTRY` 变量是否正确 | +| 数据库迁移失败 | `docker compose logs -f ` | +| 健康检查超时 | 增大 `MAX_ATTEMPTS`;检查服务启动日志 |