07-持久化存储和StorageClass

在k8s中部署的应用都是以pod容器的形式运行的,假如我们部署MySQL、Redis等数据库,需要对这些数据库产生的数据做备份。因为Pod是有生命周期的,如果pod不挂载数据卷,那pod被删除或重启后这些数据会随之消失,如果想要长久的保留这些数据就要用到pod数据持久化存储。

1、持久化存储

我们可以通过下面的命令查询Kubernetes的Pod支持哪些持久化存储方案:

kubectl  explain pods.spec.volumes
  • awsElasticBlockStore:AWSElasticBlockStore 表示一个 AWS 磁盘资源,它附加到 kubelet 的主机,然后暴露给 pod。
  • azureDisk:AzureDisk 表示主机上的 Azure 数据磁盘挂载,并将挂载绑定到 pod。
  • azureFile:AzureFile 表示主机上的 Azure 文件服务挂载,并将挂载绑定到 pod。
  • cephfs:CephFS 表示主机上的 Ceph FS 挂载,共享 pod 的生命周期。
  • cinder:Cinder 表示附加并安装在 kubelets 主机上的 cinder 卷。
  • configMap:ConfigMap 表示应该填充此卷的 configMap。
  • csi:CSI(容器存储接口)表示由某些外部 CSI 驱动程序(Beta 功能)处理的临时存储。
  • downwardAPI:DownwardAPI 表示关于应该填充此卷的 pod 的向下 API。
  • emptyDir:EmptyDir 表示共享 pod 生命周期的临时目录。
  • ephemeral:Ephemeral 表示由集群存储驱动程序(Alpha 功能)处理的卷。 卷的生命周期与定义它的 pod 相关联——它将在 pod 启动之前创建,并在 pod 被删除时删除。
  • fc:FC 表示连接到 kubelet 主机然后暴露给 pod 的光纤通道资源。
  • flexVolume:FlexVolume 表示使用基于 exec 的插件配置/附加的通用卷资源。
  • flocker:Flocker 表示附加到 kubelet 主机的 Flocker 卷。 这取决于正在运行的 Flocker 控制服务。
  • gcePersistentDisk:GCEPersistentDisk 表示一个 GCE 磁盘资源,它附加到 kubelet 的主机上,然后暴露给 pod。
  • gitRepo:GitRepo 代表特定版本的 git 存储库。 GitRepo 已弃用。 要使用 git repo 配置容器,请将 EmptyDir 挂载到使用 git 克隆 repo 的 InitContainer 中,然后将 EmptyDir 挂载到 Pod 的容器中。
  • glusterfs:Glusterfs 表示主机上的 Glusterfs 挂载,共享一个 pod 的生命周期。
  • hostPath:HostPath 表示主机上直接暴露给容器的预先存在的文件或目录。 这通常用于系统代理或其他允许查看主机的特权事物。 大多数容器不需要这个。
  • iscsi:ISCSI 代表一个 ISCSI 磁盘资源,它连接到 kubelet 的主机,然后暴露给 pod。
  • name:卷的名称。 必须是 DNS_LABEL 并且在 pod 中是唯一的。
  • nfs:NFS 表示共享 pod 生命周期的主机上的 NFS 挂载。
  • persistentVolumeClaim:PersistentVolumeClaimVolumeSource 表示对同一命名空间中的 PersistentVolumeClaim 的引用。
  • photonPersistentDisk:PhotonPersistentDisk 表示附加并挂载在 kubelets 主机上的 PhotonController 永久磁盘。
  • portworxVolume:PortworxVolume 表示连接并挂载在 kubelets 主机上的 portworx 卷。
  • projected:一站式资源秘密、配置映射和向下 API 的项目。
  • quobyte:Quobyte 表示共享 pod 生命周期的主机上的 Quobyte 挂载。
  • rbd:RBD 表示在共享 pod 生命周期的主机上挂载的 Rados 块设备。
  • scaleIO:ScaleIO 表示附加并安装在 Kubernetes 节点上的 ScaleIO 持久卷。
  • secret:Secret 表示应填充此卷的密钥。
  • storageos:StorageOS 表示附加并安装在 Kubernetes 节点上的 StorageOS 卷。
  • vsphereVolume:VsphereVolume 表示附加并安装在 kubelets 主机上的 vSphere 卷。

