k8s☞09存储资源

阅读量: zyh 2020-09-30 18:23:10
Categories: > Tags:

基本

在使用云服务的时候,我们创建磁盘,需要在存储服务中提交一个请求,里面包含了磁盘类型,磁盘大小.之后,我们再把这个申请的磁盘挂载到所需的计算资源上即可.

而在k8s中. 当使用存储的时候,我们会涉及到以下主要概念

构建存储的概念:

使用存储的概念:

你可能会发现,podtemplate.spec.volumespodtemplate.spec.containers.volumeMounts之间缺少了一个格式化文件系统的过程,这是因为k8s已经在你通过pv资源帮你创建好了文件系统。当然你也可以通过将pv的模式改为块设备,不过这样一来,程序就需要确认是否可以识别块设备了。我想一般的应用程序是用不上这个类型的。https://kubernetes.io/docs/concepts/storage/persistent-volumes/#volume-mode

最后,pod 与 pv 与 pvc 之间是一对一强依赖关系。三者之间,在A资源对象【被B调用】的过程中,任何一个删除A资源对象的操作都不会立即执行,而是在【B调用方】生命周期结束之后才会执行。

volume卷

支持的存储类型

https://kubernetes.io/docs/concepts/storage/volumes/#volume-types

在使用某一个存储服务类型之前,详细的阅读存储服务的限制很有必要。

存储类型支持的模式:

https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes

  1. ReadWriteOnce(单节点挂载读写,单节点内多pod读写)

  2. ReadOnlyMany(多节点挂载只读,多pod只读)

  3. ReadWriteMany(多节点挂载读写,多pod读写)

  4. ReadWriteOncePod(单Pod读写)

⚠️ReadWriteOncePod 是新增,仅支持 csi + k8s 1.22+

NFS

使用NFS之前,需要先搭建NFS服务端。

例如云服务已有的NFS服务,或者自行搭建。

下面是k8s官方给出的自行搭建:

https://github.com/kubernetes/examples/tree/master/staging/volumes/nfs

本地

local volume 特点:

一个例子:

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    
---
 apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer

PV持久卷

状态

kubectl get pv -n <ns>

类型

PV分为静态和动态。

静态pv: 需要先创建一定数量的pv, 然后才能通过pvc对应申请。若pvc无法匹配任意静态pv,则pvc会一直等待下去。

动态pv: 需要先构建供应商.主流的云商存储基本都有供应商, 区别在于是内置了,还是需要自行外部创建.然后再通过pvc去申请.

内置供应商列表: https://kubernetes.io/docs/concepts/storage/storage-classes/#provisioner

外置供应商部署: https://github.com/kubernetes-retired/external-storage

回收策略

spec.persistentVolumeReclaimPolicy 定义 PVC 被删除的时候,集群如何处理PV。

Retain 不处理PV。但 PV 默认不能直接再次复用,因为里面还有之前Pod的数据。

Delete 同时删除PVC和Volume。只不过Volume是否删除取决于每一个StorageClass中parameters的定义。

Recycle 删除PV。

静态PV

静态PV,必须由管理员用户先创建好pv.就如同你想构建一个云服务器,但需要先创建好一个云盘。

一个静态pv的例子, 这里以 nfs 为例:

apiVersion: v1
kind: PersistentVolume
metadata:
  name:  nas-nginx-pv  # pv 名字,会被 pvc 引用
spec:
  claimRef:
    name: nas-nginx-pvc
    namespace: default
  capacity:
    storage: 10Gi  # 定义本持久卷大小
  accessModes:
  - ReadWriteMany   # 定义本持久卷访问模式
  persistentVolumeReclaimPolicy: Retain   # 定义pvc删除后的策略
  nfs:
    path: /volume1/k8s  # 定义 nfs 共享路径
    server: 10.200.10.4        # 定义 nfs 服务器地址

