同一个页面刷新两次,第一次加载 /static/version1720000000/,第二次却变成另一个版本;或者 HTML 引用了新签名,CDN 回源却落到还没有新文件的节点。结果就是一部分用户正常,一部分用户 CSS/JS 404。

这种问题不能靠“每台机器都跑一遍 static-content:deploy”解决。恰恰相反,各节点独立生成产物和版本号,往往就是不一致的来源。

用响应把请求落点标出来

先让负载均衡或 Web Server 临时返回不含敏感信息的节点标识,例如 X-App-Node 和发布版本 X-Release。连续请求页面,记录:

  • HTML 来自哪个节点;
  • HTML 中的 static version;
  • 失败静态文件回源到哪个节点;
  • CDN 是 HIT、MISS 还是回源错误。

如果 HTML 节点和静态文件节点属于不同 release,问题已经很清楚:发布过程没有做到版本一致性。

比较的不只是 deployed_version.txt

cat pub/static/deployed_version.txt
sha256sum pub/static/frontend/Vendor/theme/zh_Hans/js/*.js | sort
sha256sum app/etc/config.php composer.lock
readlink -f pub/static
readlink -f current

每台节点上的 deployed_version.txt、主题关键文件哈希、代码 release 和 pub/static 实际指向都应一致。若使用共享存储,还要确认所有节点看到的是同一挂载点,而不是路径同名的本地目录。

三种发布方式,只有一种适合你的架构

构建一次,分发同一产物

在 CI 构建 Magento 代码和静态内容,生成一个不可变 artifact,再分发到所有节点。版本文件与静态资源天然一致,这是最容易审计的方式。

共享 pub/static

所有节点读取同一份静态目录,但写入必须由单一构建流程完成。滚动发布时要避免旧代码读取到只适用于新代码的静态产物。

各节点本地静态目录

可以使用,但节点必须从同一个构建产物解压,不能各自运行生成命令。还要确保流量只在解压完成后切入。

原子切换比清缓存重要

可靠做法是把新版本部署到独立 release 目录,完成代码、依赖和静态资源校验后,再一次性切换 current 软链接或负载均衡目标。不要在正在服务的 pub/static 上边删边生成。

CDN 清理也应在源站全部可用之后执行。过早 purge 会让全球节点同时回源,恰好击中尚未完成部署的应用节点;过晚则继续提供旧 HTML 或旧静态内容。具体顺序取决于 CDN 缓存的是 HTML 还是只有静态文件。

验证时故意绕过“幸运命中”

对每个应用节点分别请求同一个 HTML 和两个关键 JS/CSS,再通过真实域名请求一遍 CDN。连续至少覆盖一个负载均衡轮次,并检查 404、MIME type、缓存头和文件哈希。只在浏览器里刷新一次,很可能只是幸运地落到了已经完成发布的节点。