Docker + Nginx 打造可回滚的静态站点发布:从目录规范到原子切换

举报
yd_266295732 发表于 2026/11/02 19:46:39 2026/11/02
【摘要】 静态站点看起来最简单:打包、上传、完事。但真正跑起来之后,最容易出问题的恰恰是"上传"这一步——传到一半断开、新版本有 bug 想立刻退回、缓存没刷导致用户看到半新半旧的页面。下面这套做法不依赖任何 CI 平台,只用 Docker 和 Nginx,就能做到"发布即原子切换、回滚一条命令"。 一、目录规范:一切问题的根源不要直接往 /usr/share/nginx/html 里覆盖文件。正确做...

静态站点看起来最简单:打包、上传、完事。但真正跑起来之后,最容易出问题的恰恰是"上传"这一步——传到一半断开、新版本有 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 强制不缓存——这是"发版后用户仍看到旧页面"最常见的解法;

      • 如果站点部署在子路径(例如 /hc-activity/),记得同时设置 root 与 try_files,否则相对路径的图片会 404。

      • 三、发布脚本:先同步,再切换

    • #!/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
      

      两个细节值得强调:

      1. 用 mv -Tf 而不是 ln -sfn /srv/site/current。对于指向目录的符号链接,ln -sfn 有可能把链接建到目标目录内部,结果变成 current/20261102-1930,站点立刻 404。mv -Tf 对目录链接是真正的替换。

        1. 切换前先 nginx -t,配置有错就别 reload,避免把线上打挂。

        2. 四、回滚:一条命令

      2. 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),这也是上面脚本里用相对路径的原因。

        小结

        静态站点的可靠性不来自"上传工具多先进",而来自三点:不可变的版本目录、原子切换、可秒回滚。加上合理的缓存头,一套几十行的脚本就足够支撑绝大多数中小站点,比引入一整套发布平台要轻得多。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。