一、volumes介绍 1.1 在Docker中,也有volumes这个概念,volume只是磁盘上一个简单的目录,或者其他容器中的volume。生命周期也不受管理,并且直到最近他们都是基于本地后端存储的。 Kubernetes的volume,有着明显的生命周期——和使用它的pod生命周期一致。因此,volume生命周期就比运行在pod中的容器要长久,即使容器重启,volume上的数据依然保存着。当然,pod不再存在时,volume也就消失了。更重要的是,Kubernetes支持多种类型的volume,并且pod可以同时使用多种类型的volume。 内部实现中,volume只是一个目录,目录中可能有一些数据,pod的容器可以访问这些数据。这个目录是如何产生的,它后端基于什么存储介质,其中的数据内容是什么,这些都由使用的特定volume类型来决定。 要使用volume,pod需要指定volume的类型和内容(spec.volumes字段),和映射到容器的位置(spec.containers.volumeMounts字段)。 容器中的进程可以看到Docker image和volumes组成的文件系统。Docker image处于文件系统架构的root,任何volume都映射在镜像的特定路径上。Volume不能映射到其他volume上,或者硬链接到其他volume。容器中的每个容器必须堵路地指定他们要映射的volume。
1.2容器中的文件在磁盘上是临时存放的,这给容器中运行的特殊应用程序带来一些问题。首先,当容器崩溃时,kubelet 将重新启动容器,容器中的文件将会丢失,因为容器会以干净的状态重建。其次,当在一个 Pod 中同时运行多个容器时,常常需要在这些容器之间共享文件。 Kubernetes 抽象出 Volume 对象来解决这两个问题。
Kubernetes 卷具有明确的生命周期,与包裹它的 Pod 相同。 因此,卷比 Pod 中运行的任何容器的存活期都长,在容器重新启动时数据也会得到保留。 当然,当一个 Pod 不再存在时,卷也将不再存在。也许更重要的是,Kubernetes 可以支持许多类型的卷,Pod 也能同时使用任意数量的卷。
卷不能挂载到其他卷,也不能与其他卷有硬链接。 Pod 中的每个容器必须独立地指定每个卷的挂载位置。 1.3Kubernetes 支持下列类型的卷:
awsElasticBlockStore 、azureDisk、azureFile、cephfs、cinder、configMap、csi downwardAPI、emptyDir、fc (fibre channel)、flexVolume、flocker gcePersistentDisk、gitRepo (deprecated)、glusterfs、hostPath、iscsi、local、 nfs、persistentVolumeClaim、projected、portworxVolume、quobyte、rbd scaleIO、secret、storageos、vsphereVolume二、emptyDir卷 当 Pod 指定到某个节点上时,首先创建的是一个 emptyDir 卷,并且只要 Pod 在该节点上运行,卷就一直存在。 就像它的名称表示的那样,卷最初是空的。 尽管 Pod 中的容器挂载 emptyDir 卷的路径可能相同也可能不同,但是这些容器都可以读写 emptyDir 卷中相同的文件。 当 Pod 因为某些原因被从节点上删除时,emptyDir 卷中的数据也会永久删除。 2.1emptyDir 的使用场景:
缓存空间,例如基于磁盘的归并排序。 为耗时较长的计算任务提供检查点,以便任务能方便地从崩溃前状态恢复执行。 在 Web 服务器容器服务数据时,保存内容管理器容器获取的文件。默认情况下, emptyDir 卷存储在支持该节点所使用的介质上;这里的介质可以是磁盘或 SSD 或网络存储,这取决于您的环境。 但是,您可以将 emptyDir.medium 字段设置为 "Memory",以告诉 Kubernetes 为您安装 tmpfs(基于内存的文件系统)。 虽然 tmpfs 速度非常快,但是要注意它与磁盘不同。 tmpfs 在节点重启时会被清除,并且您所写入的所有文件都会计入容器的内存消耗,受容器内存限制约束。 2.3emptyDir 示例
[root@server2 ~]# mkdir vol [root@server2 ~]# cd vol/ 里面有两个镜像:一个卷被挂载到不同容器的挂载点上,数据实时同步 [root@server2 vol]# vim vol1.yaml apiVersion: v1 kind: Pod metadata: name: vol1 spec: containers: - image: busyboxplus 默认访问pod内的第一个镜像 name: vm1 command: ["sleep", "300"] volumeMounts: - mountPath: /cache name: cache-volume - name: vm2 image: nginx volumeMounts: - mountPath: /usr/share/nginx/html name: cache-volume volumes: - name: cache-volume emptyDir: medium: Memory sizeLimit: 100Mi 限制内存使用卷是共享卷:信息也是共享 启动pod访问ip 验证信息共享
指定访问VM1也即是第一个镜像,看到的还是Nginx的默认发布页面 即使不指定第一个镜像默认访问的还是pod中的第一个镜像 [root@server2 vol]# kubectl exec -it vol1 -c vm1 -- sh Defaulting container name to vm1. Use 'kubectl describe pod/vol1 -n default' to see all of the containers in this pod. / # ls bin dev home lib64 media opt root sbin tmp var cache etc lib linuxrc mnt proc run sys usr / # cd cache/ /cache # ls index.html /cache # cat index.html westos /cache #2.4emptydir缺点: 2.4.1不能及时禁止用户使用内存。虽然过1-2分钟kubelet会将Pod挤出,但是这个时间内,其实对node还是有风险的; 2.4.2影响kubernetes调度,因为empty dir并不涉及node的resources,这样会造成Pod“偷偷”使用了node的内存,但是调度器并不知晓; 2.4.3用户不能及时感知到内存不可用 。 接下来我们验证一下 使用上面建立的yml文件
[root@server2 vol]# kubectl apply -f vol1.yaml pod/vol1 created [root@server2 vol]# kubectl get pod -o wide 查看容器运行的节点主机 NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES vol1 2/2 Running 0 9s 10.244.1.7 server3 <none> <none> [root@server3 ~]# free -h 查看节点主机的内存剩余量 total used free shared buff/cache available Mem: 1.8G 323M 583M 33M 912M 1.2G Swap: 0B 0B 0B[root@server3 ~]# free -h 截取之前的内存量 total used free shared buff/cache available Mem: 1.8G 316M 390M 32M 1.1G 1.2G Swap: 0B 0B 0B [root@server3 ~]# free -h 截取之后的内存量少了200 total used free shared buff/cache available Mem: 1.8G 318M 188M 232M 1.3G 1.0G Swap: 0B 0B 0B [root@server2 vol]# kubectl get pod 等待一分钟查看pod已经被节点主机强制剔除 NAME READY STATUS RESTARTS AGE vol1 0/2 Evicted 0 11m [root@server3 ~]# free -h 再次查看节点主机内存量已经恢复至正常 total used free shared buff/cache available Mem: 1.8G 299M 613M 32M 906M 1.2G Swap: 0B 0B 0B
可以看到文件超过sizeLimit: 100Mi,则一段时间后(1-2分钟)会被kubelet evict掉。之所以不是“立即”被evict,是因为kubelet是定期进行检查的,这里会有一个时间差。 三、hostPath 卷 hostPath能将主机节点文件系统上的文件或目录挂载到您的 Pod 中。 虽然这不是大多数 Pod 需要的,但是它为一些应用程序提供了强大的逃生舱。 hostPath 的一些用法有: 运行一个需要访问 Docker 引擎内部机制的容器,挂载 /var/lib/docker 路径。
在容器中运行 cAdvisor 时,以 hostPath 方式挂载 /sys。
允许 Pod 指定给定的 hostPath 在运行 Pod 之前是否应该存在,是否应该创建以及应该以什么方式存在。 除了必需的 path 属性之外,用户可以选择性地为 hostPath 卷指定 type 当使用这种类型的卷时要小心,因为: 具有相同配置(例如从 podTemplate 创建)的多个 Pod 会由于节点上文件的不同而在不同节点上有不同的行为。 当 Kubernetes 按照计划添加资源感知的调度时,这类调度机制将无法考虑由 hostPath 使用的资源。 基础主机上创建的文件或目录只能由 root 用户写入。您需要在 特权容器 中以 root 身份运行进程,或者修改主机上的文件权限以便容器能够写入 hostPath 卷。 接下来操作演示:
[root@server2 vol]# vim vol1.yaml apiVersion: v1 kind: Pod metadata: name: test-pd spec: containers: - image: nginx name: test-container volumeMounts: - mountPath: /usr/share/nginx/html 指定挂载地址 name: test-volume 指定卷名称 volumes: 指定卷 - name: test-volume 名称 hostPath: 类型 path: /data 挂载的文件路径 type: DirectoryOrCreate 如果没有自动创建 运行pod [root@server2 vol]# kubectl apply -f vol1.yaml pod/test-pd created [root@server2 vol]# kubectl get pod -o wide 查看在那个主机 NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES test-pd 1/1 Running 0 6s 10.244.1.8 server3 <none> <none> [root@server3 ~]# ll /data/ 此目录不存在会自动创建 total 0 [root@server3 ~]# cd /data/ [root@server3 data]# echo www.baidu.com > index.html 编辑Nginx的默认发布页面 [root@server3 data]# cat index.html www.baidu.com 测试访问 [root@server2 vol]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES test-pd 1/1 Running 0 6s 10.244.1.8 server3 <none> <none> [root@server2 vol]# curl 10.244.1.8 www.baidu.com如何使pod容器运行在指定目录当前在server3,手动指定到server4
kubectl get nodes --show-labels 查看指定主机的标签 [root@server2 vol]# vim vol1.yaml apiVersion: v1 kind: Pod metadata: name: test-pd spec: nodeSelector: 指定主机 kubernetes.io/hostname: server4 主机名称标签 containers: - image: nginx name: test-container volumeMounts: - mountPath: /usr/share/nginx/html name: test-volume volumes: - name: test-volume hostPath: path: /data type: DirectoryOrCreate[root@server2 vol]# curl 10.244.2.5 www.taobao.com
结论:两个节点上的/data/目录下的文件内容不一样。切换节点默认发布页面也会更改 四、NFS 示例 持久化存储实战 实验目的:实现不同节点切换但是内容不变 首先在nfs数据输出主机上和其他节点安装nfs服务;master节点不用安装,此主机不参加调度。
yum install -y nfs-utils.x86_64 systemctl enable --now nfs [root@server1 ~]# mkdir /nfsdata [root@server1 ~]# vim /etc/nfsexports [root@server1 ~]# cat /etc/nfsexports /nfsdata *(rw,sync) [root@server1 ~]# chmod 777 /nfsdata/ [root@server1 ~]# systemctl enable --now nfs [root@server1 ~]# showmount -e Export list for server1: /nfsdata * [root@server2 vol]# cat vol1.yaml apiVersion: v1 kind: Pod metadata: name: test-pd spec: containers: - image: nginx name: test-container volumeMounts: - mountPath: /usr/share/nginx/html name: test-volume volumes: - name: test-volume nfs: server: 172.25.254.31 nfs输出主机 path: /nfsdata 目录 [root@server2 vol]# kubectl apply -f vol1.yaml pod/test-pd created [root@server2 vol]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES test-pd 1/1 Running 0 3s 10.244.1.9 server3 <none> <none> [root@server1 ~]# cd /nfsdata/ [root@server1 nfsdata]# echo www.westos.org > index.html [root@server2 vol]# curl 10.244.1.9 www.westos.org再次受到把主机指定到server4上
方法同上:
[root@server2 vol]# vim vol1.yaml spec: nodeSelector: 指定主机 kubernetes.io/hostname: server4 主机名称标签 [root@server2 vol]# kubectl apply -f vol1.yaml pod/test-pd created [root@server2 vol]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES test-pd 1/1 Running 0 5s 10.244.2.6 server4 <none> <none> [root@server2 vol]# curl 10.244.2.6 www.westos.org五、PersistentVolume(持久卷PV) PersistentVolume(持久卷,简称PV)是集群内,由管理员提供的网络存储的一部分。就像集群中的节点一样,PV也是集群中的一种资源。它也像Volume一样,是一种volume插件,但是它的生命周期却是和使用它的Pod相互独立的。PV这个API对象,捕获了诸如NFS、ISCSI、或其他云存储系统的实现细节。
PersistentVolumeClaim(持久卷声明,简称PVC)是用户的一种存储请求。它和Pod类似,Pod消耗Node资源,而PVC消耗PV资源。Pod能够请求特定的资源(如CPU和内存)。PVC能够请求指定的大小和访问的模式(可以被映射为一次读写或者多次只读)。
有两种PV提供的方式:静态和动态。 静态PV:集群管理员创建多个PV,它们携带着真实存储的详细信息,这些存储对于集群用户是可用的。它们存在于Kubernetes API中,并可用于存储使用。
动态PV:当管理员创建的静态PV都不匹配用户的PVC时,集群可能会尝试专门地供给volume给PVC。这种供给基于StorageClass。
PVC与PV的绑定是一对一的映射。没找到匹配的PV,那么PVC会无限期得处于unbound未绑定状态。 使用 Pod使用PVC就像使用volume一样。集群检查PVC,查找绑定的PV,并映射PV给Pod。对于支持多种访问模式的PV,用户可以指定想用的模式。一旦用户拥有了一个PVC,并且PVC被绑定,那么只要用户还需要,PV就一直属于这个用户。用户调度Pod,通过在Pod的volume块中包含PVC来访问PV。
释放 当用户使用PV完毕后,他们可以通过API来删除PVC对象。当PVC被删除后,对应的PV就被认为是已经是“released”了,但还不能再给另外一个PVC使用。前一个PVC的属于还存在于该PV中,必须根据策略来处理掉。
回收 PV的回收策略告诉集群,在PV被释放之后集群应该如何处理该PV。当前,PV可以被Retained(保留)、 Recycled(再利用)或者Deleted(删除)。保留允许手动地再次声明资源。对于支持删除操作的PV卷,删除操作会从Kubernetes中移除PV对象,还有对应的外部存储(如AWS EBS,GCE PD,Azure Disk,或者Cinder volume)。动态供给的卷总是会被删除。
访问模式
ReadWriteOnce -- 该volume只能被单个节点以读写的方式映射 ReadOnlyMany -- 该volume可以被多个节点以只读方式映射 ReadWriteMany -- 该volume可以被多个节点以读写的方式映射在命令行中,访问模式可以简写为:
RWO - ReadWriteOnce ROX - ReadOnlyMany RWX - ReadWriteMany回收策略
Retain:保留,需要手动回收 Recycle:回收,自动删除卷中数据 Delete:删除,相关联的存储资产,如AWS EBS,GCE PD,Azure Disk,or OpenStack Cinder卷都会被删除当前,只有NFS和HostPath支持回收利用,AWS EBS,GCE PD,Azure Disk,or OpenStack Cinder卷支持删除操作。
状态:
Available:空闲的资源,未绑定给PVC Bound:绑定给了某个PVC Released:PVC已经删除了,但是PV还没有被集群回收 Failed:PV在自动回收中失败了 命令行可以显示PV绑定的PVC名称。六、NFS PV 示例 持久化存储实战 pod不直接挂载pvc,而是挂载pv,pv绑定pvc 定义pv
[root@server2 pv]# vim pv.yml apiVersion: v1 kind: PersistentVolume 创建pv metadata: name: pv1 spec: capacity: storage: 5Gi 大小5G volumeMode: Filesystem pv卷模式 accessModes: - ReadWriteOnce 该volume只能被单个节点以读写的方式映射 persistentVolumeReclaimPolicy: Recycle 可以再利用 storageClassName: nfs 存储于 nfs: 使用nfs作为后端 path: /nfsdata server: 172.25.254.31 [root@server2 pv]# kubectl apply -f pv.yml persistentvolume/pv1 configured [root@server2 pv]# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pv1 5Gi RWO Recycle Available(未绑定状态) nfs 34s定义pvc
与pv写在同一个文件中 --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc1 spec: storageClassName: nfs 存储类必须与当前定义的pv的存储类一致 accessModes: - ReadWriteOnce 访问模式 resources: requests: storage: 1Gi 存储满足1G 当前我们是5G接下来创建多个Pod挂载多个PV及pvc进行应用 首先把上面的文件pv.yml 改为pv1.yml ,方便观看实验效果 第一步在nfs数据输出端做以下操作
[root@server1 ~]# mkdir /nfsdata2 [root@server1 ~]# mkdir /nfsdata3 [root@server1 ~]# vim /etc/exports /nfsdata *(rw,sync) 读写 /nfsdata2 *(rw,sync) 读写 /nfsdata3 *(ro,) 只读 [root@server1 ~]# ll -d /nfsdata3 只读 drwxr-xr-x 2 root root 6 Jul 5 00:12 /nfsdata3 [root@server1 ~]# chmod 777 /nfsdata2 [root@server1 ~]# exportfs -rv 刷新 exporting *:/nfsdata3 exporting *:/nfsdata2 exporting *:/nfsdata [root@server1 ~]# showmount -e 挂载 Export list for server1: /nfsdata3 * /nfsdata2 * /nfsdata *三种模式同时应用
ReadWriteOnce -- 该volume只能被单个节点以读写的方式映射 ReadOnlyMany -- 该volume可以被多个节点以只读方式映射 ReadWriteMany -- 该volume可以被多个节点以读写的方式映射 在命令行中,访问模式可以简写为: RWO - ReadWriteOnce ROX - ReadOnlyMany RWX - ReadWriteMany [root@server2 pv]# cat pv1.yml apiVersion: v1 kind: PersistentVolume metadata: name: pv1 spec: capacity: storage: 5Gi volumeMode: Filesystem accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Recycle storageClassName: nfs nfs: path: /nfsdata server: 172.25.254.31 --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc1 spec: storageClassName: nfs accessModes: - ReadWriteOnce resources: requests: storage: 5Gi --- apiVersion: v1 kind: Pod metadata: name: test-pd-1 spec: containers: - image: nginx name: nginx volumeMounts: - mountPath: /usr/share/nginx/html name: pv1 volumes: - name: pv1 persistentVolumeClaim: claimName: pvc1 [root@server2 pv]# cat pv2.yml apiVersion: v1 kind: PersistentVolume metadata: name: pv2 spec: capacity: storage: 10Gi volumeMode: Filesystem accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Recycle storageClassName: nfs nfs: path: /nfsdata2 server: 172.25.254.31 --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc2 spec: storageClassName: nfs accessModes: - ReadWriteMany resources: requests: storage: 10Gi --- apiVersion: v1 kind: Pod metadata: name: test-pd-2 spec: containers: - image: nginx name: nginx volumeMounts: - mountPath: /usr/share/nginx/html name: pv2 volumes: - name: pv2 persistentVolumeClaim: claimName: pvc2 [root@server2 pv]# cat pv3.yml apiVersion: v1 kind: PersistentVolume metadata: name: pv3 spec: capacity: storage: 15Gi volumeMode: Filesystem accessModes: - ReadOnlyMany persistentVolumeReclaimPolicy: Recycle storageClassName: nfs nfs: path: /nfsdata3 server: 172.25.254.31 --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc3 spec: storageClassName: nfs accessModes: - ReadOnlyMany resources: requests: storage: 10Gi --- apiVersion: v1 kind: Pod metadata: name: test-pd-3 spec: containers: - image: nginx name: nginx volumeMounts: - mountPath: /usr/share/nginx/html name: pv3 volumes: - name: pv3 persistentVolumeClaim: claimName: pvc3 [root@server2 pv]# kubectl apply -f . persistentvolume/pv1 created persistentvolumeclaim/pvc1 created pod/test-pd-1 created persistentvolume/pv2 created persistentvolumeclaim/pvc2 created pod/test-pd-2 created persistentvolume/pv3 created persistentvolumeclaim/pvc3 created pod/test-pd-3 createdpod的运行状态
[root@server2 pv]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES test-pd-1 1/1 Running 0 2m27s 10.244.1.10 server3 <none> <none> test-pd-2 1/1 Running 0 2m27s 10.244.1.11 server3 <none> <none> test-pd-3 1/1 Running 0 2m27s 10.244.2.7 server4 <none> <none>测试访问
[root@server2 pv]# curl 10.244.1.10 www.westos.org 在nfs数据输出端更改文件内容 [root@server1 ~]# cd /nfsdata [root@server1 nfsdata]# ls index.html www.westos.org [root@server1 nfsdata]# vim index.html [root@server1 nfsdata]# cat index.html www.westos.org www.westos.org www.westos.org www.westos.org www.westos.org www.westos.org 测试 [root@server2 pv]# curl 10.244.1.10 www.westos.org www.westos.org www.westos.org www.westos.org www.westos.org www.westos.org指定进入test-pd-1容器内部更改文件
[root@server2 pv]# kubectl exec -it test-pd-1 -- bash root@test-pd-1:/# cd /usr/share/nginx/html/ root@test-pd-1:/usr/share/nginx/html# ls index.html 这个文件是在nfs数据输出端超级用户创建的在容器内是不可以更改的 root@test-pd-1:/usr/share/nginx/html# echo www.qq.com > index.html bash: index.html: Permission denied root@test-pd-1:/usr/share/nginx/html# echo www.qq.com > test.html 只能新建 root@test-pd-1:/usr/share/nginx/html# ls -l * -rw-r--r-- 1 root root 90 Jul 4 16:23 index.html -rw-r--r-- 1 nobody nogroup 11 Jul 4 16:30 test.html [root@server2 pv]# curl 10.244.1.10 www.westos.org www.westos.org www.westos.org www.westos.org www.westos.org www.westos.org [root@server2 pv]# curl 10.244.1.10/test.html www.qq.com指定进入test-pd-2容器内部更改文件
[root@server2 pv]# kubectl exec -it test-pd-2 -- bash root@test-pd-2:/# cd /usr/share/nginx/html/ root@test-pd-2:/usr/share/nginx/html# echo www.baidu.com > index.html root@test-pd-2:/usr/share/nginx/html# ls index.html root@test-pd-2:/usr/share/nginx/html# cat index.html www.baidu.com [root@server1 ~]# cd /nfsdata2 [root@server1 nfsdata2]# ls index.html [root@server1 nfsdata2]# cat index.html www.baidu.com [root@server2 pv]# curl 10.244.1.11 www.baidu.com指定进入test-pd-3容器内部更改文件
[root@server2 pv]# kubectl exec -it test-pd-3 -- bash root@test-pd-3:/# cd /usr/share/nginx/html/ root@test-pd-3:/usr/share/nginx/html# ls root@test-pd-3:/usr/share/nginx/html# echo www.taobao.com > index.html bash: index.html: Read-only file system 没有写的权限,nfsdata3目录只能读。容器内不可以更改 外部可以更改 [root@server1 nfsdata2]# cd /nfsdata3 [root@server1 nfsdata3]# echo www.pinduoduo.com > index.html [root@server1 nfsdata3]# ls index.html [root@server1 nfsdata3]# cat index.html www.pinduoduo.com [root@server2 pv]# curl 10.244.2.7 成功访问 www.pinduoduo.com