其中比较常用的挂在卷类型有:

  • emptyDir
  • hostPath
  • nfs
  • persistentVolumeClaim
  • glusterfs
  • cephfs
  • configMap
  • secret

想要使用存储卷,需要经历下面两个步骤:

  1. 定义pod的volume,这个volume指明它要关联到哪个存储上的
  2. 在容器中要使用volumemounts挂载对应的存储

1.1 emptyDir

emptyDir类型的Volume是在Pod分配到Node上时被创建,Kubernetes会在Node上自动分配一个目录,因此无需指定宿主机Node上对应的目录文件。 这个目录的初始内容为空,当Pod从Node上移除时,emptyDir中的数据会被永久删除。emptyDir Volume主要用于某些应用程序无需永久保存的临时目录,多个容器的共享目录等。

官方文档:https://kubernetes.io/docs/concepts/storage/volumes#emptydir

示例:创建一个Pod挂载emptyDir:

emptydir.yaml

apiVersion: v1
kind: Pod
metadata:
  name: pod-emptydir
  namespace: default
  labels:
    app: pod-emptydir
spec:
  containers:
    - name: pod-emptydir
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      volumeMounts:
        - mountPath: /cache
          name: cache-volume
      resources:
        requests:
          cpu: "250m"
          memory: "128Mi"
        limits:
          cpu: "500m"
          memory: "256Mi"
  restartPolicy: Always
  volumes:
    - name: cache-volume
      emptyDir:
        {}

更新资源清单:

kubectl apply -f emptydir.yaml

查看Pod描述信息,可以看到对应的挂载卷为cache-volume,类型为EmptyDir

然后去Pod所在的节点查看对应的Volume挂在卷:

kubectl get pods -l app=pod-emptydir -o wide
kubectl get pods -l app=pod-emptydir -o yaml | grep uid

获得对应Pod所在的节点以及uid信息:

在对应的节点上查看Pod相关信息:

tree /var/lib/kubelet/pods/18168545-1292-4086-aaba-0bf71edadc0e

右下图可知,临时目录在对应工作节点的/var/lib/kubelet/pods/18168545-1292-4086-aaba-0bf71edadc0e/volumes/目录下:

1.2 hostPath

hostPath Volume是指Pod挂载宿主机上的目录或文件。 hostPath Volume使得容器可以使用宿主机的文件系统进行存储,hostpath(宿主机路径):节点级别的存储卷,在pod被删除,这个存储卷还是存在的,不会被删除,所以只要同一个pod被调度到同一个节点上来,在pod被删除重新被调度到这个节点之后,对应的数据依然是存在的。

示例:创建一个Pod挂载hostPath类型的存储卷:

hostpath.yaml

apiVersion: v1
kind: Pod
metadata:
  name: pod-hostpath
  namespace: default
  labels:
    app: pod-hostpath
spec:
  containers:
    - name: pod-hostpath-nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      volumeMounts:
        - mountPath: /test-nginx
          name: hostpath-volume
    - name: pod-hostpath-tomcat
      image: tomcat:8.5-jre8-alpine
      imagePullPolicy: IfNotPresent
      volumeMounts:
        - mountPath: /test-tomcat
          name: hostpath-volume
  restartPolicy: Always
  volumes:
    - name: hostpath-volume
      hostPath:
        path: /data1
        type: DirectoryOrCreate

更新资源清单:

kubectl apply -f hostpath.yaml

查看Pod被调度到了哪个节点:

get pods -l app=pod-hostpath -o wide

结果如下:

前往对应的工作节点,查看是否创建了/data1目录:

ll /data1/

结果如下:

进入/data1目录,创建一个文件夹,并且进入对应的Pod中检查:

# 在对应的工作节点上执行
cd /data1 && mkdir -p testDir

结果如下:

# 到控制节点上执行进入Pod并检查的操作
kubectl exec -it pod-hostpath -c pod-hostpath-nginx  -- /bin/bash
ls -l /test-nginx

