云其它

关注公众号 jb51net

关闭
首页 > 网站技巧 > 服务器 > 云和虚拟化 > 云其它 > K8S临时存储-本地存储-PV和PVC-动态存储

K8S临时存储-本地存储-PV和PVC详解-动态存储(StorageClass)

作者:文静小土豆

文章介绍了Kubernetes中容器数据的持久化解决方案,包括hostPath、emptyDir、NFS卷以及PV和PVC,详细说明了这些存储卷的类型、配置方法和使用场景,并提供了创建和绑定存储卷的示例,此外,还讨论了存储卷的回收策略和卷模式,以及如何使用StorageClass进行动态存储配置

介绍

容器中的文件在磁盘上是临时存放的,当容器崩溃或停止时容器上面的数据未保存, 因此在容器生命周期内创建或修改的所有文件都将丢失。

在崩溃期间,kubelet 会以干净的状态重新启动容器。 当多个容器在一个 Pod 中运行并且需要共享文件时,会出现另一个问题,跨所有容器设置和访问共享文件系统具有一定的挑战性。

K8S 卷(Volume) 这一抽象概念能够解决这两个问题。

存储卷的分类

hostPath 存储

警告:

HostPath 卷存在许多安全风险,最佳做法是尽可能避免使用 HostPath。 当必须使用 HostPath 卷时,它的范围应仅限于所需的文件或目录,并以只读方式挂载。HostPath 仅适用于单个节点上的存储,不支持跨节点访问(如果Pod偏移到其他宿主机节点上面可能会出问题)。

支持的 type 值如下

type值说明
空字符串(默认)用于向后兼容,这意味着在安装 hostPath 卷之前不会执行任何检查
DirectoryOrCreate如果在给定路径上什么都不存在,那么将根据需要创建空目录,权限设置为 0755,具有与 kubelet相同的组和属主信息
Directory在给定路径上必须存在的目录
FileOrCreate如果在给定路径上什么都不存在,那么将在那里 根据需要创建空文件,权限设置为 0644,具有与 kubelet 相同的组和所有权
File在给定路径上必须存在的文件
Socket在给定路径上必须存在的 UNIX 套接字
CharDevice在给定路径上必须存在的字符设备
BlockDevice在给定路径上必须存在的块设备

hostPath 配置示例

如有多个节点,在使用 hostPath 存储需要指定pod部署在那个节点上面,例如在Pod上面添加nodeSelector字段指定到某一个节点(需要提前给节点打标签)

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
#  nodeSelector:      # 指定部署到特定节点上面
#    disktype: ssd     # 标签
  - image: nginx
    name: test-container
    volumeMounts:
    - mountPath: /test-pd
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      path: /data          # 宿主机上目录位置,注意权限
      type: Directory      # 此字段为可选,详细解释在上面

emptyDir

定义了 emptyDir 卷的 Pod,在 Pod 被指派到某节点时此卷会被创建。emptyDir 卷最初是空的。Pod 中的容器挂载 emptyDir 卷的路径可能相同也可能不同,但这些容器都可以读写 emptyDir 卷中相同的文件。 当 Pod 因为某些原因被从节点上删除时,emptyDir 卷中的数据也会被永久删除。主要作用是为了共享数据用。

emptyDir 卷存储可以使用任何磁盘、SSD 或网络存储,这取决于你的环境。 你可以将 emptyDir.medium 字段设置为 “Memory”, 以告诉 Kubernetes 为你挂载 tmpfs(基于 RAM 的文件系统)。虽然 tmpfs 速度非常快,但是要注意它与磁盘不同, 并且你所写入的所有文件都会计入容器的内存消耗,受容器内存限制约束

说明:

容器崩溃并不会导致 Pod 被从节点上移除,因此容器崩溃期间 emptyDir 卷中的数据是安全的。

emptyDir 配置示例

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: nginx
    name: test-container
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir:
      sizeLimit: 500Mi  # 写入磁盘的大小限制,如果没限制直接删除这行 在emptyDir: []即可

nfs

nfs 卷能将 NFS (网络文件系统) 挂载到你的 Pod 中。 不像 emptyDir 那样会在删除 Pod 的同时也会被删除,nfs 卷的内容在删除 Pod 时会被保存,卷只是被卸载。 这意味着 nfs 卷可以被预先填充数据,并且这些数据可以在 Pod 之间共享。

配置示例

