kubernetes/k8s CSI分析-容器存储接口分析
更多 k8s CSI 的分析,可以查看这篇博客kubernetes ceph-csi分析,以 ceph-csi 为例,做了详细的源码分析。
概述
kubernetes的设计初衷是支持可插拔架构,从而利于扩展kubernetes的功能。在此架构思想下,kubernetes提供了3个特定功能的接口,分别是容器网络接口CNI、容器运行时接口CRI和容器存储接口CSI。kubernetes通过调用这几个接口,来完成相应的功能。
下面我们来对容器存储接口CSI来做一下介绍与分析。
在本文中,会对CSI是什么、为什么要有CSI、CSI系统架构做一下介绍,然后对CSI所涉及的k8s对象与组件进行了简单的介绍,以及k8s对CSI存储进行相关操作的流程分析,存储相关操作包括了存储创建、存储扩容、存储挂载、解除存储挂载以及存储删除操作。
CSI是什么
CSI是Container Storage Interface(容器存储接口)的简写。
CSI的目的是定义行业标准“容器存储接口”,使存储供应商(SP)能够开发一个符合CSI标准的插件并使其可以在多个容器编排(CO)系统中工作。CO包括Cloud Foundry, Kubernetes, Mesos等。
kubernetes将通过CSI接口来跟第三方存储厂商进行通信,来操作存储,从而提供容器存储服务。
为什么要有CSI
其实在没有CSI之前kubernetes就已经提供了强大的存储卷插件系统,但是这些插件系统实现是kubernetes代码的一部分,需要随kubernetes组件二进制文件一起发布,这样就会存在一些问题。
(1)如果第三方存储厂商发现有问题需要修复或者优化,即使修复后也不能单独发布,需要与kubernetes一起发布,对于k8s本身而言,不仅要考虑自身的正常迭代发版,还需要考虑到第三方存储厂商的迭代发版,这里就存在双方互相依赖、制约的问题,不利于双方快速迭代;
(2)另外第三方厂商的代码跟kubernetes代码耦合在一起,还会引起安全性、可靠性问题,还增加了kubernetes代码的复杂度以及后期的维护成本等等。
基于以上问题,kubernetes将存储体系抽象出了外部存储组件接口即CSI,kubernetes通过grpc接口与第三方存储厂商的存储卷插件系统进行通信。
这样一来,对于第三方存储厂商来说,既可以单独发布和部署自己的存储插件,进行正常迭代,而又无需接触kubernetes核心代码,降低了开发的复杂度。同时,对于kubernetes来说,这样不仅降低了自身的维护成本,还能为用户提供更多的存储选项。
CSI系统架构
这是一张k8s csi的系统架构图,图中所画的组件以及k8s对象,接下来会一一进行分析。

CSI相关组件一般采用容器化部署,减少环境依赖。
涉及k8s对象
1. PersistentVolume
持久存储卷,集群级别资源,代表了存储卷资源,记录了该存储卷资源的相关信息。
回收策略
(1)retain:保留策略,当删除pvc的时候,保留pv与外部存储资源。
(2)delete:删除策略,当与pv绑定的pvc被删除的时候,会从k8s集群中删除pv对象,并执行外部存储资源的删除操作。
(3)resycle(已废弃)
pv状态迁移
available --> bound --> released
2. PersistentVolumeClaim
持久存储卷声明,namespace级别资源,代表了用户对于存储卷的使用需求声明。
示例:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test
namespace: test
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Gi
storageClassName: csi-cephfs-sc
volumeMode: Filesystem
pvc状态迁移
pending --> bound
3. StorageClass
定义了创建pv的模板信息,集群级别资源,用于动态创建pv。
示例:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: csi-rbd-sc
parameters:
clusterID: ceph01
imageFeatures: layering
imageFormat: "2"
mounter: rbd
pool: kubernetes
provisioner: rbd.csi.ceph.com
reclaimPolicy: Delete
volumeBindingMode: Immediate
4. VolumeAttachment
VolumeAttachment 记录了pv的相关挂载信息,如挂载到哪个node节点,由哪个volume plugin来挂载等。
AD Controller 创建一个 VolumeAttachment,而 External-attacher 则通过观察该 VolumeAttachment,根据其状态属性来进行存储的挂载和卸载操作。
示例:
apiVersion: storage.k8s.io/v1
kind: VolumeAttachment
metadata:
name: csi-123456
spec:
attacher: cephfs.csi.ceph.com
nodeName: 192.168.1.10
source:
persistentVolumeName: pvc-123456
status:
attached: true
5. CSINode
CSINode 记录了csi plugin的相关信息(如nodeId、driverName、拓扑信息等)。
当Node Driver Registrar向kubelet注册一个csi plugin后,会创建(或更新)一个CSINode对象,记录csi plugin的相关信息。
示例:
apiVersion: storage.k8s.io/v1
kind: CSINode
metadata:
name: 192.168.1.10
spec:
drivers:
- name: cephfs.csi.ceph.com
nodeID: 192.168.1.10
topologyKeys: null
- name: rbd.csi.ceph.com
nodeID: 192.168.1.10
topologyKeys: null
涉及组件与作用