kubectl exec -it pod-hostpath -c pod-hostpath-tomcat  -- /bin/bash
ls -l /test-tomcat

分别进入两个Pod中检查,都存在testDir这个目录:

由此可知,pod-hostpath-nginxpod-hostpath-tomcat两个Pod这两个容器是共享存储卷的。

hostPath类型存储卷的缺点:

  • 单节点:pod删除之后重新创建必须调度到同一个node节点,数据才不会丢失

要解决单节点问题,可以使用分布式存储来解决,例如:nfs、cephfs、glusterfs等。

1.3 nfs

1.3.1 搭建nfs服务

NFS服务端搭建:

# 1. nfs服务端安装nfs-utils
yum install -y nfs-utils

# 2. 创建NFS共享的目录
mkdir -pv /data/volumes

# 3. 配置nfs共享目录
cat > /etc/exports << EOF
/data/volumes 192.168.126.40/24(rw,no_root_squash)
EOF

# 4. 启动nfs服务
systemctl start nfs.service
systemctl status nfs.service
systemctl enable nfs.service

NFS客户端搭建:

在Kubernetes的各个工作节点上操作:

yum install -y nfs-utils

systemctl enable nfs.service

# 手动挂载nfs
mkdir -p /test
mount 192.168.126.40:/data/volumes /test/
df -Th

挂载成功显示如下:

# 手动卸载
umount /test
df -Th

卸载成功:

1.3.2 创建Pod挂载nfs作为存储卷

Kubernetes挂载NFS的官方文档:https://kubernetes.io/zh/docs/concepts/storage/volumes/#nfs

nfs.yaml

apiVersion: v1
kind: Pod
metadata:
  name: test-nfs-volume
  namespace: default
  labels:
    app: test-nfs-volume
spec:
  containers:
    - name: nfs-volume-nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - containerPort: 80
          protocol: TCP
      volumeMounts:
        - mountPath: /usr/share/nginx/html
          name: nfs-volume
  restartPolicy: Always
  volumes:
    - name: nfs-volume
      nfs:
        path: /data/volumes
        server: 192.168.126.40

更新资源清单:

kubectl apply -f nfs.yaml

检查Pod是否创建成功:

kubectl get pods -l app=test-nfs-volume -o wide

结果如下:

进入到NFS共享目录下,创建一个index.html文件:

# 在nfs-server服务器上操作
cd /data/volumes/
echo 'NFS Volume Successed!' > index.html

效果如下:

通过curl Pod IP查看结果:

curl 10.244.85.208

结果如下:

NFS挂载成功,nfs支持多个客户端挂载,可以创建多个pod,挂载同一个nfs服务器共享出来的目录;但是nfs如果宕机了,数据也就丢失了,所以需要使用分布式存储,常见的分布式存储有glusterfs和cephfs。

2、PVC

官方文档:https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes

2.1 什么是PV?

PersistentVolume(PV)是群集中的一块存储,由管理员配置或使用存储类动态配置。 它是集群中的资源,就像pod是k8s集群资源一样。 PV是容量插件,如Volumes,其生命周期独立于使用PV的任何单个pod。

2.2 什么是PVC?

PersistentVolumeClaim(PVC)是一个持久化存储卷,我们在创建pod时可以定义这个类型的存储卷。 它类似于一个pod。 Pod消耗节点资源,PVC消耗PV资源。 Pod可以请求特定级别的资源(CPU和内存)。 pvc在申请pv的时候也可以请求特定的大小和访问模式(例如,可以一次读写或多次只读)。

2.3 PVC和PV工作原理

PV是群集中的资源。 PVC是对这些资源的请求。

PV和PVC之间的相互作用遵循以下生命周期。

2.3.1 pv的供应方式

  • 静态的:集群管理员创建了许多PV。它们包含可供群集用户使用的实际存储的详细信息。它们存在于Kubernetes API中,可供使用。
  • 动态的:当管理员创建的静态PV都不匹配用户的PersistentVolumeClaim时,群集可能会尝试为PVC专门动态配置卷。此配置基于StorageClasses,PVC必须请求存储类,管理员必须创建并配置该类,以便进行动态配置。

2.3.2 绑定

