概述

kubernetes 当前存储对接第三方存储后端实现有两种模式共存

  1. in-tree:各种存储后端功能侵入式存在于源码中,耦合性高,不便于扩展与维护
  2. out-of-tree:和源码解耦,类似于 CNI 的 CSI 有统一的接口规范,便于维护扩展

以 ceph-rbd 为例看 provisioner(github.com\kubernetes-incubator\external-storage\ceph\rbd)

  1. 入口:ceph/rbd/cmd/rbd-provisioner/main.go
    1. 通过 命令行/环境变量 获取配置
    2. 根据配置 起客户端 clientset
    3. 通过环境变量获取 provisioner name(默认值:“ceph.com/rbd”),通过命令行获取 provisioner id(默认值 取 provisioner name),
    4. 启动 provisioner:查看log 发现name=id=“ceph.com/rbd”
    5. 通过 clientset NewRBDProvisioner,就是一个结构体
    6. 核心:NewProvisionController,并启动 Run
  2. 工具函数:ceph/rbd/pkg/provision
    1. rbdProvisioner实现了provisioner接口(在github.com/kubernetes-sigs/sig-storage-lib-external-provisioner/controller/volume.go):
      1. Provision(返回pv),
      2. Delete(删除pv后边的存储对象,不删除pv
      3. 接下来都不是接口内容:
      4. parseParameters,解析 sc 中的参数并返回
      5. parsePVSecret 通过 namespace 和 name 获取 secret
    2. RBDUtil 和 rbd 进行交互
      1. kernelRBDMonitorsOpt 返回 monitor 地址
      2. CreateImage
      3. rbdStatus 查看 rbd 状态
      4. DeleteImage
      5. execCommand 执行 rbd 命令
      6. 通过查看调用 execCommand 发现 没有resize 的调用
    3. docker 镜像包括
      1. ceph-common(需要先装 epel-release 包)
      2. rbd-provisioner 即 上边 入口中 编译出的二进制程序

external即将废弃——转到 sigs.k8s.io\sig-storage-lib-external-provisioner\controller

  1. 核心 controller

    1. provision controller 并且 private (non-shared) informer
  2. 通过日志进行 逆向工程

    • 成功启动
    [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"
    
    1. provisioner 创建 controller
    2. 调用 github.com\kubernetes-incubator\external-storage\vendor\k8s.io\client-go\tools\leaderelection\leaderelection.go 进行选举
    3. 调用 sigs.k8s.io\sig-storage-lib-external-provisioner\controller 进行启动 RBD Controller
    4. Event github.com\kubernetes-incubator\external-storage\vendor\k8s.io\client-go\tools\record\event.go
    5. 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

  1. rbd.go
    1. 实现了 VolumeHost 接口
  2. rbd_util.go:
    1. 执行 rbd 相关命令——info(),status,resize,rm(image),create(image),unmap(DetachBlockDisk),modprobe …
    2. 由命令的完整性 可以推断,in-tree 虽然耦合度较高,但是理应可以完成ceph-rbd 各种比较常见的功能;相关节点上由 ceph-common 工具,理应可以调用相关命令行
    3. 不断深入查看调用,发现代码层面最终由 kubelet.nodeLeaseController 调用,跟 kube-controller-manager 没关系呀

volume 目录下共用部分:

  1. metrics_*.go: 都实现了接口 MetricsProvider

问题小结

  1. in-tree 和 out-of-tree 混乱交织:
    1. 从文档中得知 csi 从 1.14 版本已经 GA ,但是 ceph-rbd 相关功能实现不全,且使用条件苛刻
    2. in-tree 代码依然大量留存在源码中
    3. 部分功能实现(ceph rbd resize)需要同时部署 in-tree 和 out-of-tree 两种模式
  2. 排查 in-tree 源码发现 rbd 实际上是 kubelet 进行调用,但是最终部署时发现时 controller-manager 镜像需要 rbd 二进制程序

参考

  • 源码与文档

in-tree 源码 storageclass 官方文档 CSI 设计文档 本地静态存储 provisioner 外部存储 provisioner 源码中的 ceph-rbd 例子

  • blog 文章

kubernetes使用ceph存储卷 官方blog