说明:
在使用 NFS 卷之前,你必须部署自己的 NFS 服务器才可以使用,这里不再叙述。

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: registry.k8s.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /my-nfs-data
      name: test-volume
  volumes:
  - name: test-volume
    nfs:
      server: my-nfs-server.example.com   # nfs服务的地址
      path: /my-nfs-volume                # nfs 服务共享的路径
      readOnly: true                    

PV和PVC的使用

在 Kubernetes 中,PV(PersistentVolume)和 PVC(PersistentVolumeClaim)是用于持久化存储的核心概念,其本身并没有存储的相关功能所以需要准备后端存储的环境。

PV(PersistentVolume)

下面是 hostPath PersistentVolume 的配置文件:

apiVersion: v1
kind: PersistentVolume
metadata:
  labels:
    type: local
  name: task-pv-volume
spec:
  accessModes:
  - ReadWriteOnce
  capacity:
    storage: 10Gi
  hostPath:
    path: /mnt
    type: ""
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: manual
  volumeMode: Filesystem

关键词解释

pv存储卷的回收策略

卷模式

Kubernetes 支持两种卷模式(volumeModes):

访问模式

创建PV

kubectl create -f pv0003.yaml

创建好以后查看

kubectl get pv
NAME             CAPACITY   ACCESSMODES   RECLAIMPOLICY   STATUS      CLAIM     STORAGECLASS   REASON    AGE
pv0003           5Gi        RWO           Recycle         Available              manual                  4s

PV状态说明:

在命令行接口(CLI)中,访问模式也使用以下缩写形式:

PV每个阶段的状态

每个持久卷会处于以下阶段(Phase)之一:

使用 kubectl describe persistentvolume 查看已绑定到 PV 的 PVC 的名称。

PVC(PersistentVolumeClaims)

创建一个PVC:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: myclaim
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 5Gi

这里面的字段意思基本和PV里面的一样,访问模式和卷模式也是一样的,就不再解释了。

PV和PVC的绑定模式

匹配步骤:

自动绑定

创建PV和PVC

apiVersion: v1
kind: PersistentVolume
metadata:
  name: task1
  labels:
    type: local1
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/mnt"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: task-pv-claim
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 9Gi

查看PV和PVC状态

[root@master01 pv]# kubectl get pvc,pv
NAME                                  STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/task-pv-claim   Bound    task1    10Gi       RWO                           26s

NAME                     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                   STORAGECLASS   REASON   AGE
persistentvolume/task1   10Gi       RWO            Retain           Bound       default/task-pv-claim                           10m

都是已经绑状态

根据标签绑定

创建PV和PVC

apiVersion: v1
kind: PersistentVolume
metadata:
  name: my-pv
  labels:
    desk: "test1"       # 设置标签
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: /mnt
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  selector:
    matchLabels:
      desk: "test1"     # 匹配PV的标签

查看PV和PVC状态

可以看到 my-pvc 已经绑定到 my-pv 上面了,如果没有标签自动绑定的话 则会绑定到 task2 的PV上面

[root@master01 pv]# kubectl get pv
NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM            STORAGECLASS   REASON   AGE
my-pv   10Gi       RWO            Retain           Bound       default/my-pvc                           3s
task1   10Gi       RWO            Retain           Available                                            46s
task2   5Gi        RWO            Retain           Available                                            46s
[root@master01 pv]# kubectl get pvc
NAME     STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
my-pvc   Bound    my-pv    10Gi       RWO                           12s

指定PV名字绑定

apiVersion: v1
kind: PersistentVolume
metadata:
  name: my-pv
spec:
  capacity:
    storage: 20Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: /mnt/data
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
spec:
  accessModes:
    - ReadWriteOnce
  volumeName: my-pv   # 指定要绑定的 PV 的名称
  resources:
    requests:
      storage: 5Gi

查看PV和PVC绑定情况

[root@master01 pv]# kubectl get pv,pvc
NAME                     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM            STORAGECLASS   REASON   AGE
persistentvolume/my-pv   20Gi       RWO            Retain           Bound       default/my-pvc                           5s
persistentvolume/task1   10Gi       RWO            Retain           Available                                            158m
persistentvolume/task2   5Gi        RWO            Retain           Available                                            158m

NAME                           STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/my-pvc   Pending   my-pv    0                                        5s

注意事项

PV 和 PVC 之间的绑定是一对一的关系。一个 PVC 只能绑定到一个 PV 上,而一个 PV 可以同时被多个 PVC 绑定。如果没有足够的 PV 来满足 PVC 的需求,或者没有满足 PVC 的要求的可用 PV,则 PVC 将处于 Pending(挂起)状态,直到满足条件为止。

