k8s挂载目录_k8s十一 | 持久化存储 PV&PVC&StorageClass
在kubernetes中Pod的生命周期是很短暂的,会被频繁地销毁和创建。容器销毁时,保存在容器内部文件系统中的数据都会被清除。为了持久化保存容器的数据,需要使用 Kubernetes Volume数据卷,下面文章分别以本地卷,网络数据卷的方式来实践集群的持久化存储。. 一、hostPath/emptyDirhostPath:用于将目录从宿主机节点的文件系统挂载到pod中,属于单节点...
在kubernetes中Pod的生命周期是很短暂的,会被频繁地销毁和创建。容器销毁时,保存在容器内部文件系统中的数据都会被清除。为了持久化保存容器的数据,需要使用 Kubernetes Volume数据卷,下面文章分别以本地卷,网络数据卷的方式来实践集群的持久化存储。
. 一、hostPath/emptyDir
hostPath:用于将目录从宿主机节点的文件系统挂载到pod中,属于单节点集群中的持久化存储,删除pod后,卷里面的文件会继续保持,在同一节点运行的Pod可以继续使用数据卷中的文件,但pod被重新调度到其他节点时,就无法访问到原数据。不适合作为存储数据库数据的目录。
emptyDir:用于存储临时数据的简单空目录,生命周期是和pod捆绑的,随着pod创建而创建;删除而销毁,卷的内容将会丢失。emptyDir卷适用于同一个pod中运行的容器之间共享文件。
hostPath定义如下:
1....
2 volumeMounts:
3 - name: data
4 mountPath: /data
5 volumes:
6 - name: data
7 hostPath:
8 path: /opt/data
9 type: Directory
emptyDir定义如下:
1....
2 volumeMounts:
3 - name: data
4 mountPath: /data
5 volumes:
6 - name: data
7 emptyDir: {}
. 二、PV/PVC
前面使用的 hostPath 和 emptyDir 类型的 Volume 并不具备持久化特征,它们既有可能被 kubelet 清理掉,也不能被“迁移”到其他节点上。所以,大多数情况下,持久化 Volume 的实现,往往依赖于一个远程存储服务,比如:远程文件存储(比如,NFS、GlusterFS)、远程块存储(比如,公有云提供的远程磁盘)等等。而 Kubernetes 需要做的工作,就是使用这些存储服务,来为容器准备一个持久化的宿主机目录,以供将来进行绑定挂载时使用。而所谓“持久化”,指的是容器在这个目录里写入的文件,都会保存在远程存储中,从而使得这个目录具备了“持久性”。为了屏蔽底层的技术实现细节,让用户更加方便的使用,Kubernetes 便引入了 PV 和 PVC 两个重要的资源对象来实现对存储的管理。
PV:持久化存储数据卷,全称为PersistentVolume,PV其实是对底层存储的一种抽象,通常是由集群的管理员进行创建和配置 ,底层存储可以是Ceph,GlusterFS,NFS,hostpath等,都是通过插件机制完成与共享存储的对接。
PVC:持久化数据卷声明,全称为PersistentVolumeClaim,PVC 对象通常由开发人员创建,描述 Pod 所希望使用的持久化存储的属性。比如,Volume 存储的大小、可读写权限等等。PVC绑定PV,消耗的PV资源。
下面创建一个NFS类型的PV,首先节点部署NFS,共享/data/k8s目录
1$ systemctl stop firewalld.service
2$ yum -y install nfs-utils rpcbind
3$ mkdir -p /data/k8s
4$ chmod 755 /data/k8s
5$ vim /etc/exports
6/data/k8s *(rw,sync,no_root_squash)
7$ systemctl start rpcbind.service
8$ systemctl start nfs.service
9$ mount 192.168.16.173:/data/k8s /data/k8s
创建nfspv.yaml
1apiVersion: v1
2kind: PersistentVolume
3metadata:
4 name: nfs
5spec:
6 storageClassName: manual
7 capacity:
8 storage: 5Gi
9 accessModes:
10 - ReadWriteMany
11 nfs:
12 server: 192.168.16.173
13 path: "/data/k8s"
文件的内容定义如下:
spec.storageClassName:定义了名称为 manual 的 StorageClass,该名称用来将 PVC请求绑定到该 PV。
spec.Capacity:定义了当前PV的存储空间为storage=10G。
spec.nfs:定义了PV的类型为NFS,并指定了该卷位于节点上的 /data/k8s/目录。
spec.accessModes:定义了当前的访问模式,可定义的模式如下:
ReadWriteMany(RWX):读写权限,可以被多个节点挂载。
ReadWriteOnce(RWO):读写权限,但是只能被单个节点挂载;
ReadWriteMany(ROX):只读权限,可以被多个节点挂载。
下图是一些常用的 Volume 插件支持的访问模式:
创建资源对象:
1$ kubectl create -f nfspv.yaml
2persistentvolume/nfs created
3$ kubectl get pv
4NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
5nfs 5Gi RWX Retain Available manual 4s
可以看到创建后的PV的Status为Available 可用状态,意味着当前PV还没有被PVC绑定使用。
PV生命周期的不同状态如下:
Available(可用):表示可用状态,还未被任何 PVC 绑定
Bound(已绑定):表示 PVC 已经被 PVC 绑定
Released(已释放):PVC 被删除,但是资源还未被集群重新声明
Failed(失败):表示该 PV 的自动回收失败
另外还有一个RECLAIM POLICY字段输出内容为Retain,这个字段输出的其实是PV的回收策略,目前 PV 支持的策略有三种:
Retain(保留):保留数据,需要管理员手工清理数据
Recycle(回收):清除 PV 中的数据,效果相当于执行 rm -rf /thevoluem/*
Delete(删除):与 PV 相连的后端存储完成 volume 的删除操作,当然这常见于云服务商的存储服务,比如 ASW EBS。
注意:目前只有 NFS 和 HostPath 两种类型支持回收策略。
现在我们创建一个PVC来绑定上面的PV
1apiVersion: v1
2kind: PersistentVolumeClaim
3metadata:
4 name: nfs-pvc
5spec:
6 storageClassName: manual
7 accessModes:
8 - ReadWriteMany
9 resources:
10 requests:
11 storage: 3Gi
创建PVC
1$ kubectl get pvc
2 NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
3 nfs-pvc Bound nfs 5Gi RWX manual 3m59s
4$ kubectl get pv
5 NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
6 nfs 5Gi RWX Retain Bound default/nfs-pvc manual 20m
可以看到PV和PVC的状态已经为Bound绑定状态,其中绑定需要检查的条件,包括两部分:
第一个条件,当然是 PV 和 PVC 的 spec 字段。比如,PV 的存储(storage)大小,权限等就必须满足 PVC 的要求。
而第二个条件,则是 PV 和 PVC 的 storageClassName 字段必须一样。
创建一个Pod,PVC使用方式和hostpath类似
1apiVersion: v1
2kind: Pod
3metadata:
4 name: web-front
5spec:
6 containers:
7 - name: web
8 image: nginx
9 ports:
10 - name: web
11 containerPort: 80
12 volumeMounts:
13 - name: nfs
14 mountPath: "/usr/share/nginx/html"
15 volumes:
16 - name: nfs
17 persistentVolumeClaim:
18 claimName: nfs-pvc
Pod将PVC挂载到容器的html目录,我们在PVC的目录下创建一个文件用来验证
1$ echo "hello nginx" > /data/k8s/index.html
创建Pod,并验证是否将文件挂载至容器
1$ kubectl create -f nfs-nginxpod.yaml
2$ kubectl exec -it web-front -- /bin/bash
3 root@web-front:/# curl localhost
4 hello nginx
可以看到输出结果是我们前面写到PVC卷中的index.html 文件内容。
. 三、StorageClass
在上面PV对象创建的方式为Static Provisioning(静态资源调配),在大规模的生产环境里,面对集群中大量的PVC,需要提前手动创建好PV来与之绑定,这其实是一个非常麻烦的工作。还好kubernetes提供了Dynamic Provisioning(动态资源调配)的机制,即:StorageClass对象, 它的作用其实就是创建 PV 的模板。
StorageClass 对象会定义如下两个部分内容:
PV 的属性。比如,存储类型、Volume 的大小等等。
创建这种 PV 需要用到的存储插件。比如,Ceph 等等。
Kubernetes 有了这样两个信息之后,就能够根据用户提交的 PVC,找到一个对应的StorageClass,然后Kubernetes 就会调用该 StorageClass 声明的存储插件,自动创建出需要的 PV。
现在我们创建一个NFS类型的StorageClass,首先需要创建nfs-client-provisioner(存储插件):nfs-client 的自动配置程序
1apiVersion: apps/v1
2kind: Deployment
3metadata:
4 name: nfs-client-provisioner
5 labels:
6 app: nfs-client-provisioner
7spec:
8 replicas: 1
9 strategy:
10 type: Recreate
11 selector:
12 matchLabels:
13 app: nfs-client-provisioner
14 template:
15 metadata:
16 labels:
17 app: nfs-client-provisioner
18 spec:
19 serviceAccountName: nfs-client-provisioner
20 containers:
21 - name: nfs-client-provisioner
22 image: quay.io/external_storage/nfs-client-provisioner:latest
23 volumeMounts:
24 - name: nfs-client-root
25 mountPath: /persistentvolumes
26 env:
27 - name: PROVISIONER_NAME
28 value: fuseim.pri/ifs
29 - name: NFS_SERVER
30 value: 192.168.16.173 # 修改成自己的 IP
31 - name: NFS_PATH
32 value: /data/k8s
33 volumes:
34 - name: nfs-client-root
35 nfs:
36 server: 192.168.16.173 # 修改成自己的 IP
37 path: /data/k8s
为nfs-client程序绑定相应的集群操作权限 :
1apiVersion: v1
2kind: ServiceAccount
3metadata:
4 name: nfs-client-provisioner
5
6---
7kind: ClusterRole
8apiVersion: rbac.authorization.k8s.io/v1
9metadata:
10 name: nfs-client-provisioner-runner
11rules:
12 - apiGroups: [""]
13 resources: ["persistentvolumes"]
14 verbs: ["get", "list", "watch", "create", "delete"]
15 - apiGroups: [""]
16 resources: ["persistentvolumeclaims"]
17 verbs: ["get", "list", "watch", "update"]
18 - apiGroups: ["storage.k8s.io"]
19 resources: ["storageclasses"]
20 verbs: ["get", "list", "watch"]
21 - apiGroups: [""]
22 resources: ["events"]
23 verbs: ["list", "watch", "create", "update", "patch"]
24 - apiGroups: [""]
25 resources: ["endpoints"]
26 verbs: ["create", "delete", "get", "list", "watch", "patch", "update"]
27
28---
29kind: ClusterRoleBinding
30apiVersion: rbac.authorization.k8s.io/v1
31metadata:
32 name: run-nfs-client-provisioner
33subjects:
34 - kind: ServiceAccount
35 name: nfs-client-provisioner
36 namespace: default
37roleRef:
38 kind: ClusterRole
39 name: nfs-client-provisioner-runner
40 apiGroup: rbac.authorization.k8s.io
创建StorageClass
1apiVersion: storage.k8s.io/v1
2kind: StorageClass
3metadata:
4 name: es-data-db
5provisioner: fuseim.pri/ifs
provisioner 字段的值是:fuseim.pri/ifs,这个是NFS提供的分配器,kubernetes也内置了一些存储的分配器:https://kubernetes.io/zh/docs/concepts/storage/storage-classes/
创建资源对象
1$ kubectl create -f nfs-client-Provisioner.yaml
2$ kubectl create -f nfs-client-sa.yaml
3$ kubectl create -f nfs-storageclass.yaml
4$ kubectl get storageclass
5NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
6nfs-data-db fuseim.pri/ifs Delete Immediate false 136m
StorageClass创建完成后,开发人员只需要在 PVC 里指定要使用的 StorageClass 名字即可,PV则会根据PVC的属性定义自动创建。
创建nfs-pvc02.yaml
1apiVersion: v1
2kind: PersistentVolumeClaim
3metadata:
4 name: nfs-pvc02
5spec:
6 accessModes:
7 - ReadWriteOnce
8 storageClassName: nfs-data-db
9 resources:
10 requests:
11 storage: 1Gi
创建PVC并查看是否绑定相应的PV
1$ kubectl create -f nfs-pvc02.yaml
2$ kubectl get pvc
3 NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
4 nfs-pvc Bound nfs 5Gi RWX manual 7h49m
5 nfs-pvc02 Bound pvc-9df576f0-b2d4-41cf-9d92-27958f68e7e0 1Gi RWO nfs-data-db 7s
6$ kubectl get pv
7 NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
8 nfs 5Gi RWX Retain Bound default/nfs-pvc manual 8h
9 pvc-9df576f0-b2d4-41cf-9d92-27958f68e7e0 1Gi RWO Delete Bound default/nfs-pvc02 nfs-data-db 17s
10$ ls /data/k8s
11 default-nfs-pvc02-pvc-9df576f0-b2d4-41cf-9d92-27958f68e7e0 index.html
可以看到创建完PVC后,StorageClass自动为PVC创建并绑定了对应的PV,而且PV的属性是和PVC相同的,在共享卷中也创建了相关的PV目录,这样我们创建Pod时只需要指定PVC的名字即可,不用再去手动的为PVC创建PV。
注意:Kubernetes 只会将StorageClass 定义相同的 PVC 和 PV 绑定起来
总结:
hostPath:属于单节点集群中的持久化存储,Pod需要绑定集群节点。删除pod后,卷里面的文件会继续保持,但pod被重新调度到其他节点时,就无法访问到原数据。不适合作为存储数据库数据的目录。
emptyDir:用于存储临时数据的简单空目录,生命周期是和pod捆绑的,随着pod创建而创建;删除而销毁,卷的内容将会丢失。emptyDir卷适用于同一个pod中运行的容器之间共享文件。
PVC 描述的,是 Pod 想要使用的持久化存储的属性,比如存储的大小、读写权限等。
PV 描述的,则是一个具体的 Volume 的属性,比如 Volume 的类型、挂载目录、远程存储服务器地址等。
StorageClass 的作用,则是充当 PV 的模板。并且,只有同属于一个 StorageClass 的 PV 和 PVC,才可以绑定在一起。当然,StorageClass 的另一个重要作用,是指定 PV 的 Provisioner(存储插件)。这时候,如果你的存储插件支持 Dynamic Provisioning 的话,Kubernetes 就可以自动为你创建 PV 了。
参考资料:
深入剖析Kubernetes-张磊
关注公众号回复【k8s】获取视频教程及更多资料:
更多推荐
所有评论(0)