kubernetes存储类与PV与PVC关系及实践
StorageClass & PV & PVC关系图
- Volumes是最基础的存储抽象,其支持多种类型,包括本地存储、NFS、FC以及众多的云存储,我们也可以编写自己的存储插件来支持特定的存储系统。Volume可以被Pod直接使用,也可以被PV使用。普通的Volume和Pod之间是一种静态的绑定关系,在定义Pod的同时,通过volume属性来定义存储的类型,通过volumeMount来定义容器内的挂载点。
- PersistentVolume。与普通的Volume不同,PV是Kubernetes中的一个资源对象,创建一个PV相当于创建了一个存储资源对象,这个资源的使用要通过PVC来请求。
- PersistentVolumeClaim。PVC是用户对存储资源PV的请求,根据PVC中指定的条件Kubernetes动态的寻找系统中的PV资源并进行绑定。目前PVC与PV匹配可以通过StorageClassName、matchLabels或者matchExpressions三种方式。
- StorageClass。存储类,目前kubernetes支持很多存储,例如ceph,nfs,glusterfs等等。。。
一、Volume
-
Pod中的容器异常退出被重新拉起后,保证之前产生的数据没有丢失
-
Pod中的多个容器共享数据
-
本地存储
-
Projected Volumes:其实是将一些配置信息(如secret/configmap)用卷的形式挂载在容器中,让容器中的程序可以通过POSIX接口来访问配置数据,详见ConfigMap & Secret
-
网络存储
-
Deployment升级时,新Pod难以复用旧Pod的数据;
-
Pod跨主机迁移时,做不到带Volume迁移;
-
只能做到同一个Pod中的多个容器共享数据,做不到多个Pod之间共享数据;
-
很难实现功能扩展
创建Nginx Pod的yaml文件
我们首先创建一个nginx的pod的yaml文件,volumeMounts 定义了pod内的存储目录是根目录的nginx-volume文件夹下,volumes 定义了的挂载类型是hostPath , 目录是 /tmp/volume-pod,我们都是有经验的开发人员,我想这个文件应该很好理解
apiVersion: v1 kind: Pod metadata: name: volume-pod spec: containers: - name: nginx-container image: nginx ports: - containerPort: 80 volumeMounts: - name: volume-pod mountPath: /nginx-volume volumes: - name: volume-pod hostPath: path: /tmp/volume-pod
然后查看详细信息,我们发现它在worker02节点创建了pod。
接下来就是验证:我们要查看worker02节点和pod内目录是否进行了关联
宿主机:
Pod内:
果然,宿主机和Pod内都存在!那么接下来我们验证一下,我在宿主机这个目录建个文件,Pod内会不会出现这个文件。我在宿主机上建了一个 index.html文件,在Pod查看。
果然,同步了!
那接下来,我们删除Pod,重新拉起一个Pod,看会不会还存在这个index.html文件
果然,index.html存在!
二、PV/PVC/SC体系
PersistenVolume(PV):对存储资源创建和使用的抽象,使得存储作为集群中的资源管理
PV分为静态和动态,动态能够自动创建PV
• PersistentVolumeClaim(PVC):让用户不需要关心具体的Volume实现细节
容器与PV、PVC之间的关系,可以如下图所示:
总的来说,PV是提供者,PVC是消费者,消费的过程就是绑定
PersistentVolume 静态绑定
NFS
k8s支持的 PV 类型有很多,常见的有 Ceph、GlusterFs、nfs以及 hostPath,不过 hostPath 仅仅可用于单机测试。方便起见,我们以NFS存储资源进行演示。
接下来我们在节点192.168.248.139上面安装nfs服务,共享的数据目录为/data/nfs/
1、安装配置nfs
yum install nfs-utils rpcbind -y
mkdir -p /data/nfs && chmod 755 /data/nfs/ cat /etc/exports /data/nfs *(rw,sync,no_root_squash) systemctl start rpcbind nfs && systemctl enable rpcbind nfs
在k8s上配置使用pv与pvc之前,我们需要在所有的node节点上安装nfs客户端,我所使用的环境节点如下:
[root@k8s-master-01 ~]# kubectl get nodes -o wide NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME k8s-node-01 Ready <none> 90d v1.16.0 192.168.248.134 <none> CentOS Linux 7 (Core) 3.10.0-957.el7.x86_64 docker://18.9.6 k8s-node-02 Ready <none> 90d v1.16.0 192.168.248.135 <none> CentOS Linux 7 (Core) 3.10.0-957.el7.x86_64 docker://18.9.6
必须在所有节点都安装 nfs 客户端,否则可能会导致 PV 挂载不上的问题。
创建pv
部署完nfs存储,接下来我们就可以通过编辑yaml文件,来创建pv和pvc资源了。下面我们来创建一个 PV 资源对象,使用 nfs 类型的后端存储,2G 的存储空间,访问模式为 ReadWriteOnce,回收策略为 Recyle。
vim pv.yaml
apiVersion: v1 kind: PersistentVolume metadata: name: pv-01 labels: name: pv-01 spec: nfs: path: /data/nfs server: 192.168.248.139 accessModes: ["ReadWriteOnce"] persistentVolumeReclaimPolicy: Retain capacity: storage: 2Gi
-------------------------------------------------------------------------
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key:xxx
operator:In
values:
- xxx
csi:
driver: iscsi.csi.tds.com
volumeHandle: csi-iscsi-pvc-f0d4d3dc-e9bd-40dd-891d-c8c61d847a6a
fsType: xfs
volumeAttributes:
accessPaths: testk8
fsType: xfs
pool: poolk8s
storage.kubernetes.io/csiProvisionerIdentity: 1582596319059-8081-iscsi.csi.tds.com
xmsServers: 132.252.43.115,132.252.43.116,132.252.43.117
对以上yaml文件中一些参数做以下解释
nfs:表示使用的后端存储为nfs
path:表示后端存储共享的数据目录
spec.capacity.storage字段表明创建的存储的大小
accessMode:字段是PV访问策略控制列表,表明创建出来的存储的访问方式
-
ReadWriteOnce(RWO):读写权限,只能被单个节点挂载
-
ReadOnlyMany(ROX):只读权限,可以被多个节点挂载
-
ReadWriteMany(RWX):读写权限,可以被多个节点挂载
persistentVolumeReclaimPolicy:表示pv的回收策略,默认为Retain,目前支持的有三种:
-
Retain(保留)
-
Recycle(回收)
-
Delete(删除)
spec.nodeAffinity字段限制了可以访问该volumes的node,对使用该Volume的Pod调度有影响
spec.csi表明此处使用的是CSI存储:
driver:指定由什么volume plugin来挂载该volume(需要提前在node上部署)
volumeHandle:指示PV的唯一标签
volumeAttribute:用于附加参数,比如PV定义的是OSS,就可以在这里定义bucket、访问的地址等信息
spec.flexVolume表明此处使用的是flexVolume存储:
- driver:实现的驱动类型
-
fsType:文件系统类型
-
options:包含的具体的参数
然后使用kubectl命令创建即可
[root@k8s-master-01 pv]# kubectl apply -f pv1.yaml
persistentvolume/pv-01 created [root@k8s-master-01 pv]# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE mysql-pv-volume 20Gi RWO Retain Bound default/mysql-pv-claim manual 2d2h pv-01 2Gi RWO Recycle Available
如上,pv-01已经创建成功,状态为Available,表示pv-01已经准备就绪,可以被pvc申请使用。
pv的生命周期中可能会处于4种不同的阶段:
-
Available(可用)
-
Bound(已绑定)
-
Released(已释放)
-
Failed(失败)
创建pvc
对应的yaml文件内容如下:
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc-nfs spec: accessModes: ["ReadWriteOnce"] resources: requests: storage: 2Gi
spec.resources.requests.storage字段声明存储的大小需求
然后使用kubectl命令创建即可
[root@k8s-master-01 pv]# kubectl apply -f pvc-nfs.yaml persistentvolumeclaim/pvc-nfs created [root@k8s-master-01 pv]# kubectl get pv,pvc NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE persistentvolume/mysql-pv-volume 20Gi RWO Retain Bound default/mysql-pv-claim manual 2d3h persistentvolume/pv-01 2Gi RWO Retain Bound default/pvc-nfs 19s NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE persistentvolumeclaim/mysql-pv-claim Bound mysql-pv-volume 20Gi RWO manual 2d3h persistentvolumeclaim/pvc-nfs Bound pv-01 2Gi RWO
可以看到 pvc-nfs已经创建成功,并且处于绑定状态,pv也处于绑定状态。
使用pvc
上面我们已经完成了pv和pvc的创建及绑定,接下来我们创建一个deployment,来使用上面的pvc
vim deployment-pvc.yaml apiVersion: v1 kind: Service metadata: name: svc-nginx-demo spec: ports: - port: 80 protocol: TCP targetPort: 80 nodePort: 31080 selector: app: liheng type: NodePort --- apiVersion: apps/v1 kind: Deployment metadata: name: deployment-pvc-demo labels: app: deployment-pvc-demo annotations: liheng86876/created-by: "LIHENG" spec: replicas: 3 selector: matchLabels: app: liheng template: metadata: labels: app: liheng spec: containers: - name: web-test image: nginx imagePullPolicy: IfNotPresent ports: - name: http containerPort: 80 volumeMounts: - name: html mountPath: /usr/share/nginx/html/ volumes: - name: html persistentVolumeClaim: claimName: pvc-nfs
然后使用kubectl命令创建即可
[root@k8s-master-01 pv]# kubectl get deploy,pod,svc NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/deployment-pvc-demo 3/3 3 3 2m6s NAME READY STATUS RESTARTS AGE pod/deployment-pvc-demo-77859488fc-bmlhd 1/1 Running 0 2m5s pod/deployment-pvc-demo-77859488fc-c8xkn 1/1 Running 0 2m5s pod/deployment-pvc-demo-77859488fc-pz6g9 1/1 Running 0 2m5s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 90d service/svc-nginx-demo NodePort 10.0.0.194 <none> 80:31080/TCP 2m6s
可以看到pod已经处于running状态,我们可以通过如下命令查看deployment和svc的详细信息
[root@k8s-master-01 pv]# kubectl describe deploy deployment-pvc-demo Name: deployment-pvc-demo Namespace: default CreationTimestamp: Mon, 17 Feb 2020 18:04:26 +0800 Labels: app=deployment-pvc-demo Annotations: deployment.kubernetes.io/revision: 1 kubectl.kubernetes.io/last-applied-configuration: {"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{"liheng86876/created-by":"LIHENG"},"labels":{"app":"deployment-pvc-... liheng86876/created-by: LIHENG Selector: app=liheng Replicas: 3 desired | 3 updated | 3 total | 3 available | 0 unavailable StrategyType: RollingUpdate MinReadySeconds: 0 RollingUpdateStrategy: 25% max unavailable, 25% max surge Pod Template: Labels: app=liheng Containers: web-test: Image: nginx Port: 80/TCP Host Port: 0/TCP Environment: <none> Mounts: /usr/share/nginx/html/ from html (rw) Volumes: html: Type: PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace) ClaimName: pvc-nfs ReadOnly: false Conditions: Type Status Reason ---- ------ ------ Available True MinimumReplicasAvailable Progressing True NewReplicaSetAvailable OldReplicaSets: <none> NewReplicaSet: deployment-pvc-demo-77859488fc (3/3 replicas created) Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal ScalingReplicaSet 4m48s deployment-controller Scaled up replica set deployment-pvc-demo-77859488fc to 3
[root@k8s-master-01 pv]# kubectl describe svc svc-nginx-demo Name: svc-nginx-demo Namespace: default Labels: <none> Annotations: kubectl.kubernetes.io/last-applied-configuration: {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"svc-nginx-demo","namespace":"default"},"spec":{"ports":[{"nodePor... Selector: app=liheng Type: NodePort IP: 10.0.0.194 Port: <unset> 80/TCP TargetPort: 80/TCP NodePort: <unset> 31080/TCP Endpoints: 10.244.0.134:80,10.244.0.135:80,10.244.1.129:80 Session Affinity: None External Traffic Policy: Cluster Events: <none>
访问测试
我们就可以通过任意节点的 IP:31080 端口来访问我们这里的Nginx服务
为什么会出现403呢???那是应为我们的nfs共享目录没有文件导致
[root@localhost nfs]# pwd /data/nfs [root@localhost nfs]# ls [root@localhost nfs]# 我们创建一个index.html,然后访问测试 [root@localhost nfs]# echo "<h1>Welcome k8s</h1>" > index.html
然后在刷新页面:
我们可以看到已经可以正常访问到页面。
数据持久化测试
测试方法:
1、我们将创建的nginx应用删除掉,然后重新创建nginx应用,然后访问测试
结果:后端nfs存储里面的数据不会丢失,页面访问正常
2、我们将创建的nginx应用和pv及pvc全部删除,然后重新创建,然后访问测试
结果:后端nfs存储里面的数据不会丢失,页面访问正常
我们也可以在创建pv是将persistentVolumeReclaimPolicy参数分别设置为Recycle和Delete,然后做以上测试,这里就不一一进行测试了,有兴趣的同学可以深入研究一下。
PersistentVolume动态绑定
当我们k8s业务上来的时候,大量的pvc,此时我们人工创建匹配的话,工作量就会非常大了,需要动态的自动挂载相应的存储,‘
我们需要使用到StorageClass,来对接存储,靠他来自动关联pvc,并创建pv。
Kubernetes支持动态供给的存储插件:
https://kubernetes.io/docs/concepts/storage/storage-classes/
因为NFS不支持动态存储,所以我们需要借用这个存储插件。
nfs动态相关部署可以参考:
https://github.com/kubernetes-incubator/external-storage/tree/master/nfs-client/deploy
部署步骤:
集群管理员不预分配PV,而是预先准备StoregeClass(创建PV的模板),它包含了创建某种具体类型(块存储、文件存储等)的PV所需要的参数信息。
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: standard provisioner: kubernetes.io/aws-ebs reclaimPolicy: Retain allowVolumeExpansion: true mountOptions: - debug volumeBindingMode: WaitForFirstConsumer allowedTopologies: - matchLabelExpressions: - key: failure-domain.beta.kubernetes.io/zone values: - us-central1-a - us-central1-b parameters: type: gp2
-
provisioner
-
reclaimPolicy
-
allowVolumeExpansion
-
mountOptions
-
volumeBindingMode
-
allowedTopologies
-
parameters
1、定义一个storage
[root@master storage]# cat storageclass-nfs.yaml apiVersion: storage.k8s.io/v1beta1 kind: StorageClass metadata: name: managed-nfs-storage provisioner: fuseim.pri/ifs
2、部署授权
因为storage自动创建pv需要经过kube-apiserver,所以要进行授权
[root@master storage]# cat rbac.yaml apiVersion: v1 kind: ServiceAccount metadata: name: nfs-client-provisioner --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1beta1 metadata: name: nfs-client-provisioner-runner rules: - apiGroups: [""] resources: ["persistentvolumes"] verbs: ["get", "list", "watch", "create", "delete"] - apiGroups: [""] resources: ["persistentvolumeclaims"] verbs: ["get", "list", "watch", "update"] - apiGroups: ["storage.k8s.io"] resources: ["storageclasses"] verbs: ["get", "list", "watch"] - apiGroups: [""] resources: ["events"] verbs: ["list", "watch", "create", "update", "patch"] --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1beta1 metadata: name: run-nfs-client-provisioner subjects: - kind: ServiceAccount name: nfs-client-provisioner namespace: default roleRef: kind: ClusterRole name: nfs-client-provisioner-runner apiGroup: rbac.authorization.k8s.io
3、部署一个自动创建pv的服务
这里自动创建pv的服务由nfs-client-provisioner 完成,
[root@master storage]# cat deployment-nfs.yaml kind: Deployment apiVersion: extensions/v1beta1 metadata: name: nfs-client-provisioner spec: replicas: 1 strategy: type: Recreate template: metadata: labels: app: nfs-client-provisioner spec: # imagePullSecrets: # - name: registry-pull-secret serviceAccount: nfs-client-provisioner containers: - name: nfs-client-provisioner #image: quay.io/external_storage/nfs-client-provisioner:latest image: lizhenliang/nfs-client-provisioner:v2.0.0 volumeMounts: - name: nfs-client-root mountPath: /persistentvolumes env: - name: PROVISIONER_NAME #这个值是定义storage里面的那个值 value: fuseim.pri/ifs - name: NFS_SERVER value: 192.168.1.39 - name: NFS_PATH value: /opt/container_data volumes: - name: nfs-client-root nfs: server: 192.168.1.39 path: /opt/container_data
创建:
kubectl apply -f storageclass-nfs.yaml kubectl apply -f rbac.yaml kubectl apply -f deployment-nfs.yaml
查看创建好的storage:
[root@master storage]# kubectl get sc
NAME PROVISIONER AGE
managed-nfs-storage fuseim.pri/ifs 11h
nfs-client-provisioner 会以pod运行在k8s中,
[root@master storage]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nfs-client-provisioner-855887f688-hrdwj 1/1 Running 0 10h
4、部署有状态服务,测试自动创建pv
部署yaml文件参考:https://kubernetes.io/docs/tutorials/stateful-application/basic-stateful-set/
我们部署一个nginx服务,让其html下面自动挂载数据卷,
[root@master ~]# cat nginx.yaml apiVersion: v1 kind: Service metadata: name: nginx labels: app: nginx spec: ports: - port: 80 name: web clusterIP: None selector: app: nginx --- apiVersion: apps/v1 kind: StatefulSet metadata: name: web spec: serviceName: "nginx" replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx ports: - containerPort: 80 name: web volumeMounts: - name: www mountPath: /usr/share/nginx/html volumeClaimTemplates: - metadata: name: www spec: accessModes: [ "ReadWriteOnce" ] storageClassName: "managed-nfs-storage" resources: requests: storage: 1Gi
创建:
kubectl apply -f nginx.yaml
进入其中一个容器,创建一个文件:
kubectl exec -it web-0 sh # cd /usr/share/nginx/html # touch 1.txt
进入nfs数据目录:
此处可见到,nfs下面自动创建了三个有标识的数据卷文件夹。
现在我们可以将web-0这个pod删掉,测试数据卷里面的文件会不会消失。
[root@master ~]# kubectl delete pod web-0
pod "web-0" deleted
查看web-0数据卷,是否有刚刚创建的1.txt
[root@master container_data]# ls default-www-web-0-pvc-2b7c8ce1-13b6-11e9-b1a2-0262b716c880/
1.txt
经过测试我们可以得到,删掉这个pod以后,又会迅速再拉起一个web-0,并且数据不会丢失,这样我们也就达到了动态的数据持久化。
三、存储快照
apiVersion: snapshot.storage.k8s.io/v1a1pha1 kind: VolumeSnapshotClass metadata: name: disk-snapshotclass snapshotter: diskplug.csi.alibabacloud.com
apiVersion: snapshot.storage.k8s.io/v1a1pha1 kind: VolumeSnapshot metadata: name:disk-snapshot namespace: xxx spec: snapshotClassName: disk-snapshotclass source: name: disk-pvc kind: PersistentVolumeClaim
参考:https://www.cnblogs.com/yangyuliufeng/p/14360558.html
https://www.cnblogs.com/yangyuliufeng/p/14301359.html
https://blog.csdn.net/cuixhao110/article/details/105858553
https://blog.csdn.net/qq_33591903/article/details/103783627
https://www.cnblogs.com/yangyuliufeng/p/14301359.html
https://www.cnblogs.com/cocowool/p/kubernetes_storage.html