用户创建pvc并指定需要的资源和访问模式。在找到可用pv之前,pvc会保持未绑定状态。

2.3.3 使用

  1. 需要找一个存储服务器,把它划分成多个存储空间;
  2. k8s管理员可以把这些存储空间定义成多个pv;
  3. 在pod中使用pvc类型的存储卷之前需要先创建pvc,通过定义需要使用的pv的大小和对应的访问模式,找到合适的pv;
  4. pvc被创建之后,就可以当成存储卷来使用了,我们在定义pod时就可以使用这个pvc的存储卷;
  5. pvc和pv它们是一一对应的关系,pv如果被pvc绑定了,就不能被其他pvc使用了;
  6. 我们在创建pvc的时候,应该确保和底下的pv能绑定,如果没有合适的pv,那么pvc就会处于pending状态。

2.3.4 回收策略

当我们创建pod时如果使用pvc做为存储卷,那么它会和pv绑定,当删除pod,pvc和pv绑定就会解除,解除之后和pvc绑定的pv卷里的数据需要怎么处理,目前,卷可以保留,回收或删除:

  • Retain
  • Recycle (不推荐使用,1.15可能被废弃了)
  • Delete

从其声明中释放持久卷时会发生什么。 有效选项是 Retain(手动创建的 PersistentVolumes 的默认值)、Delete(动态配置的 PersistentVolumes 的默认值)和 Recycle(已弃用)。 此 PersistentVolume 下的卷插件必须支持回收。

2.3.4.1 Retain

当删除pvc的时候,pv仍然存在,处于released状态,但是它不能被其他pvc绑定使用,里面的数据还是存在的,当我们下次再使用的时候,数据还是存在的,这个是默认的回收策略。

2.3.4.2 Delete

删除pvc时即会从Kubernetes中移除PV,也会从相关的外部设施中删除存储资产。

2.4 通过PVC给Pod提供持久化存储卷

2.4.1 创建NFS共享目录

# 在nfs-server服务器上操作
mkdir -p  /data/volume_test/v{1,2,3,4,5,6,7,8,9,10}

# 配置nfs
cat > /etc/exports << EOF
/data/volumes 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v1 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v2 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v3 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v4 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v5 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v6 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v7 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v8 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v9 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v10 192.168.126.40/24(rw,no_root_squash)
EOF

# 重新加载配置
exportfs -arv

2.4.2 PV资源清单