Pod使用PVC存储

创建PV和PVC:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: task-pv-volume
  labels:
    type: local
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/mnt/data"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: task-pv-claim
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 3Gi

创建一个Pod使用上面这个PVC

apiVersion: v1
kind: Pod
metadata:
  name: task-pv-pod
spec:
  containers:
    - name: task-pv-container
      image: nginx
      ports:
        - containerPort: 80
          name: "http-server"
      volumeMounts:
        - mountPath: "/usr/share/nginx/html"
          name: task-pv-storage
  volumes:
    - name: task-pv-storage
      persistentVolumeClaim:          # 指定使用PVC存储
        claimName: task-pv-claim      # PVC存储的名字

在Pod里面多次挂在PVC

apiVersion: v1
kind: Pod
metadata:
  name: test
spec:
  containers:
    - name: test
      image: nginx
      volumeMounts:
        # 网站数据挂载
        - name: config
          mountPath: /usr/share/nginx/html
          subPath: html
        # Nginx 配置挂载
        - name: config
          mountPath: /etc/nginx/nginx.conf
          subPath: nginx.conf
  volumes:
    - name: config
      persistentVolumeClaim:
        claimName: test-nfs-claim

PVC绑定常见问题

创建PVC后,一直绑定不上PV(Pending):

Pod挂在PVC后,一直处于Pending状态:

删除PVC后k8s会创建一个用于回收的Pod,根据PV的回收策略进行pv的回收,回收完以后PV的状态就会变成可被绑定的状态也就是空闲状态,其他的Pending状态的PVC如果匹配到了这个PV,他就能和这个PV进行绑定。

动态存储(StorageClass)

主要功能:

环境准备

在开始之前请安装后端存储,我这里使用的是CEPH存储并且安装到K8S集群里,下面是安装方法。(所有环境中如果有用到的安装包或者容器镜像什么的都可以私信我)

安装方法:点击跳转

查看已经部署好的StorageClass

注意: 本次演示默认已经安装好ceph存储。

## sc 是 StorageClass 简写

[root@master01 ~]# kubectl get sc
NAME            PROVISIONER                     RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
rook-ceph-rbd   rook-ceph.rbd.csi.ceph.com      Retain          Immediate           true                   228d
rook-cephfs     rook-ceph.cephfs.csi.ceph.com   Retain          Immediate           true                   228d

适用场景:

查看 rook-ceph-rbd

查看命令

kubectl get sc rook-ceph-rbd -oyaml
allowVolumeExpansion: true
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: rook-ceph-rbd
parameters:
  clusterID: rook-ceph
  csi.storage.k8s.io/controller-expand-secret-name: rook-csi-rbd-provisioner
  csi.storage.k8s.io/controller-expand-secret-namespace: rook-ceph
  csi.storage.k8s.io/fstype: ext4
  csi.storage.k8s.io/node-stage-secret-name: rook-csi-rbd-node
  csi.storage.k8s.io/node-stage-secret-namespace: rook-ceph
  csi.storage.k8s.io/provisioner-secret-name: rook-csi-rbd-provisioner
  csi.storage.k8s.io/provisioner-secret-namespace: rook-ceph
  imageFeatures: layering
  imageFormat: "2"
  pool: replicapool
provisioner: rook-ceph.rbd.csi.ceph.com
reclaimPolicy: Retain
volumeBindingMode: Immediate

关键字段解释

StorageClass 使用方法

创建 StatefulSet 使用

部署成功以后 StorageClass 会根据PVC申请的去自动创建PV并绑定好

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: my-statefulset
spec:
  serviceName: "my-statefulset"
  replicas: 3
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: my-container
        image: nginx:latest
        ports:
        - containerPort: 80
        volumeMounts:
        - name: my-persistent-storage
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: my-persistent-storage
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "rook-ceph-rbd" # 指定刚刚查出来的 StorageClass 名字 
      resources:
        requests:
          storage: 5Gi

使用PVC申请动态存储

创建PVC成功以后会自动创建PV并绑定好,后面Pod使用方式和上面手动创建PV和PVC的方式一样。

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: rook-ceph-rbd    # 指定要使用的 StorageClass 的名称
  resources:
    requests:
      storage: 1Gi                   # 请求 1GB 的存储容量

PVC扩容

直接修改 “storage: 1Gi ” 修改为2G即可,但是生效还需要等几分钟。

kubectl edit pvc my-pvc

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文