回望K8S 持久化存储
PV
、PVC
、StorageClass
说的啥?
PV
: 持久化存储数据卷,这个 API 主要定义的是一个持久化存储在宿主机上的一个目录。一般由运维人员进行定义,比如定义一个 NFS 类型的 PV
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs
spec:
storageClassName: manual
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
nfs:
server: 10.244.1.5
path: "/"
PVC
: POD 所希望使用的持久化存储的属性. 比如 Volume
的存储大小、可读写权限等.
PVC
一般由开发人员创建、或者由 PVC
模板的方式成为StatefulSet
的一部分,由StatefulSet
控制器负责创建带编号的PVC
.
# 创建一个 1 GB 大小的PVC
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs
spec:
accessModes:
- ReadWriteMany
storageClassName: manual
resources:
requests:
storage: 1Gi
...
用户创建的PVC
要真正的被容器使用的化,需要于心和某个符合条件的PV
进行绑定:
- 第一个条件,
PV
和PVC
的spec
字段。例如:PV
的存储(storage
)大小就必须满足PVC
的要求 - 第二个条件,
PV
和PVC
的storageClassName
字段名称必须一样。
下面是去使用这个PVC
apiVersion: v1
kind: Pod
metadata:
labels:
role: web-frontend
spec:
containers:
- name: web
image: nginx
ports:
- name: web
containerPort: 80
volumeMounts:
- name: nfs
mountPath: "/usr/share/nginx/html"
volumes:
- name: nfs
persistentVolumeClaim:
claimName: nfs
PVC
理解为持久化存储的 接口,它提供了对某种持久化存储的描述,但不提供具体的实现,而这个持久化的实现部分由 PV
完成。
假设,我们在创建 POD
的时候,系统内并没有合适的 PV
跟它定义的 PVC
绑定,这个时候容器想要使用的 Volume
不存在,怎么办呢?
在 Kubernetes
内,时机存在一个专门处理持久化存储的控制器,叫做 VolumeController
。这个VolumeController
维护着多个控制循环,
其中有个循环就是用来撮合 PV
和 PVC
进行绑定的角色,名字叫 PersistentVolumeController
. 它会不断的查看当前的每一个 PVC
, 是否处于 Bound
状态
如果不是,它会遍历所以的、可用的 PV
,并尝试将其与这个声明的PVC进行绑定。这样,Kubernetes
就可以保证用户提交的每一个 PVC
,只要有合适的 PV
出现,它能快速的进入绑定状态。
而所谓的绑定,其实就是将这个 PV
对象的名字填充在了 PVC
对象的 spec.volumeName
字段上。接下来 Kubernetes
只要获取到这个 PVC
对象,就一定能够找到它所绑定的 PV
.
PV
对象如何变成容器里的一个持久化存储的呢?
所谓容器的 Volume
,其实就是将一个宿主机上的目录,跟容器里的目录进行绑定挂载在一起的,
所谓的 持久化Volume
,指的就是这个宿主机上的目录,具备持久化
,即当目录里面的内容,既不会因为容器的删除而被清理、也不会跟当前的宿主机绑定,当容器进行重启或者在其他节点上重建之后,依然能够挂载到这个 Volume
, 访问这些内容,
所以,大多数情况下,持久化的 Volume
的实现,往往依赖一个远程存储服务,比如远程文件存储(NFS
、GlusterFS
),远程块存储(公有云的远程磁盘)。
持久化
宿主机目录的过程,这个形象的成为 两阶段处理
当一个 POD
调度到一个节点上后, Kubelet
就要为这个 POD
创建一个 Volume
目录, 默认情况下 Kubelet
为 Volume
创建的目录如下所示(在宿主机上)
/var/lib/kubelet/pods/<Pod的ID>/volumes/kubernetes.io~<Volume类型>/<Volume名字>
接下来, Kubelet
要做的操作取决于 Volume
类型.
如果 Volume
类型是远程块存储,比如 Google CLoud
的 Persistent Disk
, 那么 Kubelet
就需要先调用 Google Cloud
的 API
, 将它所提供的 Persistent Disk
挂载到 Pod
所在的宿主机上。
相当于执行了
gcloud compute instances attach-disk <虚拟机名字> --disk <远程磁盘名字>
为虚拟机挂载远程磁盘的操作,对应的正是 两阶段处理
的第一段。即在 Kubernetes
中的 Attach
阶段。
Attach
阶段后,要能够使用这个远程磁盘, Kubelet
需要进行第二个操作: 即使会这个磁盘设备, 然后将它挂载到宿主机知道的挂载点上。 这一步相当于执行:
# 通过lsblk命令获取磁盘设备ID
sudo lsblk
# 格式化成ext4格式
sudo mkfs.ext4 -m 0 -F -E lazy_itable_init=0,lazy_journal_init=0,discard /dev/<磁盘设备ID>
# 挂载到挂载点
sudo mkdir -p /var/lib/kubelet/pods/<Pod的ID>/volumes/kubernetes.io~<Volume类型>/<Volume名字>
这个将磁盘设备格式化并挂载到 Volume
宿主机目录的操作,对应的正是 “两阶段处理” 的第二段: Mount
Kubernetes
如何定义和区分这两个阶段的?
在具体的 Volume
插件的实现接口上, Kubernetes
分别给这两个阶段提供了两种不同的参数列表:
- 第一阶段
Attach
,Kubernetes
提供的可用参数是nodeName
, 即宿主机的名字 - 第二阶段
Mount
,Kubernetes
提供的可用参数是dir
, 即Volume
的宿主机目录
在经过 “两阶段处理”, 我们得到了一个 持久化
的 Volume
宿主机目录, 然后, Kubelet
只要把这个 Volume
目录通过 CRI
里的 Mounts
参数,传递给 Docker
, 然后就可以为 POD
里的容器挂载这个 “持久化” 的 Volume
。
另外还有一个核心的概念 StorageClass
, Kubernetes
为我们提供了一套可以自动创建 PV
的机制, 即 Dynamic Provisioning
, 前面人工管理的 PV
方式叫做 Static Provisioning
。
Dynamic Provisioning
机制工作的核心, 在于一个名叫 StorageClass
的API对象。而这个对象的作用,就是创建 PV 的模板。StorageClass
对象会定义如下两个部分内容:
- 第一,
PV
的属性。比如 存储类型、Volume
大小等 - 第二, 创建这种
PV
需要用到的存储插件。比如Ceph
等 有这两个信息后,Kubernetes
就可以根据用户提交的PVC
,找到一个对应的StorageClass
,然后Kubernetes
就好调用该StorageClass
声明的存储插件, 创建出需要的PV
。
#在这个 YAML 文件里,我们定义了一个名叫 block-service 的 StorageClass。
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: block-service
provisioner: kubernetes.io/gce-pd # GCE PD 存储插件的名字
parameters:
type: pd-ssd # SSD格式的GCE远程磁盘
Kubernetes PV 和 PVC 体系是不是多此一样
Kubernetes 支持多种的持久化 Kubernetes内置的持久化数据卷实现
在通过 PV
和 PVC
, 以及 StorageClass
这套存储体系, 为后来添加持久化存储方案,对已有的 Kubernetes
的影响,几乎可以忽略不及。作位用户, POD
的 YAML
和 PVC
的 YAML
并没有任何特殊的改变.
总结
PVC
描述POD
想要使用的持久化存储的属性,比如存储的大小、读写权限。PV
描述的 一个具体的Volume
的属性、比如Volume
的类型、挂载目录、远程存储服务器地址StorageClass
作用,充当PV
的模板,只要同属于一个StorageClass
的PV
和PVC
,才可以绑定在一起。
Driver Registrar
组件负责将插件注册到kubelet
阶段,请求CSI
插件的Identity服务
获取插件信息External Provisioner
组件Provision 阶段
,External Provisioner
监听(Watch
)了APIServer
里的PVC对象
。当一个PVC
被创建时,它就会调用CSI Controller
的CreateVolume
方法,为你创建对应PV
External Attacher
组件负责正是 “Attach 阶段
”, 它监听了APIServer
里的VolumeAttachment 对象
的变化,VolumeAttachment 对象
是Kubernetes
确认一个Volume
可以进入Attach 阶段
的重要标志