文章内容
2026/1/14 17:58:42,作 者: 黄兵
系统重启导致 OpenStack Swift Cors 出现问题
最近重启运行 OpenStack Swift 服务的 VPS,重启之后导致访问 OpenStack Swift 上的对象出现 Cors 错误。
Access to script at 'https://www.example.com/5.bundle.js' from origin 'https://www.a.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
出现问题的原因:
出现这个问题的原因主要和 OpenStack Swift 的架构特性 以及 Linux 启动顺序 有关。
重启后 CORS 丢失(或暂时失效)通常是以下三个原因之一造成的,按可能性排序:
1. 为什么会出现这个问题?(深度解析)
原因一:Memcached 缓存丢失(最核心原因) Swift 的 Proxy Server 极其依赖 Memcached。容器的元数据(包括 CORS 设置)是缓存在 Memcached 里的。
平时:Nginx 请求 -> Swift Proxy -> 直接从 Memcached 读取 CORS -> 返回。
重启后:Memcached 是内存数据库,重启即清空。
故障时刻:重启刚完成时,Nginx 请求 -> Swift Proxy -> Memcached (空的) -> 尝试去读 Container Server 硬盘。
问题点:如果此时 Container Server 的硬盘还没挂载好,或者服务还在初始化,Proxy 读不到数据,就会默认“没有 CORS 设置”,并把这个“空结果”又缓存到了 Memcached 里。这就导致了即便服务后面好了,CORS 依然报错。
原因二:磁盘挂载延迟(Race Condition) Linux 启动时,服务启动和磁盘挂载有时是并行或存在竞争的。 如果
swift-container-server启动了,但/srv/node/d1硬盘还没挂载上:Swift 会在根分区的
/srv/node/d1创建一个新的、空的数据库。这个新库里当然没有你之前设置的 CORS。
Proxy 读到了空数据。
原因三:CORS 是“容器级”属性,而非“对象级”属性 你访问的是图片(对象),但 CORS 规则存在桶(容器)上。如果容器服务(Account/Container Ring)初始化比对象服务慢,就会出现“能下载图片,但拿不到 CORS 头”的怪象。
具体 OpenStack Swift Metadata 存储的位置,可以参考这篇文章:OpenStack Swift 存储桶的 CORS 设置保存在那里的?
解决方案:
我们通过 Ubuntu 的服务启动 OpenStack Swift,所以我们需要调整服务启动顺序。
由于 OpenStack Swift 的需要启动的服务非常多,我们编写了 swift.target 服务,具体内容如下:
[Unit] Description=OpenStack Swift Object Storage System Documentation=https://docs.openstack.org/swift/latest/ # === 强依赖配置 === # Requires: 这些服务必须启动。如果其中一个失败,target 可能会报告失败。 # Wants: 这些服务"想"要启动。即使某个(如 account-reaper)挂了,target 依然算启动成功。 # 为了稳定性,我们使用 Wants,防止一个次要服务卡死导致整个系统无法启动。 # 1. 核心接口与存储服务 Wants=openstack-swift-proxy.service Wants=openstack-swift-account.service Wants=openstack-swift-container.service Wants=openstack-swift-object.service # 2. 对象后台服务 (Object Rings) Wants=swift-object-replicator.service Wants=swift-object-updater.service Wants=swift-object-auditor.service # 3. 容器后台服务 (Container Rings) Wants=swift-container-replicator.service Wants=swift-container-updater.service Wants=swift-container-auditor.service # 4. 账户后台服务 (Account Rings) Wants=swift-account-replicator.service Wants=swift-account-auditor.service Wants=swift-account-reaper.service [Install] WantedBy=multi-user.target
这样我们就可以通过swift.target 服务一键启动所有的 OpenStack Swift 服务。
但是,这里出现了 CORS 丢失问题,上面的配置有一个致命的隐患:
它只定义了“包含哪些服务”,没有定义“启动时机”和“前置依赖”。
也就是说,当系统启动时,swift.target 会尝试同时拉起所有服务,而此时:
Memcached 可能还没好 -> Proxy 启动失败或缓存空数据 -> 导致 CORS 丢失。
硬盘可能还没挂载 -> Object/Container Server 读写空目录 -> 导致 502 或数据丢失。
为了避免重启后的故障,我们需要给它加上**“必须等待(After)”**的强约束。
🚀 优化后的 swift.target
请用下面的内容替换你原本的配置。我主要增加了 [Unit] 部分的依赖约束。
[Unit] Description=OpenStack Swift Object Storage System Documentation=https://docs.openstack.org/swift/latest/ # ======================================================= # ✅ 核心修复:启动顺序与依赖约束 # ======================================================= # 1. 必须等待网络完全就绪 (network-online 而非仅仅 network) After=network-online.target Wants=network-online.target # 2. 必须等待所有本地磁盘挂载完成 (防止写到系统盘) After=local-fs.target remote-fs.target # 3. 必须等待 Memcached 启动 (Swift 的心脏,防止 502/CORS 丢失) After=memcached.service Requires=memcached.service # ======================================================= # 原有服务配置 (保持不变,但逻辑更安全了) # ======================================================= # 1. 核心接口与存储服务 Wants=openstack-swift-proxy.service Wants=openstack-swift-account.service Wants=openstack-swift-container.service Wants=openstack-swift-object.service # 2. 对象后台服务 (Object Rings) Wants=swift-object-replicator.service Wants=swift-object-updater.service Wants=swift-object-auditor.service # 3. 容器后台服务 (Container Rings) Wants=swift-container-replicator.service Wants=swift-container-updater.service Wants=swift-container-auditor.service # 4. 账户后台服务 (Account Rings) Wants=swift-account-replicator.service Wants=swift-account-auditor.service Wants=swift-account-reaper.service [Install] WantedBy=multi-user.target
💡 修改点深度解析 (为什么这么改?)
1. After=network-online.target
旧代码:可能默认只依赖
network.target。问题:
network.target只代表“网络服务启动了”,但不代表“IP地址分配好了”。Swift Proxy 需要绑定 IP,如果网卡还没分配 IP 就启动,Proxy 会报错退出。改进:
network-online.target会等到网络真正通了才往下走。
2. After=local-fs.target
问题:这正是你之前遇到问题的核心。如果
/srv/node/d1还没挂载,Swift 就启动了,它会把数据写到空的文件夹里。改进:强制要求文件系统加载完毕后,才允许 Swift 启动。
3. Requires=memcached.service & After=memcached.service
问题:Memcached 没启动时,Swift Proxy 也能强行启动,但会处于“半残”状态(无法处理 Token,无法缓存 CORS),导致 Nginx 502。
改进:
Requires:强绑定。如果 Memcached 启动失败,Swift 干脆别启动了(防止脏数据)。After:确保 Memcached 先跑起来,Swift 后跑起来。
⚠️ 还需要做的一件事 (双重保险)
虽然修改 swift.target 很有用,但 systemd 有时比较“任性”,如果具体的子服务(比如 openstack-swift-object.service)自己没有写依赖,它们可能会被 systemd 并行启动。
为了绝对防止磁盘未挂载就启动,建议给具体的存储服务加一个“挂载点检查”。
执行命令(无需修改文件,直接用 systemd 的 override 功能):
# 为 object-server 添加强依赖 systemctl edit openstack-swift-object.service
在打开的编辑器中粘贴:
[Unit] # 只要 /srv/node 没挂载好,绝对不允许启动这个服务 RequiresMountsFor=/srv/node
保存退出。对 container 和 account 服务也可以做同样的操作。
总结
你现在的 swift.target 只是一个“集合”,加上我提供的 After 和 Requires 语句后,它就变成了一个**“有序的启动流程”**,能最大程度避免重启后的 502 和 CORS 故障。
其它相关推荐:
1、OpenStack Swift 存储桶的 CORS 设置保存在那里的?
评论列表