Introduction
CSI is a standard for exposing storage systems in arbitrary block and file storage to containerized workloads on Container Orchestrations like Kubernetes, Mesos, and Cloud Foundry. It becomes very extensible for third-party storage provider to expose their new storage systems using CSI, without actually touching the Kubernetes code. Single independent implementation of CSI Driver by a storage provider will work on any orchestrator.
CSI 是一种标准,用于将任意块和文件存储中的存储系统暴露给Kubernetes、Mesos 和 Cloud Foundry 等容器编排上的容器化工作负载。第三方存储提供商使用 CSI 公开他们的新存储系统变得非常可扩展,而无需实际接触 Kubernetes 代码。存储提供商对 CSI 驱动程序的单一独立实现将适用于任何编排器。
This new plugin mechanism has been one of the most powerful features of Kubernetes. It enables the storage vendors to:
- Automatically create storage when required.
- Make storage available to containers wherever they’re scheduled.
- Automatically delete the storage when no longer needed.
This decoupling helps the vendors to maintain the independent release and feature cycles and focus on the API implementation without actually worrying about the backward incompatibility and to support their plugin just as easy as deploying a few pods.
这种解耦有助于供应商保持独立的发布和功能周期,专注于 API 实现,而无需担心向后不兼容,并且像部署几个 pod 一样简单地支持他们的插件。
Image Source: Weekly Geekly
Why CSI?
Prior to CSI, k8s volume plugins have to be “In-tree”, compiled and shipped with core kubernetes binaries. This means, it will require the storage providers to check-in their into the core k8s codebase if they wish to add the support for a new storage system.
在 CSI 之前,k8s 卷插件必须是“In-tree”,编译并随核心 kubernetes 二进制文件一起提供。这意味着,如果他们希望添加对新存储系统的支持,则需要存储提供商将其签入核心 k8s 代码库。
A plugin-based solution, flex-volume, tried to address this issue by exposing the exec based API for external plugins. Although it also tried to work on the similar notion of being detached with k8s binary, there were several major problems with that approach. Firstly, it needed the root access to the host and master file system to deploy the driver files.
基于插件的解决方案 flex-volume 试图通过为外部插件公开基于 exec 的 API 来解决这个问题。尽管它也尝试处理与 k8s 二进制分离的类似概念,但这种方法存在几个主要问题。首先,它需要对主机和主文件系统的 root 访问权限来部署驱动程序文件。
Secondly, it comes with the huge baggage of prerequisites and OS dependencies which are assumed to be available on the host. CSI implicitly solves all these issues by being containerized and using the k8s storage primitives.
其次,它带来了大量的先决条件和操作系统依赖项,假设它们在主机上可用。CSI 通过容器化和使用 k8s 存储原语隐式地解决了所有这些问题。
CSI has evolved as the one-stop solution addressing all the above issues which enables storage plugins to be out-of-tree and deployed via standard k8s primitives, such as PVC, PV and StorageClasses.
The main aim of introducing CSI is to establish a standard mechanism of exposing any type of storage system under-the-hood for all the container orchestrators.
其实,通过前面对 FlexVolume 的讲述,你应该可以明白,默认情况下,Kubernetes 里通过存储插件管理容器持久化存储的原理,可以用如下所示的示意图来描述:
可以看到,在上述体系下,无论是 FlexVolume,还是 Kubernetes 内置的其他存储插件,它们实际上担任的角色,仅仅是 Volume 管理中的“Attach 阶段”和“Mount 阶段”的具体执行者。而像 Dynamic Provisioning 这样的功能,就不是存储插件的责任,而是 Kubernetes 本身存储管理功能的一部分。
相比之下,CSI 插件体系的设计思想,就是把这个 Provision 阶段,以及 Kubernetes 里的一部分存储管理功能,从主干代码里剥离出来,做成了几个单独的组件。这些组件会通过 Watch API 监听 Kubernetes 里与存储相关的事件变化,比如 PVC 的创建,来执行具体的存储管理动作。
而这些管理动作,比如“Attach 阶段”和“Mount 阶段”的具体操作,实际上就是通过调用 CSI 插件来完成的。
这种设计思路,我可以用如下所示的一幅示意图来表示:
可以看到,这套存储插件体系多了三个独立的外部组件(External Components),即:Driver Registrar、External Provisioner 和 External Attacher,对应的正是从 Kubernetes 项目里面剥离出来的那部分存储管理功能。
需要注意的是,External Components 虽然是外部组件,但依然由 Kubernetes 社区来开发和维护。
而图中最右侧的部分,就是需要我们编写代码来实现的 CSI 插件。一个 CSI 插件只有一个二进制文件,但它会以 gRPC 的方式对外提供三个服务(gRPC Service),分别叫作:CSI Identity、CSI Controller 和 CSI Node。
我先来为你讲解一下这三个 External Components。
其中,Driver Registrar 组件,负责将插件注册到 kubelet 里面(这可以类比为,将可执行文件放在插件目录下)。而在具体实现上,Driver Registrar 需要请求 CSI 插件的 Identity 服务来获取插件信息。
而 External Provisioner 组件,负责的正是 Provision 阶段。在具体实现上,External Provisioner 监听(Watch)了 APIServer 里的 PVC 对象。当一个 PVC 被创建时,它就会调用 CSI Controller 的 CreateVolume 方法,为你创建对应 PV。
此外,如果你使用的存储是公有云提供的磁盘(或者块设备)的话,这一步就需要调用公有云(或者块设备服务)的 API 来创建这个 PV 所描述的磁盘(或者块设备)了。
不过,由于 CSI 插件是独立于 Kubernetes 之外的,所以在 CSI 的 API 里不会直接使用 Kubernetes 定义的 PV 类型,而是会自己定义一个单独的 Volume 类型。