静态PV对应的PVC

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: nas-nginx-pvc
  namespace: default
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 10Gi
  volumeName: nas-nginx-pv

动态PV

动态PV目的在于用户可以自行通过PVC申请资源,无需提前通过管理员创建PV。

动态PV的构建需要一个新的资源对象StroageClass,它通过provisioner指定卷插件,从而对接不同的储存。卷插件其实就是一个存储类型的客户端。

卷插件都会拥有自己的一些特定参数,从而满足向卷供应商提供创建卷时所需要的信息。

当前支持的volume类型以及对应的卷插件参数:

https://kubernetes.io/docs/concepts/storage/storage-classes/#provisioner

⚠️ k8s并不提供所有的卷插件。

基于nfs构建StroageClass

以网络存储nfs为例。nfs属于外置供应商,因此没有内置卷插件。因此需要先创建。创建文档:

https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner/blob/master/charts/nfs-subdir-external-provisioner/README.md

⚠️ 需要注意的是,nfs-client正常运行条件:

安装命令:

#############################################国外源###################################################
helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
helm repo update
helm search repo --max-col-width 200 | grep nfs
helm install nfs-client nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
         --set storageClass.name=nfs-client \
         --set nfs.server=10.200.10.4 \
         --set nfs.path=/volume1/k8s \
         --set storageClass.reclaimPolicy=Delete \
         --set storageClass.archiveOnDelete=true \
         --set storageClass.allowVolumeExpansion=true \
         --set storageClass.defaultClass=true
#############################################阿里源###################################################
helm repo add apphub https://apphub.aliyuncs.com
helm repo update
helm install nfs-client apphub/nfs-client-provisioner \
--set storageClass.name=nfs-client \
--set nfs.server=10.200.10.4 \
--set nfs.path=/volume1/k8s \
--set storageClass.reclaimPolicy=Delete \
--set storageClass.archiveOnDelete=true \
--set storageClass.allowVolumeExpansion=true \
--set storageClass.defaultClass=true

nfs-client会自动帮你创建一个 StroageClass。

kubectl get StorageClass/nfs-client -o yaml
allowVolumeExpansion: true
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  annotations:
    meta.helm.sh/release-name: nfs-client
    meta.helm.sh/release-namespace: default
    storageclass.kubernetes.io/is-default-class: "true"
  creationTimestamp: "2021-12-27T06:25:24Z"
  labels:
    app: nfs-subdir-external-provisioner
    app.kubernetes.io/managed-by: Helm
    chart: nfs-subdir-external-provisioner-4.0.14
    heritage: Helm
    release: nfs-client
  ......
  name: nfs-client
parameters:
  archiveOnDelete: "true"
provisioner: cluster.local/nfs-client-nfs-subdir-external-provisioner
reclaimPolicy: Delete
volumeBindingMode: Immediate

动态PV对应的PVC

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: nas-vsftpd-pvc
spec:
  storageClassName: nfs-client
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 100Gi

在这里,我们通过 spec.storageClassName显式指定StroageClass。

Pod中的PVC模板

PVC模板可以动态的创建PVC,然后PVC再通过StorageClass动态创建PV。

💥 volumeClaimTemplates 不支持 Deployment.spec

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: app-demo
spec:
...
    spec:
      containers:
...
        volumeMounts:
        - name: appdata
          mountPath: /app/data
  volumeClaimTemplates:
  - metadata:
      name: appdata  # appdata 即 volumeMounts.name
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi

在线扩展(仅支持动态pv)

https://kubernetes.io/docs/concepts/storage/persistent-volumes/#expanding-persistent-volumes-claims 文档中列举了支持扩展的存储类。

💔NFS是不被支持的。

另外,还需要满足下列条件:

  1. feature-gates的ExpandInUsePersistentVolumes被开启。它在1.15版本以后默认开启。

  2. StorageClass的allowVolumeExpansion被开启。

  3. 文件系统是XFS, Ext3, or Ext4。这个需要StorageClass的支持。一般来说volume属于块设备类型的资源才支持,比如aws的ebs。

    你可以在这里确认StorageClass是否支持fstype属性。https://kubernetes.io/docs/concepts/storage/storage-classes/#parameters

