项目从单体结构重构为 pnpm monorepo (shared/backend/frontend), 新增 YouTube、Instagram、Twitter/X、哔哩哔哩、微博 5 个平台适配器, 包含完整的单元测试和 E2E 测试覆盖。 - 完成 T-031~T-044: 5 个适配器实现、注册、配置和测试 - 重构前后端分离: Hono 后端 + Next.js 前端 - 151 个单元测试 + 21 个 Mock E2E + 25 个真实 E2E - 适配器基于真实 TikHub API 响应结构实现 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
10 KiB
10 KiB
| name | description |
|---|---|
| deploy | 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
核心原则(踩坑总结,不可违反):
- 使用
docker:27-cli+ 宿主机 Docker socket,不用plugins/dockerDinD - 使用
environment: { VAR: { from_secret: name } }注入密钥,不用secrets:字段 - 使用
${DRONE_TAG:-latest}作为镜像 tag,不自定义中间变量(Drone 变量替换冲突) - 触发条件只用
event: [tag, cron],不叠加cron: [name](AND 运算陷阱)
生成内容包括:
trigger: tag + cronvolumes: 挂载宿主机 Docker socket- 每个服务的
build-<service>step deploystep(使用appleboy/drone-ssh)notify-success/notify-failurestep(如配置了 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 本地验证(自己执行)
# 检查 .drone.yml 语法合法性(YAML 解析)
# 检查 deploy-remote.sh 语法(bash -n)
# 检查文件是否已正确写入
6.2 远程验证引导(输出命令,让用户在服务器上执行并反馈结果)
# 推送 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:
services:
drone-server:
image: drone/drone:2
container_name: drone-server
restart: always
ports:
- "3080:80"
environment:
- DRONE_GITEA_SERVER=https://<your-gitea-domain>
- DRONE_GITEA_CLIENT_ID=<gitea-oauth-client-id>
- DRONE_GITEA_CLIENT_SECRET=<gitea-oauth-client-secret>
- DRONE_SERVER_HOST=<your-drone-domain>
- DRONE_SERVER_PROTO=https
- DRONE_RPC_SECRET=<openssl rand -hex 16 生成>
- DRONE_USER_CREATE=username:<gitea用户名>,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 内网直连,不走 HTTPSDRONE_USER_CREATE的 username 必须与 Gitea 登录用户名完全一致(不是邮箱)
B. 私有 Registry
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 添加:
{
"insecure-registries": ["<registry-host>:5000"]
}
不要带 http:// 前缀,直接写 host:port。修改后 sudo systemctl restart docker。
D. SSH 免密
# Drone CI 服务器上生成密钥
ssh-keygen -t ed25519 -C "drone-ci-deploy" -f ~/.ssh/drone_deploy -N ""
# 将公钥添加到生产服务器
ssh-copy-id -i ~/.ssh/drone_deploy.pub -p <port> <user>@<production-ip>
# 验证
ssh -i ~/.ssh/drone_deploy -p <port> <user>@<production-ip> "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 <user> 后重新登录 |
| 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 <service> |
| 健康检查超时 | 增大 MAX_ATTEMPTS;检查服务启动日志 |