ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Ceph RGW多区部署实战:从概念原理到排障调优

Ceph RGW多区部署实战:从概念原理到排障调优

1. 项目概述与多区部署的适用场景

先说说我自己的理解。Ceph对象网关(RGW)多区部署,本质上解决的是“一个地区的网关挂了,数据不能丢、服务不能断”和“不同地域的用户都要就近读写数据”这两个最现实的问题。官方文档里管这套东西叫 multisite,配置起来有点绕,网上能搜到的资料大多只讲了命令怎么敲,却没讲清楚背后的设计取舍,导致很多人照着文档敲完,发现同步根本不生效,或者主备切换之后客户端彻底连不上了。这篇就把我从零搭建、踩坑、调优的完整过程梳理出来,给后来的人省点时间。

这套方案的适用场景很清楚:如果你的业务只需要单机房、单集群,那完全没必要上多区,徒增复杂度;但如果你是跨地域部署、有灾备诉求、或者希望不同区域的用户都能就近访问对象存储,那么多区基本是现在唯一成熟、开箱即用的选择。适合的读者包括准备在生产环境落地Ceph RGW多区的运维工程师,以及想搞明白realm、zonegroup、zone这几个概念之间到底什么关系,而不是只会抄命令的同学。

先说一个容易误解的地方:Ceph多区不等于多集群的简单复制。它有一套自己独立的逻辑体系,数据同步不是靠底层的rados复制,而是靠RGW层跑后台进程拉取和推送增量日志。这个设计决定了它既能跨集群工作,也让故障排查的思路和普通Ceph集群完全不同。

2. 核心概念梳理与架构设计

2.1 Realm、Zonegroup、Zone 三者关系

我见过太多人栽在这三个概念上,其实用“国家、省、市”来类比,一下就通了。

  • Realm(领域):相当于“国家”,是一套隔离的命名空间。每个realm有自己的全局配置,不同的realm之间互不可见、互不复制。
  • Zonegroup(区域组):相当于“省”,一个realm下可以有多个zonegroup。在普通双活或主备场景下,通常只有一个zonegroup。
  • Zone(区域):相当于“市”,真正存储数据和处理请求的单元。一个zonegroup里可以有多个zone。

realm创建之后,会生成一个叫做period的东西,你可以把它理解成“配置快照”。所有zone之间要同步,靠的就是把当前period发布出去,其他zone拉取到这个period之后,才知道全局有哪些zonegroup、哪些zone、各自的endpoint是什么。很多人配置完发现别的zone看不到自己,十有八九是period没发布或者没拉取成功。

2.2 多区复制链路的基本工作方式

RGW多区的同步,分两条链路:元数据同步和数据同步。

元数据同步,解决的是“每个区都知道现在有哪些bucket、哪些用户”。它靠的是zone内一个叫做meta master的角色,元数据变更先落到这个zone,再由meta master广播到其他zone。数据同步,解决的是“对象内容本身怎么过去”。每个zone会为每个log shard维护一个游标,后台进程从游标位置读取数据变更日志,然后跨集群执行复制写入。

理解这个机制对排障特别重要。比如你发现某个区域新增的bucket迟迟没有出现在远端,多半要查metadata同步队列;如果bucket能看到,但里面的对象同步不过去,那问题就在data sync那一层。两条链路独立工作,一个挂了另一个不会受影响,但也正因如此,自检的时候要分别看。

2.3 设计决策:双区还是多区、双活还是主备

我做了几个实际部署项目之后,总结出来的经验是:在动手配置之前,一定要先想清楚你到底是哪种模式。

  • 双活模式:两个zone都能写入,业务流量可以按地域就近接入。代价是容易出现并发写同一个对象的冲突,需要业务侧有冲突处理机制,而且网络延迟和带宽要求高。
  • 主备模式:平时只有主区对外服务,备区只读或干脆不接入流量,只在主区故障时切换。配置简单,运维心里踏实,绝大多数“灾备”诉求其实选这个就够了。
  • 三区甚至更多:通常是“主备套备”或者“两个双活区加一个只读区”。多一个区,同步链路的复杂度、带宽占用、故障排查面都是成倍增长的,不是单纯加个机房的事。

