kubernetes ceph rbd storage 源码探索
概述
kubernetes 当前存储对接第三方存储后端实现有两种模式共存
- in-tree:各种存储后端功能侵入式存在于源码中,耦合性高,不便于扩展与维护
- out-of-tree:和源码解耦,类似于 CNI 的 CSI 有统一的接口规范,便于维护扩展
以 ceph-rbd 为例看 provisioner(github.com\kubernetes-incubator\external-storage\ceph\rbd)
- 入口:ceph/rbd/cmd/rbd-provisioner/main.go
- 通过 命令行/环境变量 获取配置
- 根据配置 起客户端 clientset
- 通过环境变量获取 provisioner name(默认值:“ceph.com/rbd”),通过命令行获取 provisioner id(默认值 取 provisioner name),
- 启动 provisioner:查看log 发现name=id=“ceph.com/rbd”
- 通过 clientset NewRBDProvisioner,就是一个结构体
- 核心:NewProvisionController,并启动 Run
- 工具函数:ceph/rbd/pkg/provision
- rbdProvisioner实现了provisioner接口(在github.com/kubernetes-sigs/sig-storage-lib-external-provisioner/controller/volume.go):
- Provision(返回pv),
- Delete(删除pv后边的存储对象,不删除pv
- 接下来都不是接口内容:
- parseParameters,解析 sc 中的参数并返回
- parsePVSecret 通过 namespace 和 name 获取 secret
- RBDUtil 和 rbd 进行交互
- kernelRBDMonitorsOpt 返回 monitor 地址
- CreateImage
- rbdStatus 查看 rbd 状态
- DeleteImage
- execCommand 执行 rbd 命令
- 通过查看调用 execCommand 发现 没有resize 的调用
- docker 镜像包括
- ceph-common(需要先装 epel-release 包)
- rbd-provisioner 即 上边 入口中 编译出的二进制程序
- rbdProvisioner实现了provisioner接口(在github.com/kubernetes-sigs/sig-storage-lib-external-provisioner/controller/volume.go):
external即将废弃——转到 sigs.k8s.io\sig-storage-lib-external-provisioner\controller
-
核心 controller
- provision controller 并且 private (non-shared) informer
-
通过日志进行 逆向工程
- 成功启动
[root@k8s-master01 deploy]# kubectl logs -f rbd-provisioner-75b85f85bd-t9pww I1226 20:54:31.913709 1 main.go:85] Creating RBD provisioner ceph.com/rbd with identity: ceph.com/rbd I1226 20:54:31.915234 1 leaderelection.go:185] attempting to acquire leader lease default/ceph.com-rbd... I1226 20:54:31.938131 1 leaderelection.go:194] successfully acquired lease default/ceph.com-rbd I1226 20:54:31.938891 1 controller.go:631] Starting provisioner controller ceph.com/rbd_rbd-provisioner-75b85f85bd-t9pww_ea5be0da-2821-11ea-b197-ea9a5de46164! I1226 20:54:31.947584 1 event.go:221] Event(v1.ObjectReference{Kind:"Endpoints", Namespace:"default", Name:"ceph.com-rbd", UID:"020f21dc-4ec3-467e-96a2-66b92706e5c3", APIVersion:"v1", ResourceVersion:"18423", FieldPath:""}): type: 'Normal' reason: 'LeaderElection' rbd-provisioner-75b85f85bd-t9pww_ea5be0da-2821-11ea-b197-ea9a5de46164 became leader I1226 20:54:32.039430 1 controller.go:680] Started provisioner controller ceph.com/rbd_rbd-provisioner-75b85f85bd-t9pww_ea5be0da-2821-11ea-b197-ea9a5de46164!
- 使用 non-rbac 方式,未能成功启动
[root@k8s-master01 examples]# kubectl logs -f rbd-provisioner-7bc755794d-mkpd7 I1226 20:46:44.298781 1 main.go:85] Creating RBD provisioner ceph.com/rbd with identity: ceph.com/rbd I1226 20:46:44.305708 1 leaderelection.go:185] attempting to acquire leader lease default/ceph.com-rbd... E1226 20:46:44.392398 1 leaderelection.go:234] error retrieving resource lock default/ceph.com-rbd: endpoints "ceph.com-rbd" is forbidden: User "system:serviceaccount:default:default" cannot get resource "endpoints" in API group "" in the namespace "default"
- provisioner 创建 controller
- 调用
github.com\kubernetes-incubator\external-storage\vendor\k8s.io\client-go\tools\leaderelection\leaderelection.go
进行选举 - 调用
sigs.k8s.io\sig-storage-lib-external-provisioner\controller
进行启动 RBD Controller - Event
github.com\kubernetes-incubator\external-storage\vendor\k8s.io\client-go\tools\record\event.go
- error: namespace/endpointName 获取不到,最终要到 k8s 核心代码中(vendor/k8s.io/client-go/kubernetes/typed/core/v1/endpoints.go) 原因是权限问题,暂时无法再往下深入
in-tree 源码分析(k8s.io\kubernetes\pkg\volume\rbd)
不需要部署额外的插件应该就能操作 ceph-rbd
- rbd.go
- 实现了 VolumeHost 接口
- rbd_util.go:
- 执行 rbd 相关命令——info(),status,resize,rm(image),create(image),unmap(DetachBlockDisk),modprobe …
- 由命令的完整性 可以推断,in-tree 虽然耦合度较高,但是理应可以完成ceph-rbd 各种比较常见的功能;相关节点上由 ceph-common 工具,理应可以调用相关命令行
- 不断深入查看调用,发现代码层面最终由 kubelet.nodeLeaseController 调用,跟 kube-controller-manager 没关系呀
volume 目录下共用部分:
- metrics_*.go: 都实现了接口
MetricsProvider
问题小结
- in-tree 和 out-of-tree 混乱交织:
- 从文档中得知 csi 从 1.14 版本已经 GA ,但是 ceph-rbd 相关功能实现不全,且使用条件苛刻
- in-tree 代码依然大量留存在源码中
- 部分功能实现(ceph rbd resize)需要同时部署 in-tree 和 out-of-tree 两种模式
- 排查 in-tree 源码发现 rbd 实际上是 kubelet 进行调用,但是最终部署时发现时 controller-manager 镜像需要 rbd 二进制程序
参考
- 源码与文档
in-tree 源码 storageclass 官方文档 CSI 设计文档 本地静态存储 provisioner 外部存储 provisioner 源码中的 ceph-rbd 例子
- blog 文章
- 原文作者:战神西红柿
- 原文链接:https://tomatoares.github.io/posts/storage/storage-source-code/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。