K8s存储emptyDir、hostPath、pv和pvc
pod容器组在宕机或者删除以后,数据会随着pod的销毁而消失,所以需要使用一些存储方案来做数据的持久化,目前有很多存储方案,主要有emptyDir、hostPath、NFS、云存储
我们下面就通过例子来看看上述几种存储方式的区别
1.emptyDir
emptyDir类型的volume在pod分配到node上时被创建,kubernetes会在node上自动分配一个目录,因此无需指定宿主机node上对应的目录文件。这个目录的初始内容为空,当Pod从node上移除时,emptyDir中的数据会被永久删除。emptyDir Volume主要用于某些应用程序无需永久保存的临时目录
例:
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-emptydir spec: selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: nodeName: k8s-node1 # 指定部署节点 containers: - name: nginx image: nginx volumeMounts: # 挂载容器内部目录 - mountPath: /data/empty name: emptydir #存储卷名称 volumes: - name: emptydir emptyDir: {}
2.hostpath
HostPath 卷存在许多安全风险,最佳做法是尽可能避免使用 HostPath。 当必须使用HostPath卷时,它的范围应仅限于所需的文件或目录,并以只读方式挂载
例:
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-hostpath spec: selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: nodeName: k8s-node1 # 指定部署节点 containers: - name: nginx image: nginx volumeMounts: # 挂载容器内部目录 - mountPath: /data/share name: localpath #存储卷名称 volumes: - name: localpath hostPath: path: /opt/hostpath # 这里会在node节点创建目录并对应容器内部的mountPath type: DirectoryOrCreate # 类型:没有就创建,有就不创建
用户可以选择性地为 hostPath 卷指定 type
支持的 type 值如下:
DirectoryOrCreate 宿主机上不存在创建此目录 Directory 必须存在挂载目录 FileOrCreate 宿主机上不存在挂载文件就创建 File 必须存在文件
当使用这种类型的卷时要小心,因为:
HostPath 卷可能会暴露特权系统凭据(例如 Kubelet)或特权 API(例如容器运行时套接字), 可用于容器逃逸或攻击集群的其他部分。
具有相同配置(例如基于同一 PodTemplate 创建)的多个 Pod 会由于节点上文件的不同 而在不同节点上有不同的行为。
下层主机上创建的文件或目录只能由 root 用户写入。你需要在 特权容器 中以 root 身份运行进程,或者修改主机上的文件权限以便容器能够写入 hostPath卷
3.pv/pvc
PersistentVolume(PV)是群集中的一块存储(类似一块磁盘),由管理员配置或使用存储类动态配置。 它是集群中的资源,就像节点是集群资源一样。 PV是容量插件,如Volumes,但其生命周期独立于使用PV的任何单个pod。 此API对象捕获存储实现的详细信息,包括NFS,iSCSI或特定于云提供程序的存储系统
PersistentVolumeClaim(PVC)是一个持久化存储卷,我们在创建pod时可以定义这个类型的存储卷。 它类似于一个pod。 Pod消耗节点资源,PVC消耗PV资源。 Pod可以请求特定级别的资源(CPU和内存)。 pvc在申请pv的时候也可以请求特定的大小和访问模式(例如,可以一次读/写或多次只读)
PV的访问模式
ReadWriteOnce(RWO) 可读可写,但只支持被单个节点挂载。 ReadOnlyMany(ROX) 只读,可以被多个节点挂载。 ReadWriteMany(RWX) 多路可读可写。这种存储可以以读写的方式被多个节点共享。不是每一种存储都支持这三种方式,像共享方式,目前支持的还比较少,比较常用的是 NFS。在 PVC 绑定 PV 时通常根据两个条件来绑定,一个是存储的大小,另一个就是访问模式。
PV的回收策略
Retain 不清理, 保留 Volume(需要手动清理) Recycle 删除数据,即 rm -rf /thevolume/*(只有 NFS 和 HostPath 支持) Delete 删除存储资源,比如删除 AWS EBS 卷(只有 AWS EBS, GCE PD, Azure Disk 和 Cinder 支持)
PVC状态
Available(可用)——一块空闲资源还没有被任何声明绑定 Bound(已绑定)——卷已经被声明绑定 Released(已释放)——声明被删除,但是资源还未被集群重新声明 Failed(失败)——该卷的自动回收失败
下面来一组例子:
pv.yaml apiVersion: v1 kind: PersistentVolume metadata: name: pv-nfs-1 labels: app: pv-nfs-1 spec: volumeMode: Filesystem accessModes: ["ReadWriteMany","ReadWriteOnce"] persistentVolumeReclaimPolicy: Retain capacity: storage: 2Gi volumeMode: Filesystem mountOptions: - hard - nfsvers=4.1 nfs: path: /data/nas1 server: 192.168.137.10 --- apiVersion: v1 kind: PersistentVolume metadata: name: pv-nfs-2 labels: app: pv-nfs-2 spec: volumeMode: Filesystem accessModes: ["ReadWriteMany","ReadWriteOnce"] persistentVolumeReclaimPolicy: Retain capacity: storage: 3Gi volumeMode: Filesystem mountOptions: - hard - nfsvers=4.1 nfs: path: /data/nas2 server: 192.168.137.10 --- apiVersion: v1 kind: PersistentVolume metadata: name: pv-nfs-3 labels: app: pv-nfs-3 spec: volumeMode: Filesystem accessModes: ["ReadWriteMany","ReadWriteOnce"] persistentVolumeReclaimPolicy: Retain capacity: storage: 4Gi # 申请多大的PV,PVC会申请一个匹配或者稍大于的PV资源 volumeMode: Filesystem mountOptions: - hard - nfsvers=4.1 nfs: path: /data/nas3 server: 192.168.137.10 --- #pvc.yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc-nfs spec: accessModes: ["ReadWriteMany"] resources: requests: storage: 1Gi selector: matchLabels: app: pv-nfs-2
我们可以用过命令查看卷状态
kubectl get pv/pvc
可以看到pvc和pv-nfs-2是bound状态
最后我们再重新编辑下deploy,挂载下这个pvc看看效果
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-pvc spec: selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: #nodeName: k8s-node1 # 指定部署节点 containers: - name: nginx image: nginx volumeMounts: # 挂载容器内部目录 - mountPath: /data/nfs name: nginx-nfs #存储卷名称 volumes: - name: nginx-nfs persistentVolumeClaim: claimName: pvc-nfs
注意:
我们每次创建pvc的时候,需要事先有划分好的pv,这样可能不方便,那么可以在创建pvc的时候直接动态创建一个pv这个存储类,pv事先是不存在的
pvc和pv绑定,如果使用默认的回收策略retain,那么删除pvc之后,pv会处于released状态,我们想要继续使用这个pv,需要手动删除pv,kubectl delete pv pv_name,删除pv,不会删除pv里的数据,当我们重新创建pvc时还会和这个最匹配的pv绑定,数据还是原来数据,不会丢失
删除流程:POD->PVC->PV