满足上述条件后,你就可以通过修改pvc提升存储大小了。即使 pvc 正在被使用。

局限性

当一个 pod 同时申请多个 volume 的时候,可能会出现 volume-A 申请成功,volume-B 因物理容量不够申请失败的问题,此时 pod 将卡住。这种情况下,需要手动介入去清理。

PVC申请失败

需要我们手动的时候,都是资源申请失败或者不小心删除了PVC之类的。而不管是什么,我们第一目的是数据不丢。

因此,我们在清理故障对象资源的时候,应该遵循下列步骤:

  1. 将pv的回收策略persistentVolumeReclaimPolicy定义为Retain
  2. 删除pvc,这个时候 pv 对象将会保留,但是 pv 状态会变成 Released,这个状态下 pv 无法再被使用
  3. 删除pv对象字段spec.claimRef.uid,从而将pv与pvc解绑,从而使 pv 状态变为 Available
  4. 将pvc对象字段volumeName设置为pv的名字,重建pvc
  5. 恢复pv的回收策略。

Pod使用PVC

上述例子中的 nfs 卷类型为例。

Deployment配置:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: vsftpd
  namespace: it
  labels:
    app: vsftpd
spec:
  serviceName: vsftpd
  revisionHistoryLimit: 10
  replicas: 1
  selector:
    matchLabels:
      app: vsftpd
  template:
    metadata:
      labels:
        app: vsftpd
    spec:
      hostNetwork: true
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: kubernetes.io/hostname
                operator: In
                values:
                - k8s01
      containers:
      - name: vsftp
        image: fauria/vsftpd
        ports:
        - containerPort: 20
        - containerPort: 21
        env:
        - name: LOG_STDOUT
          value: STDOUT
        resources:
          requests:
            memory: "128Mi"
            cpu: "125m"
          limits:
            memory: "512Mi"
            cpu: "250m"
        volumeMounts:
        - name: vsftpd-vol
          subPath: vsftpd/data
          mountPath: /home/vsftpd
        - name: vsftpd-vc
          subPath: vsftpd.conf
          mountPath: /etc/vsftpd/vsftpd.conf
          readOnly: true
        - name: vsftpd-vc
          subPath: virtual_users.txt
          mountPath: /etc/vsftpd/virtual_users.txt
          readOnly: true
        - name: vsftpd-vc
          subPath: admin
          mountPath: /etc/vsftpd/virtual/admin
          readOnly: true
      volumes:
      - name: vsftpd-vol
        persistentVolumeClaim:
          claimName: vsftpd-pvc
      - name: vsftpd-vc
        configMap:
          name: vsftpd-cm
  1. spec.template.spec.volumes:定义Pod所用的卷

  2. spec.template.spec.containers.volumeMounts:定义 nas-vsftpd-vol如何挂载

    这里将 nfs 共享目录<nfs_root>/vsftpd/data挂载到容器里的 /home/vsftpd路径。subPath不存在的时候,nfs会自动创建。

  3. configMap 参见临时卷

⚠️ subPath 方式的挂载无法接收数据更新。

临时卷

https://kubernetes.io/docs/concepts/storage/ephemeral-volumes/#types-of-ephemeral-volumes

k8s用来进行临时的数据存储或者临时调用数据。例如缓存/会话/密码/配置一类的。

ℹ️ 他们无需人为的提前创建PVC去绑定,而是直接在Pod中去调用。

临时卷均通过节点的kubelet自动管理,实现临时卷的PVC将随着POD的删除而自动删除。

Projected Volumes

可实现将volumes的所有列表项挂载在Pod同一个位置上。

详细使用见【k8s☞10应用配置与密码与信息提供】