Files
.pouch/skills/deployer/SKILL.md
T

9.9 KiB
Raw Blame History

name, description
name description
deployer 管理多 VPS / NAS 的 Docker Compose 配置中心:仓库只存服务数据(compose.yaml、静态配置), 部署方法与脚本由本 skill 提供。当用户要求部署、同步、升级、重启远程 Docker 服务, 新增/迁移/下线服务,梳理节点与服务清单,向节点分发安装 deb 包(scp 上传 + dpkg/apt 安装, 或从 URL 远程拉取安装),或提到 make sync/deploy/upgrade/TGT、_config.yaml、rsync 同步、 tar over SSH、Synology NAS 部署失败时使用。

deployer:多机 Compose 配置中心

仓库 = 数据(各机器的 compose.yaml 与静态配置);方法 = 本 skill 的脚本与规范。 本地改配置 → skill 脚本同步到对应 SSH 节点 → 远程 docker compose 应用。


何时使用

  • 部署 / 同步 / 升级 / 重启某个远程 Docker Compose 服务
  • 向节点安装 deb 包:scp 上传本地 .deb 后 dpkg/apt 安装,或远程从 URL 直接拉取安装
  • 新增、迁移、下线一个服务;梳理「哪台机器跑什么」
  • 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.yamlname: 可显式指定。

步骤

0. 定位部署根

skill 目录下的 scripts/deploy/ 是通用部署工具链(lib/sync/remote/list), 不依赖具体项目路径。部署根按以下顺序解析:

  1. 环境变量 DEPLOYER_ROOT 显式指定(独立配置中心仓库用这个)
  2. 从当前目录向上找 .skiff/deployer/(项目内环境布局自动发现)
  3. skill 安装位置兜底(仅用于查看,没有可部署服务)
# <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. 解析单个服务

# 查看 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.yamlname:
  5. 首次部署前确认目标主机的远程目录不存在旧内容(rsync --delete 会清掉)

6. 下线服务

独立仓库布局:配置移入 unused/(自动脱离发现体系),远程按需手动清理: ssh <node> "cd <base_path>/<name> && docker compose down",数据卷按需保留或删除。 项目环境布局:删除对应 .skiff/deployer/{env}/ 目录即可脱离发现体系,远程清理同上。

7. 向节点安装 deb 包

deb.py 把 deb 包发到节点并安装。目标两种写法:仓库内目录 (复用 _config.yaml 继承链解析 node/port/identity_file,如 hosts/web1), 或裸 SSH 别名 / user@host(须在 ~/.ssh/config 中,可加 --port/--identity)。

# 本地 .deb → scp 上传 → 远程 apt 安装(失败自动 apt -f 修依赖),成功后删暂存包
python3 <skill-dir>/scripts/deploy/deb.py <target> push ./foo_1.0_amd64.deb --yes

# 仅上传到远程暂存目录(默认 {base_path}/.debs;裸主机为 /tmp/deployer-debs
python3 <skill-dir>/scripts/deploy/deb.py <target> scp ./foo_1.0_amd64.deb

# 安装该节点暂存目录里已上传的全部 .deb(配合 scp 分步操作)
python3 <skill-dir>/scripts/deploy/deb.py <target> dpkg --yes

# 远程直接从 URL 下载安装(机器能出网时免上传)
python3 <skill-dir>/scripts/deploy/deb.py <target> apt https://example.com/foo_1.0_amd64.deb --yes
  • 非 root 用户走 sudo -n(需配好免密 sudo);--yes-y 免交互, 无终端交互能力,没配 sudo 免密/密钥时会直接失败。
  • 升级同版本号前想先看包信息:ssh <node> "dpkg -I <暂存路径>" 装完验证:ssh <node> "dpkg -l | grep <pkg>"

注意事项

  • 禁止节点级批量操作:所有 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 推送:

      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/scp 参数构造
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/deb.py deb 包分发安装:push(scp+apt)/scp/dpkg/apt(URL)
scripts/deploy/list.py 扫描全部可部署服务

references/

文件 用途
references/config-reference.md _config.yaml 字段完整说明与继承合并规则