认识K8s容器存储接口CSI
从临时存储、半持久存储、持久化存储认识CSI
容器存储接口(Container Storage Interface),简称 CSI,CSI试图建立一个行业标准接口的规范,借助CSI容器编排系统(CO)可以将任意存储系统暴露给自己的容器工作负载。
存储可分为临时存储、半持久存储、持久化存储。常见的临时存储主要是emptyDir卷,enptyDir最初是空的,当pod从节点上删除后,emptyDir卷中的数据也会被永久删除,如果pod因为某些原因重启,enptyDir卷内的数据不会丢失。emptyDir卷存储在支持该节点所使用的存储介质上,可以是本地磁盘或者网络存储。emptyDir的空间位于系统根盘,被所有容器共享,所以在磁盘的使用率较高时会触发Pod的eviction操作,故需要根据实际需要来使用emptyDir,一般情况下emptyDir存储中间数据,用于快速恢复。
半持久化存储主要是hostPath卷,hostPath卷将主机节点上文件系统上的文件或者目录挂载到指定pod中,对于需要获取节点系统信息的pod,需要挂载hostPath,例如Calico插件,需要将配置文件等存放到节点的目录上。在使用hostPath卷时需要注意的三个点
1.使用同一个目录的pod由于调度到不同的节点,导致目录中内容不同
2.Kubernetes在调度时无法顾及由hostPath使用的资源
3.pod被删除后,hostPath上的数据会遗留在节点上,会占用磁盘空间
支持持久化存储是所有分布式系统必备的特性,针对持久化存储,Kubernetest引入了StorageClass,Volume,PVC,PV的概念。Kubernetes支持的持久化存储包括主流的块存储和文件存储,在大类上又可以分成网络存储和本地存储两类。
StorageClass:用于指示存储的类型,不同的存储类型可以通过不同的StorageClass来为用户提供服务,StorageClass中主要包含存储插件、provisioner、卷创建和mount参数字段。以下就是定义一个StorageClass的yaml文件例子,每个 StorageClass 都有一个制备器(Provisioner),用来决定使用哪个卷插件制备 PV。
下图是一些常用的Provisioner,更多Provisioner的信息可以查看这里。
通过上面的信息描述,可以知道StorageClass对象本质上是创建PV的模板,StorageClass的定义涉及两部分:
第一:PV的属性。比如,存储类型、Volume的大小等。
第二:创建这种PV需要用到的存储插件,如Ceph等。
有了这两个信息之后,Kubernetes就能够根据用户提交的PVC,找到一个对应的StorageClass了。然后Kubernetes就会调用该StorageClass声明的存储插件创建出需要的PV,且把StorageClass相同的PVC和PV进行绑定。这种更具StorageClass自动创建PV且完成绑定的机制叫Dynamic Provisioning机制,有了这种机制运维人员只需要创建出需要的StorageClass即可,PVC本身是由开发人员创建。
PVC:由用户创建,代表用户对存储需求的声明,主要包括需要的存储大小,存储卷的访问模式、StorageClass等类型,其中存储卷的访问模式必须与存储类型一致。下图是一个PVC的yaml文件例子。
PV:由集群管理员提前创建,或者根据PVC的申请动态创建,它代表系统后端的真实存储空间,可以称之为卷空间。备注:如果是通过前面Dynamic Provisioning机制,可以不用定义PV。
下面将通过例子演示如何配置emptyDir实现临时存储,下面是一个部署nginx的yaml文件例子,在yaml文件中指定了emptyDir。
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- mountPath: /cache
name: cache-volume
volumes:
- name: cache-volume
emptyDir: {}
启动pod后,进入pod,查看文件目录,可以看到有个cache目录,且该目录是空目录。
拷贝一个文件到cache目录下,此时删除pod,等新的pod重启后,再次进入新的pod,可以看到存储的文件消失,故emptyDir适用于临时存储一些内容。
接下来再看看hostPath的例子,以下是创建PV,PVC,Pod的yaml文件。下面的文件中定义了一个PV,PV关联的hostPath是/mnt/data,pod中mount的地址是usr/share/nginx/html,在主机上创建/mnt/data的目录,然后在该目录下创建一个html文件,然后依次创建下面的对象,pod对象创建成功后,理论上进入pod,在pod的/usr/share/nginx/html目录下存在在主机上创建的html文件。
apiVersion: v1
kind: PersistentVolume
metadata:
name: task-pv-volume
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 100Mi
accessModes:
- ReadWriteOnce
hostPath:
path: "/mnt/data"
--
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: task-pv-claim
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 100Mi
--
apiVersion: v1
kind: Pod
metadata:
name: task-pv-pod-anti-affinity.yaml
spec:
volumes:
- name: task-pv-storage
persistentVolumeClaim:
claimName: task-pv-claim
containers:
- name: task-pv-container
image: nginx
ports:
- containerPort: 80
name: "http-server"
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: task-pv-storage
在定义PV和PVC过程中都涉及到accessMode配置,accessMode支持三种类型
- ReadWriteMany 多路读写,卷能被集群多个节点挂载并读写
- ReadWriteOnce 单路读写,卷只能被单一集群节点挂载读写
- ReadOnlyMany 多路只读,卷能被多个集群节点挂载且只能读
接着再看看PVC和StorageClass,如下图所示一个myslq的pod部署时,在pod的部署文件中配置了volume信息,volume配置中设置了需要的PVC名称。
下面是PVC的定义,可以看到PVC中又指定了StorageClass,故如果要让Mysql部署成功,那么首选需要创建StorageClass,创建完成后可以通过kubectl get storageclass查看。关于如何在aws上创建StorageClass可以查看aws官网的详细文档。
下图是在aws上申请eks集群后,按照官网文档安装存储插件后,创建出来的storageClass。
创建出StorageClass后再部署上面的mysql服务,能成功部署。
mysql服务部署的方式属于动态挂载volume的方式,按volume的挂载方式可以分为动态挂载和本地独占两种模式,接下来看看这两个模式的过程。
动态挂载volume过程
- 创建PVC:用户创建PVC,PVC处于pending状态。
- 创建Pod:用户创建Pod
- Pod选择节点:kube-sheduler开始调度pod,通过PVC的resource.reqeust.storage等选择满足条件的节点。
- 更新PVC:选择节点后,kube-scheduler会给PVC添加包含节点信息的annotation:volume.kubernetes.io/selected-node:节点名称
- 创建卷:运行的节点上的容器external-provisioner监听到PVC有该节点相关的annotation,向相应的CSI驱动申请分配卷
- 创建PV:PVC申请到所需的存储空间后,external-provisioner创建PV,该PV的pv.Spec.claimRef设置为对应的PVC
- PVC和PV绑定:kube-controller-manager将PVC和PV进行绑定,状态修改为Bound
- 监听PVC状态:kube-scheduler等待PVC变成Bound状态
- Pod调度到节点:当PVC的状态为Bound时,Pod才算真正调度成功,如果PVC一直处于pending状态,超时后会再次进行调度。
- Mound卷:kubelet监听到有Pod已经调度到节点上,对本地存储进行mount操作。
- 启动容器:启动容器。
如果是通过kubeadm初始化的集群,也可以通过如下方式创建storageclass和PVC来感受下动态存储的挂载过程。
8.创建PVC,里面需要配置正确的storageClassName的名字,创建pvc后进行查看,是binding状态即表示存储绑定成功。
独占的Local Volume
- 创建PV:通过local-volume-provisioner Daemonset创建本地存储的PV
- 创建PVC:用户创建PVC,由于它处于pending状态,所以kube-controller-manager并不会对PVC做任何操作
- 创建Pod:用户创建Pod
- Pod挑选节点:kube-sheduler开始调度pod,通过PVC的resource.reqeust.storage和volumeMode选择满足条件的PV,并且为Pod选择一个合适的节点
- 更新PV:kube-scheduler将PV的pv.Spec.claimRef设置为对应PVC,并且设置annotaion pv.kubernetes.io/bound-by-controller的值为yes
- PVC和PV绑定:pv_controller同步PVC和PV的状态,并将PVC和PV进行绑定
- 监听PVC对象:kube-scheduler等待PVC的状态变成Bound状态
- Pod调度到节点:如果PVC的状态变为Bound则说明调度成功,如果PVC一直处于pending状态,超时后会再次调度。
- Mount卷启动容器:kubelet监听到有Pod已经调度到节点上,对本地存储进行mount操作,并启动容器。
定义PV的yaml文件如下图所示:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv0003
spec:
capacity:
storage: 5Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
storageClassName: slow
mountOptions:
- hard
- nfsvers=4.1
nfs:
path: /tmp
server: 172.17.0.2
更多推荐
所有评论(0)