下面来介绍下涉及的组件与作用。
1. volume plugin
扩展各种存储类型的卷的管理能力,实现第三方存储的各种操作能力与k8s存储系统的结合。调用第三方存储的接口或命令,从而提供数据卷的创建/删除、attach/detach、mount/umount的具体操作实现,可以认为是第三方存储的代理人。前面分析组件中的对于数据卷的创建/删除、attach/detach、mount/umount操作,全是调用volume plugin来完成。
根据源码所在位置,volume plugin分为in-tree与out-of-tree。
in-tree
在k8s源码内部实现,和k8s一起发布、管理,更新迭代慢、灵活性差。
out-of-tree
代码独立于k8s,由存储厂商实现,有csi、flexvolume两种实现。
csi plugin
csi plugin分为ControllerServer与NodeServer,各负责不同的存储操作。
external plugin
external plugin包括了external-provisioner、external-attacher、external-resizer、external-snapshotter等,external plugin辅助csi plugin组件,共同完成了存储相关操作。external plugin负责watch pvc、volumeAttachment等对象,然后调用volume plugin来完成存储的相关操作。如external-provisioner watch pvc对象,然后调用csi plugin来创建存储,最后创建pv对象;external-attacher watch volumeAttachment对象,然后调用csi plugin来做attach/dettach操作;external-resizer watch pvc对象,然后调用csi plugin来做存储的扩容操作等。
Node-Driver-Registrar
Node-Driver-Registrar组件负责实现csi plugin(NodeServer)的注册,让kubelet感知csi plugin的存在。
组件部署方式
csi plugin controllerServer与external plugin作为容器,使用deployment部署,多副本可实现高可用;而csi plugin NodeServer与Node-Driver-Registrar作为容器,使用daemonset部署,即每个node节点都有。
2. kube-controller-manager
PV controller
负责pv、pvc的绑定与生命周期管理(如创建/删除底层存储,创建/删除pv对象,pv与pvc对象的状态变更)。
(1)in-tree:创建/删除底层存储、创建/删除pv对象的操作,由PV controller调用volume plugin(in-tree)来完成。
(2)out-tree CSI:创建/删除底层存储、创建/删除pv对象的操作由external-provisioner与csi plugin共同来完成。
AD controller
AD Cotroller全称Attachment/Detachment 控制器,主要负责创建、删除VolumeAttachment对象,并调用volume plugin来做存储设备的Attach/Detach操作(将数据卷挂载到特定node节点上/从特定node节点上解除挂载),以及更新node.Status.VolumesAttached等。
不同的volume plugin的Attach/Detach操作逻辑有所不同,对于csi plugin(out-tree volume plugin)来说,AD controller的Attach/Detach操作只是修改VolumeAttachment对象的状态,而不会真正的将数据卷挂载到节点/从节点上解除挂载,真正的节点存储挂载/解除挂载操作由kubelet中volume manager调用csi plugin来完成。
3. kubelet
volume manager
主要是管理卷的Attach/Detach(与AD controller作用相同,通过kubelet启动参数控制哪个组件来做该操作)、mount/umount等操作。
对于csi来说,volume manager的Attach/Detach操作只创建/删除VolumeAttachment对象,而不会真正的将数据卷挂载到节点/从节点上解除挂载;csi-attacer组件也不会做挂载/解除挂载操作,只是更新VolumeAttachment对象,真正的节点存储挂载/解除挂载操作由kubelet中volume manager调用调用csi plugin来完成。
kubernetes创建与挂载volume(in-tree volume plugin)
先来看下kubernetes通过in-tree volume plugin来创建与挂载volume的流程