从我实操的角度,强烈建议绝大多数场景先做一主一备。RGW多区的复制不是实时的,在最坏情况下,数据延迟取决于同步队列的积压程度和对象大小。把预期管理好,后续加区再扩展,比一上来就铺开三区稳得多。

3. 部署前准备与环境规划

3.1 网络规划:带宽、时延和端口

多区同步对网络的要求,很多人一开始会低估。你可以把RGW多区复制想象成两地之间每天跑一趟物流货车:车就是同步线程,货物的体积取决于你上传对象的速度和大小,而路况就是网络时延和抖动。

先说时延。RGW同步进程是跨集群发HTTP请求的,如果两个机房之间往返延迟超过100毫秒,你会在同步速度上立刻感觉到差距。尤其是元数据同步,它是偏串行的,时延高会直接拖慢整个创建bucket、创建用户这类操作在远端生效的节奏。

再说带宽。你需要估一个数字:业务峰值写入量乘以一个冗余系数,就是同步需要的带宽。比如单日产生5TB新数据,按8小时业务活跃时间折算,峰值写入大概是1.5Gbps左右,那两区之间的专线带宽至少得按2Gbps起步规划,否则数据永远追不上,积压越来越深。

端口方面,RGW同步本质上是调用对端RGW的API,所以只需要把RGW端口(默认7480,尤其是用于同步的端口)在两个区之间互相放通,不需要开放Ceph集群内部的各个端口。很多人误以为多区同步要集群底层网络互通,其实完全不必要,这也正是RGW多区方案的一个巨大优点:两个集群可以深度隔离。

3.2 时钟同步与DNS解析

不要笑,时钟同步真的是我踩过的一个大坑。RGW同步日志里大量用时间戳做游标和排序,如果两个区的系统时钟偏差超过几秒,会出现同步服务反复重启、同步位点诡异回退、甚至老数据永远同步不过去的现象。

建议在两边的所有Ceph节点上,统一配置好NTP,并定期用 chronyc tracking 检查偏差。控制在100毫秒以内比较稳。

DNS解析要提前规划好。每个zone都会配置一个或多个endpoint,这些endpoint会被写进period并分发给其他zone。问题在于,如果你用的是机房内网域名,另一个机房根本解析不了,那同步请求就会直接失败。我建议直接用公网域名或者两边都能解析的独立域名,然后在两个机房分别配好对应的解析关系。

另外,客户端访问的域名和RGW同步用的endpoint,最好分离。比如对外提供 store.example.com,但zone的endpoint配置成 rgw-sync-a.internal.example.com。这样将来做故障切换的时候,只需要动客户端的解析,不需要动zone配置。

3.3 软件版本选择与集群健康检查

多区部署对版本比较敏感。我见过有人拿两套大版本不同的集群做多区,结果某个新增的桶属性在旧版本上无法识别,日志里全是“unknown attr”之类的警告。所以首选方案永远是一边升级到同一个版本,再做多区配置;如果实在没法统一,至少确保两个版本之间的RGW API兼容,并且以低版本那一边作为功能基准。

配置多区之前,还要确保两套集群各自都是健康的,存储池有足够的余量。别觉得这是废话,我就处理过一次案例:某集群的存储池余量只剩5%,用户说“同步过去的数据都是坏的”,实际上是因为对端集群的池空间耗尽,写入直接报了错,而RGW的管理日志里只有一句不痛不痒的“failed to write”。先看集群健康,能省掉一半的排障时间。

4. 多区部署实操全流程

4.1 主区的初始配置

当基础环境没有问题后,开始真正的部署动作。以下命令基于常见版本,不同小版本在参数细节上略有差异,但整体流程不会变。我在主集群上执行:

首先,创建realm并设为默认:

radosgw-admin realm create --rgw-realm=prod-realm --default

注意,realm一旦创建,集群内已有RGW的旧配置可能会陷入“没有realm”的空白状态,所以建议大家在配置维护窗口内操作,并且确认所有RGW都在同一个realm下。

接着创建zonegroup和zone:

radosgw-admin zonegroup create --rgw-zonegroup=zg-prod --rgw-realm=prod-realm --default radosgw-admin zone create --rgw-zonegroup=zg-prod --rgw-zone=zone-a --master --default

