在k8s中为什么要做持久化存储?
在k8s中部署的应用都是以pod容器的形式运行的,假如我们部署MySQL、Redis等数据库,需要对这些数据库产生的数据做备份。因为Pod是有生命周期的,如果pod不挂载数据卷,那pod被删除或重启后这些数据会随之消失,如果想要长久的保留这些数据就要用到pod数据持久化存储。
1、持久化存储(emptyDir) 1、查看k8s支持哪些存储 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 [root@master1 ~] KIND: Pod VERSION: v1 RESOURCE: volumes <[]Object> DESCRIPTION: List of volumes that can be mounted by containers belonging to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes Volume represents a named volume in a pod that may be accessed by any container in the pod. FIELDS: awsElasticBlockStore <Object> azureDisk <Object> azureFile <Object> cephfs <Object> cinder <Object> configMap <Object> csi <Object> downwardAPI <Object> emptyDir <Object> ephemeral <Object> fc <Object> flexVolume <Object> flocker <Object> gcePersistentDisk <Object> gitRepo <Object> glusterfs <Object> hostPath <Object> iscsi <Object> name <string> -required- nfs <Object> persistentVolumeClaim <Object> photonPersistentDisk <Object> portworxVolume <Object> projected <Object> quobyte <Object> rbd <Object> scaleIO <Object> secret <Object> storageos <Object> vsphereVolume <Object>
2、常用的存储 1 2 3 4 5 6 7 8 emptyDir hostPath nfs persistentVolumeClaim glusterfs cephfs configMap secret
3、定义和使用存储卷的步骤 我们想要使用存储卷,需要经历如下步骤
1、定义pod的volume,这个volume指明它要关联到哪个存储上的
2、在容器中要使用volumemounts挂载对应的存储
经过以上两步才能正确的使用存储卷
4、emptyDir介绍 emptyDir类型的Volume是在Pod分配到Node上时被创建,Kubernetes会在Node上自动分配一个目录,因此无需指定宿主机Node上对应的目录文件。 这个目录的初始内容为空,当Pod从Node上移除时,emptyDir中的数据会被永久删除。emptyDir Volume主要用于某些应用程序无需永久保存的临时目录,多个容器的共享目录等。
5、创建emptyDir.yml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 [root@master1 volume] apiVersion: v1 kind: Pod metadata: name: emptydir spec: containers: - name: emptydir image: nginx volumeMounts: - mountPath: /cache name: cache-volume volumes: - emptyDir: {} name: cache-volume
6、查看pod的uid 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 [root@master1 volume] uid: f6e86389-07a5-4347-953f-e749a3dbfa44 [root@node2 ~] /var/lib/kubelet/pods/f6e86389-07a5-4347-953f-e749a3dbfa44 ├── containers │ └── emptydir │ └── afa6d914 ├── etc-hosts ├── plugins │ └── kubernetes.io~empty-dir │ ├── cache-volume │ │ └── ready │ └── wrapped_default-token-qd799 │ └── ready └── volumes ├── kubernetes.io~empty-dir │ └── cache-volume └── kubernetes.io~secret └── default-token-qd799 ├── ca.crt -> ..data/ca.crt ├── namespace -> ..data/namespace └── token -> ..data/token
7、测试emptdir挂载容器卷 1 2 3 4 5 6 7 8 [root@master1 volume] root@emptydir:/ root@emptydir:/cache [root@node2 cache-volume] /var/lib/kubelet/pods/f6e86389-07a5-4347-953f-e749a3dbfa44/volumes/kubernetes.io~empty-dir/cache-volume [root@node2 cache-volume] 100
2、持久化存储(hostPath) hostPath Volume是指Pod挂载宿主机上的目录或文件。 hostPath Volume使得容器可以使用宿主机的文件系统进行存储,hostpath(宿主机路径):节点级别的存储卷,在pod被删除,这个存储卷还是存在的,不会被删除,所以只要同一个pod被调度到同一个节点上来,在pod被删除重新被调度到这个节点之后,对应的数据依然是存在的。
1、查看hostPath的用法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 [root@master1 volume] KIND: Pod VERSION: v1 RESOURCE: hostPath <Object> DESCRIPTION: HostPath represents a pre-existing file or directory on the host machine that is directly exposed to the container. This is generally used for system agents or other privileged things that are allowed to see the host machine. Most containers will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes Represents a host path mapped into a pod. Host path volumes do not support ownership management or SELinux relabeling. FIELDS: path <string> -required- type <string>
2、创建pod配置文件,挂载hostPath存储卷 DirectoryOrCreate表示本地有/data1目录,就用本地的,本地没有就会在pod调度到的节点自动创建一个
两个容器是共享这个/data1的存储卷的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 [root@master1 volume] apiVersion: v1 kind: Pod metadata: name: hostpath spec: containers: - name: hostpath image: nginx volumeMounts: - mountPath: /nginx name: test-volume - name: test-tomcat image: tomcat:8.5-jre8-alpine volumeMounts: - mountPath: /test-tomcat name: test-volume volumes: - hostPath: path: /data1 type : DirectoryOrCreate name: test-volume
3、删除容器,检查/data1目录下数据是否还存在 1 2 3 4 5 6 [root@master1 volume] [root@node2 data1] /data1 [root@node2 data1] 50
3、持久化存储(nfs) 1、搭建nfs服务 1 2 3 4 5 6 7 8 9 10 11 12 13 14 [root@master1 ~] [root@master1 data] /root/volume/data 192.168.106.0/24(rw,no_root_squash) [root@master1 data] exporting 192.168.106.0/24:/root/volume/data [root@master1 data]
2、在node节点上挂载nfs目录 1 2 3 [root@node1 ~] [root@node2 ~] [root@node3 ~]
3、创建pod配置文件,挂载nfs存储 nfs支持多个客户端挂载,可以创建多个pod,挂载同一个nfs服务器共享出来的目录;但是nfs如果宕机了,数据也就丢失了,所以需要使用分布式存储,常见的分布式存储有glusterfs和cephfs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 [root@master1 volume] apiVersion: v1 kind: Pod metadata: name: nfs-volume spec: containers: - name: nfs image: nginx ports: - containerPort: 80 protocol: TCP volumeMounts: - name: nfs mountPath: /usr/share/nginx/html volumes: - name: nfs nfs: path: /root/volume/data server: 192.168.106.11 [root@master1 volume]
4、持久化存储(PVC) 参考官网:
https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes
1、PVC简介 PersistentVolume(PV)是群集中的一块存储,由管理员配置或使用存储类动态配置。 它是集群中的资源,就像pod是k8s集群资源一样。 PV是容量插件,如Volumes,其生命周期独立于使用PV的任何单个pod。
PersistentVolumeClaim(PVC)是一个持久化存储卷,我们在创建pod时可以定义这个类型的存储卷。 它类似于一个pod。 Pod消耗节点资源,PVC消耗PV资源。 Pod可以请求特定级别的资源(CPU和内存)。 pvc在申请pv的时候也可以请求特定的大小和访问模式(例如,可以一次读写或多次只读)。
2、PVC工作原理 PV是群集中的资源。 PVC是对这些资源的请求。
PV和PVC之间的相互作用遵循以下生命周期:
(1)pv的供应方式 可以通过两种方式配置PV:静态或动态。
静态的:
集群管理员创建了许多PV。它们包含可供群集用户使用的实际存储的详细信息。它们存在于Kubernetes API中,可供使用。
动态的:
当管理员创建的静态PV都不匹配用户的PersistentVolumeClaim时,群集可能会尝试为PVC专门动态配置卷。此配置基于StorageClasses,PVC必须请求存储类,管理员必须创建并配置该类,以便进行动态配置。
(2)绑定 用户创建pvc并指定需要的资源和访问模式。在找到可用pv之前,pvc会保持未绑定状态
(3)使用 a)需要找一个存储服务器,把它划分成多个存储空间;
b)k8s管理员可以把这些存储空间定义成多个pv;
c)在pod中使用pvc类型的存储卷之前需要先创建pvc,通过定义需要使用的pv的大小和对应的访问模式,找到合适的pv;
d)pvc被创建之后,就可以当成存储卷来使用了,我们在定义pod时就可以使用这个pvc的存储卷
e)pvc和pv它们是一一对应的关系,pv如果被pvc绑定了,就不能被其他pvc使用了;
f)我们在创建pvc的时候,应该确保和底下的pv能绑定,如果没有合适的pv,那么pvc就会处于pending状态。
(4)回收策略 当我们创建pod时如果使用pvc做为存储卷,那么它会和pv绑定,当删除pod,pvc和pv绑定就会解除,解除之后和pvc绑定的pv卷里的数据需要怎么处理,目前,卷可以保留,回收或删除:
Retain (保留)
Recycle (不推荐使用,1.15可能被废弃了,回收)
Delete (删除)
1、Retain
当删除pvc的时候,pv仍然存在,处于released状态,但是它不能被其他pvc绑定使用,里面的数据还是存在的,当我们下次再使用的时候,数据还是存在的,这个是默认的回收策略
2、Delete
删除pvc时即会从Kubernetes中移除PV,也会从相关的外部设施中删除存储资产
3、创建pod,使用pvc作为持久化存储 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 [root@master1 data] [root@master1 data] exporting 192.168.106.0/24:/root/volume/data/v10 exporting 192.168.106.0/24:/root/volume/data/v9 exporting 192.168.106.0/24:/root/volume/data/v8 exporting 192.168.106.0/24:/root/volume/data/v7 exporting 192.168.106.0/24:/root/volume/data/v6 exporting 192.168.106.0/24:/root/volume/data/v5 exporting 192.168.106.0/24:/root/volume/data/v4 exporting 192.168.106.0/24:/root/volume/data/v3 exporting 192.168.106.0/24:/root/volume/data/v2 exporting 192.168.106.0/24:/root/volume/data/v1 exporting 192.168.106.0/24:/root/volume/data [root@master1 volume] apiVersion: v1 kind: PersistentVolume metadata: name: v1 spec: capacity: storage: 1Gi accessModes: ["ReadWriteOnce" ] nfs: path: /root/volume/data/v1 server: 192.168.106.11 --- apiVersion: v1 kind: PersistentVolume metadata: name: v2 spec: capacity: storage: 2Gi accessModes: ["ReadWriteMany" ] nfs: path: /root/volume/data/v2 server: 192.168.106.11 --- apiVersion: v1 kind: PersistentVolume metadata: name: v3 spec: capacity: storage: 3Gi accessModes: ["ReadOnlyMany" ] nfs: path: /root/volume/data/v3 server: 192.168.106.11 --- apiVersion: v1 kind: PersistentVolume metadata: name: v4 spec: capacity: storage: 4Gi accessModes: ["ReadWriteOnce" ,"ReadWriteMany" ] nfs: path: /root/volume/data/v4 server: 192.168.106.11 --- apiVersion: v1 kind: PersistentVolume metadata: name: v5 spec: capacity: storage: 5Gi accessModes: ["ReadWriteOnce" ,"ReadWriteMany" ] nfs: path: /root/volume/data/v5 server: 192.168.106.11 --- apiVersion: v1 kind: PersistentVolume metadata: name: v6 spec: capacity: storage: 6Gi accessModes: ["ReadWriteOnce" ,"ReadWriteMany" ] nfs: path: /root/volume/data/v6 server: 192.168.106.11 --- apiVersion: v1 kind: PersistentVolume metadata: name: v7 spec: capacity: storage: 7Gi accessModes: ["ReadWriteOnce" ,"ReadWriteMany" ] nfs: path: /root/volume/data/v7 server: 192.168.106.11 --- apiVersion: v1 kind: PersistentVolume metadata: name: v8 spec: capacity: storage: 8Gi accessModes: ["ReadWriteOnce" ,"ReadWriteMany" ] nfs: path: /root/volume/data/v8 server: 192.168.106.11 --- apiVersion: v1 kind: PersistentVolume metadata: name: v9 spec: capacity: storage: 9Gi accessModes: ["ReadWriteOnce" ,"ReadWriteMany" ] nfs: path: /root/volume/data/v9 server: 192.168.106.11 --- apiVersion: v1 kind: PersistentVolume metadata: name: v10 spec: capacity: storage: 10Gi accessModes: ["ReadWriteOnce" ,"ReadWriteMany" ] nfs: path: /root/volume/data/v10 server: 192.168.106.11 [root@master1 volume] apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc-test spec: accessModes: ["ReadWriteMany" ] resources: requests: storage: 2Gi [root@master1 volume] NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE VOLUMEMODE pvc-test Bound v2 2Gi RWX 31s Filesystem [root@master1 volume] apiVersion: v1 kind: Pod metadata: name: pod-pvc spec: containers: - name: nginx image: nginx volumeMounts: - name: nginx-html mountPath: /usr/share/nginx/html volumes: - name: nginx-html persistentVolumeClaim: claimName: pvc-test
注:使用pvc和pv的注意事项
1、我们每次创建pvc的时候,需要事先有划分好的pv,这样可能不方便,那么可以在创建pvc的时候直接动态创建一个pv这个存储类,pv事先是不存在的
2、pvc和pv绑定,如果使用默认的回收策略retain,那么删除pvc之后,pv会处于released状态,我们想要继续使用这个pv,需要手动删除pv,kubectl delete pv pv_name,删除pv,不会删除pv里的数据,当我们重新创建pvc时还会和这个最匹配的pv绑定,数据还是原来数据,不会丢失。
5、StorageClass存储类动态生成存储 PV和PVC模式都是需要先创建好PV,然后定义好PVC和pv进行一对一的Bond,但是如果PVC请求成千上万,那么就需要创建成千上万的PV,对于运维人员来说维护成本很高,Kubernetes提供一种自动创建PV的机制,叫StorageClass,它的作用就是创建PV的模板。k8s集群管理员通过创建storageclass可以动态生成一个存储卷pv供k8s pvc使用。
每个StorageClass都包含字段provisioner,parameters和reclaimPolicy。
具体来说,StorageClass会定义以下两部分:
1、PV的属性 ,比如存储的大小、类型等;
2、创建这种PV需要使用到的存储插件,比如Ceph、NFS等
有了这两部分信息,Kubernetes就能够根据用户提交的PVC,找到对应的StorageClass,然后Kubernetes就会调用 StorageClass声明的存储插件,创建出需要的PV。
1、查看资源类型storageclass需要的字段 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [root@master1 ~] KIND: StorageClass VERSION: storage.k8s.io/v1 DESCRIPTION: StorageClass describes the parameters for a class of storage for which PersistentVolumes can be dynamically provisioned. StorageClasses are non-namespaced; the name of the storage class according to etcd is in ObjectMeta.Name. FIELDS: allowVolumeExpansion <boolean> allowedTopologies <[]Object> apiVersion <string> kind <string> metadata <Object> mountOptions <[]string> parameters <map[string]string> provisioner <string> -required- reclaimPolicy <string> volumeBindingMode <string>
provisioner:供应商,storageclass需要有一个供应者,用来确定我们使用什么样的存储来创建pv,常见的provisioner如下(https://kubernetes.io/zh/docs/concepts/storage/storage-classes/):
provisioner既可以由内部供应商提供,也可以由外部供应商提供,如果是外部供应商可以参考https://github.com/kubernetes-incubator/external-storage/下提供的方法创建。
https://github.com/kubernetes-sigs/sig-storage-lib-external-provisioner
以NFS为例,要想使用NFS,我们需要一个nfs-client的自动装载程序,称之为provisioner,这个程序会使用我们已经配置好的NFS服务器自动创建持久卷,也就是自动帮我们创建PV。
reclaimPolicy:回收策略
allowVolumeExpansion:允许卷扩展,PersistentVolume 可以配置成可扩展。将此功能设置为true时,允许用户通过编辑相应的 PVC 对象来调整卷大小。当基础存储类的allowVolumeExpansion字段设置为 true 时,以下类型的卷支持卷扩展。**(只能用于扩展卷,不能用于压缩卷)**
2、安装nfs provisioner配合存储类动态生成pv 创建运行nfs-provisioner需要的sa账号 sa的全称是serviceaccount。
serviceaccount是为了方便Pod里面的进程调用Kubernetes API或其他外部服务而设计的。
指定了serviceaccount之后,我们把pod创建出来了,我们在使用这个pod时,这个pod就有了我们指定的账户的权限了
1 2 3 4 5 6 7 [root@master1 nfs-provisioner] apiVersion: v1 kind: ServiceAccount metadata: name: nfs-provisioner [root@master1 nfs-provisioner]
对sa的账户进行授权 1 [root@master1 nfs-provisioner]
安装nfs-provisioner 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 [root@master1 storageclass] [root@master1 storageclass] exporting 192.168.106.0/24:/root/volume/data/nfs_pro exporting 192.168.106.0/24:/root/volume/data/v10 exporting 192.168.106.0/24:/root/volume/data/v9 exporting 192.168.106.0/24:/root/volume/data/v8 exporting 192.168.106.0/24:/root/volume/data/v7 exporting 192.168.106.0/24:/root/volume/data/v6 exporting 192.168.106.0/24:/root/volume/data/v5 exporting 192.168.106.0/24:/root/volume/data/v4 exporting 192.168.106.0/24:/root/volume/data/v3 exporting 192.168.106.0/24:/root/volume/data/v2 exporting 192.168.106.0/24:/root/volume/data/v1 exporting 192.168.106.0/24:/root/volume/data [root@master1 nfs-provisioner] apiVersion: apps/v1 kind: Deployment metadata: name: nfs-provisioner spec: replicas: 1 selector: matchLabels: app: nfs-provisioner strategy: type : Recreate template: metadata: labels: app: nfs-provisioner spec: serviceAccount: nfs-provisioner containers: - name: nfs-provisioner image: registry.cn-beijing.aliyuncs.com/mydlq/nfs-subdir-external-provisioner:v4.0.0 volumeMounts: - name: nfs-client-root mountPath: /persistentvolumes env: - name: PROVISIONER_NAME value: example.com/nfs - name: NFS_SERVER value: 192.168.106.11 - name: NFS_PATH value: /root/volume/data/nfs_pro volumes: - name: nfs-client-root nfs: server: 192.168.106.11 path: /root/volume/data/nfs_pro [root@master1 nfs-provisioner]
创建nfs-pvc-test 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [root@master1 storageclass] apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: nfs provisioner: example.com/nfs [root@master1 storageclass] apiVersion: v1 kind: PersistentVolumeClaim metadata: name: nfs-pvc-test spec: accessModes: ["ReadWriteMany" ] resources: requests: storage: 1Gi storageClassName: nfs
3、创建pod配置文件使用 默认的回收策略是delete,当pod和pvc被删除时,pv也会被删除,但是目录名字会变化,数据不会丢失,再次创建pvc时。会生成新的目录文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 [root@master1 storageclass] apiVersion: v1 kind: Pod metadata: name: pod spec: containers: - name: pod image: nginx volumeMounts: - name: nfs-pvc mountPath: /usr/share/nginx/html imagePullPolicy: IfNotPresent restartPolicy: Never volumes: - name: nfs-pvc persistentVolumeClaim: claimName: nfs-pvc-test
使用Retain策略测试 更改reclaimPolicy为Retain,删除pvc后目录名字未变且数据仍然存在(pv不会被删除)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 [root@master1 storageclass] apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: nfs provisioner: example.com/nfs reclaimPolicy: Retain [root@master1 storageclass] [root@master1 storageclass] pod/pod created [root@master1 default-nfs-pvc-test-pvc-3f58a561-caa1-4c14-8dac-77381ae98c9b] total 4 -rw-r--r-- 1 root root 15 Aug 28 14:16 index.html