(1)用户创建pvc;
(2)PV controller watch到pvc的创建,寻找合适的pv与之绑定。
(3)(4)当找不到合适的pv时,将调用volume plugin来创建volume,并创建pv对象,之后该pv对象与pvc对象绑定。
(5)用户创建挂载pvc的pod;
(6)kube-scheduler watch到pod的创建,为其寻找合适的node调度。
(7)(8)pod调度完成后,AD controller/volume manager watch到pod声明的volume没有进行attach操作,将调用volume plugin来做attach操作。
(9)volume plugin进行attach操作,将volume挂载到pod所在node节点,成为如/dev/vdb的设备。
(10)(11)attach操作完成后,volume manager watch到pod声明的volume没有进行mount操作,将调用volume plugin来做mount操作。
(12)volume plugin进行mount操作,将node节点上的第(9)步得到的/dev/vdb设备挂载到指定目录。
kubernetes创建与挂载volume(out-of-tree volume plugin)
再来看下kubernetes通过out-of-tree volume plugin来创建与挂载volume的流程,以csi-plugin为例。

(1)用户创建pvc;
(2)PV controller watch到pvc的创建,寻找合适的pv与之绑定。当寻找不到合适的pv时,将更新pvc对象,添加annotation:volume.beta.kubernetes.io/storage-provisioner,让external-provisioner组件开始开始创建存储与pv对象的操作。
(3)external-provisioner组件watch到pvc的创建/更新事件,判断annotation:volume.beta.kubernetes.io/storage-provisioner的值,即判断是否是自己来负责做创建操作,是则调用csi-plugin ControllerServer来创建存储,并创建pv对象(这里的pv对象使用了提前绑定特性,将pvc信息填入了pv对象的spec.claimRef属性)。
(4)PV controller将上一步创建的pv与pvc绑定。
(5)用户创建挂载pvc的pod;
(6)kube-scheduler watch到pod的创建,为其寻找合适的node调度。
(7)(8)pod调度完成后,AD controller/volume manager watch到pod声明的volume没有进行attach操作,将调用csi-attacher来做attach操作(实际上只是创建volumeAttachement对象)。
(9)external-attacher组件watch到volumeAttachment对象的新建,调用csi-plugin进行attach操作(如果volume plugin是ceph-csi,external-attacher组件watch到volumeAttachment对象的新建后,只是修改该对象的状态属性,不会做attach操作,真正的attach操作由kubelet中的volume manager调用volume plugin ceph-csi来完成)。
(10)csi-plugin ControllerServer进行attach操作,将volume挂载到pod所在node节点,成为如/dev/vdb的设备。
(11)(12)attach操作完成后,volume manager watch到pod声明的volume没有进行mount操作,将调用csi-mounter来做mount操作。
(13)csi-mounter调用csi-plugin NodeServer进行mount操作,将node节点上的第(10)步得到的/dev/vdb设备挂载到指定目录。
kubernetes存储相关操作流程具体分析(out-of-tree volume plugin,以csi plugin:ceph-csi为例)
下面来看下kubernetes通过ceph-csi volume plugin来创建/删除、挂载/解除挂载ceph存储的流程。
1. 存储创建
流程图

流程分析
(1)用户创建pvc对象;
(2)pv controller监听pvc对象,寻找现存的合适的pv对象,与pvc对象绑定。当找不到现存合适的pv对象时,将更新pvc对象,添加annotation:volume.beta.kubernetes.io/storage-provisioner,让external-provisioner组件开始开始创建存储与pv对象的操作;当找到时,将pvc与pv绑定,结束操作。
(3)external-provisioner组件监听到pvc的新增事件,判断pvc的annotation:volume.beta.kubernetes.io/storage-provisioner的值,即判断是否是自己来负责做创建操作,是则调用ceph-csi组件进行存储的创建;
(4)ceph-csi组件调用ceph创建底层存储;
(5)底层存储创建完成后,external-provisioner根据存储信息,拼接pv对象,创建pv对象(这里的pv对象使用了提前绑定特性,将pvc信息填入了pv对象的spec.claimRef属性);
(6)pv controller监听pvc对象,将第(5)步创建的pv对象,与pvc对象绑定。
2. 存储扩容
流程图

流程分析
(1)修改pvc对象,修改申请存储大小(pvc.spec.resources.requests.storage);
(2)修改成功后,external-resizer监听到该pvc的update事件,发现pvc.Spec.Resources.Requests.storgage比pvc.Status.Capacity.storgage大,于是调ceph-csi组件进行 controller端扩容;
(3)ceph-csi组件调用ceph存储,进行底层存储扩容;
(4)底层存储扩容完成后,ceph-csi组件更新pv对象的.Spec.Capacity.storgage的值为扩容后的存储大小;
(5)kubelet的volume manager在reconcile()调谐过程中发现pv.Spec.Capacity.storage大于pvc.Status.Capacity.storage,于是调ceph-csi组件进行 node端扩容;
(6)ceph-csi组件对node上存储对应的文件系统扩容;
(7)扩容完成后,kubelet更新pvc.Status.Capacity.storage的值为扩容后的存储大小。
3. 存储挂载
流程图
kubelet启动参数--enable-controller-attach-detach,该启动参数设置为 true 表示启用 Attach/Detach controller进行Attach/Detach 操作,同时禁用 kubelet 执行 Attach/Detach 操作(默认值为 true)。实际上Attach/Detach 操作就是创建/删除VolumeAttachment对象。
(1)kubelet启动参数--enable-controller-attach-detach=true,Attach/Detach controller进行Attach/Detach 操作。