zone-a指定了master,意味着这个zone同时承担元数据中心的角色,也是整个多区体系的“源”。随后要把配置文件更新到ceph.conf里。很多发行版要求RGW启动时显式指定rgw_zone,如果不写,网关会按默认zone启动,跟你的多区配置完全对不上。

之后就是最容易漏掉的一步:发布period。

radosgw-admin period update --commit

这个命令会把当前配置固化成一个新的period版本,并推送到所有endpoint。我遇到过有人在主区把所有配置都做好了,也确认了radosgw-admin zone list能正常输出,但忘记敲period update,结果从区那边怎么pull都只能看到默认的old realm配置。这个命令直接决定了其他区能否感知到你的新配置,优先级很高。

主区的zone端点要设置好:

radosgw-admin zone modify --rgw-zone=zone-a --endpoints=http://rgw-a.example.com:7480 radosgw-admin period update --commit

然后重启主区的RGW进程,确认它以新realm启动即可。

4.2 从区的配置与拉取

从区的第一次配置,核心操作是“pull”。先在从区机器上执行:

radosgw-admin realm pull --rgw-realm=prod-realm --url=http://rgw-a.example.com:7480 --access-key=... --secret=...

这里的access-key和secret不是你的Ceph集群keyring,而是为同步专门生成的系统账号。它存储于新realm的配置数据中,你需要通过以下命令获取:

radosgw-admin period pull --remote_token --access-key=... --secret=...

实际上,主区可以通过如下方式为用户生成带有特定权限的凭据,并把它安全地分享给从区,用于初始化拉取:

radosgw-admin user create --uid=sync-user --display-name="Sync User" --system

这一步给同步用户赋予了系统级权限,这个用户创建好之后,两台集群的RGW之间通信都依赖它。因为涉及密钥交互,建议通过加密渠道传递,不要明文写在工单或聊天记录里。

realm拉取成功之后,继续拉取最新的period配置。然后再创建从区的zone:

radosgw-admin zone create --rgw-zonegroup=zg-prod --rgw-zone=zone-b --access-key=... --secret=...

注意,创建从区zone时,不要加--master参数。此时从区集群上已经存在一个名为default的zone,它会和zone-b冲突。最省事的做法是直接修改期:

radosgw-admin zone rm --rgw-zone=default

顺序不能反:先pull realm,再删旧zone,再创建新zone,最后重新发布period。如果先删default再做realm pull,从区在拉取前就已经失去了zone配置,操作过程可能直接报错。

最后,将zone-b设置为默认zone,并把endpoint指向本区的网关:

radosgw-admin zone modify --rgw-zone=zone-b --default radosgw-admin zone modify --rgw-zone=zone-b --endpoints=http://rgw-b.example.com:7480 radosgw-admin period update --commit

到这一步,从区的配置数据其实已经具备,但要真正跑起来,还需要把从区的RGW进程重启,确保加载的是新realm。

4.3 启动与初步验证

启动从区RGW进程之后,第一步就是登录从区的管理接口查看realm状态。我习惯用的检查方式比较朴素:

radosgw-admin realm list radosgw-admin zone list radosgw-admin sync status

sync status是排障精度最高的命令之一。它会输出当前zone的元数据同步状态和数据同步状态,包含同步位点、追赶延迟、是否有错误。第一次部署时,如果你看到类似“synchronization not fully caught up”的信息,是正常的,等一小段时间再查。

更直观的验证方式是直接操作业务数据。在主区创建一个测试bucket,上传一个对象,然后在从区用radosgw-admin bucket list看看是否能列出这个bucket,再通过radosgw-admin bucket stats确认对象的字节数。如果这些都对得上,说明整条链路已经通了。

这里要留个心眼:从区如果也配置了对外域名,客户端直接往从区写数据,在双活模式下会实时同步回主区;如果只想让从区作为灾备,应该在网关前面做一层流量调度,让写请求只在主区落地。否则一旦两个区同时写同一个对象,版本冲突会让你后面排查很痛苦。

5. 同步工作机制与调优经验

5.1 元数据同步与数据同步的差异

