Kubernetes 中 Volume 用来储存数据,它被定义在 Pod 上,可以被 Pod 里的多个容器挂载。
介绍
一般存储分分以下几类:
- SAN: iSCSI, …
- NAS: NFS, CIFS, …
- 分布式存储:GlusterFS, Ceph RBD, CephFS, …
- 云存储:EBS, Cinder Volume, …
Volume 类型
Kubernetes 存储类型包括:
- emptyDir
- hostPath
- persistentVolumeClaim
- awsElasticBlockStore
- azureDisk
- azureFile
- cephfs
- cinder
- configMap
- csi(Container Storage Interface)
- ephemeral
- fc
- flexVolume
- flocker
- gcePersistentDisk
- gitRepo
- glusterfs
- hostPath
- iscsi
- nfs
- photonPersistentDisk
- portworxVolume
- quobyte
- rbd
- scaleIO
- secret
- storageos
- vsphereVolume
查看帮助:kubectl explain pod.spec.volumes
Volume 架构
说明:
- Storage 有 Storage Admin 管理
- PV 有 Cluster Admin 管理
- PVC 由 Users and Developers 管理
Access Modes
- ReadWriteOnce
- ReadOnlyMany
- ReadWriteMany
- ReadWriteOncePod
Volume 生命周期
- Volume 与 Pod 的生命周期相同,与 Pod 中容器的生命周期不相关,即容器终止或重启时,Volume 中的数据也不会丢失
- 当 Pod 被删除时,Volume 的数据可能被清理,这与 Volume 的类型相关,其中
- emptyDir 类型的 Volume 数据会丢失
- PV 类型的数据则不会丢失
Volume 资源使用
spec:
volumes:
...
containers:
volumeMounts:
...
emptyDir
emptyDir
是一个空目录,它的生命周期和所属的 Pod 是完全一致的。emptyDir 类型的 Volume 在 Pod 分配到 Node 上时会被创建,Kubernetes 会在 Node 上自动分配一个目录或内存。这个目录的初始内容为空,当 Pod 从 Node 上移除(Pod 被删除或者 Pod 发生迁移)时,emptyDir 中的数据会被永久删除。
apiVersion: v1
kind: Pod
metadata:
name: volume-ed-pod
namespace: default
spec:
containers:
- name: nginx
image: nginx:latest
imagePullPolicy: IfNotPresent
volumeMounts:
- name: cache-v1
mountPath: /cache
volumes:
- name: cache-v1
emptyDir: {}
$ kubectl exec -it volume-ed-pod -- sh
# cat /proc/mounts | grep cache
/dev/sda2 /cache xfs rw,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota 0 0
$ kubectl describe pod volume-ed-pod
...
Volumes:
cache-v1:
Type: EmptyDir (a temporary directory that shares a pod's lifetime)
Medium:
SizeLimit: <unset>
...
说明:
- 容器崩溃并不会导致 Pod 被从节点上移除,因此容器崩溃时
emptyDir
卷中的数据是安全的。
hostPath
hostPath 类型的 Volume 允许用户挂载 Node 宿主机上的文件或目录到 Pod 中
- 由于每个节点上的文件都不同,具有相同配置(例如:从 podTemplate 创建的)的 Pod 在不同节点上的行为可能会有所不同。
- 在底层主机上创建的文件或目录只能由 root 写入。您需要在特权容器中以 root 身份运行进程,或修改主机上的文件权限才可以写入 hostPath 卷。
场景中:
- 运行中的容器需要访问 Docker 内部的容器,使用 /var/lib/docker 来做为 hostPath 让容器内应用可以直接访问 Docker 的文件系统。
- 在容器中运行 cAdvisor,使用 /dev/cgroups 来做为 hostPath。
- 和 DaemonSet 搭配使用,用来操作主机文件。例如:日志采集方案 FLK 中的 FluentD 就采用这种方式来加载主机的容器日志目录,达到收集本主机所有日志的目的。
下面演示示例:
apiVersion: v1
kind: Pod
metadata:
name: volume-hp-pod
namespace: default
spec:
containers:
- name: nginx
image: nginx:latest
imagePullPolicy: IfNotPresent
volumeMounts:
- name: cache-hp
mountPath: /cache
volumes:
- name: cache-hp
hostPath:
path: /data/pod/volume
type: DirectoryOrCreate
NFS
apiVersion: v1
kind: Pod
metadata:
name: volume-nfs-pod
namespace: default
spec:
containers:
- name: nginx
image: nginx:latest
imagePullPolicy: IfNotPresent
volumeMounts:
- name: cache-nfs
mountPath: /cache
volumes:
- name: cache-nfs
nfs:
path: /data/nfs/volume
server: nfs.kb.cx
$ kubectl describe pod volume-nfs-pod
...
Volumes:
cache-nfs:
Type: NFS (an NFS mount that lasts the lifetime of a pod)
Server: nfs.kb.cx
Path: /data/nfs/volume
ReadOnly: false
...
# 对应的 node 节点
root@k8s-node-1:~# docker inspect id
...
"Mounts": [
{
"Type": "bind",
"Source": "/var/lib/kubelet/pods/95e8a281-79ea-4383-ac1c-6e8914dc468d/volumes/kubernetes.io~nfs/cache-nfs",
"Destination": "/cache",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
...
root@k8s-node-1:~# ls /var/lib/kubelet/pods/95e8a281-79ea-4383-ac1c-6e8914dc468d/volumes/kubernetes.io~nfs/cache-nfs
root@k8s-node-1:~# cat /proc/mounts | grep nfs.kb.cx
nfs.kb.cx:/data/nfs/volume /var/lib/kubelet/pods/95e8a281-79ea-4383-ac1c-6e8914dc468d/volumes/kubernetes.io~nfs/cache-nfs nfs4 rw,relatime,vers=4.2,rsize=524288,wsize=524288,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=172.20.0.82,local_lock=none,addr=172.20.0.132 0 0
说明:
- 使用 NFS 时,kubelet 先将 NFS 共享盘挂载到宿主机,然后 mount 到对用的容器上
- path 也可以是子目录,且必须在 NFS 中存在的子目录,如在 yaml 中配置
path: /data/nfs/volume/abc
时,挂载情况为:
root@k8s-node-1:~# nfs.kb.cx:/data/nfs/volume/abc /var/lib/kubelet/pods/1d3867a9-7401-4c9f-a0b7-38a3cc67d477/volumes/kubernetes.io~nfs/cache-nfs nfs4 rw,relatime,vers=4.2,rsize=524288,wsize=524288,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=172.20.0.82,local_lock=none,addr=172.20.0.132 0 0
Local Persistent Volume
Local Persistent Volume(本地持久化存储)
指的就是利用机器上的磁盘来存放业务需要持久化的数据
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: local-test
spec:
serviceName: "local-service"
replicas: 3
selector:
matchLabels:
app: local-test
template:
metadata:
labels:
app: local-test
spec:
containers:
- name: test-container
image: k8s.gcr.io/busybox
command:
- "/bin/sh"
args:
- "-c"
- "sleep 100000"
volumeMounts:
- name: local-vol
mountPath: /usr/test-pod
volumeClaimTemplates:
- metadata:
name: local-vol
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "local-storage"
resources:
requests:
storage: 368Gi
$ kubectl get pvc
NAMESPACE NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
default csi-cephfs-pvc Bound pvc-878a667c-8e34-11ec-b8d5-fa163e06eeca 2Gi RWX csi-cephfs-sc 1h
持久化存储
持久化存储的 3 中方式:
- StorageClass
- PersistentVolume
- PersistentVolumeClaim
存储资源的访问方式:
架构
持久化存储 <---> PV <--1:1--> PVC <--1:n--> Pod(mount Volume)
PV
PV(PersistentVolume,持久化卷)
是 Volume 的一种类型,是对底层的共享存储的一种抽象。PV 由集群管理员进行创建和配置,就像 节点 (Node)
是集群中的资源一样,PV 也是集群资源的一种:
- PV 包含存储类型、存储大小和访问模式(accessModes,参考)
- PV 的生命周期独立于 Pod
- PV 是
集群级别
的资源,并不属于某个 命名空间
- 帮助命令:
kubectl explain pv.spec
下面演示示例:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pvnfs1
labels:
backend: nfs
spec:
accessModes:
- ReadWriteOnce
- ReadWriteMany
capacity:
storage: 5Gi
nfs:
path: /data/nfs/volume/abc
server: nfs.kb.cx
root@k8s-master:~/manifests/volume# kubectl apply -f pv-nfs.yaml
persistentvolume/pvnfs1 created
root@k8s-master:~/manifests/volume# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvnfs1 5Gi RWO,RWX Retain Available 3s
PVC
帮助命令:kubectl explain pvc.spec
PVC(PersistentVolumeClaim,持久化卷声明)
是用户对存储资源的一种请求,PVC 和 Pod 比较类似:
- Pod 消耗的是节点资源,PVC 消耗的是 PV 资源
- Pod 可以请求 CPU 和内存,PVC 可以请求特定的存储空间和访问模式
- 对于真正使用存储的用户不需要关心底层的存储实现细节,只需要直接使用 PVC 即可
- PVC 是
命名空间
级别的资源,PV
可以与任何命名空间的 PVC
资源绑定
下面演示示例:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvcnfs1
namespace: default
spec:
accessModes:
- ReadWriteOnce
- ReadWriteMany
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: Pod
metadata:
name: pod-pvc-nfs
namespace: default
spec:
containers:
- name: nginx
image: nginx:latest
imagePullPolicy: IfNotPresent
volumeMounts:
- name: cache-v1
mountPath: /cache
volumes:
- name: cache-v1
persistentVolumeClaim:
claimName: pvcnfs1
$ kubectl apply -f pod-pvc-nfs-vol.yaml
persistentvolumeclaim/pvcnfs1 created
pod/pod-pvc-nfs configured
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvnfs1 5Gi RWO,RWX Retain Bound default/pvcnfs1 19m
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
pvcnfs1 Bound pvnfs1 5Gi RWO,RWX 9s
# 对应的 node 节点
root@k8s-node-1:~# cat /proc/mounts |grep nfs.kb.cx
nfs.kb.cx:/data/nfs/volume/abc /var/lib/kubelet/pods/136d3cf5-285c-4b38-8e96-1192878a5f43/volumes/kubernetes.io~nfs/pvnfs1 nfs4 rw,relatime,vers=4.2,rsize=524288,wsize=524288,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=172.20.0.82,local_lock=none,addr=172.20.0.132 0 0
说明:
- volume 也是通过先挂载在 node 节点,在映射到容器内部
StorageClass
SC(StorageClass,储存类别)
由于不同的应用程序对于存储性能的要求也不尽相同,比如:读写速度、并发性能、存储大小等。如果只能通过 PVC 对 PV 进行静态申请,显然这并不能满足各种应用对于存储的需求。为了解决这一问题,Kubernetes 引入了一个新的资源对象:StorageClass,通过 StorageClass 的定义,集群管理员可以先将存储资源定义为不同类型的资源,比如快速存储、慢速存储等
当用户通过 PVC 对存储资源进行申请时,StorageClass 会使用 Provisioner(不同 Volume 对应不同的 Provisioner)来自动创建用户所需 PV。这样应用就可以随时申请到合适的存储资源,而不用担心集群管理员没有事先分配好需要的 PV
- 自动创建的 PV 以
${namespace}-${pvcName}-${pvName}
这样的命名格式创建在后端存储服务器上的共享数据目录中 - 自动创建的 PV 被回收后会以
archieved-${namespace}-${pvcName}-${pvName}
这样的命名格式存在后端存储服务器上
其他
TopoLVM
是一个用于本地存储的新 CSI 插件,可使用 LVM
动态配置卷