(2)kubelet启动参数--enable-controller-attach-detach=false,kubelet端volume manager进行Attach/Detach 操作。

流程分析
(1)用户创建一个挂载了pvc的pod;
(2)AD controller或volume manager中的reconcile()发现有volume未执行attach操作,于是进行attach操作,即创建VolumeAttachment对象;
(3)external-attacher组件list/watch VolumeAttachement对象,更新VolumeAttachment.status.attached=true;
(4)AD controller更新node对象的.Status.VolumesAttached属性值,将该volume记为attached;
(5)kubelet中的volume manager获取node.Status.VolumesAttached属性值,发现volume已被标记为attached;
(6)于是volume manager中的reconcile()调用ceph-csi组件的NodeStageVolume与NodePublishVolume完成存储的挂载。
4. 解除存储挂载
流程图
(1)kubelet启动参数--enable-controller-attach-detach=true,Attach/Detach controller进行Attach/Detach 操作。

(2)kubelet启动参数--enable-controller-attach-detach=false,kubelet端volume manager进行Attach/Detach 操作。

流程分析
(1)用户删除声明了pvc的pod;
(2)AD controller或volume manager中的reconcile()发现有volume未执行dettach操作,于是进行dettach操作,即删除VolumeAttachment对象;
(3)AD controller或volume manager等待VolumeAttachment对象删除成功;
(4)AD controller更新node对象的.Status.VolumesAttached属性值,将标记为attached的该volume从属性值中去除;
(5)kubelet中的volume manager获取node.Status.VolumesAttached属性值,找不到相关的volume信息;
(6)于是volume manager中的reconcile()调用ceph-csi组件的NodeUnpublishVolume与NodeUnstageVolume完成存储的解除挂载操作。
5. 删除存储
流程图

流程分析
(1)用户删除pvc对象;
(2)pv controller发现与pv绑定的pvc对象被删除,于是更新pv的状态为released;
(3)external-provisioner watch到pv更新事件,并检查pv的状态是否为released,以及回收策略是否为delete;
(4)确认了pv对象的状态以及回收策略之后,接下来external-provisioner组件会调用ceph-csi的DeleteVolume来删除存储;
(5)ceph-csi组件的DeleteVolume方法,调用ceph集群命令,删除底层存储;
(6)删除底层存储后,external-provisioner组件删除pv对象。
总结
CSI即Container Storage Interface(容器存储接口)。
为了解决第三方存储厂商的存储卷插件代码集成到kubernetes代码中所带来的各种问题,kubernetes将存储体系抽象出了外部存储组件接口即CSI,kubernetes通过grpc接口与第三方存储厂商的存储卷插件系统进行通信,来操作存储,从而提供容器存储服务。
这样一来,对于第三方存储厂商来说,既可以单独发布和部署自己的存储插件,进行正常迭代,而又无需接触kubernetes核心代码,降低了开发的复杂度。同时,对于kubernetes来说,这样不仅降低了自身的维护成本,还能为用户提供更多的存储选项。
最后,再来回顾一下kubernetes CSI的架构。

