文章内容

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 硬盘还没挂载上:

    1. Swift 会在根分区的 /srv/node/d1 创建一个新的、空的数据库。

    2. 这个新库里当然没有你之前设置的 CORS。

    3. 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 会尝试同时拉起所有服务,而此时:

  1. Memcached 可能还没好 -> Proxy 启动失败或缓存空数据 -> 导致 CORS 丢失。

  2. 硬盘可能还没挂载 -> 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

保存退出。对 containeraccount 服务也可以做同样的操作。

总结

你现在的 swift.target 只是一个“集合”,加上我提供的 AfterRequires 语句后,它就变成了一个**“有序的启动流程”**,能最大程度避免重启后的 502 和 CORS 故障。


其它相关推荐:

1、OpenStack Swift 存储桶的 CORS 设置保存在那里的?

2、Dell R740XD 全 SSD 做 OpenStack Swift 对象存储

3、OpenStack Swift 202 代码

4、OpenStack Swift 单节点构造文件添加磁盘

5、OpenStack Swift 单节点部署

分享到:

发表评论

评论列表