Docker + Nginx 打造可回滚的静态站点发布:从目录规范到原子切换
静态站点看起来最简单:打包、上传、完事。但真正跑起来之后,最容易出问题的恰恰是"上传"这一步——传到一半断开、新版本有 bug 想立刻退回、缓存没刷导致用户看到半新半旧的页面。下面这套做法不依赖任何 CI 平台,只用 Docker 和 Nginx,就能做到"发布即原子切换、回滚一条命令"。
一、目录规范:一切问题的根源
不要直接往 /usr/share/nginx/html 里覆盖文件。正确做法是每次发布生成一个带版本号的独立目录,再用一个符号链接指向当前版本:
/srv/site/
├── releases/
│ ├── 20261102-1930/
│ └── 20261102-1712/
└── current -> releases/20261102-1930
Nginx 的 root 指向 /srv/site/current。因为符号链接的切换是原子的,用户不会看到"删了一半的旧站点",要么全旧、要么全新。
二、Nginx 配置里三个容易被忽略的点
server {
listen 80;
root /srv/site/current;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location ~* \.(js|css|png|jpg|svg|woff2)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
location = /index.html {
add_header Cache-Control "no-cache";
}
}
try_files保证前端路由刷新不 404;-
-
带 hash 的静态资源长缓存,
index.html强制不缓存——这是"发版后用户仍看到旧页面"最常见的解法; -
#!/usr/bin/env bash set -euo pipefail SRC="$1" REL="/srv/site/releases/$(date +%Y%m%d-%H%M%S)" mkdir -p "$REL" rsync -a --delete "$SRC"/ "$REL"/ ln -sfn "$REL" /srv/site/current.new mv -Tf /srv/site/current.new /srv/site/current nginx -t && nginx -s reload两个细节值得强调:
-
用
mv -Tf而不是ln -sfn /srv/site/current。对于指向目录的符号链接,ln -sfn有可能把链接建到目标目录内部,结果变成current/20261102-1930,站点立刻 404。mv -Tf对目录链接是真正的替换。 -
ln -sfn /srv/site/releases/20261102-1712 /srv/site/current.new mv -Tf /srv/site/current.new /srv/site/current因为旧版本目录还在,回滚是秒级的,不需要重新构建。建议保留最近 5 个版本,用
ls -1t releases | tail -n +6 | xargs -I{} rm -rf releases/{}清理。五、放进容器时注意什么
如果 Nginx 跑在容器里,
current这个符号链接必须在容器内可见,否则容器会看到空目录。两种做法:把/srv/site整个挂载进去;或者干脆让发布脚本在容器内执行(docker exec)。前者的坑是:符号链接如果在宿主机上创建、指向宿主机路径,容器内解析不到,务必使用相对链接(ln -sfn releases/xxx current.new),这也是上面脚本里用相对路径的原因。小结
静态站点的可靠性不来自"上传工具多先进",而来自三点:不可变的版本目录、原子切换、可秒回滚。加上合理的缓存头,一套几十行的脚本就足够支撑绝大多数中小站点,比引入一整套发布平台要轻得多。
-
-
- 点赞
- 收藏
- 关注作者
评论(0)