type
status
date
slug
summary
tags
category
icon
password
基本概念
K8s 的存储插件用于对接容器平台和底层存储资源,例如挂载 Volume 时可以配置的 nfs 或 gitRepo 等;第三方平台,如 AWS、Google Cloud 也都会提供存储插件用于接入它们的存储服务
存储插件分为 In-Tree 和 Out-of-Tree 两类:
- In-Tree 的存储插件是包含在 K8s 内部的,因此构建、编译、交付都与 Kuberentes 本身绑定,前面的 nfs 和 gitRepo 都是 In-Tree 的插件
- Out-of-Tree 插件则是独立的,可以单独部署。Out-of-Tree 插件主要分为 FlexVolume 和 CSI(Container Storage Interface)两类方式,其中前者在 1.23 版本已经废弃
从 1.17 版本开始,K8s 开始测试 CSI Migration,用于将 In-Tree 内的存储插件迁移到 Out-of-Tree 的 CSI 上,并在 1.25 中正式发布该功能
因此,存储插件的开发目前基本只剩下 CSI 一种选择,其不仅仅局限于 K8s,更是目前容器存储的工业标准
CSI 架构 & 规范
CSI Driver 在 K8s 中的架构如下所示
CSI Driver 会作为 Pod 运行在 K8s 中,通过监听资源(PVC、PV 等)事件触发对底层存储资源的操作,还有一些操作是 kubelet 通过 UDS(Unix Domain Socket)调用 CSI Driver 进行
监听资源变更这部分都是通用逻辑,实现重复度比较高,因此 Kubernetes Team 提供了一系列 Sidecar 来完成(上图粉色部分),以简化开发,同时解耦与 K8s API 的交互实现:CSI Sidecar Containers
那么我们要实现的 CSI Driver 最后可以分为三个 gRPC 服务(上图绿色部分):
- Identity Service:用于暴露插件本身的信息和进行健康检查
- Controller Service:操作底层存储资源,对存储卷进行管理
- Node Service:执行和宿主机相关的操作,例如 mount 等
CSI 规范文档中有几个比较重要的点:
- 首先,CSI 接口必须保证幂等性。虽然 K8s 保证在在给定时间内对每个卷「正在进行」的调用不超过一个,但当发生 failover 时,这个保证可能就会失效,导致诸如重复创建同一个卷的情况发生,因此 CSI 侧必须实现幂等性,以防止存储卷泄露
- 规范返回值,CSI 的 gRPC 接口必须返回标准错误码,以便 K8s 正确响应,这部分在标准文档的 Error Scheme 部分可以看到
最后部署时一般分为 Node Plugin 和 Controller Plugin 进行部署:
- 前者需要实现 Identity Service 和 Node Service,以 DaemonSet 方式运行在每个节点上
- 后者需要实现 Identity Service 和 Controller Service,可以在任何地方运行,通常是个 Deployment 或 StatefulSet
一个卷的生命周期如下所示
例如,当创建一个使用 PVC 的 Pod 时:
- Volume Controller 监听到 PVC 创建,但其只负责 In-Tree 模式的管理,跳过执行
- Sidecar 监听到 PVC 创建,调用 CSI Controller Service 的
CreateVolume
,CSI Driver 这时会创建底层存储资源。之后卷处于 CREATED 状态,PV 被创建,并绑定到 PVC 上
- Volume Controller 创建 VolumeAttachment 资源,表示需要将 PV 挂载到宿主机上
- Sidecar 监听到 VolumeAttachment 创建,调用 CSI Controller Service 的
ControllerPublishVolume
。CSI Driver 这时一般会将底层存储资源与目标节点关联起来,之后卷处于 NODE_READY 状态,对于 Node 可见
- kubelet 感知到卷的存在,执行 MountDevice 操作,调用 CSI Node Service 的
NodeStageVolume
。CSI Driver 此时会初始化卷,例如进行分区和格式化、创建文件系统等,之后卷处于 VOL_READY 状态
- 最后 kubelet 执行 Setup 操作,调用 CSI Node Service 的
NodePublishVolume
。CSI Driver 将卷挂载到容器(kubelet 指定的 Volume 目录)内,卷进入 PUBLISHED 状态,可以正常使用
部署
前面提到,CSI Driver 是以 Pod 运行在 K8s 内的,因此本质上就是拉 Pod 运行即可。但 CSI 部署时也分为 Controller Plugin 和 Node Plugin,且可能涉及相关存储底层配置,要升级时也比较麻烦。因此一般会通过 Helm 进行部署:
测试
kubernetes-csi/csi-test 仓库下提供了一些测试工具
首先 csi-test 里的
pkg/sanity
包可以帮助进行单元测试,其还提供了一个 CLI 程序 csi-sanity 可以用于检测 API 是否符合规范,但其没法很好的进行 API 和 E2E(End-to-End,端到端) 测试API 测试要手动进行也是可以的,CSI Driver 只是提供几个 gRPC 接口,所以接口测试时使用 grpcurl 等 gRPC 调试工具即可,但 csc(Container Storage Client)等工具也提供了更便利的包装:
E2E 测试可以通过 kubectl 手动进行,官方也提供了一些套件用于进行自动化的 E2E 测试:
E2E 测试这部分,之后有空打算单独写一篇文章讲一下