1. 在一个Pod使用过程中,会进行很多操作,修改一些文件,安装一些程序等等。
但当我们重启容器之后,会发现容器往往有回到了初始的状态,所有的修改都丢失了
2. 除了希望数据不在Pod 重启后丢失,我们有时候也需要在Pod 间共享文件,因此,kubernetes抽象出来Volume 对象来解决这两个文件
Kubernetes 支持的卷类型非常丰富,包括: - NFS 文件系统 - Cephfs 等分布式存储系统 - awsElasticBlockStore,azureDisk等公有云存储服务 - emptyDir,configMap,hostPath等kubernetes内置存储类型 - ISCSI,FC等等 ... ...
1. 当Pod 指定到某个节点上时,首先创建的是一个emptyDir 卷,并且只要Pod 在该节点上运行,卷就一直存在。就像它的名称表示的那样,卷最初是空的。尽管Pod中的容器挂载emptyDir卷的路径可能相同也可能不同,但是这些容器都可以读写emptyDir 卷中相同的文件。当Pod因为某些原因被从节点上删除时,enptyDir卷中的数据也会永久删除
1. 创建使用emptyDir 时需要配置两个参数:
① Spec.containers.volumeMounts: 设置volume的挂载点
② Spec.volumes: 配置volume
$ kubectl apply -f- <<EOF apiVersion: v1 kind: Pod metadata: name: em spec: containers: - image: ubuntu name: test-container volumeMounts: - mountPath: /cache #容器中的/cache目录 name: cache-volume #挂载的是cache-volume,要和volumes中一致 args: - /bin/sh - -c - sleep 30000 volumes: #定义volume - name: cache-volume #卷名 emptyDir: {} #卷,没有做限制 EOF
$ kubectl get pod em #查看pod是否创建完成 NAME READY STATUS RESTARTS AGE em 1/1 Running 0 13m $ kubectl describe pod em | grep -A 1 Mount #查看把那个卷挂载/cache这个目录 Mounts: '/cache' from 'cache-volume' (rw) $ kubectl describe pod em | grep -A 4 ^Volume #查看挂载类型等信息 Volumes: 'cache-volume:' #卷的类型、意思是:临时的目录共享给了pod的生命周期中 Type: 'EmptyDir' (a temporary directory that shares a pod's lifetime) Medium: SizeLimit:#没有限制大小 参考文档: 卷 | Kubernetes
2. 如果保持emptyDir 的默认配置,格式如上。若限制emptyDir容量(1G),需要如下配置
emptyDir: sizeLimit: 1Gi #容量限制为1G EOF参考文档: Volume | Kubernetes
1. 进入对应目录后,可以发现如果在pod中创建一个文件,在这个目录中也可呈现
$ kubectl exec -it em -- /bin/sh #进入pod # df -h # Filesystem Size Used Avail Use% Mounted on overlay 38G 5.7G 30G 16% / tmpfs 64M 0 64M 0% /dev /dev/mapper/ubuntu--vg-ubuntu--lv 38G 5.7G 30G 16% /cache #目录大小与根一样 shm 64M 0 64M 0% /dev/shm tmpfs 3.8G 12K 3.8G 1% /run/secrets/kubernetes.io/serviceaccount tmpfs 1.9G 0 1.9G 0% /proc/acpi tmpfs 1.9G 0 1.9G 0% /proc/scsi tmpfs 1.9G 0 1.9G 0% /sys/firmware # touch /cache/my.txt #创建文件 # ls /cache my.txt # exit #退出容器
$ kubectl get pods em1 NAME READY STATUS RESTARTS AGE em1 1/1 Running 0 28s $ kubectl describe pods em1 | grep -A 4 ^Volumes Volumes: cache-volume: Type: EmptyDir (a temporary directory that shares a pod's lifetime) Medium: SizeLimit: 1Gi' #大小限制成了1G
2. emptyDir 的生命周期与Pod一致,如果将pod删除,可以看到对应的目录也不复存在
1. emptyDir 可以进行容量限制,如限制为1G
$ kubectl apply -f- <<EOF apiVersion: v1 kind: Pod metadata: name: em1 spec: containers: - image: ubuntu name: test-container volumeMounts: - mountPath: /cache name: cache-volume args: - /bin/sh - -c - sleep 30000 volumes: - name: cache-volume # 下 2 行区别 emptyDir: sizeLimit: 1Gi #容量限制为1G EOF
2. 进入Pod 查看分配文件夹的大小,可以看到空间是Host存储空间大小,并非1G
$ kubectl exec -it em1 -- /bin/sh #进入容器 # df -h Filesystem Size Used Avail Use% Mounted on overlay 38G 5.7G 30G 16% / tmpfs 64M 0 64M 0% /dev /dev/mapper/ubuntu--vg-ubuntu--lv 38G 5.7G 30G 16% /cache #显示大小依然没变 shm 64M 0 64M 0% /dev/shm tmpfs 3.8G 12K 3.8G 1% /run/secrets/kubernetes.io/serviceaccount tmpfs 1.9G 0 1.9G 0% /proc/acpi tmpfs 1.9G 0 1.9G 0% /proc/scsi tmpfs 1.9G 0 1.9G 0% /sys/firmware
3. 尝试在容器内写入一个2G 的文件
# dd if=/dev/zero of=/cache/test2g bs=1M count=2048 #制作2G的文件 2048+0 records in 2048+0 records out 2147483648 bytes (2.1 GB, 2.0 GiB) copied, 21.5983 s, 99.4 MB/s # exit #退出容器
$ kubectl get pods em1 #再次查看发现容器运行异常 NAME READY STATUS RESTARTS AGE em1 0/1 ContainerStatusUnknown 1 11m '原因容量限制了1G,结果输入的内容超过了1个G,所有容器变为不可用'
1. hostPath 卷能将主机节点文件系统上的文件或目录挂载到Pod中
2. 但比如希望Pod使用一些docker 引擎或系统已经包含的内部程序的时候,
会使用到这种方式。如以下为kube-proxy 中配置的hostPath
参考资料: 卷 | Kubernetes
3. 查看kube-system命名空间的pod的配置
$ kubectl -n kube-system get pod kube-proxy-5w5bz -o yaml|grep -A 12 volume volumeMounts: #卷的挂载 - mountPath: /var/lib/kube-proxy #将不同的路径挂载到哪 name: kube-proxy - mountPath: /run/xtables.lock name: xtables-lock - mountPath: /lib/modules name: lib-modules readOnly: true - mountPath: /var/run/secrets/kubernetes.io/serviceaccount name: kube-api-access-ffznd readOnly: true #只读权限 dnsPolicy: ClusterFirst enableServiceLinks: true -- volumes: - configMap: defaultMode: 420 name: kube-proxy name: kube-proxy - hostPath: path: /run/xtables.lock type: FileOrCreate #文件存在或创建 name: xtables-lock - hostPath: path: /lib/modules type: "" name: lib-modules
1. hostPath 配置与 emptyDir类似,但类型需指定为:
① hostPath
② path 参数需要配置为主机上已存在的目录
③ type 指定为目录
$ kubectl apply -f- <<EOF apiVersion: v1 kind: Pod metadata: name: hppod spec: containers: - image: ubuntu name: hp-container volumeMounts: - mountPath: /hp-dir #挂载路径:pod中的/hp-dir name: hp-volume args: - /bin/sh - -c - sleep 30000 volumes: - name: hp-volume #卷名 hostPath: path: /mnt/hostpathdir #主机路径 type: DirectoryOrCreate #目录或创建 EOF #优点:当不知道工作节点时,会判断目录是否会存在,不存在时会创建
$ kubectl get pods hppod -o wide #查看pod所在位置 NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES hppod 1/1 Running 0 32s 172.16.194.75 k8s-worker1 kiosk@k8s-worker1:~$ ls /mnt/ #查看目录已经创建 hostpathdir
$ kubectl exec -it hppod -- /bin/sh # ls bin dev home lib lib64 media opt root sbin sys usr boot etc hp-dir lib32 libx32 mnt proc run srv tmp var # ls /hp-dir # touch /hp-dir/test.txt #创建测试文件 # ls /hp-dir test.txt kiosk@k8s-worker1:~$ ls /mnt/hostpathdir/ #查看宿主机中文件已经创建 test.txt kiosk@k8s-worker1:~$ echo test | sudo tee /mnt/hostpathdir/Test.txt #创建文件 test # ls /hp-dir/ #pod中可以看到文件已经生成 Test.txt test.txt 无论是在当前pod中,还是在宿主机中,这两个目录实际是映射的关系参考文档: 卷 | Kubernetes
1. 创建hostPath时,需要指定类型(type)
2. 如果选择类型不正确,或主机上不存在对应资源(如不存在指定文件夹),
kubernetes系统将无法继续创建Pod,创建步骤终止。
Pod状态长时间处于ContainerCreating中
取值 行为 空字符串(默认)用于向后兼容,这意味着在安装hostPath卷之前不会执行任何检查 DirectoryOrCreate 如果在给定路径上什么都不存在,那么将根据需要创建空目录,权限设置为0755,具有与 kubelet 相同的组合所有权 Directory 在给定路径上必须存在的目录 FileOrCreate 如果在给定路径上什么都不存在,那么将再那里根据需要创建空文件,权限设置为0644,具有与 Kubelet 相同的组合所有权 File 在给定路径上必须存在的文件 Socket 在给定路径上必须存在的UNIX 套接字 CharDevice 在给定路径上必须存在的字符设备 BlockDevice 在给定路径上必须存在的块设备
3. 方式缺点
数据只能在各节点上,如之后pod运行时换了node,数据在另一个node,那么数据就会找不到;实际是从PV和PVC比较常用
1. PersistentVolume持久卷 (pv) 和 PersistentVolumeClaim持久卷声明 (pvc)是k8s提供的两种API资源,用于抽象存储细节。管理员关注如何通过pv 提供存储功能而无需关注用户如何使用,同样的用户只需要挂载pvc 到容器中而不需要关注存储卷采用何种技术实现
① PV 是集群中由管理员配置的一块存储空间
它是集群中的资源,就像节点是集群中的资源一样。PV是卷插件,和之前介绍的volumes类似,但他又一个独立于单个Pod的生命周期。PV的后端可以是NFS,ISCSI或者云存储等等
② PVC是用户的存储请求(PVC与Pod绑定)
它类似于 Pod:Pod消耗节点资源,而PVC消耗PV资源(PV和PVC是映射关系)。Pod可以请求特定级别的资源(CPU和内存),PVC可以请求PV特定的接入模式(读写等)和大小
1. kind选择PersistentVolume
2. name命名为PV,在PVC中可调用
3. capacity 指定 PV 的容量
4. accessModes 指定访问模式
① ReadWriteOnce:该卷能够以读写模式被加载到一个节点上
② ReadOnlyMany:该卷能够以只读模式加载到多个结点上
③ ReadWriteMany:该卷能够以读写模式被多个节点同时加载
④ ReadWriteOncePod:卷可以被单个 Pod 以读写方式挂载。 如果你想确保整个集群中只有一个 Pod 可以读取或写入该 PVC
5. persistentVolumeReclaimPolicy 指定PV的回收策略
① Retain(保留),不删除,需要手动回收
② Recycle(回收),基本擦除,类似 rm -rf ,使其可供其他 PVC 申请
③ Delete(删除),关联存储将被删除,如Azure Disk 或 OpenStack Cinder 卷
6. nfs 字段配置 NFS服务器信息,在创建PV前,已搭建完NFS服务器,服务器的IP地址是192.168.147.102,共享的文件夹是/nfs
① PV支持的挂载选项包括 NFS,iSCSI,Cinder卷,CephFS等
7.创建 NFS
#安装nfs软件包和创建文件目录 kiosk@k8s-master:~$ sudo apt -y install nfs-kernel-server && sudo mkdir /nfs #exports意思是将谁共享,共享的权限; #root具有root的权限,root具有root的权限; #默认是root_squash(root共享、root访问的时候,变成匿名用户,还想让他就是root,所以设置成no_root_squash) kiosk@k8s-master:~$ echo '/nfs *(rw,no_root_squash)' | sudo tee /etc/exports /nfs *(rw,no_root_squash) kiosk@k8s-master:~$ sudo systemctl enable nfs-server #设置开机自启 kiosk@k8s-master:~$ sudo systemctl restart nfs-server #重启服务 kiosk@k8s-master:~$ showmount -e #查看 Export list for k8s-master: /nfs *
8. 创建PV
$ kubectl apply -f- <<EOF apiVersion: v1 kind: PersistentVolume metadata: name: mypv spec: capacity: storage: 1Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Recycle nfs: path: /nfs server: 192.168.147.102 EOF
$ kubectl get pv #查询PV NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE mypv 1Gi RWO Recycle Available参考文档: 持久卷 | Kubernetes
1. 创建NFS
$ kubectl apply -f- <<EOF apiVersion: v1 kind: PersistentVolumeClaim metadata: name: mypvc spec: accessModes: - ReadWriteOnce #访问模式要与pv一致 volumeName: mypv #名称也要和PV对上 resources: requests: storage: 1Gi #大小 EOF
$ kubectl get pv #查看状态变为Bound,绑定的default命名空间中的mypvc NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE mypv 1Gi RWO Recycle Bound default/mypvc 25h $ kubectl get pvc #查看pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE mypvc Bound mypv 1Gi RWO 2m25s $ kubectl describe pv #查看pv的pod名称 $ kubectl get pods recycler-for-mypv #查看pod状态 NAME READY STATUS RESTARTS AGE recycler-for-mypv 0/1 ContainerCreating 0 40s
2. Kind,类型指定为PVC
3. accessModes,保持与PV一致
4. volumeName,使用的PV名称,用于PVC找到正确的PV
5. requests:指定PV的容量,如果不存在满足该容量需求的PV,则PVC无法绑定任何PV
6. 创建Pod时,可以使用该方式定义volumes,使用PV 和 PVC
1. PV状态,创建完成后为 Available
2. PVC状态,创建完成后为Pending
1. 由于创建时选择的回收策略时Recycle,删除PVC 的时候kubernetes会删除原有PV的数据。它采用的方式是创建一个回收专用Pod 来完成这一操作
2. 如果不希望数据被删除,可以配置回收策略为Retain,
这样即使Pod、PVC删除后,PV的数据仍然存在,PV状态如下:
$ kubectl delete pvc mypvc #删除PVC persistentvolumeclaim "mypvc" deleted $ kubectl get pods recycler-for-mypv #删除后会自动重建Pod,可使用describe命令查看pod详细信息 NAME READY STATUS RESTARTS AGE recycler-for-mypv 0/1 ContainerCreating 0 76s $ kubectl get pv #状态变为可用 NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE mypv 1Gi RWO Recycle Available 8s
$ kubectl apply -f- <<EOF #创建PV apiVersion: v1 kind: PersistentVolume metadata: name: mypv spec: capacity: storage: 1Gi accessModes: - ReadWriteOnce # 只修改 1 行 persistentVolumeReclaimPolicy: Retain #策略改为保留 nfs: path: /nfs server: 192.168.147.102 EOF
$ kubectl get pv #策略变为Retain NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE mypv 1Gi RWO Retain Available 2m19s
$ kubectl apply -f- <<EOF #创建PVC apiVersion: v1 kind: PersistentVolumeClaim metadata: name: mypvc spec: accessModes: - ReadWriteOnce volumeName: mypv resources: requests: storage: 1Gi EOF
$ kubectl get pv #PV状态变为Bound NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE mypv 1Gi RWO Retain Bound default/mypvc 5m $ kubectl delete pvc mypvc #删除PVC persistentvolumeclaim "mypvc" deleted $ kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE mypv 1Gi RWO Retain Released default/mypvc 7m2s '状态变为释放,数据依然保留'
3. 数据未被删除,但由于PV处于Released状态,依然无法直接被PVC使用。
这是由于PV保存了之前关联的PVC状态,如需关联新PVC,需要删除其中ClaimRef参数
1. 如果我们重复PV和PVC的实验,将PVC中volumeName:mypv这一参数删除,可以发现PVC扔能申请到PV。
那么PVC是以什么机制找到匹配的PV呢?
①. PVC首先根据筛选条件,如容量大小和访问模式筛选掉不符合条件的PV
②. 筛选掉不符合volumeName的PV
③. 筛选掉不符合StorageClass 的 PV
④. 根据其他条件筛选符合的 PV
1. 创建PV 时指定 storageClassName,可以在PVC中申请该PV
1. storageClass 除了能够用上述用法之外,主要使用场景是动态供给PV
2. 由于kubernetes 集群中存在大量的Pod,也就意味着很可能有大量的PV和PVC,如果需要一个一个手工创建无疑是一个巨大的工程,也不符合自动化的特点
3. 以nfs为例,使用动态供给功能需要完成以下几个步骤:
① 创建 nfs-provisiones,provisioner 用于动态创建符合要求的PV
② 创建StorageClass,在配置时指定使用的provisioner
③ 创建PVC,只需要指定StorageClass,容量及访问模式即可
4.如果将一个StorageClass标注为default,
则PVC在申请时可以不指定StorageClass 而默认使用该 defaultStorageClass
kubectl patch storageclass <存储类名> -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
-p后的参数必须为json格式