通过kubectl explain pv命令可以查看pv资源对象的字段和配置信息。

  • apiVersion:pv对象的API组和版本信息。当前版本信息为v1,可以通过kubectl explain pv.apiVersion查看当前Kubernetes版本的API版本信息。
  • kind:资源对象类型,PersistentVolume,可以通过kubectl explain pv.kind查询。
  • metadata:PV资源对象的元数据信息
    • name:PV对象的名称
    • namespace:PV资源对象所属的名称空间
  • spec:PV资源对象的详细配置
    • accessModes:AccessModes 包含可以挂载卷的访问方式。参考:https://kubernetes.io/zh/docs/concepts/storage/persistent-volumes/#access-modes
      • ReadWriteOnce:卷可以被一个节点以读写方式挂载。 ReadWriteOnce 访问模式也允许运行在同一节点上的多个 Pod 访问卷。
      • ReadOnlyMany:卷可以被多个节点以只读方式挂载。
      • ReadWriteMany:卷可以被多个节点以读写方式挂载。
      • ReadWriteOncePod:卷可以被单个 Pod 以读写方式挂载。 如果你想确保整个集群中只有一个 Pod 可以读取或写入该 PVC, 请使用ReadWriteOncePod 访问模式。这只支持 CSI 卷以及需要 Kubernetes 1.22 以上版本。
    • awsElasticBlockStore:AWSElasticBlockStore 表示一个 AWS 磁盘资源,它附加到 kubelet 的主机,然后暴露给 pod。
    • azureDisk:AzureDisk 表示主机上的 Azure 数据磁盘挂载,并将挂载绑定到 pod。
    • azureFile:AzureFile 表示主机上的 Azure 文件服务挂载,并将挂载绑定到 pod。
    • capacity:持久卷的资源和容量的描述。参考:https://kubernetes.io/zh/docs/concepts/storage/persistent-volumes/#capacityhttps://kubernetes.io/zh/docs/reference/glossary/?all=true#term-quantity
      • storage:PV存储卷的大小。例如:1Gi
    • cephfs:CephFS 表示主机上的 Ceph FS 挂载,共享 pod 的生命周期。
    • cinder:Cinder 表示附加并安装在 kubelets 主机上的 cinder 卷。
    • claimRef:ClaimRef 是 PersistentVolume 和 PersistentVolumeClaim 之间双向绑定的一部分。 绑定时预计为非零。 claim.VolumeName 是 PV 和 PVC 之间的权威绑定。
    • csi:CSI(容器存储接口)表示由某些外部 CSI 驱动程序(Beta 功能)处理的临时存储。
    • fc:FC 表示连接到 kubelet 主机然后暴露给 pod 的光纤通道资源。
    • flexVolume:FlexVolume 表示使用基于 exec 的插件配置/附加的通用卷资源。
    • flocker:Flocker 表示附加到 kubelet 主机的 Flocker 卷。 这取决于正在运行的 Flocker 控制服务。
    • gcePersistentDisk:GCEPersistentDisk 表示一个 GCE 磁盘资源,它附加到 kubelet 的主机上,然后暴露给 pod。
    • glusterfs:Glusterfs 表示主机上的 Glusterfs 挂载,共享一个 pod 的生命周期。
    • hostPath:HostPath 表示主机上直接暴露给容器的预先存在的文件或目录。 这通常用于系统代理或其他允许查看主机的特权事物。 大多数容器不需要这个。
    • iscsi:ISCSI 代表一个 ISCSI 磁盘资源,它连接到 kubelet 的主机,然后暴露给 pod。
    • local:Local 表示具有节点亲和性的直连存储。
    • mountOptions:挂载选项列表,例如rosoft
    • nfs:NFS 表示主机上的 NFS 挂载。 由管理员提供。
    • nodeAffinity:NodeAffinity 定义了限制可以从哪些节点访问此卷的约束。 此字段影响使用此卷的 pod 的调度。
    • persistentVolumeReclaimPolicy:PV回收策略,从其声明中释放持久卷时会发生什么。 有效选项是 Retain(手动创建的 PersistentVolumes 的默认值)、Delete(动态配置的 PersistentVolumes 的默认值)和 Recycle(已弃用)。 此 PersistentVolume 下的卷插件必须支持回收。
    • photonPersistentDisk:PhotonPersistentDisk 表示附加并挂载在 kubelets 主机上的 PhotonController 永久磁盘。
    • portworxVolume:PortworxVolume 表示连接并挂载在 kubelets 主机上的 portworx 卷。
    • quobyte:Quobyte 表示共享 pod 生命周期的主机上的 Quobyte 挂载。
    • rbd:RBD 表示在共享 pod 生命周期的主机上挂载的 Rados 块设备。
    • scaleIO:ScaleIO 表示附加并安装在 Kubernetes 节点上的 ScaleIO 持久卷。
    • storageClassName:此持久卷所属的 StorageClass 的名称。 空值表示该卷不属于任何 StorageClass。
    • storageos:StorageOS 表示附加并安装在 Kubernetes 节点上的 StorageOS 卷。
    • volumeMode:volumeMode 定义卷是打算与格式化的文件系统一起使用还是保持在原始块状态。 当规范中不包含文件系统的值时,它是隐含的。
    • vsphereVolume:VsphereVolume 表示附加并安装在 kubelets 主机上的 vSphere 卷。
  • status:Kubernetes创建完PV之后自动生成的状态信息,无法手动修改。

2.4.3 创建PV

pv.yaml

apiVersion: v1
kind: PersistentVolume
metadata:
  name: v1
  namespace: default
spec:
  capacity:
    storage: 1Gi  # pv的存储空间容量
  accessModes: ["ReadWriteOnce"]
  nfs:
    path: /data/volume_test/v1  # 把nfs的存储空间创建成pv
    server: 192.168.126.40
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: v2
  namespace: default
