feat: add deployer

This commit is contained in:
2026-08-24 09:44:59 +08:00
parent 31bc5f45ce
commit e4d4319919
20 changed files with 1971 additions and 288 deletions
+200
View File
@@ -0,0 +1,200 @@
---
name: deployer
description: >-
管理多 VPS / NAS 的 Docker Compose 配置中心:仓库只存服务数据(compose.yaml、静态配置),
部署方法与脚本由本 skill 提供。当用户要求部署、同步、升级、重启远程 Docker 服务,
新增/迁移/下线服务,梳理节点与服务清单,或提到 make sync/deploy/upgrade/TGT、_config.yaml、
rsync 同步、tar over SSH、Synology NAS 部署失败时使用。
---
# deployer:多机 Compose 配置中心
仓库 = 数据(各机器的 `compose.yaml` 与静态配置);方法 = 本 skill 的脚本与规范。
本地改配置 → skill 脚本同步到对应 SSH 节点 → 远程 `docker compose` 应用。
---
## 何时使用
- 部署 / 同步 / 升级 / 重启某个远程 Docker Compose 服务
- 新增、迁移、下线一个服务;梳理「哪台机器跑什么」
- sync 失败排查、证书丢失、改了配置不生效等运维问题
- 提到 `make deploy TGT=...``TGT=``_config.yaml`、rsync/tar 同步
## 不适用
- 单机 docker 日常使用(无多机同步诉求)
- K8s / Nomad 等编排系统
- CI/CD 流水线构建发布(本流程是 push 式运维,不是流水线)
---
## 核心模型(先读懂再动手)
- **仓库只放数据**`compose.yaml`、Caddyfile、Traefik 动态配置等静态配置进 Git;
运行时数据(证书、数据库、上传文件)永不进 Git,也永不参与同步范围。
- **每个可部署服务目录必须有 `compose.yaml`**,且能解析出目标节点 `node`
(来自该目录、部署根或祖先目录的 `_config.yaml`,或父目录名恰为 SSH Host 别名)。
- **`node` 即 SSH Host 别名**`~/.ssh/config`),支持 `user@host` 形式。
- `unused/` 下不参与自动发现与部署。
### 两种布局
**A. 独立配置中心仓库**(如 app00):仓库根即部署根,
`DEPLOYER_ROOT=/path/to/repo` 指定后按仓库内相对路径操作:
```
repo/
├── _config.yaml # 可选,全局默认
├── vyyo1/_config.yaml # node: vyyo1(主机目录)
│ └── naiveproxy/ # 服务目录:compose.yaml + 可选 _config.yaml
└── unused/
```
远程目录名 = 目录末级名:`vyyo1/naiveproxy``/opt/app/naiveproxy`
**B. 项目内环境布局**:项目根放 `.skiff/deployer/{prod,test,dev}/`
每个环境一个目录。从项目内任意位置运行脚本即自动发现(也可用 `DEPLOYER_ROOT`
显式指定),无需环境变量:
```
my-project/
├── src/ ... # 项目本体
└── .skiff/deployer/
├── _config.yaml # 三个环境共享默认(node/base_path 等)
├── prod/
│ ├── compose.yaml # 生产 compose 与配置
│ └── _config.yaml # 环境级覆盖
├── test/compose.yaml
└── dev/compose.yaml
```
项目模式下远程目录名自动加项目前缀 `{git仓库名}-{env}`
(如 `my-project-prod`),防止同主机多项目的同名环境互相覆盖;
`_config.yaml``name:` 可显式指定。
## 步骤
### 0. 定位部署根
skill 目录下的 `scripts/deploy/` 是通用部署工具链(lib/sync/remote/list),
不依赖具体项目路径。部署根按以下顺序解析:
1. 环境变量 `DEPLOYER_ROOT` 显式指定(独立配置中心仓库用这个)
2. 从当前目录向上找 `.skiff/deployer/`(项目内环境布局自动发现)
3. skill 安装位置兜底(仅用于查看,没有可部署服务)
```bash
# <skill-dir> = 本 SKILL.md 所在目录,先解析出来记下
# 布局 A:显式指定仓库根
export DEPLOYER_ROOT=/path/to/your/compose-repo
python3 <skill-dir>/scripts/deploy/list.py
# 布局 B:在项目内直接跑即可(cwd 在项目里)
python3 <skill-dir>/scripts/deploy/list.py
```
### 1. 摸底:列出服务与节点
上一步的 `list.py` 输出全部服务与节点分布;新增环境/服务后重跑确认被发现。
项目布局下 `prod/test/dev` 各显示为 `{项目名}-{env}`
### 2. 解析单个服务
```bash
# 查看 node、远程路径、排除规则(sync.py 干跑会打印这些信息)
python3 <skill-dir>/scripts/deploy/sync.py <service-path>
```
或直接读服务目录及祖先的 `_config.yaml`
### 3. 命令选择(语义严格区分)
| 意图 | 命令 |
|------|------|
| 只同步文件,不动容器 | `sync.py <svc>` |
| 应用 compose/配置变更 | `sync.py <svc> && remote.py <svc> up` |
| 改配置后强制重建 | `remote.py <svc> recreate`(配合前置 sync |
| 镜像 tag 变更升级 | `sync.py <svc> && remote.py <svc> upgrade` |
| 仅重启,不同步文件 | `remote.py <svc> restart` |
| 排查 | `remote.py <svc> ps` / `remote.py <svc> logs` |
### 4. 项目侧 Makefile(可选薄封装)
若项目有 Makefile 封装,命令形如 `make deploy TGT=<服务路径>`
没有 Makefile 时直接调 python 脚本即可,不要新建封装层。
### 5. 新增服务 / 环境 checklist
独立仓库布局:
1. 在合适分类目录创建服务文件夹,写 `compose.yaml`
2. 在服务目录或祖先目录放 `_config.yaml`(至少能解析出 `node`
3. 有运行时目录 → 加进 `sync_exclude`
4. 远程首次建目录:`ssh <node> "mkdir -p <base_path>/<name>"`
5. 首次部署:sync + up
6. 验证:ps + logs,必要时 curl/ssh 检查端口
项目环境布局:
1. 项目根建 `.skiff/deployer/{env}/`env 通常为 prod/test/dev
2. 每个环境写 `compose.yaml`;三个环境共享的 node/base_path 放
`.skiff/deployer/_config.yaml`
3. 环境有差异(不同主机、不同排除项)→ 在该环境的 `_config.yaml` 覆盖
4. 同名冲突或需要固定远程目录名 → `_config.yaml``name:`
5. 首次部署前确认目标主机的远程目录不存在旧内容(rsync `--delete` 会清掉)
### 6. 下线服务
独立仓库布局:配置移入 `unused/`(自动脱离发现体系),远程按需手动清理:
`ssh <node> "cd <base_path>/<name> && docker compose down"`,数据卷按需保留或删除。
项目环境布局:删除对应 `.skiff/deployer/{env}/` 目录即可脱离发现体系,远程清理同上。
---
## 注意事项
- **禁止节点级批量操作**:所有 sync/up/recreate/upgrade/restart 必须按单服务执行。
批量升级风险过高,逐个来。
- **rsync 带 `--delete`**:远程多余文件会被删除。运行时数据必须放在
默认排除的 `data/``_data/`,或 compose 挂载的远程绝对路径
(如 `/data01/docker/<svc>/`),否则会被清掉。
- **镜像固定 tag**,不用 `:latest` 漂移;成对升级的服务(如 proxy 客户端/服务端)要同步升。
- **密钥**:优先放远程 `.env` 或环境变量,不要提交新密钥进 Git。
- **Git 安全**:不 `--force` 推送、不硬 reset,除非用户明确要求。
- **NAS / Synology 特例**:部分 NAS 的 SSH 用户禁用 rsync 协议(Permission denied)。
表现是 sync 报错但 ssh 正常。处理顺序:
1. 该节点 `_config.yaml` 写真实 `base_path`(如 `/volume1/docker`,避开符号链接路径)
2. 仍失败则手动 tar over SSH 推送:
```bash
tar czf - -C <服务目录> . --exclude='data' --exclude='_data' \
| ssh <node> "mkdir -p <base_path>/<name> && cd <base_path>/<name> && tar xzf -"
ssh <node> "cd <base_path>/<name> && /usr/local/bin/docker compose up -d"
```
tar 不会删除远程多余文件;需清理旧文件时手动 SSH 删除。
3. Synology 上 docker 路径可能是 `/usr/local/bin/docker`
## 验证
- `list.py` 输出全部服务与节点分布,数量与预期一致
- 每次 sync/deploy 后 `remote.py <svc> ps` 容器 Up、`logs` 无报错
- 升级后额外确认镜像 tag 与 compose.yaml 一致
- 改 Traefik/Caddy 路由后 curl 对应域名验证生效
## scripts/
| 文件 | 用途 |
|------|------|
| `scripts/deploy/lib.py` | 解析服务目录、合并继承 `_config.yaml`、SSH/rsync 参数构造 |
| `scripts/deploy/sync.py` | rsync -avz --delete 同步;无 rsync 时 tar over SSH 兜底 |
| `scripts/deploy/remote.py` | SSH 远程 docker composeup/recreate/restart/upgrade/ps/logs |
| `scripts/deploy/list.py` | 扫描全部可部署服务 |
## references/
| 文件 | 用途 |
|------|------|
| `references/config-reference.md` | `_config.yaml` 字段完整说明与继承合并规则 |