目录导航
一、Volume
默认情况下容器中的磁盘文件是非持久化的,对于运行在容器中的应用来说面临两个问题,第一:当容器挂掉kubelet将重启启动它时,文件将会丢失;第二:当 Pod 中同时运行多个容器,容器之间需要共享文件时。Kubernetes的Volume解决了这两个问题。
Kubernetes 支持很多类型的卷。 Pod 可以同时使用任意数目的卷类型。 临时卷类型的生命周期与 Pod 相同,但持久卷可以比 Pod 的存活期长。 当 Pod 不再存在时,Kubernetes 也会销毁临时卷;不过 Kubernetes 不会销毁 持久卷。对于给定 Pod 中任何类型的卷,在容器重启期间数据都不会丢失。
Kubernetes支持Volume类型有:
- emptyDir、hostPath、local、gcePersistentDisk
- awsElasticBlockStore、azureFileVolume、azureDisk
- nfs、iscsi、fc、secret、downwardAPI
- flocker、glusterfs、rbd、cephfs、gitRepo
- projected、vsphereVolume、persistentVolumeClaim
- Quobyte、PortworxVolume、ScaleIO、StorageOS
1.1 emptyDir
当 Pod 分派到某个 Node 上时,emptyDir 卷会被创建,并且在 Pod 在该节点上运行期间,卷一直存在。 就像其名称表示的那样,卷最初是空的。当 Pod 因为某些原因被从节点上删除时,emptyDir 卷中的数据也会被永久删除。目的是同一 Pod 内的不同容器之间可以共享工作过程中产生的文件。
emptyDir 的用途:
- 缓存空间,例如基于磁盘的归并排序。
- 为耗时较长的计算任务提供检查点,以便任务能方便地从崩溃前状态恢复执行。
- 在 Web 服务器容器服务数据时,保存内容管理器容器获取的文件。
# cat emptydirdemo.yml
apiVersion: v1
kind: Pod
metadata:
name: volume-emptydir
namespace: dev
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
volumeMounts: # 将logs-volume挂载到nginx容器中,对应的目录为 /var/log/nginx
- name: logs-volume
mountPath: /var/log/nginx
- name: busybox
image: busybox
command: ["/bin/sh","-c","tail -f /logs/access.log"] # 初始命令,动态读取指定文件中内容
volumeMounts: # 将logs-volume挂载到busybox容器中,对应的目录为 /logs
- name: logs-volume
mountPath: /logs
volumes: # 声明volume,name为logs-volume,类型为emptyDir
- name: logs-volume
emptyDir: {}
1.2 hostPath
由于EmptyDir中数据不会被持久化,它会随着Pod的结束而销毁,如果想简单地将数据持久化到主机中,可以选择HostPath。hostPath允许挂载Node上的文件系统到Pod里面去,如果Pod需要使用Node上的文件,可以使用hostPath。
HostPath 卷存在许多安全风险,最佳做法是尽可能避免使用 HostPath。 当必须使用 HostPath 卷时,它的范围应仅限于所需的文件或目录,并以只读方式挂载。
hostPath 的用法:
- 运行一个需要访问 Docker 内部机制的容器;可使用 hostPath 挂载 /var/lib/docker 路径。
- 在容器中运行 cAdvisor 时,以 hostPath 方式挂载 /sys。
- 允许 Pod 指定给定的 hostPath 在运行 Pod 之前是否应该存在,是否应该创建以及应该以什么方式存在。
# cat hostPathdemo.yml
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: nginx
name: nginx
volumeMounts:
- mountPath: /var/www/html
name: test-volume
volumes:
- name: test-volume
hostPath:
# 宿主上目录位置
path: /data
# 此字段为可选
type: Directory
1.3 local
Local 是Kubernetes集群中每个节点的本地存储(如磁盘,分区或目录),Local 卷只能用作静态创建的持久卷。尚不支持动态配置。
与 hostPath 卷相比,Local 卷能够以持久和可移植的方式使用,Kubernets 可自动对 Pod 进行调度,而无需手动将 Pod 调度到节点。
使用 Local 卷时,你需要设置 PersistentVolume 对象的 nodeAffinity 字段。 Kubernetes 调度器使用 PersistentVolume 的 nodeAffinity 信息来将使用 local 卷的 Pod 调度到正确的节点。
# cat local-pv.yml
apiVersion: v1
kind: PersistentVolume
metadata:
name: example-pv
spec:
capacity:
storage: 100Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Delete
storageClassName: local-storage
local:
path: /mnt/disks/ssd1
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- example-node
1.4 nfs
hostPath可以解决数据持久化的问题,但是一旦Node节点故障了,Pod如果转移到了别的节点,又会出现问题了,此时需要准备单独的网络存储系统,比较常用的有NFS、ISCSI。
NFS是一个网络文件存储系统,可以搭建一台NFS服务器,然后将Pod中的存储直接连接到NFS系统上,这样的话,无论Pod在任何节点上,只要与NFS可通就没问题。
# cat volume-nfs.yml
apiVersion: v1
kind: Pod
metadata:
name: volume-nfs
namespace: dev
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
volumeMounts:
- name: logs-volume
mountPath: /var/log/nginx
volumes:
- name: logs-volume
nfs:
server: 10.10.10.1
path: /root/data/nfs
二、Persistent Volumes
Pod 虽然可以直接挂载存储,但是管理不方便,特别是 Pod 的数量越来越多。K8S引入了一组叫作Persistent Volume Claim(PVC)和Persistent Volume(PV)的API对象,大大降低了用户声明和使用持久化Volume的门槛。
-
PersistentVolume(PV)
PV是集群中的的一部分,像Node一样是集群中的一种资源,用于描述一个具体的 Volume 属性,比如 Volume 的类型、挂载目录、远程存储服务器地址等。可以由管理员事先供应,或者使用存储类(Storage Class)来动态供应。就像节点是集群中的资源一样,PV 也是集群中的资源。PV 是 Volume 之类的卷插件,只是它们拥有独立于任何使用 PV 的 Pod 的生命周期。此 API 对象中记录了存储的实现细节,即 NFS、iSCSI 或特定于云供应商的存储系统。 -
PersistentVolumeClaim(PVC)
PVC是用户对存储的请求,用于描述 Pod 想要使用的持久化属性,比如存储大小、读写权限等。它与 Pod 相似。Pod 消耗节点资源,PVC 消耗 PV 资源。Pod 可以请求特定级别的资源(CPU 和内存)。同样PVC也可以请求特定的大小和访问模式(例如,可以要求 PV 卷能够以 ReadWriteOnce、ReadOnlyMany 或 ReadWriteMany 模式之一来挂载。 -
StorageClass(SC)
尽管 PVC 允许用户消耗抽象的存储资源,常见的情况是针对不同的 问题用户需要的是具有不同属性(如性能)的 PV 卷。 集群管理员需要能够提供不同性质的 PV,并且这些 PV 卷之间的差别不 仅限于卷大小和访问模式,同时又不能将卷是如何实现的这些细节暴露给用户。 为了满足这类需求,就有了 存储类(StorageClass) 资源。相当于充当 PV 的模板,自动为 PVC 创建 PV。
2.1 基本概念
2.1.1 生命周期
PV 卷是集群中的资源。PVC 是对这些资源的请求,也被用来执行对资源的申领检查。 PV 卷和 PVC 交互存在如下生命周期:
- 供应 (Provisioning):pv的创建
- 绑定 (Binding): pvc 绑定pv
- 使用 (Using): pod通过pvc使用vloume
- 释放 (Releasing): pod释放volume并删除pvc
- 回收 (Reclaiming): 回收pv
根据上述的5个阶段,存储卷的存在下面的4种状态:
- Available: 可用状态,表面此PV可被PVC绑定
- Bound: 绑定状态,表明PV已被分配给了PVC。
- Released: 释放状态,表明PVC解绑PV,但还未执行回收策略。
- Failed: 错误状态,表明PV发生错误。
2.1.2 供应
PV 卷的供应有两种方式:静态供应或动态供应。
- 静态供应
集群管理员创建若干 PV 卷。这些卷对象带有真实存储的细节信息,并且对集群用户可见。PV 卷对象存在于 Kubernetes API 中,可供用户消费。管理员创建PVC和Pod,Pod通过PVC使用PV提供的存储。
- 动态供应
动态供应就是在创建PVC之后,自动创建出PV。当管理员所创建的所有静态 PV 卷都无法与用户的 PersistentVolumeClaim 匹配, 集群可以尝试为该 PVC 动态供应一个存储卷。 这一供应操作是基于 StorageClass 来实现的:PVC 必须请求某个 StorageClass,同时集群管理员必须 已经创建并配置了该类,这样动态供应卷的动作才会发生。 如果 PVC 指定存储类为"",则相当于为自身禁止使用动态供应的卷。
2.1.3 绑定
在Kubernetes中,会动态的将PVC与可用的PV的进行绑定。Master节点中的控制回路会监测新的 PVC 对象,寻找与之匹配的 PV 卷(如果有), 并将二者绑定到一起。 如果新的 PVC 动态供应了 PV 卷,则控制回路总是将该 PV 卷绑定到这一 PVC 请求。 否则,用户总是能够获得他们所请求的资源,只是所获得的 PV 卷可能会超出所请求的配置。 一旦绑定关系建立,则 PVC 绑定就是排他性的,无论该 PVC 如何与 PV 卷建立绑定关系。 PVC 与 PV 卷之间的绑定是一种一对一的映射,实现上使用 ClaimRef 来描述 PV 卷 与 PVC 间的双向绑定关系。
如果找不到匹配的 PV 卷,PVC 会无限期地处于未绑定状态。 当与之匹配的 PV 卷可用时,PVC 会被绑定。 例如,即使某集群上供应了很多 50 G大小的 PV 卷,也无法与请求 100 G大小存储的 PVC 匹配。当新的 100 G PV 卷被加入到集群时,该 PVC 才有可能被绑定。
2.1.4 使用
Pod 将 PVC 作为卷来使用,Kubernetes集群会检查 PVC 查找绑定的PV,并将其挂接至Pod。对于支持多种访问方式的卷,用户在使用 PVC 作为卷时,可以指定期望的访问方式。一旦用户拥有了一个已经绑定的PVC,被绑定的PV就归该用户所有。用户能够通过在Pod的存储卷中包含的PVC,从而访问所占有的PV。
2.1.5 释放
当用户对存储资源使用哪个完毕后,用户可以删除 PVC,与该 PVC 绑定的 PV 将会被标记为已释放,但还不能立刻与其他 PVC 进行绑定。通过之前 PVC 写入的数据可能还留在存储设备上,只有在清除之后该 PV 才能继续使用。
2.1.6 回收
当用户不再使用其存储卷时,他们可以从 API 中将 PVC 对象删除,从而允许该资源被回收再利用。PV 对象的回收策略(Reclaim Policy)用于设置与之绑定的 PVC 释放资源之后,对于遗留数据的处理。
- 保留(Retained)
保留回收策略允许用户手动回收资源。当 PVC 对象被删除时,PV 卷仍然存在,对应的数据卷被视为"已释放(released)"。由于以前的数据仍然保留在数据中,所以它对于其他的PVC是不可用的。管理员能够通过下面的步骤手工回收存储卷:
1)删除PV:在PV被删除后,在外部设施中相关的存储资产仍然还在;
2)根据情况,手动清除所关联的存储资产上的数据。
3)手动删除存储资产,如果需要重用这些存储资产,则需要创建新的PV。
- 删除(Deleted)
对于支持 Delete 回收策略的卷插件,删除动作会将 PersistentVolume 对象从 Kubernetes 中移除,同时也会从外部基础设施(如 AWS EBS、GCE PD、Azure Disk 或 Cinder 卷)中移除所关联的存储资产。
- 回收(Recycle)
回收策略 Recycle 已被废弃。取而代之的建议方案是使用动态供应。
2.2 持久化卷
在Kubernetes中,PV 通过各种插件进行实现。
2.2.1 持久化卷类型
- awsElasticBlockStore、azureDisk、cephfs
- csi、fc、flexVolume、gcePersistentDisk、glusterfs
- hostPath、iscsi、local、nfs、portworxVolume
- rbd、vsphereVolume
2.2.2 存储卷模式
针对 PV 持久卷,Kubernetes 支持两种卷模式(volumeModes):Filesystem(文件系统) 和 Block(块)。 volumeMode 是一个可选的 API 参数,如果不进行设定,则默认为Filesystem。
volumeMode 属性设置为 Filesystem 的卷会被 Pod 挂载(Mount) 到某个目录。 如果卷的存储来自某块设备而该设备目前为空,Kuberneretes 会在第一次挂载卷之前 在设备上创建文件系统。
2.2.3 存储卷访问模式
PersistentVolume 卷可以以资源提供者支持的任何方式挂载到主机上。 如下表所示,提供者(驱动)的能力不同,每个 PV 卷的访问模式都会设置为该卷所支持的特定模式。 例如NFS可以支持多个读写客户,但是某个特定的 NFS PV 卷可能在服务器上以只读的方式导出。每个PV都有自己的一系列的访问模式,这些访问模式取决于PV的能力。
- ReadWriteOnce:卷可以被单个节点以读/写模式挂载
- ReadOnlyMany: 卷可以被多个节点以只读方式挂载
- ReadWriteMany:卷可以读写模式被多个节点同时加载
在 CLI 下,访问模式缩写为:
- RWO:ReadWriteOnce
- ROX: ReadOnlyMany
- RWX:ReadWriteMany
2.3 实例演示
2.3.1 创建PV
# cat pv-demo.yml
apiVersion: v1
kind: PersistentVolume
metadata:
name: local-pv
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
local:
path: /data/k8s/pv/pv1
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- loc-k8s-node1
# kubectl get pv -A
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
local-pv 10Gi RWO Retain Available local-storage 1d
2.3.2 创建PVC
# cat pvc-demo.yml
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: pvc1
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-storage
resources:
requests:
storage: 8Gi
2.3.2 创建PVC
# cat deploy-nginx.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: local-pv
volumes:
- name: local-pv
persistentVolumeClaim:
claimName: pvc1
# kubectl get pvc -A
NAMESPACE NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
default pvc1 Bound local-pv 10Gi RWO local-storage 1d
Reference:
https://kubernetes.io/zh/docs/concepts/storage/volumes
https://kubernetes.io/zh/docs/concepts/storage/persistent-volumes/
https://www.kubernetes.org.cn/4069.html
http://docs.kubernetes.org.cn/429.html