为什么要把这一节单独拿出来写,是因为我遇到过不少同事,一旦同步出问题,就盲目地重启RGW、甚至重启整个集群,结果毫无变化。把同步机制搞清楚之后,很多问题根本不需要重启。

元数据同步的消费端,依赖的是zone自身的meta queue,它记录着在本zone“发生”的元数据变更。数据同步则依赖每个zone的data queue,负责承载对象级别的操作日志。RGW后端进程里每个不同的queue有独立的游标和并发度设置,这也就意味着你完全可以在不动业务的情况下,单独调大某个同步线程的参数。

在实际表现上,元数据同步通常很快,因为元数据条目小、变更频率相对低。数据同步才是大头,尤其是大对象和高并发上传场景。如果你看到主区写入很快,但从区数据迟迟不齐,建议先查数据同步游标是否在推进,再检查是否有大文件卡住了某个分片。

分片(shard)这个概念也要提一嘴。每个zone的data log都分了多个shard,默认配置下shard数不会太高。如果业务写入非常密集,而shard太少,会出现热点效应:某个shard的日志积压很多,其他shard却闲着。你可以通过修改rgw_data_log_window等参数,并给zone配置更大的shard数来改善,但注意修改量越大,占用的存储和元数据开销也越大,建议在测试环境先跑几天看看效果。

5.2 同步参数调优的实践经验

先说结论:默认参数能应对大多数场景,但如果你对RPO有严格诉求,下面几个参数值得调。

  • rgw_sync_data_increase_bw:提高数据同步瞬时带宽,适合有小带宽抖动但总带宽富裕的专线。
  • rgw_sync_data_trim_deferred:控制同步完成后延迟清理日志条目的时间。太短会导致大量日志对象被反复扫描,太长会占用额外存储。
  • rgw_max_chunk_size:多段上传时单次同步的对象块大小。块越大,网络往返次数越少,但单次失败需要重传的粒度也大,一般建议保持默认。

调参的核心逻辑,其实是“在每一轮同步中让它少吃多跑”。RGW数据同步线程是周期性遍历游标之后,再批量拉取条目并执行副本写入的。增大batch size可以减少遍历次数,但会增加单轮的执行时间。如果没有大数据量场景的压测数据支撑,别随意改batch size。

还有一个小技巧,是我自己摸索的:调高从区RGW进程的日志级别,观察同步线程的运行节奏,可以帮助定位是“每轮同步卡在等待网络IO”还是“本地写入池子太慢”。这比单纯看同步状态有效得多。之前有一次我以为是带宽问题,结果一查日志,发现是远端zone的RGW开启了系统限流,qps被打满,导致每个同步请求都成功连上但排队时间很长。这种问题不看同步线程内部日志根本发现不了。

6. 常见问题与排查技巧实录

6.1 同步位点不推进

最典型的现象是:sync status一直卡在同一个“last sync”时间点,日志里也不报错。我先说结论,八成是消费端有写入失败被反复重试,但日志级别太低,没打出来;或者对端有慢请求,导致同步线程长期等在一个HTTP请求上。

排查顺序建议是:

  1. 先看对端集群的RGW访问日志,确认同步请求是持续到达还是根本没有到达。
  2. 再看同步线程日志,把日志级别临时调到20,观察是否有长时间卡住的写操作。
  3. 检查对端Ceph集群的容量和健康状态,确认是否有pool满或者PG降级。

我遇到过一次很典型的案例:从区的某个存储池的配额被设成了1TB,而实际数据已经写了1.1TB。起初同步一切正常,后来容量超限,所有写入都失败,但问题是失败原因被Ceph内部消化掉了,RGW同步线程只会无限重试,不报FATAL级错误。最后通过对端客户端侧的访问日志才看出来,所有来自主区同步用户的请求都返回了503。这个案例提醒我,多区同步问题不要只看RGW本身,对端存储的任何异常都可能表现为“同步位点卡住”。

6.2 双活场景下的写入冲突

双活模式最怕的事情,是两个区同时写同一个key,而且写的内容还不一样。RGW的冲突解决机制,本质上是按时间戳比较的,两个zone各自生成的时间戳在极端情况下可能不一致,导致最终结果跟业务预期不符。