更多 k8s CSI 的分析,可以查看这篇博客kubernetes ceph-csi分析,以 ceph-csi 为例,做了详细的源码分析。
kubernetes/k8s CSI分析-容器存储接口分析的更多相关文章
- kubernetes/k8s CNI分析-容器网络接口分析
关联博客:kubernetes/k8s CSI分析-容器存储接口分析 kubernetes/k8s CRI分析-容器运行时接口分析 概述 kubernetes的设计初衷是支持可插拔架构,从而利于扩展k ...
- kubernetes/k8s CRI分析-容器运行时接口分析
关联博客:kubernetes/k8s CSI分析-容器存储接口分析 概述 kubernetes的设计初衷是支持可插拔架构,从而利于扩展kubernetes的功能.在此架构思想下,kubernetes ...
- kubernetes/k8s CRI分析-kubelet创建pod分析
先来简单回顾上一篇博客<kubernetes/k8s CRI 分析-容器运行时接口分析>的内容. 上篇博文先对 CRI 做了介绍,然后对 kubelet CRI 相关源码包括 kubele ...
- kubernetes/k8s CRI分析-kubelet删除pod分析
关联博客<kubernetes/k8s CRI 分析-容器运行时接口分析> <kubernetes/k8s CRI分析-kubelet创建pod分析> 之前的博文先对 CRI ...
- DOCKER 学习笔记9 Kubernetes (K8s) 生产级容器编排 上
前言 在上一节的学习中.我们已经可以通过最基本的 Docker Swarm 创建集群,然后在集群里面加入我们需要运行的任务 以及任务的数量 这样我们就创建了一个服务. 当然,这样的方式在我们本地虚拟机 ...
- Docker+Kubernetes(k8s)微服务容器化实践
第1章 初识微服务微服务的入门,我们从传统的单体架构入手,看看在什么样的环境和需求下一步步走到微服务的,然后再具体了解一下什么才是微服务,让大家对微服务的概念有深入的理解.然后我们一起画一个微服务的架 ...
- DOCKER 学习笔记9 Kubernetes (K8s) 弹性伸缩容器 下
前言 从上一篇看来,我们已经对于Kubernetes ,通过minikube 建立集群,而后使用kubectl 进行交互,对Deployment 部署以及服务的暴露等.这节,将学习弹性的将服务部署到多 ...
- 《两地书》--Kubernetes(K8s)基础知识(docker容器技术)
大家都知道历史上有段佳话叫“司马相如和卓文君”.“皑如山上雪,皎若云间月”.卓文君这么美,却也抵不过多情女儿薄情郎. 司马相如因一首<子虚赋>得汉武帝赏识,飞黄腾达之后便要与卓文君“故来相 ...
- Kubernetes(K8s)基础知识(docker容器技术)
今天谈谈K8s基础知识关键词: 一个目标:容器操作:两地三中心:四层服务发现:五种Pod共享资源:六个CNI常用插件:七层负载均衡:八种隔离维度:九个网络模型原则:十类IP地址:百级产品线:千级物理机 ...
随机推荐
- 如何使用TensorCores优化卷积
如何使用TensorCores优化卷积 本文将演示如何在TVM中使用TensorCores编写高性能的卷积计划.假设卷积的输入有大量数据.首先介绍如何在GPU上优化卷积. TensorCore简介 每 ...
- Turing渲染着色器网格技术分析
Turing渲染着色器网格技术分析 图灵体系结构通过使用 网格着色器 引入了一种新的可编程几何着色管道.新的着色器将计算编程模型引入到图形管道中,因为协同使用线程在芯片上直接生成紧凑网格( meshl ...
- springboot——简单通过Map将错误提示输出到页面显示
主要思路:在controller层我们将错误信息put进map中,然后通过视图解析器跳转到目标页面,在目标页面中在通过指定标签内的th:text将错误消息取出. 例: 1.编写controller代码 ...
- .Net Redis实战指南——常用命令
本问主要介绍rabbitmqctl工具的常用命令. vhost 一个RabbitMQ服务器可以创建多个虚拟的消息服务器,称之为虚拟主机(virtual host),简称为vhost.vhost之间是绝 ...
- xxl-job执行器的注册
一.执行器注册流程 二.具体流程 1.注册监控线程 //类:JobRegistryHelper.java:方法:public void start() registryMonitorThread = ...
- Spring Boot WebFlux-02——WebFlux Web CRUD 实践
第02课:WebFlux Web CRUD 实践 上一篇基于功能性端点去创建一个简单服务,实现了 Hello.这一篇用 Spring Boot WebFlux 的注解控制层技术创建一个 CRUD We ...
- java并发编程实战之线程安全性(一)
1.1什么是线程安全性 要对线程安全性给出一个确切的定义是非常复杂的.最核心的概念就是正确性.正确性:某个类的行为与其规范完全一致.在良好的规范中通常会定义各种不变性条件来约束对象的状态,以及定义各种 ...
- noip2012 总结
Vigenère 密码 题目描述 16 世纪法国外交家 Blaise de Vigenère 设计了一种多表密码加密算法――Vigenère 密码.Vigenère 密码的加密解密算法简单易用,且破译 ...
- NOIP模拟测试14「旋转子段·走格子·柱状图」
旋转子段 连60分都没想,考试一直肝t3,t2,没想到t1最简单 我一直以为t1很难,看了题解发现也就那样 题解 性质1 一个包含a[i]旋转区间值域范围最多为min(a[i],i)----max(a ...
- Web 动画原则及技巧浅析
在 Web 动画方面,有一套非常经典的原则 -- Twelve basic principles of animation,也就是关于动画的 12 个基本原则(也称之为迪士尼动画原则),网上对它的解读 ...