在k8s中为什么要做持久化存储?
在k8s中部署的应用都是以pod容器的形式运行的,因为Pod是有生命周期的,如果pod不挂载数据卷,那pod被删除或重启后这些数据会随之消失,如果想要长久的保留这些数据就要用到pod数据持久化存储。
PersistentVolume:简称PV,是外部存储系统中的一块存储空间,由管理员配置或使用存储类动态配置。与Volume一样,PV是容量插件,如Volumes,其生命周期独立于使用PV的任何单个pod。严格来说,PV是k8s里面的一个概念,它本身不是存储,只不过是创建PV的资源清单文件中指定了网络存储的地址,同时也指定了一些存储的参数,例如大小,性能等指标。
PersistentVolumeClaim:简称PVC,是对PV的申请(Claim)。PVC通常由普通用户创建和维护。需要为Pod分配存储资源时,用户可以创建一个PVC,指明存储资源的容量大小和访问模式(比如只读)等信息,Kubernetes 会查找并提供满足条件的PV与之绑定。它类似于一个pod。 Pod消耗节点资源,PVC消耗PV资源。 Pod可以请求特定级别的资源(CPU和内存)。 pvc在申请pv的时候也可以请求特定的大小和访问模式(例如,可以一次读写或多次只读)。
在大规模的kubernetes生产集群中,可能有非常多的PVC,随着项目的增加,可能需要不断添加新的PV,否则就有可能新的pod因为PVC绑定不到PV而创建失败。所以,Kubernetes提供了一套可以自动创建PV的机制,即:Dynamic Provisioning.而这个机制的核心在于:StorageClass这个API对象。
StorageClass对象会定义下面两部分内容:
1,PV的属性.比如,存储类型,Volume的大小等
2,创建这种PV需要用到的存储插件
有了这两个信息之后,Kubernetes就能够根据用户提交的PVC,找到一个对应的StorageClass,之后Kubernetes就会调用该StorageClass声明的存储插件,进而创建出需要的PV。
总结:
1、PV是一个具体的Volume属性,比如Volume的类型,挂载目录等
2、PVC描述的是Pod想要使用的持久化存储的属性,比如存储的大小,读写权限等
3、StorageClass的作用,则是充当PV的模板,从而在找不到合适的PV之后可以动态创建PV的一个机制
参考如下链接:看懂StorageClass pv pvc关系
PV 持久卷是用插件的形式来实现的。Kubernetes 目前支持以下插件,如下图所示:
Available – 资源尚未被claim使用
Bound – 卷已经被绑定到claim了
Released – claim被删除,卷处于释放状态,但未被集群回收。
Failed – 卷自动回收失败
PV是群集中的资源。PVC是对这些资源的请求,并且还充当对资源的检查。PV和PVC之间的相互作用遵循以下生命周期:Provisioning —> Binding —>Using—>Releasing—>Recycling
供应准备Provisioning—通过集群外的存储系统或者云平台来提供存储持久化支持。
1、静态提供Static:集群管理员创建多个PV。 它们携带可供集群用户使用的真实存储的详细信息。 它们存在于Kubernetes API中,可用于消费。
2、动态提供Dynamic:当管理员创建的静态PV都不匹配用户的PersistentVolumeClaim时,集群可能会尝试为PVC动态配置卷。 此配置基于StorageClasses:PVC必须请求一个类,并且管理员必须已创建并配置该类才能进行动态配置。
绑定Binding—用户创建pvc并指定需要的资源和访问模式。在找到可用pv之前,pvc会保持未绑定状态。
使用Using—用户可在pod中像volume一样使用pvc。
释放Releasing—用户删除pvc来回收存储资源,pv将变成“released”状态。由于还保留着之前的数据,这些数据需要根据不同的策略来处理,否则这些存储资源无法被其他pvc使用。
回收Recycling—pv可以设置三种回收策略:保留(Retain),回收(Recycle)和删除(Delete)。
- 保留策略:允许人工处理保留的数据。
- 删除策略:将删除pv和外部关联的存储资源,需要插件支持。
- 回收策略:将执行清除操作,之后可以被新的pvc使用,需要插件支持。
注:目前只有NFS和HostPath类型卷支持回收策略,AWS EBS,GCE PD,Azure Disk和Cinder支持删除(Delete)策略。
PVC访问模式
ReadWriteOnce
卷可以被一个节点以读写方式挂载。 ReadWriteOnce访问模式也允许运行在同一节点上的多个Pod访问卷。
ReadOnlyMany
卷可以被多个节点以只读方式挂载。
ReadWriteMany
卷可以被多个节点以读写方式挂载。
ReadWriteOncePod
卷可以被单个Pod以读写方式挂载。如果你想确保整个集群中只有一个Pod可以读取或写入该PVC,请使用ReadWriteOncePod访问模式。这只支持CSI卷以及需要Kubernetes1.22以上版本。
参考链接: Introducing Single Pod Access Mode for PersistentVolumes
在命令行接口(CLI)中,访问模式也使用以下缩写形式:
RWO - ReadWriteOnce
ROX - ReadOnlyMany
RWX - ReadWriteMany
RWOP - ReadWriteOncePod
示例如下:
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: shared-cache
spec:
accessModes: #定义pvc的访问模式
- ReadWriteMany # Allow many pods to access shared-cache simultaneously.
resources: #定义所需存储空间大小
requests:
storage: 1Gi
当Pod分派到某个Node上时,emptyDir卷会被创建,并且在 Pod在该节点上运行期间,卷一直存在。就像其名称表示的那样,卷最初是空的。 尽管Pod中的容器挂载emptyDir卷的路径可能相同也可能不同,这些容器都可以读写 emptyDir 卷中相同的文件。当Pod因为某些原因被从节点上删除时,emptyDir卷中的数据也会被永久删除。
说明: 容器崩溃并不会导致 Pod 被从节点上移除,因此容器崩溃期间 emptyDir 卷中的数据是安全的。
示例如下:
apiVersion: v1
kind: Pod
metadata:
name: nginx-pd
spec:
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: nginx-containers
ports:
- name: http
containerPort: 80
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
- name: busybox
image: busybox:latest
name: busybox-containers
imagePullPolicy: IfNotPresent
volumeMounts:
- name: html
mountPath: /data/ #在容器内定义挂载存储名称和挂载路径
command: ['/bin/sh','-c','while true;do echo $(date) >> /data/index.html;sleep 2;done']
volumes:
- name: html
emptyDir: {}
在上面,我们定义了2个容器,其中一个容器是输入日期到index.html中,然后验证访问nginx的html是否可以获取日期。以验证两个容器之间挂载的emptyDir实现共享。如下访问验证:
两个容器之间挂载的emptyDir是可以实现共享的,如下所示:
当 Pod 因为某些原因被从节点上删除时,emptyDir卷中的数据也会被永久删除,如下所示:
说明: 生产环境不建议使用emptyDir存储作为pod的后端存储方式。
hostPath Volume是指Pod挂载宿主机上的目录或文件。 hostPath Volume使得容器可以使用宿主机的文件系统进行存储,hostpath(宿主机路径):节点级别的存储卷,在pod被删除,这个存储卷还是存在的,不会被删除,所以只要同一个pod被调度到同一个节点上来,在pod被删除重新被调度到这个节点之后,对应的数据依然是存在的。
除了必需的path属性之外,用户可以选择性地为hostPath卷指定type。
示例如下:
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: test-container
volumeMounts:
- mountPath: /usr/share/nginx/html
name: test-volume
volumes:
- name: test-volume
hostPath:
path: /data/nginx #宿主机存储目录地址
type: DirectoryOrCreate #如果存储目录不存在,则会重新创建该目录,权限设置为 0755,具有与kubelet相同的组和属主信息。
测试存储卷是可以正常使用的,如下所示:
删除pod数据还是存在,如下所示:
重新调度pod到另外一台主机,无法访问,之前创建的index.html文件不存在
总结: pod删除之后重新创建必须调度到同一个node节点,数据才不会丢失。
nfs卷能将 NFS (网络文件系统) 挂载到你的Pod 中。 不像 emptyDir那样会在删除Pod的同时也会被删除,nfs卷的内容在删除Pod时会被保存,卷只是被卸载。 这意味着nfs卷可以被预先填充数据,并且这些数据可以在Pod之间共享。
说明:建议找一台单独且磁盘容量足够大的服务器作为nfs-server端。
# 1、安装nfs-server端
yum -y install nfs-utils rpcbind
systemctl start nfs && systemctl enable nfs
systemctl start rpcbind && systemctl enable rpcbind
# 3、创建共享目录
mkdir -p /data/volumes/{v1,v2,v3}
# 3、编辑配置文件
cat > /etc/exports << EOF
/data/volumes/v1 192.168.1.0/24(rw,no_root_squash,no_all_squash)
/data/volumes/v2 192.168.1.0/24(rw,no_root_squash,no_all_squash)
/data/volumes/v3 192.168.1.0/24(rw,no_root_squash,no_all_squash)
EOF
# 4、发布
[root@k8s-client volumes]# exportfs -arv
exporting 192.168.1.0/24:/data/volumes/v1
exporting 192.168.1.0/24:/data/volumes/v2
exporting 192.168.1.0/24:/data/volumes/v3
# 5、查看
[root@k8s-client ~]# showmount -e 192.168.1.17
Export list for 192.168.1.17:
/data/volumes/v1 192.168.1.0/24
/data/volumes/v2 192.168.1.0/24
/data/volumes/v3 192.168.1.0/24
说明:当前环境kubernetes集群master节点和worker节点则为nfs客户端。
# 客户端不需要创建共享目录和编辑配置文件,只安装服务就行
yum -y install nfs-utils
说明:具有kubectl权限的主机。
apiVersion: v1
kind: Pod
metadata:
name: pod-vol-nfs
namespace: default
spec:
containers:
- name: myapp
image: nginx
imagePullPolicy: IfNotPresent
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
nfs:
path: /data/volumes/v2 #nfs共享目录
server: 192.168.1.17 #nfs server端ip地址
经测试pod被k8s-worker-16主机上,在共享目录下创建index.html文件,nginx可以正常访问该文件
pod被调度到其它主机上,nginx也是可以正常访问
总结: pod删除之后重新创建,及时被调度到其它主机,数据不会丢失,依然可以访问。
缺点: nfs如果宕机了,数据也就丢失了,所以建议使用分布式存储,常见的分布式存储有glusterfs和cephfs。
说明:建议找一台单独且磁盘容量足够大的服务器作为nfs-server端。
# 1、安装nfs-server端
yum -y install nfs-utils rpcbind
systemctl start nfs && systemctl enable nfs
systemctl start rpcbind && systemctl enable rpcbind
# 3、创建共享目录
mkdir -p /data/volumes_test/{v1,v2,v3,v4}
# 3、编辑配置文件
cat > /etc/exports << EOF
/data/volumes_test/v1 192.168.1.0/24(rw,no_root_squash,no_all_squash)
/data/volumes_test/v2 192.168.1.0/24(rw,no_root_squash,no_all_squash)
/data/volumes_test/v3 192.168.1.0/24(rw,no_root_squash,no_all_squash)
/data/volumes_test/v4 192.168.1.0/24(rw,no_root_squash,no_all_squash)
EOF
# 4、发布
[root@k8s-client volumes]# exportfs -arv
exporting 192.168.1.0/24:/data/volumes_test/v4
exporting 192.168.1.0/24:/data/volumes_test/v3
exporting 192.168.1.0/24:/data/volumes_test/v2
exporting 192.168.1.0/24:/data/volumes_test/v1
# 5、查看
[root@k8s-client ~]# showmount -e 192.168.1.17
Export list for 192.168.1.17:
/data/volumes_test/v4 192.168.1.0/24
/data/volumes_test/v3 192.168.1.0/24
/data/volumes_test/v2 192.168.1.0/24
/data/volumes_test/v1 192.168.1.0/24
说明:当前环境kubernetes集群master节点和worker节点则为nfs客户端。
# 客户端不需要创建共享目录和编辑配置文件,只安装服务就行
yum -y install nfs-utils
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs-1
spec:
capacity: #存储大小
storage: 100Mi
accessModes: ["ReadWriteMany","ReadWriteOnce"] #该存储目录允许访问的模式,ReadWriteMany表示可以被多个节点已读写方式挂载,ReadWriteOnce表示可以被一个节点以读写方式挂载
nfs:
server: 192.168.1.17
path: "/data/volumes_test/v1"
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs-2
spec:
capacity: #存储大小
storage: 200Mi
accessModes: ["ReadWriteMany","ReadWriteOnce"] #该存储目录允许访问的模式,ReadWriteMany表示可以被多个节点已读写方式挂载,ReadWriteOnce表示可以被一个节点以读写方式挂载
nfs:
server: 192.168.1.17
path: "/data/volumes_test/v2"
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs-3
spec:
capacity: #存储大小
storage: 300Mi
accessModes: ["ReadOnlyMany"] #该存储目录允许访问的模式,ReadOnlyMany表示可以被多个节点以只读方式挂载
nfs:
server: 192.168.1.17
path: "/data/volumes_test/v3"
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mypvc
namespace: default
spec:
accessModes: ["ReadWriteMany"]
resources:
requests:
storage: 100Mi
---
apiVersion: v1
kind: Pod
metadata:
name: pod-vol-pvc
namespace: default
spec:
containers:
- name: myapp
image: nginx
imagePullPolicy: IfNotPresent
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
persistentVolumeClaim:
claimName: mypvc
这里定义了pvc的访问模式为多路读写,该访问模式必须在前面pv定义的访问模式之中。定义PVC申请的大小为100Mi,此时PVC会自动去匹配多路读写且大小为100Mi的PV,匹配成功获取PVC的状态即为Bound
说明:建议找一台单独且磁盘容量足够大的服务器作为nfs-server端。
# 1、安装nfs-server端
yum -y install nfs-utils rpcbind
systemctl start nfs && systemctl enable nfs
systemctl start rpcbind && systemctl enable rpcbind
# 3、创建共享目录
mkdir -p /data/volumes/{v1,v2,v3}
# 3、编辑配置文件
cat > /etc/exports << EOF
/data/volumes/v1 192.168.1.0/24(rw,no_root_squash,no_all_squash)
/data/volumes/v2 192.168.1.0/24(rw,no_root_squash,no_all_squash)
/data/volumes/v3 192.168.1.0/24(rw,no_root_squash,no_all_squash)
EOF
# 4、发布
[root@k8s-client volumes]# exportfs -arv
exporting 192.168.1.0/24:/data/volumes/v1
exporting 192.168.1.0/24:/data/volumes/v2
exporting 192.168.1.0/24:/data/volumes/v3
# 5、查看
[root@k8s-client ~]# showmount -e 192.168.1.17
Export list for 192.168.1.17:
/data/volumes/v1 192.168.1.0/24
/data/volumes/v2 192.168.1.0/24
/data/volumes/v3 192.168.1.0/24
说明:当前环境kubernetes集群master节点和worker节点则为nfs客户端。
# 客户端不需要创建共享目录和编辑配置文件,只安装服务就行
yum -y install nfs-utils
说明:具有kubectl权限的主机。
官方测试资源下载
NFS动态存储StorageClass资源清单文件
kubectl create -f rbac.yaml
3、创建nfs-client
kubectl create -f deployment.yaml
4、创建nfs-client kubernetes storage class
kubectl create -f class.yaml
5、设置StorageClass为默认
说明:默认的 StorageClass 将被用于动态的为没有特定 storage class 需求的PersistentVolumeClaims 配置存储:(只能有一个默认StorageClass),如果没有默认StorageClass,PVC 也没有指定storageClassName 的值,那么意味着它只能够跟 storageClassName相同的PV进行绑定。
kubectl patch storageclass managed-nfs-storage -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
[root@k8s-client-17 nfs]# kubectl apply -f test-claim.yaml
问题:创建的pvc一直处于pending状态
解决方案:分别在三个master节点上,修改/etc/kubernetes/manifests/kube-apiserver.yaml 文件,添加添加–feature-gates=RemoveSelfLink=false,当前kubernetes集群是使用kubeadm部署的,修改kube-apiserver.yaml 文件会自动重启apiserver服务,不用手动重启,如下图所示:
如下图所示,则表明pvc创建成功
7、测试pod
[root@k8s-client-17 nfs]# kubectl apply -f test-pod.yaml
如下图所示,则表示pod运行成功
查看共享目录/data/volumes/v1/,下面有SUCCESS文件
更多详细内容请参考:企业级K8s集群运维实战