Context
通过把变更文件覆盖到线上树上来部署,会有访问者看到部分发布的窗口风险:某些文件是新的、另一些是旧的,相关联的资源彼此不一致。单个损坏文件也可能弄坏一个本来完好的线上站点。任何原地编辑被服务目录的发布路径,都让"上一个发布"难以命名和恢复。
Decision
发布时在线上树旁边暂存一棵完整的新树,然后重命名 Caddy 服务的单一目录指向它。旧树保留在磁盘上,用其先前的名字。
rsync -a public/ /srv/www/example-site.new/
mv /srv/www/example-site.new /srv/www/example-site
因为服务根是一个目录名,交换从客户端角度看是原子的:每个请求要么解析到旧的完整树,要么解析到新的完整树,绝不会是一个混合。
后果
- 无部分状态: 访问者总是看到一个连贯的发布。
- 回滚廉价: 旧树仍然存在;恢复是一次反向重命名。
- 交换前可验证: 暂存的树在被服务之前可以被检查和哈希。
- 存储取舍: 服务器持有当前树外加一棵暂存树;额外磁盘是原子性的代价。
为什么它胜过就地同步
就地同步承诺在线调整一棵树,但它无法原子地回答"此刻正在被服务的字节是哪些",并且让回滚复杂化为"回到昨天"。完整目录交换用一个持久事实——被服务的目录名——来回答发布问题,让前进与后退都是单次重命名。
来源
- Martin Fowler:蓝绿部署,完整树与蓝绿/暂存交换模型,已核验 2026-08-20。
- POSIX
rename(),支撑交换的原子重命名语义,已核验 2026-08-20。