spec:
  capacity:
    storage: 2Gi  # pv的存储空间容量
  accessModes: ["ReadWriteMany"]
  nfs:
    path: /data/volume_test/v2  # 把nfs的存储空间创建成pv
    server: 192.168.126.40
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: v3
  namespace: default
spec:
  capacity:
    storage: 3Gi  # pv的存储空间容量
  accessModes: ["ReadOnlyMany"]
  nfs:
    path: /data/volume_test/v3  # 把nfs的存储空间创建成pv
    server: 192.168.126.40
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: v4
  namespace: default
spec:
  capacity:
    storage: 4Gi  # pv的存储空间容量
  accessModes: ["ReadWriteOnce", "ReadWriteMany"]
  nfs:
    path: /data/volume_test/v4  # 把nfs的存储空间创建成pv
    server: 192.168.126.40
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: v5
  namespace: default
spec:
  capacity:
    storage: 5Gi  # pv的存储空间容量
  accessModes: ["ReadWriteOnce", "ReadWriteMany"]
  nfs:
    path: /data/volume_test/v5  # 把nfs的存储空间创建成pv
    server: 192.168.126.40
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: v6
  namespace: default
spec:
  capacity:
    storage: 6Gi  # pv的存储空间容量
  accessModes: ["ReadWriteOnce", "ReadWriteMany"]
  nfs:
    path: /data/volume_test/v6  # 把nfs的存储空间创建成pv
    server: 192.168.126.40
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: v7
  namespace: default
spec:
  capacity:
    storage: 7Gi  # pv的存储空间容量
  accessModes: ["ReadWriteOnce", "ReadWriteMany"]
  nfs:
    path: /data/volume_test/v7  # 把nfs的存储空间创建成pv
    server: 192.168.126.40
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: v8
  namespace: default
spec:
  capacity:
    storage: 8Gi  # pv的存储空间容量
  accessModes: ["ReadWriteOnce", "ReadWriteMany"]
  nfs:
    path: /data/volume_test/v8  # 把nfs的存储空间创建成pv
    server: 192.168.126.40
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: v9
  namespace: default
spec:
  capacity:
    storage: 9Gi  # pv的存储空间容量
  accessModes: ["ReadWriteOnce", "ReadWriteMany"]
  nfs:
    path: /data/volume_test/v9  # 把nfs的存储空间创建成pv
    server: 192.168.126.40
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: v10
  namespace: default
spec:
  capacity:
    storage: 10Gi  # pv的存储空间容量
  accessModes: ["ReadWriteOnce", "ReadWriteMany"]
  nfs:
    path: /data/volume_test/v10  # 把nfs的存储空间创建成pv
    server: 192.168.126.40

更新资源清单:

kubectl apply -f pv.yaml

Kubernetes会自动创建pv:

STATUSAvailable表示PV是可用的。

2.4.4 创建PVC绑定PV

pvc.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc1
  namespace: default
spec:
  accessModes: ["ReadWriteMany"]
  resources:
    requests:
      storage: 2Gi

更新资源清单:

kubectl apply -f pvc.yaml

创建完成之后,查看PVC和哪个PV绑定成功了:

STATUSBound表示已经被绑定了,CLAIN表示绑定了哪一个PVC。

2.4.5 创建Pod挂载PVC

pod-pvc.yaml

apiVersion: v1
kind: Pod
metadata:
  name: pod-pvc
  namespace: default
  labels:
    app: pod-pvc
spec:
  containers:
    - name: nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      volumeMounts:
        - mountPath: /usr/share/nginx/html
          name: nginx-html
  restartPolicy: Always
  volumes:
    - name: nginx-html
      persistentVolumeClaim:
        claimName: my-pvc1

更新资源清单:

kubectl apply -f pod-pvc.yaml

查看Pod是否成功创建:

到nfs-serve节点上,在对应的共享路径下创建index.html

cd /data/volume_test/v2/
echo 'PVC my-pvc1 Mounted Successed!' > index.html

效果如下:

然后返回控制节点,访问Pod IP:

PVPVC使用注意事项:

  1. 我们每次创建pvc的时候,需要事先有划分好的pv,这样可能不方便,那么可以在创建pvc的时候直接动态创建一个pv这个存储类,pv事先是不存在的。
  2. pvc和pv绑定,如果使用默认的回收策略retain,那么删除pvc之后,pv会处于released状态,我们想要继续使用这个pv,需要手动删除pv,kubectl delete pv pv_name,删除pv,不会删除pv里的数据,当我们重新创建pvc时还会和这个最匹配的pv绑定,数据还是原来数据,不会丢失。

3、Kubernetes存储类StorageClass

上面提到的PV和PVC绑定的模式都需要提前创建好PV,然后再由PVC和PV进行绑定,但是如果成千上万个PVC需要绑定PV,那么我们需要手动创建深千上万个PV,对于运维人员来说维护成本很高。所以Kubernetes提供了一种自动创建PV的机制,叫做StorageClass,它的作用就是创建PV模板。Kubernetes管理员提供创建StorageClass可以动态生成一个存储卷PV供PVC使用。

每个StorageClass都包含字段provisioner,parameters和reclaimPolicy。

具体来说,StorageClass会定义以下两个部分:

  1. PV的属性,比如PV存储的大小、类型、名称等
  2. 创建这种PV需要使用的存储插件,比如Ceph、NFS、Glusterfs等等

有了这两部分信息,Kubernetes就能够根据用户提交的PVC,找到对应的StorageClass,然后Kubernetes就会调用 StorageClass声明的存储插件,创建出需要的PV。

3.1 StorageClass资源清单

通过命令kubectl explain storageclass查看StorageClass定义所需的字段和配置信息。

  • allowVolumeExpansion:AllowVolumeExpansion 显示存储类是否允许卷扩展。布尔类型,是否许卷扩展,PersistentVolume 可以配置成可扩展。将此功能设置为true时,允许用户通过编辑相应的 PVC 对象来调整卷大小。当基础存储类的allowVolumeExpansion字段设置为 true 时,以下类型的卷支持卷扩展。

    此功能仅用于扩容卷,不能用于缩小卷。

  • allowedTopologies:限制可以动态供应卷的节点拓扑。 每个卷插件都定义了自己支持的拓扑规范。 空的 TopologySelectorTerm 列表意味着没有拓扑限制。 此字段仅适用于启用 VolumeScheduling 功能的服务器。

  • apiVersion:StorageClass所属的API组和版本信息,storage.k8s.io/v1,可以根据kubectl explain storageclass.apiVersion查询当前Kubernetes版本中StorageClass的API组和版本信息。

  • kind:资源类型,StorageClass,可以根据kubectl explain storageclass.kind命令查询资源类型

  • metadata:StorageClass资源的元数据

  • mountOptions:此存储类的动态配置 PersistentVolume 是使用这些 mountOptions 创建的,例如 [ro, soft]。 未验证 - 如果 PV 无效,则安装 PV 将失败。

  • parameters:创建此存储类的卷的配置程序的参数。

  • provisioner:供应商,Provisioner 表示 Provisioner 的类型。StorageClass需要有一个供应者,用来确定我们使用什么样的存储来创建pv。

  • reclaimPolicy:回收策略,默认的回收策略是Delete

  • volumeBindingMode:VolumeBindingMode 指示应如何配置和绑定 PersistentVolumeClaims。 未设置时,使用VolumeBindingImmediate。 此字段仅适用于启用 VolumeScheduling 功能的服务器。

这里通过NFS演示StorageClass的使用。

3.2 安装nfs provisioner

3.2.1 创建运行nfs-provisioner需要的sa账号

nfs-serviceaccount.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-provisioner
  namespace: default

更新资源清单:

kubectl apply -f nfs-serviceaccount.yaml

结果如下:

什么是sa?

sa全称是ServiceAccount。ServiceAccount是为了方便Pod里面的进程调用Kubernetes API或其他外部服务而设计的。

指定了serviceaccount之后,我们把pod创建出来了,我们在使用这个pod时,这个pod就有了我们指定的账户的权限了。

3.2.2 对sa授权

kubectl create clusterrolebinding nfs-provisioner-clusterrolebinding --clusterrole=cluster-admin --serviceaccount=default:nfs-provisioner

效果如下:

3.2.3 安装nfs-provisioner程序

