- 云原生
- 存储
- 容器编排
- 运维
【免费下载链接】rook
Storage Orchestration for Kubernetes
本文以 Rook 仓库中的设计文档 design/ceph/multus-network.md 为主线,深入剖析 Rook 如何借助 Multus 为 Ceph 集群引入多宿主网络(multi-homed networking),涵盖 CRD 网络配置的演进、IPAM 方案的取舍(Whereabouts 采纳、DHCP 否决)、各 Ceph 守护进程(mon/OSD/RGW/MDS/CSI)的接入挑战,以及最终在仓库源码中的落地印证。读完本文,你将理解 Rook 中network.provider: multus的完整设计动机、selectors的语义与校验规则、为何最终选择 Whereabouts 作为 IPAM,以及如何用仓库自带的验证工具提前检验你的 Multus 网络环境是否适合承载 Ceph。
说明:该设计文档标题注明"not finalized yet and subject to update"(尚未最终定稿、内容可能更新),属于设计阶段的早期决策记录。但其中的核心结论(采用 whereabouts、否决 DHCP、selectors 双键设计)已在当前仓库的 API 类型定义、CRD 文档与示例配置中落地,本文会一并给出对应源码与配置路径作为佐证。
一、为什么 Ceph 需要 Multus:多宿主网络的动机
文档开篇即声明:多宿主网络本身的收益已在另一份设计文档 design/common/multi-net-multus.md 中探讨过,本文不再复述,只聚焦 Ceph 后端的实现。
真正驱动 Rook 集成 Multus 的原因在于HostNetworking模式的两大缺陷:
- 暴露全部网络接口:使用
HostNetworking会把宿主的所有网络接口(整个网络协议栈)暴露进容器,而 Multus 允许你只挑选需要的那一个接口/网络; - 特权容器依赖:
HostNetworking通常要求特权容器,Multus 方案可以显著减少这种特权需求。
因此,Multus 的目标是在获得与HostNetworking相近的性能收益(绕过软件定义网络的额外延迟与带宽争抢)的同时,提升安全性(Kubernetes 应用与 Ceph 守护进程实现网络隔离)。
理解这一点还需要 Ceph 自身的双网络模型作铺垫:Ceph 守护进程最多可运行在两张独立网络上——public 网络(面向客户端的读写流量,Ceph-CSI、客户端 Pod 均走该网络)与cluster 网络(可选,隔离 OSD 间复制、数据恢复等内部流量)。若未指定 cluster 网络,则内部流量回落到 public 网络。这一模型是后文public/cluster两个 selector 键存在的根本原因。
二、CRD 网络配置的演进:selectors 的双键扩展
2.1 既有 network 属性
设计文档指出,CephCluster的 CRD 中已经存在network属性,其原始形态为:
network: provider: selectors:2.2 新增的 public / cluster 两个硬编码键
设计文档提出的扩展方案是:在selectors下加入两个硬编码的键:
selectors: public: cluster:每个 selector 的值对应 Multus 中的一个NetworkAttachmentDefinition(NAD)对象。规则如下:
- 至少提供一个selector;
public:数据守护进程的 public 网络,绑定 Ceph 的public_network配置;cluster:数据守护进程的 cluster 网络,绑定 Ceph 的cluster_network配置;- 默认行为:如果只设置了
public,则cluster自动取public的值。
2.3 源码中的落地印证
这一设计在当前仓库的 API 类型中已经完整实现,见 pkg/apis/ceph.rook.io/v1/types.go:
NetworkSpec.Provider取值由NetworkProviderType枚举约束,合法值为""、host、multus(对应常量NetworkProviderDefault、NetworkProviderHost、NetworkProviderMultus);Selectors的类型为map[CephNetworkType]string,而CephNetworkType的定义刻意"允许任意字符串"(注释说明是为了兼容历史遗留的过度指定集群),但常量只有两个:CephNetworkPublic = "public"与CephNetworkCluster = "cluster"——正是设计文档中的两个硬编码键;- 代码注释中还给出了与设计文档一致的示例:
selectors: public: "default/cluster-fast-net" cluster: "rook-ceph/ceph-backend-net"即 public 网络可以选用 default 命名空间下、面向 Kubernetes 全局应用的网络,cluster 网络可以选用 rook-ceph 命名空间下、Ceph 专用的后端网络。
校验逻辑:至少一个 selector、格式合法
设计文档要求"至少提供一个 selector",这一约束在 pkg/apis/ceph.rook.io/v1/network.go 的ValidateNetworkSpec中严格执行:
if spec.IsMultus() { if len(spec.Selectors) == 0 { return errors.Errorf("at least one network selector must be specified when using the %q network provider", NetworkProviderMultus) } if _, err := spec.GetNetworkSelection(clusterNamespace, CephNetworkPublic); err != nil { return errors.Wrap(err, "ceph public network selector provided for multus is invalid") } if _, err := spec.GetNetworkSelection(clusterNamespace, CephNetworkCluster); err != nil { return errors.Wrap(err, "ceph cluster network selector provided for multus is invalid") } }此外还有几点值得注意的细节:
GetNetworkSelection(network.go)会通过 NAD 工具库nadutils.ParseNetworkAnnotation解析 selector 字符串;为兼容旧版本用户,若 selector 是单个 JSON 对象(以{开头、}结尾)会自动包装成单元素列表再解析,且每个网络只允许一个 selection,多选会直接报错;- CRD 层还叠加了 OpenAPI 的
XValidation规则(见 types.go),在 API Server 侧就拦截"使用 multus 但 selectors 为空"与"legacy hostNetwork 与其他 provider 混用"两种非法组合; ValidateNetworkSpecUpdate(network.go)禁止在运行中变更 provider(host 与空值之间的切换除外),保证网络方案的稳定性。
三、Multus 支持的配置面:接口类型与 IPAM 取舍
3.1 接口类型(Interface type)
作为 CNI 规范的一部分,Multus 支持多种接口类型。设计文档指出:Rook 天然支持其中任意一种,因为它们不会从根本上改变 Rook 的工作行为——文档本身并未对接口类型做强制限制。
3.2 IPAM 类型:本设计的核心分歧点
相比接口类型,IPAM(IP 地址分配)才是复杂所在。当时(设计文档撰写时点)主流的三种 IPAM 方案中,有两种被明确判定为"不合适的候选":
| IPAM 类型 | 机制 | 被否决的原因 |
|---|---|---|
| host-local | 维护本机 IP 分配数据库,仅按主机维度工作 | 不适用于分布式环境——各节点各自分配,必然导致IP 冲突。whereabouts 项目(dougbtv/whereabouts)看起来能修复此问题,但当时并非官方支持 |
| static | 为容器分配静态 IPv4/IPv6 地址,适合调试 | 无法规模化——需要为所有守护进程预先手工分配 IP |
| DHCP | 由 DHCP 服务器按范围分发 IP | 见下文"被否决的方案"章节 |
文档同时提示,更详细的分析见文档末尾的"被否决的方案"章节(本文第六节会完整覆盖)。
四、各 Ceph 守护进程的实现挑战
设计文档按守护进程类型逐一拆解了接入 Multus 的网络需求与难点:
4.1 Monitors 与 OSDs
- Monitors(mon):只需要访问 public 网络;
- OSDs:需要同时访问 public 与 cluster 两张网络(OSD 既要服务客户端读写,又要承担复制与恢复流量)。
mon 的额外硬性需求有两条:
- 可预测的 IP 地址(Predictable IP addresses);
- 整个部署生命周期内 IP 保持不变——IP 必须能在 Pod 重启后幸存(survive a restart)。
这两条需求直接决定了后文对 IPAM 方案的取舍,也是 mon 引导(bootstrap)流程面临的最大难题。
4.2 RGW 实现
- 只需要访问 public 网络;
- 但 RGW 使用 Service IP + LoadBalancer 对外暴露,需要格外小心——多网络环境下 Service IP 与 Multus 附加网络之间的交互是设计难点。
4.3 MDS / RBD-MIRROR / NFS
- 都只需要访问 public 网络;
- 由于它们不使用 Service IP,无需特殊处理。
4.4 CSI Pods:一个必须修复的已知问题
设计文档引用了一个 Rook 的已知问题(对应 GitHub issue 8085),核心现象是:
当使用 Ceph CSI 挂载了 CephFS/RBD 卷的 Pod 运行期间,如果 CSI CephFS/RBD 插件 Pod 被重启或终止(例如重启或删除其 DaemonSet),则该卷上的所有操作都会卡死,即使随后重启 CSI Pod 也无法恢复。唯一的临时解决办法是重启承载 CSI 插件 Pod 的节点。
该问题的根源在于:挂载点(mount)与 CSI 插件进程所在网络命名空间绑定,CSI 插件被强杀后,挂载点的网络上下文随之失效。
为此,设计文档提出的缓解方案是:当部署配置了 multus 网络的 CephCluster 时,为所有将运行 CSI 插件 Pod 的节点,在宿主网络命名空间(host network namespace)中附加一个 multus 网络接口。这样 CSI Pod 可以继续使用宿主网络运行,同时仍然能够访问 Ceph 的 public multus 网络。
具体实现设计为:新增一个 DaemonSet 来"拥有"所有 CephFS 挂载点及 RBD 映射设备对应的网络,而现有的csi-{cephfs,rbd}pluginDaemonSet 保持不动。通过把网络"所有权"从 CSI 插件 Pod 中剥离出来,避免插件重启导致挂载失联。
五、已采纳的方案:Whereabouts IPAM
经过调研,Rook 团队最终决定采用 whereabouts 作为 IPAM 插件。它的定位是:
一个集群级(cluster-wide)的 IP 地址管理 CNI 插件。如果你喜欢 host-local 的工作方式,但需要它跨越集群中的所有节点(host-local 只知道给本机 Pod 分配 IP),那么 whereabouts 正是你要找的工具。
关键能力:
- 支持 IPv4 与 IPv6 双栈寻址;
- 跨集群静态 IP 地址分配;
- 分配的IP 在部署重启后依然存活(满足 mon 的需求);
- 分配动作由 whereabouts 完成,而非 Rook——Rook 无需自己维护 IP 数据库。
不过设计文档也明确标注了一个/!\警告:
唯一尚未解决的问题是:如何为即将启动的 monitor 预测 IP?我们可能需要小幅重构 mon 的引导方式,使其不再需要预先知道 IP。
这是当时设计状态下唯一悬而未决的技术债,也是 mon 引导流程后续演进的重点方向。
从当前仓库的最终状态看,whereabouts 已被官方文档确认为推荐方案:Documentation/CRDs/Cluster/network-providers.md 中的推荐 NAD 示例即使用"type": "whereabouts"配合range指定地址段(详见本文第七节),并注明"whereabouts 确保每个 Pod 获得集群内唯一的 IP,无需 DHCP 服务器"。
六、被否决的方案:DHCP 及其变体
文档保留了两个被否决的方案,目的是可追溯性与知识沉淀。
6.1 IPAM 类型 'DHCP'
在该场景下,由 DHCP 服务器按给定范围向 Pod 分发 IP 地址。
优点:
- Pod 在物理网络接口上获得专用 IP;
- Ceph 侧无需任何改动——Rook 通过
NetworkAttachmentDefinition探测 CIDR,然后填充 Ceph 的public_network与cluster_network配置即可。
缺点:
- IP 分配不可预测——在 Pod 启动完成前无法得知 IP,因此探测必须发生在 monitor 容器运行内部(类似于今天 OSD 代码的做法);
- 需要对 mon 引导代码做大幅改动;
- 需要在集群的每个节点上部署DHCP 守护进程,这被证明是麻烦的来源。
假设走这条路,mon 的引导流程需要重构成如下步骤:
- 让第一个 monitor 基于某个接口自行发现自己的 IP;
- 第一个 mon 引导完成后,将其 IP 注册进一个ConfigMap,同时填充 clusterInfo;
- 启动第二个 mon,从 clusterInfo 中查找 initial member(即使操作中途崩溃也不用担心——每次启动时都会基于该 ConfigMap 执行
CreateOrLoadClusterInfo()); - 以此类推,逐个启动剩余 monitor。
文档还在该方案旁标注了TBT(有待验证):Pod 重启后能否保持相同 IP。
6.2 IPAM 类型 'DHCP' + Service IP
这是一个"纯理论"的变体:使用带 DHCP 的 IPAM 并结合 Service IP。它要求与Kube-proxy 交互,而当时(乃至现在)并不存在这样的特性。即便存在,团队已经决定不走 DHCP 路线,因此该方案不再具有现实意义。
否决结论归纳:DHCP 系列方案被否的核心原因是 IP 不可预测 + 引导流程重构成本高 + 每节点 DHCP 守护进程运维负担大。这从反面印证了 mon"可预测且持久 IP"需求对方案选择的决定性影响。
七、从设计到落地:仓库中的实现与配套工具
设计文档描述的蓝图最终在仓库中体现为 API 类型、示例配置、官方文档与验证工具四个层面。
7.1 完整的 CephCluster Multus 示例
仓库中的 deploy/examples/cluster-multus-test.yaml 提供了一个可直接对照的最小化示例,其网络段为:
apiVersion: ceph.rook.io/v1 kind: CephCluster metadata: name: my-cluster namespace: rook-ceph # namespace:cluster spec: # ... 省略其他配置 ... network: provider: multus selectors: public: public-net cluster: cluster-net注意这里 selector 的值直接使用 NAD 名称(public-net/cluster-net),未带命名空间前缀——Rook 会自动以 CephCluster 所在的命名空间(rook-ceph)解析,即等价于rook-ceph/public-net。若 NAD 位于其他命名空间,则需要写成namespace/name的形式。
7.2 NAD 定义与 addressRanges 兜底
Documentation/CRDs/Cluster/network-providers.md 给出了推荐的 NAD 定义模板(macvlan + whereabouts):
apiVersion: "k8s.cni.cncf.io/v1" kind: NetworkAttachmentDefinition metadata: name: ceph-multus-net spec: config: '{ "cniVersion": "0.3.1", "type": "macvlan", "master": "eth0", "mode": "bridge", "ipam": { "type": "whereabouts", "range": "192.168.200.0/24" } }'该文档还强调了几条与设计文档呼应的实操要点:
master必须匹配各宿主上要使用的网络接口,且所有宿主必须一致;- CNI 类型推荐 macvlan——相比传统 Linux bridge 配置,CPU 与内存开销更低;
- IPAM 推荐 whereabouts——确保 Pod 获得集群内唯一 IP;若网络中已有 DHCP 服务器,务必确保 IP 范围不与其重叠;
selectors与addressRanges配合:Rook 会尝试自动探测所选网络的 CIDR,但该过程不保证成功,且每次 CephCluster reconcile 都会获取一次新的网络租约。若自动探测失败、探测结果不正确、或底层网络不支持复用旧 IP,则应在network.addressRanges中手工指定 CIDR:
network: provider: multus selectors: public: default/kube-multus-net cluster: rook-ceph/ceph-multus-net # addressRanges: # public: # - "192.168.100.0/24" # - "192.168.101.0/24" # cluster: # - "192.168.200.0/24"这一兜底机制的合法性同样由 network.go 的校验保障:addressRanges只能配合host或multusprovider 使用,且 multus 下指定 public/cluster 地址范围的前提是存在对应的 selector;每个 CIDR 都会经过net.ParseCIDR严格校验。
此外,官方文档也如实记录了当前 Multus 方案的已知限制:依赖 Kubernetes Service IP 的守护进程(mon、mgr、RGW)并不会直接监听selectors指定的 NAD,而是监听默认网络,NAD 仅作为附加网络挂载到容器中供其通信——该问题的修复工作正在进行中(multus-service 项目),何时支持尚不明确。这与设计文档中 RGW"使用 Service IP 需小心"的预警完全对应。
7.3 Multus 配置验证工具
Multus 网络环境是否真正适合承载 Ceph,官方文档强烈建议在安装 CephCluster 之前先行验证(network-providers.md)。验证工具的 CLI 入口在 cmd/rook/userfacing/multus/multus.go 中注册,核心实现位于 pkg/daemon/multus 目录(含validation.go、resources.go、statemachine.go、templates.go、config.go等)。
使用步骤:
进入 Rook operator Pod:
kubectl --namespace rook-ceph exec -it deploy/rook-ceph-operator -- bash查看验证工具帮助:
rook multus validation run --help使用配置文件做高级配置(可生成带注释说明的模板):
rook multus validation config --help运行验证;若失败,工具会给出可能的原因排查建议,并请求相关日志与输出用于定位问题。
从源码看,该验证工具是一个有状态的状态机(statemachine.go 配合validation.go中定义的一系列状态),会在目标节点上拉起多类探针 Pod 来模拟 Ceph 守护进程的网络拓扑:
- Web Server Pod:同时挂载 public 与 cluster 网络(模拟 OSD 的落点),并对外提供地址信息;
- Image Pull DaemonSet:预拉取镜像,同时探测 DaemonSet 在各节点的调度能力;
- Host Checker DaemonSet:验证宿主能否路由到 public 网络(对应设计文档中"host 到 Pod"方向的路由要求);
- Client DaemonSet:模拟两类 Ceph 守护进程——OSD(同时连接 public + cluster 网络)与非 OSD(仅连接 public 网络),每节点数量由配置的
osdsPerNode/otherDaemonsPerNode决定。
配置项定义见 pkg/daemon/multus/config.go,其中PublicNetwork/ClusterNetwork分别对应设计文档的 public/cluster 选择器,默认每节点 3 个 OSD、16 个非 OSD 守护进程、资源超时 3 分钟、网络抖动阈值 30 秒。验证结果会附带针对性的调试建议(如 macvlan 对交换机 MAC 学习数量的限制、Promiscuous 模式、防火墙策略等),参见validation.go中的unableToProvideAddressSuggestions等常量。
工具还支持hostCheckOnly模式(只做宿主侧检查)以及 OpenShift 等安全受限环境的专用示例 deploy/examples/multus-validation-test-openshift.yaml。
7.4 端到端测试脚本
仓库 tests/scripts/multus 目录下提供了完整的端到端验证脚本族,与设计文档的各个关注点一一对应:
- setup-multus.sh:搭建 Multus 测试环境;
- test-110-cli.sh:验证
rook multus validationCLI; - test-200-stretch-label-nodes.sh 与 test-200-stretch-taint-nodes.sh:stretch 集群场景的节点标签/污点准备;
- test-210-stretch-overlap.sh:验证仅 public / 仅 cluster / public+cluster 分离等网络配置组合(对应设计文档中"只设 public 时 cluster 继承 public"及双网络并存的行为);
- test-220-stretch-pub-and-cluster.sh、test-230-stretch-pub-only.sh、test-240-stretch-cluster-only.sh:分别覆盖"双网络""仅 public""仅 cluster"三种部署形态。
这些脚本从测试角度印证了设计文档中"至少提供一个 selector"以及"public 与 cluster 可独立配置"的语义边界。
八、总结:设计决策回顾与现状对照
| 设计议题 | 设计文档结论 | 仓库现状印证 |
|---|---|---|
| 网络模型 | Ceph 双网络(public/cluster),selectors 双键,cluster 可回退到 public | CephNetworkType仅 public/cluster 两常量,示例配置cluster-multus-test.yaml |
| IPAM 主方案 | 采纳 whereabouts(集群级静态 IP、重启存活、由 whereabouts 分配) | 官方文档 NAD 模板默认 whereabouts,并推荐之 |
| IPAM 否决 | host-local(IP 冲突)、static(不可扩展)、DHCP(IP 不可预测 + 引导重构 + 每节点 DHCP 守护进程) | 官方文档无 DHCP 推荐场景,仅作为示例之一保留 |
| mon 特殊需求 | 可预测、持久 IP;引导流程或需重构 | 当前 mon 仍属已知限制(依赖 Service IP 的守护进程不直接监听 NAD) |
| CSI 问题 | 新增 DaemonSet 拥有挂载网络,CSI 插件 DaemonSet 不动 | 设计层面落地于 CRD/网络附加逻辑(详见 pkg/operator/k8sutil/network.go) |
| 验证手段 | 无(设计阶段) | 官方提供rook multus validation工具与完整测试脚本族 |
整体来看,Rook 的 Multus 集成遵循了"收益明确、取舍清晰、先验证后上线"的工程路线:通过双 selector 的设计把 Ceph 的双网络模型平移到 Kubernetes 多网络模型上,通过 whereabouts 解决 IP 分配的集群级一致性与持久性问题,并用专门的验证工具把"网络环境是否合格"这一风险前置到 CephCluster 安装之前。对于计划在生产环境使用 Multus 承载 Ceph 的读者,建议按本文第七节的流程先运行验证工具,再依据 Documentation/CRDs/Cluster/network-providers.md 中的 macvlan + whereabouts 模板设计 NAD,最后在selectors无法可靠自动探测 CIDR 时使用addressRanges手工兜底。
- 云原生
- 存储
- 容器编排
- 运维
【免费下载链接】rook
Storage Orchestration for Kubernetes
相关推荐
Rook 与 Ceph-CSI Operator 集成设计:实验性部署、配置开关与新增 CRD 全解析
Rook 与 Ceph CSI Operator 集成设计:实验性部署、配置开关与新增 CRD 全解析 导读 本文基于 Rook 仓库中的设计文档 design
云原生存储容器编排运维Rook CephCluster 网络提供商完全指南:从 Kubernetes 默认网络到 Host Networking 与 Multus
Rook CephCluster 网络提供商完全指南:从 Kubernetes 默认网络到 Host Networking 与 Multus Rook 默认使用
云原生存储容器编排运维通过 CephCluster CRD 配置 Ceph 配置选项:Rook cephConfig 设计解析与实战指南
通过 CephCluster CRD 配置 Ceph 配置选项:Rook cephConfig 设计解析与实战指南 导读 本文以 Rook 的设计文档 ceph
云原生存储容器编排运维
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考