一、Volume介绍:
在k8s中Pod的生命周期可能很短,会被频繁地销毁和创建。容器销毁时,保存在容器内部文件系统中的数据都会被清除。
为了持久化保存容器数据,k8s 提供了卷(Volume)的抽象概念来解决上述问题。
卷(Volume)的核心是一个目录,其中可能存有数据,Pod 中的容器可以访问该目录中的数据。 所采用的特定的卷类型将决定该目录如何形成的、使用何种介质保存数据以及目录中存放的内容。
Kubernetes Volume 支持多种卷类型,包括 emptyDir、hostPath、nfs、local、configMap、AWSElasticBlockStore、Cinder、CSI等。
按照存储可大概划分为:
临时存储:emptydir
半持久化存储:hostpath
持久化存储:nfs、ceph、Cinder等
接下来分别介绍几种常用类型:emptyDir、hostPath、nfs(PV、PVC)
二、Volume:emptyDir
emptyDir 是最基础的 Volume 类型。正如其名字所示,一个 emptyDir Volume 是 Host 上的一个空目录。
emptyDir Volume 对于容器来说是持久的,对于 Pod 则不是。当 Pod 从节点删除时,Volume 的内容也会被删除。但如果只是容器被销毁而 Pod 还在,则 Volume 不受影响。(emptyDir Volume 的生命周期与 Pod 一致)
Pod 中的所有容器都可以共享 Volume,它们可以指定各自的挂载路径。
验证示例:
apiVersion: v1 kind: Pod metadata: name: producer-consumer spec: containers: - image: busybox name: producer volumeMounts: - mountPath: /producer_dir #挂载到容器中的路径 name: shared-volume args: - /bin/sh - -c - echo "hello world" > /producer_dir/hello; sleep 3000 - image: busybox name: consumer volumeMounts: - mountPath: /consumer_dir #挂载到容器中的路径 name: shared-volume args: - /bin/sh - -c - cat /consumer_dir/hello; sleep 3000 volumes: - name: shared-volume emptyDir: {} #指定存储方式为emptydir
- 创建一个名为[shared-volume]的emptyDir的卷
- 容器producer 将 shared-volume挂载到 producer_dir 目录
- 容器producer 通过echo 命令向hello 文件中写入文本-hello world
- 容器consumer 将 shared-volume挂载到 consumer_dir目录
- 容器consumer 通过cat命令读取consumer_dir中hello文件内容
通过命令:kubectl logs 查看读取内容。
# 输出producer-consumer Pod中容器 consumer 的日志: kubectl logs producer-consumer -c consumer hello world
显示容器 consumer
成功读取到 producer
写入的数据,验证了两个容器共享 emptyDir Volume。
三、Volume:hostPath
hostPath
卷能将主机节点文件系统上的文件或目录挂载到你的 Pod 中。 虽然这不是大多数 Pod 需要的,但是它为一些应用程序提供了强大应急方式。
hostPath
的一些用法有:
- 运行一个需要访问 Docker 内部机制的容器;可使用
hostPath
挂载/var/lib/docker
路径。 - 在容器中运行 cAdvisor 时,以
hostPath
方式挂载/sys
。 - 允许 Pod 指定给定的
hostPath
在运行 Pod 之前是否应该存在,是否应该创建以及应该以什么方式存在。
除了必需的 path
属性之外,用户可以选择性地为 hostPath
卷指定 type,下表为type取值
取值 | 行为 |
---|---|
空字符串(默认)用于向后兼容,这意味着在安装 hostPath 卷之前不会执行任何检查。 | |
DirectoryOrCreate |
如果在给定路径上什么都不存在,那么将根据需要创建空目录,权限设置为 0755,具有与 kubelet 相同的组和属主信息。 |
Directory |
在给定路径上必须存在的目录。 |
FileOrCreate |
如果在给定路径上什么都不存在,那么将在那里根据需要创建空文件,权限设置为 0644,具有与 kubelet 相同的组和所有权。 |
File |
在给定路径上必须存在的文件。 |
Socket |
在给定路径上必须存在的 UNIX 套接字。 |
CharDevice |
在给定路径上必须存在的字符设备。 |
BlockDevice |
在给定路径上必须存在的块设备。 |
hostPath示例:
apiVersion: v1 kind: Pod metadata: name: localpod spec: containers: - image: busybox name: local volumeMounts: - mountPath: /localdir #挂载到容器中的路径 name: mydir - mountPath: loacl.txt name: myfile args: - /bin/sh - -c - echo "local dir test" > /localdir/test.txt; - -c - cat loacl.txt; volumes: - name: mydir hostPath: # 确保文件所在目录成功创建。 path: /var/local type: DirectoryOrCreate - name: myfile hostPath: # 确保文件创建成功 path: /var/local/test.txt type: FileOrCreate
步骤:
1、创建两个hostPath类型的Volume
2、将这两个卷都挂载到local容器中
3、容器启动时,写入文件内容“local dir test”,并读取出来。
四、Volume:持久化存储-nfs
说到持久化存储:就需要提k8s中提供的重要的几种资源类型:
- PersistentVolume (PV): 外部存储系统中的一块存储空间,由管理员创建和维护。与 Volume 一样,PV 具有持久性,生命周期独立于 Pod。
- StorageClass:另外一种提供存储资源的方式, 提供更多的层级选型, 如iops等参数。 但是具体的参数与提供方是绑定的。 如aws和gce它们提供的storageclass的参数可选项是有不同的。
- PersistentVolumeClaim (PVC):是对 PV 的申请 (Claim)。PVC 通常由普通用户创建和维护。需要为 Pod 分配存储资源时,用户可以创建一个 PVC,指明存储资源的容量大小和访问模式(比如只读)等信息,Kubernetes 会查找并提供满足条件的 PV。
PersistentVolume :
生命周期:
- Provisioning- 配置阶段, 分为static, dynamic两种方式。静态的方式是创建一系列的pv,然后pvc从pv中请求。 动态的方式是基于storageclass的。
- Binding - 绑定阶段, pvc根据请求的条件筛选并绑定对应的pv。 一定pvc绑定pv后, 就会排斥其它绑定,即其它pvc无法再绑定同一个pv,即使这个pv设定的access mode允许多个node读写。 此外 ,pvc 如何匹配不到相应条件的pv, 那么就会显示unbound状态, 直到匹配为止。 需要注意的是,pvc请求100Gi大小的存储,即使用户创建了很多50Gi大小的存储, 也是无法被匹配的。
- Using- 使用阶段, pods 挂载存储, 即在pod的template文件中定义volumn使用某个pvc。
- Releasing - 释放阶段, 当pvc对象被删除后, 就处于释放阶段。 在这个阶段, 使用的pv还不能被其它的pvc请求。 之前数据可能还会留存下来, 取决于用户在pv中设定的policy, 见persistentVolumeReclaimPolicy。
- Reclaiming - 重声明阶段。 到这个阶段, 会告诉cluster如何处理释放的pv。 数据可能被保留(需要手工清除), 回收和删除。动态分配的存储总是会被删除掉的。
- Recycling - 回收阶段。回收阶段会执行基本的递归删除(取决于volumn plugins的支持),把pv上的数据删除掉, 以使pv可以被新的pvc请求。 用户也可以自定义一个 recycler pod , 对数据进行删除。
卷模式:
PV支持两种卷模式:
-
Filesystem
:卷会被 Pod 挂载(Mount) 到某个目录。 如果卷的存储来自某块设备而该设备目前为空,Kuberneretes 会在第一次挂载卷之前 在设备上创建文件系统。 - Block:将卷作为原始块设备来使用。 这类卷以块设备的方式交给 Pod 使用,其上没有任何文件系统。 这种模式对于为 Pod 提供一种使用最快可能方式来访问卷而言很有帮助,Pod 和 卷之间不存在文件系统层。另外,Pod 中运行的应用必须知道如何处理原始块设备。
访问模式:
PersistentVolume 卷可以用资源提供者所支持的任何方式挂载到宿主系统上。 如下表所示,提供者(驱动)的能力不同,每个 PV 卷的访问模式都会设置为 对应卷所支持的模式值。 例如,NFS 可以支持多个读写客户,但是某个特定的 NFS PV 卷可能在服务器 上以只读的方式导出。每个 PV 卷都会获得自身的访问模式集合,描述的是 特定 PV 卷的能力。
访问模式有:
-
- ReadWriteOnce (RWO) -- 卷可以被一个节点以读写方式挂载;
- ReadOnlyMany (ROX) -- 卷可以被多个节点以只读方式挂载;
- ReadWriteMany (RWX) -- 卷可以被多个节点以读写方式挂载
卷插件 | ReadWriteOnce | ReadOnlyMany | ReadWriteMany |
---|---|---|---|
AWSElasticBlockStore | ✓ | - | - |
AzureFile | ✓ | ✓ | ✓ |
AzureDisk | ✓ | - | - |
CephFS | ✓ | ✓ | ✓ |
Cinder | ✓ | - | - |
CSI | 取决于驱动 | 取决于驱动 | 取决于驱动 |
FC | ✓ | ✓ | - |
FlexVolume | ✓ | ✓ | 取决于驱动 |
Flocker | ✓ | - | - |
GCEPersistentDisk | ✓ | ✓ | - |
Glusterfs | ✓ | ✓ | ✓ |
HostPath | ✓ | - | - |
iSCSI | ✓ | ✓ | - |
Quobyte | ✓ | ✓ | ✓ |
NFS | ✓ | ✓ | ✓ |
RBD | ✓ | ✓ | - |
VsphereVolume | ✓ | - | - (Pod 运行于同一节点上时可行) |
PortworxVolume | ✓ | - | ✓ |
ScaleIO | ✓ | ✓ | - |
StorageOS | ✓ | - | - |
回收策略:
目前的回收策略有:
- Retain -- 手动回收
- Recycle -- 基本擦除 (
rm -rf /thevolume/*
) - Delete -- 诸如 AWS EBS、GCE PD、Azure Disk 或 OpenStack Cinder 卷这类关联存储资产也被删除
目前,仅 NFS 和 HostPath 支持回收(Recycle)。 AWS EBS、GCE PD、Azure Disk 和 Cinder 卷都支持删除(Delete)
持久化存储验证:
1、nfs服务搭建:
所有节点安装nfs
yum install -y nfs-common nfs-utils
在master节点创建共享目录
[root@k8s-master k8s]# mkdir /nfsdata
授权共享目录
[root@k8s-master k8s]# chmod 666 /nfsdata
编辑exports文件
[root@k8s-master k8s]# cat /etc/exports /nfsdata *(rw,no_root_squash,no_all_squash,sync)
配置生效
[root@k8s-master k8s]# export -r
启动rpc和nfs(注意顺序)
[root@k8s-master k8s]# systemctl start rpcbind [root@k8s-master k8s]# systemctl start nfs
2、创建PV:
#创建Pv:name-pv01 apiVersion: v1 kind: PersistentVolume metadata: name: pv01 labels: name: pv01 spec: # 所有节点可共享访问 accessModes: ["ReadWriteMany"]
# 回收策略:清除PV数据
persistentVolumeReclaimPolicy:Recycle
# 指定PV的Class为 nfs
storageClassName:nfs capacity: # Pv大小1G storage: 1Gi #nfs 服务设置,以及目录设置 nfs: server: 192.168.115.6 path: /home
3、创建PVC:
#创建名为mypvc的PVC apiVersion: v1 kind: PersisitentVolumeClaim metadata: name: mypvc namespace: default spec: accessmodes: ["ReadWriteMany"] #所有节点可读写 resources: #指定资源说明 requests: #指定请求 storage: 4Gi #指定请求存储空间的大小
4、创建POD使用PVC
#创建Pod并使用PVC apiVersion: v1 kind: Pod metadata: name: pvnginx labels: app: pvnginx spec: containers: - name: my-pvnginx image: nginx ports: - name: http containerPort: 80 - name: https containerPort: 443 volumeMounts: - name: html mountPath: /data/html volumes: - name: html persistentVolumeClaim: #指明使用pvc模式 claimname: mypvc #指明使用的pvc名称
总结:
1、对于不需要持久化的数据可采用emptyDir类型卷来存储相关数据,其生命周期同Pod相同
2、当需要访问当前主机文件时,可以采用hostPath类型的卷来实现
3、PV、PVC在实际场景中应用较多,需要了解透彻:包括PV的动/静态创建、PVC、生命周期等基本概念
参考:
https://kubernetes.io/zh/docs/concepts/storage/
https://kubernetes.io/zh/docs/concepts/storage/