深入讲解一下 Rook 的源码
用户创建一个 cluster.yaml 文件,内容类似:metadata:spec:mon:count: 3 # 期望有 3 个 Monitorstorage:useAllDevices: true # 期望使用所有节点上的所有可用磁盘Use code.Yaml当用户执行 kubectl apply,这个 CephCluster 对象就被存储在 Kubernetes 的 etcd 中了。声明式 A
我们来深入讲解一下 Rook 的源码。Rook 是一个非常优秀且复杂的项目,作为 CNCF 的毕业项目,它代表了云原生存储编排的业界标杆。
我会从高到低,分层次地为你解析这个项目,让你不仅知道代码的功能,更能理解其背后的设计思想。
1. 核心思想:Rook 是什么?
在看代码之前,必须先理解 Rook 的核心定位。
一句话总结:Rook 是一个开源的云原生存储编排器(Orchestrator),它将 Ceph、Cassandra、NFS 等存储系统无缝对接到 Kubernetes 中。
它解决的核心问题是:在 Kubernetes 这种动态、分布式的环境中,管理有状态的、复杂的存储系统(比如 Ceph)是一件极其困难的事情。Rook 的目标就是将这些存储系统的部署、配置、扩容、升级、监控、故障恢复等复杂运维工作,自动化和声明式地完成。
为了实现这个目标,Rook 采用了 Kubernetes 最核心的设计模式:Operator Pattern(算子模式)。
2. 关键设计模式:Operator Pattern
这是理解 Rook 源码的钥匙。一个 Operator 包含两个主要部分:
-
Custom Resource Definition (CRD - 自定义资源定义):扩展 Kubernetes API,创建新的资源类型。比如,Kubernetes 原生有 Pod、Deployment 资源,Rook 定义了 CephCluster、CephBlockPool、CephFilesystem 等新资源。这让用户可以像管理 Pod 一样,用 YAML 文件来“声明”一个存储集群。
-
Controller (控制器):一个持续运行的进程,它会“监听”(Watch)自己所管理的 CRD 资源的变化。当用户创建、更新或删除一个 CRD 对象时,Controller 会收到通知,并执行相应的业务逻辑(这个过程被称为 Reconciliation Loop - 调谐循环),以确保集群的“实际状态”与用户在 YAML 中定义的“期望状态”保持一致。
简单来说:用户写 YAML (期望状态) -> Rook Operator (Controller) 读取 YAML -> Operator 创建/管理 Ceph Pods、Services、ConfigMaps 等 (实际状态)。
3. 项目源码目录结构导览
现在,我们来看一下 Rook 的目录结构,了解核心代码都在哪里。
Generated code
rook/
├── build/ # 编译脚本、Dockerfile 等
├── cmd/ # 项目所有二进制文件的 main 入口
│ └── rook/ # Rook 的主命令,Operator、Agent 等都在这里启动
├── deploy/ # 部署 Rook 所需的全部 YAML 文件
│ ├── examples/ # 最重要!各种存储集群的示例 YAML
│ └── crds.yaml # 所有 CRD 的定义文件
├── docs/ # 项目文档
├── images/ # 构建 Rook 镜像的相关文件
├── pkg/ # **项目核心逻辑代码库,90% 的代码都在这里**
│ ├── apis/ # 所有 CRD 的 Go 语言结构体定义
│ │ └── ceph.rook.io/v1/
│ │ ├── types.go # CephCluster, CephBlockPool 等核心 CRD 的 Go 类型定义
│ │ └── zz_generated.deepcopy.go # 自动生成的深拷贝代码
│ ├── client/ # 用于与 Kubernetes API Server 交互的客户端代码 (clientset)
│ ├── operator/ # **Operator 的核心!所有 Controller 的调谐逻辑都在这里**
│ │ ├── ceph/ # Ceph Operator 的所有逻辑
│ │ │ ├── cluster/ # 管理 CephCluster 资源的核心控制器
│ │ │ │ ├── cluster.go # CephCluster 对象的具体实现
│ │ │ │ └── controller.go # CephCluster 的 Reconcile 调谐循环入口
│ │ │ ├── pool/ # 管理 CephBlockPool 资源的控制器
│ │ │ └── ... (filesystem, object, etc.)
│ │ └── csi/ # CSI 插件相关的 Operator 逻辑
│ ├── daemon/ # 在每个存储节点上运行的 Agent/Daemon 逻辑
│ │ └── ceph/ # Ceph Daemon 的逻辑,如 OSD (磁盘) 的发现、配置和启动
│ ├── util/ # 工具类代码,如 JSON、exec 等
│ └── ...
├── tests/ # 测试代码,尤其是 e2e (端到端) 测试
└── ...
Use code with caution.
关键路径速记:
-
看 CRD 定义:pkg/apis/ceph.rook.io/v1/types.go
-
看 Operator 入口:cmd/rook/rook.go
-
看 Ceph Operator 核心调谐逻辑:pkg/operator/ceph/cluster/controller.go
4. 源码深度解析:以创建一个 Ceph 集群为例
让我们跟随一个完整的流程,看看代码是如何工作的。
流程:用户 kubectl apply -f cluster.yaml -> Rook 创建一个功能完备的 Ceph 集群
第 1 步: 用户定义期望状态
用户创建一个 cluster.yaml 文件,内容类似:
Generated yaml
apiVersion: ceph.rook.io/v1
kind: CephCluster
metadata:
name: my-cluster
namespace: rook-ceph
spec:
cephVersion:
image: ceph/ceph:v17.2.5
dataDirHostPath: /var/lib/rook
mon:
count: 3 # 期望有 3 个 Monitor
storage:
useAllNodes: true
useAllDevices: true # 期望使用所有节点上的所有可用磁盘
Use code with caution.Yaml
当用户执行 kubectl apply,这个 CephCluster 对象就被存储在 Kubernetes 的 etcd 中了。
第 2 步: Operator 的启动和监听
Rook Operator 作为一个 Deployment 部署在 K8s 中。它的入口在 cmd/rook/rook.go。
这个 main 函数会解析命令行参数,如果参数是 ceph operator,就会执行 operator.go 里的 run 函数。这里会使用 controller-runtime 这个库来创建一个 Manager。
Manager 会:
-
注册 Scheme: pkg/apis/ceph.rook.io/v1/scheme.go 会把 CephCluster 等自定义类型告诉 Manager。
-
创建并启动 Controller: pkg/operator/ceph/cluster/controller.go 中的 Add 函数会被调用,它会创建一个新的 Controller,并告诉它需要 Watch (监听) CephCluster 类型的资源。
第 3 步: 调谐循环 (Reconciliation Loop)
CephCluster Controller 启动后,立刻收到了一个通知:“嘿,有一个新的 CephCluster 对象叫 my-cluster 被创建了!”
于是,pkg/operator/ceph/cluster/controller.go 中的 Reconcile() 方法被触发。这是整个 Operator 的“大脑”。
Reconcile() 函数的核心逻辑是:
-
获取对象:从 K8s API Server 获取 my-cluster 这个 CephCluster 对象的详细信息(也就是用户 YAML 的内容)。
-
实例化 Cluster 对象:创建一个 cluster.go 中的 Cluster 结构体实例。这个结构体包含了集群的所有配置和状态。
-
核心调谐逻辑 c.reconcile(clusterInfo):在 pkg/operator/ceph/cluster/cluster.go 中,reconcile() 方法会按顺序执行一系列检查和创建操作。
-
检查/创建 Ceph MONs (Monitors):
-
计算需要多少个 MON (根据 spec.mon.count)。
-
为每个 MON 创建一个 K8s Deployment 和 Service。
-
生成 MON 的配置和密钥,并存入 K8s ConfigMap 和 Secret。
-
-
检查/创建 Ceph MGR (Manager):
-
创建一个 Deployment 来运行 MGR。
-
-
检查/创建 Ceph OSDs (Object Storage Daemons):
-
这是最复杂的一步。Operator 不会直接创建 OSD Pod。
-
它会创建一个 Deployment,这个 Deployment 运行的 Pod 叫做 "OSD Prepare Pod"。
-
这个 Pod 会扫描节点上的磁盘(根据 spec.storage 的配置)。
-
对于每个符合条件的磁盘,这个 "Prepare Pod" 会调用 Ceph 的 ceph-volume 命令来准备(格式化)磁盘。
-
准备好之后,它会为这个磁盘创建一个新的 Deployment,这个 Deployment 才是真正运行 OSD 守护进程的 Pod。
-
这个过程是 动态的,如果将来你给节点加了一块新硬盘,这个流程会自动触发,为你创建新的 OSD。
-
-
创建 Crash Collector、RBD-Mirror、Dashboard 等其他组件。
-
更新状态: 将集群的当前状态(如健康状况、组件版本等)写回到 CephCluster 对象的 .status 字段中。用户可以通过 kubectl get cephcluster my-cluster -o yaml 看到这些状态。
-
第 4 步:持续的健康检查
调谐循环不会只运行一次。它会定期运行,或者在 CephCluster 对象被修改时再次运行。
-
如果一个 MON Pod 挂了:Kubernetes 的 Deployment Controller 会尝试重启它。Rook Operator 的调谐循环会检查 MON 的数量,如果发现数量不足,它会确保新的 MON Pod 能正确加入集群。
-
如果用户修改了 YAML:比如把 mon.count 从 3 改成 5。调谐循环会检测到“期望状态”和“实际状态”不符,然后会去创建两个新的 MON Deployment。
5. 如何开始阅读和贡献?
-
从 deploy/examples 开始:先不要直接看代码。尝试部署一个 Rook Ceph 集群,理解它的组件和用户体验。这是建立大局观的最好方式。
-
阅读 pkg/apis/ceph.rook.io/v1/types.go:了解 Rook 提供了哪些 CRD,以及每个 CRD 有哪些配置项。这能让你明白 Rook 的能力边界。
-
跟踪一个简单的 Controller:CephCluster 的 Controller 比较复杂。可以先从简单的开始,比如 pkg/operator/ceph/pool/pool_controller.go。这个 Controller 只负责创建/删除 Ceph 块存储池,逻辑相对简单,有助于理解调谐循环的模式。
-
设置开发环境:Rook 的文档中有详细的开发环境搭建指南。克隆代码库,尝试 make build 和 make test。
-
找一个 "good first issue":在 GitHub 的 Issues 中,寻找带有 good first issue 或 help wanted 标签的问题。这是开始贡献代码的绝佳途径。
总结
Rook 的源码体现了云原生应用设计的精髓:
-
声明式 API: 通过 CRD 将复杂操作简化为一份 YAML。
-
自动化控制循环: Operator 不断地将系统调整到用户期望的状态,实现了“自愈”和自动化运维。
-
高内聚、低耦合: 每个 Controller 关注自己的资源,分工明确。Ceph 的复杂性被封装在 Operator 内部,对用户透明。
希望这个从思想到代码的讲解能帮助你更好地理解 Rook 这个强大的项目!
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)