我建议在那段业务还没完全稳定期,先别开双活写入。如果一定要开,考虑在客户端侧做一层路由规则,让同一个业务桶的写入固定落到一个区,虽然名义上是双活,但实际是按桶拆分的负载。这样既享受了双活的容灾读取能力,又不至于出现频繁的对象内容互搏。

如果冲突已经发生,处理起来比较棘手。比较务实的思路是:以业务层认定的“源”为准,用radosgw-admin bucket rm某个版本,再重新同步。这里没有完美的自动化方案,所以更要在设计阶段规避冲突。

6.3 从区网关域名切换后的“历史残留”

还有一个高频问题:从区已经接管了业务流量,但用户通过新域名访问老对象时,会发现能列出桶,能拿到对象列表,可对象下载就是404。这通常是因为对象刚同步到从区时,同步时写的placement信息里还带着主区的endpoint,而客户端访问从区时,RGW返回的重定向链路失效了。

本质上,RGW对象在网关层是有“归属”概念的,不要以为数据到了从区,请求就一定能在从区被完整处理。遇到这种问题,需要检查bucket的placement目标配置,确认它映射到当前zone。早期版本的RGW对跨zone读取的支持还比较弱,新版本更健壮,但如果你的业务依赖跨区读已有对象,我仍然建议提前做一轮对象头信息的验证,而不是等故障发生了再研究。

7. 多区部署监控与日常运维经验

7.1 关键监控指标

不针对某个具体监控平台,只说哪些指标是我觉着必须盯的:

  • 同步滞后时间:也就是从区最新同步位点和主区当前写入位点之间的时间差。这个值能直接反映RPO。
  • data log积压条目数:如果这个数字持续上升,说明同步能力已经跟不上写入速率。
  • 同步线程活跃度和错误次数:RGW内部暴露的同步线程状态,比如List/Write同步线程每次循环的执行时间。
  • 对端集群的写延迟:从区写入慢,会倒逼主区同步线程变慢,所以监控里不能只看主区,从区也要看。

我惯用的做法是,在监控平台上把“主区对象数增长量”和“从区对象数增长量”做两个曲线叠加,正常情况它们应该几乎重合,一旦出现明显剪刀差,就说明同步出现了异常。这个方法简单粗暴,但准确率很高。

7.2 权限与安全细节

多区部署意味着两套集群之间有系统级账号通信,而且这套账号拥有跨集群的读写权限。因此安全层面的几个习惯非常重要:建议在从区上对该同步账号设置可管理的IP白名单,只允许主区的网关IP段访问;不要把同步账号的密钥写死在客户端配置里,要用独立的keyring文件,并定期轮换。

还有一点,在多区拓扑中,你在任意一个zone上用radosgw-admin创建的普通用户,如果这个zone是元数据中心,会同步到其他zone;如果是在从区创建,则需要确认从区的元数据同步回写链路是否也正常。我在实际项目里见过有人图方便,直接在从区创建了用于生产的用户,结果主区迟迟看不到这个用户,业务调用直接报InvalidAccessKeyId。搞清楚“配置管理在哪个区是权威”这个原则,能避免这类细节问题。

8. 个人体会与踩坑总结

多区部署这套东西,从文档看好像很简单,几条命令就搭起来了,但真要在生产环境稳定运行,反而比单集群更考验运维的耐心和全局理解力。

我最大的体会是:不要在对Ceph本身不熟的情况下直接上多区,否则你会分不清到底是同步逻辑的问题,还是底层存储的问题。我在某个跨机房项目中,前两周全在踩底层池、网络、域名解析的坑,真正多区配置本身反而是最后一天完成的。所以如果你是新接触Ceph,建议先在单集群里把RGW的日常管理、排障、数据迁移都跑熟了,再碰多区。

最后分享一个实用的小技巧:在做任何大范围配置变更,比如增删zone、修改zonegroup结构前,先备份一下本地的period配置文件:

radosgw-admin period pull radosgw-admin period export --file=period_backup.json

永久保留一份配置快照。别小看这一步,我有个朋友在给多区集群改zone endpoint时手滑删错了zone,最后硬是靠这份json救命,把period导入回去才恢复了配置。任何时候,多一步备份,就是多一条退路。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进