3.2.3.1 配置NFS共享目录
# 1. 创建nfs-provisioner共享目录(在nfs-server服务器上操作)
mkdir -p /data/nfs_pro

# 2. 配置nfs
# 配置nfs
cat > /etc/exports << EOF
/data/volumes 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v1 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v2 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v3 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v4 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v5 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v6 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v7 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v8 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v9 192.168.126.40/24(rw,no_root_squash)
/data/volume_test/v10 192.168.126.40/24(rw,no_root_squash)
/data/nfs_pro 192.168.126.40/24(rw,no_root_squash)
EOF

# 重新加载配置
exportfs -arv
3.2.3.2 创建Deployment运行nfs-provisioner

nfs-provisioner-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-provisioner
  labels:
    app: nfs-provisioner
spec:
  selector:
    matchLabels:
      app: nfs-provisioner
  strategy:
    type: Recreate
  replicas: 1
  template:
    metadata:
      name: nfs-provisioner
      labels:
        app: nfs-provisioner
    spec:
      serviceAccountName: nfs-provisioner
      containers:
        - name: nfs-provisioner
          image: registry.cn-beijing.aliyuncs.com/mydlq/nfs-subdir-external-provisioner:v4.0.0
          imagePullPolicy: IfNotPresent
          volumeMounts:
            - mountPath: /persistentvolumes
              name: nfs-client-root
          env:
            - name: PROVISIONER_NAME
              value: example.com/nfs
            - name: NFS_SERVER
              value: 192.168.126.40
            - name: NFS_PATH
              value: /data/nfs_pro
      restartPolicy: Always
      volumes:
        - name: nfs-client-root
          nfs:
            path: /data/nfs_pro
            server: 192.168.126.40

更新资源清单:

kubectl apply -f nfs-provisioner-deployment.yaml

检查是否成功创建资源:

3.3 创建StorageClass动态供给PV

nfs-storageclass.yaml

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-storageclass
  namespace: default
provisioner: example.com/nfs  # 此处的provisioner必须要和安装的nfs-prosivioner的PROVISIONER_NAME保持一致

provisioner处写的example.com/nfs应该跟安装nfs provisioner时候的env下的PROVISIONER_NAME的value值保持一致。

更新资源清单:

kubectl apply -f nfs-storageclass.yaml

检车是否成功创建StorageClass:

3.4 创建PVC通过StorageClass动态生成PV

nfs-persistentvolumeclaim.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-pvc
  namespace: default
spec:
  accessModes: ["ReadWriteMany"]
  resources:
    requests:
      storage: 2Gi
  storageClassName: nfs-storageclass  # 与定义的StorageClass中的name保持一致

更新资源清单:

kubectl apply -f nfs-persistentvolumeclaim.yaml

检查PVC是否创建成功并且与动态生成的PV绑定:

PVC动态生成PV并绑定的步骤如下:

  1. 供应商:创建一个nfs provisioner
  2. 创建StorageClass,指向上一步中的供应商
  3. 创建PersistentVolumeClaim,指定StorageClass后动态生成PV并绑定

3.5 创建Pod挂载StorageClass动态生成的PVC

podMountPVC.yaml

apiVersion: v1
kind: Pod
metadata:
  name: pvc-pod
  namespace: default
  labels:
    app: pvc-pod
spec:
  containers:
    - name: pvc-pod-container
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      volumeMounts:
        - mountPath: /usr/share/nginx/html
          name: nfs-pvc-volume
  restartPolicy: Always
  volumes:
    - name: nfs-pvc-volume
      persistentVolumeClaim:
        claimName: nfs-pvc  # PVC的名称

更新资源清单:

kubectl apply -f podMountPVC.yaml

检查Pod是否创建成功:

然后去nfs-server服务器上进入到该PV对应的目录下创建index.html

cd /data/nfs_pro/default-nfs-pvc-pvc-672785ee-acc5-482e-811a-71f8fdf76886

echo 'Create By nfs provisioner PV Successed!' > index.html

效果如下:

然后回到控制节点,访问该Pod:

posted @ 2022-05-17 22:38  StaryJie  阅读(211)  评论(0编辑  收藏  举报