侧边栏壁纸
  • 累计撰写 51 篇文章
  • 累计创建 23 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

K8s 共享存储2

zhanjie.me
2020-11-18 / 0 评论 / 0 点赞 / 0 阅读 / 0 字

2.1 持久存储卷(PersistentVolume)

管理存储和管理计算实例是完全不同的两回事,kubernetes 系统为集群管理者和用户提供了一个 API 来详细定义存储的使用方式。这对应着两个新的资源对象: PersistentVolume 和 PersistentVolumeClaim。

PV 的使用方式有两种,分别为静态存储和动态存储:

  • 静态存储:由集群管理员创建大量的 PV,在 PV 中定义实际存储的详细信息,然后提供给集群用户使用。
  • 动态存储:当管理员创建的静态 PV 中没有一个与用户需要的 PVC 匹配时,集群会尝试动态地为 PVC 提供一个卷。动态存储需要使用到 StorageClass,管理员需要先创建并配置 StorageClass,然后 PVC 设置使用的 StorageClass 类型,系统会自动创建符合要求的 PV 与 PVC 绑定。

PV 生命周期的 4 个阶段

  • Available: 可用状态,表示还没有与 PVC 绑定。
  • Bound: 已经 PVC 绑定。
  • Released: 绑定的 PVC 已经删除,资源已经释放,但没有被集群回收。
  • Failed: 自动资源回收失败。

它们之间的状态转换关系如下图所示:

image-qsamixco.png

1:这里表示的是 Available -> Bound,PV 的初始状态是 Available,当与某个 PVC 绑定后,状态变为 Bound。
2:这里表示的是 Bound -> Released,当 PV 删除绑定的 PVC 后,PV 状态变为 Released。
3:这里表示的是 Released -> Available,当设置 PV 的 Reclaim Policy 为 Released 后,集群会尝试自动回收 PV(执行 rm -rf /thevolume/*),如果成功回收则 PV 状态变为 Available。还有一种方式是用户手动修改 PV,解除 PV 对 PVC 的引用。
4:这里表示的是 Released -> Failed,当 3 中回收不成功时(比如存储突然不可用),PV 的状态会变为 Failed。
5:这里表示的是 Failed -> Available,出现 4 的情况时,用户手动解除 PV 对 PVC 的引用。

PV 详解

示例

下面是一个标准的 PV 定义:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv0001
spec:
  capacity: # 存储能力,设置为 5GiB 的存储空间
    storage: 5Gi
  volumeMode: Filesystem # 存储卷模式,设置为文件系统
  accessModes: # 访问模式,设置为读写权限,并只能被单个 Node 挂载
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle # 回收策略,设置为回收空间
  storageClassName: slow # 存储类型,设置为 slow(系统中必须已经存在名为 slow 的 StorageClass)
  mountOptions: # 挂载参数,设置了 hard 和 nfsvers=4.1
    - hard
    - nfsvers=4.1
  nfs: # 后端存储类型为 nfs,路径为 /tmp,地址为 172.17.0.2
    path: /tmp
    server: 172.17.0.2

通过上面的例子可以看出,在定义 PV 时有很多的字段选项都需要设置。

capacity(存储能力)

主要设置的是存储空间的大小,以后可能还支持 IOPS、吞吐率等设置。

volumeMode(存储卷模式)

存储卷的模式有两种:Filesystem(文件系统) 和 Block(块设备)。如果省略这个设置的话,默认为 Filesystem。

支持块类型的存储卷有:AWSElasticBlockStore、AzureDisk、FC、GCEPersistentDisk、iSCSI、Local volume、RBD(Ceph Block Device)、VsphereVolume。

accessModes(访问模式)

设置 PV 的访问模式,描述应用对存储资源的访问权限,现在的访问模式有如下 3 类:

  • ReadWriteOnce(RWO):读写权限,只能被单个 Node 挂载。
  • ReadOnlyMany(ROX):只读权限,可以被多个 Node 挂载。
  • ReadWriteMany(RWX):读写权限,可以被多个 Node 挂载。

某些 PV 可以支持多种访问模式(比如:NFS 可以支持多个应用的读写操作),但是在挂载的时候只能支持一种访问模式,多种访问模式并不能同时生效。

下面的表格展示了不同卷类型所支持的访问模式:

Volume Plugin ReadWriteOnce ReadOnlyMany ReadWriteMany
AWSElasticBlockStore ✔️ - -
AzureFile ✔️ ✔️ ✔️
AzureDisk ✔️ - -
CephFS ✔️ ✔️ ✔️
Cinder ✔️ - -
CSI 视驱动而定 视驱动而定 视驱动而定
FC ✔️ ✔️ -
FlexVolume ✔️ ✔️ 视驱动而定
Flocker ✔️ - -
GCEPersistentDisk ✔️ ✔️ -
Glusterfs ✔️ ✔️ ✔️
HostPath ✔️ - -
iSCSI ✔️ ✔️ -
Quobyte ✔️ ✔️ ✔️
NFS ✔️ ✔️ ✔️
RBD ✔️ ✔️ -
VsphereVolume ✔️ - -
PortworxVolume ✔️ - ✔️
ScaleIO ✔️ ✔️ -
StorageOS ✔️ - -

persistentVolumeReclaimPolicy(PV 回收策略)

当前的回收策略有 3 种:

  • Retain(保留):后续需要手工处理。
  • Recycle(回收空间):简单清除文件的操作(执行 rm -rf /thevolume/* 命令)。
  • Delete(删除):与 PV 相连的后端存储完成 Volume 的删除操作(比如:AWS EBS、GCE PD、Azure Disk 和 OpenStack Cinder 等设备内部的 Volume 清理)。

支持对应策略的卷分别有:

  • Recycle 策略:NFS、HostPath。
  • Delete 策略:AWS EBS、GCE PD、Azure Disk 和 OpenStack Cinder。

storageClassName(StorageClass 的名称)

如果需要支持动态存储的话,需要配置这个字段选项。这里填写的是 StorageClass 的名称,并且需要事先创建好对应名称的 StorageClass 才能够在这里使用。

mountOptions(挂载参数)

当 PV 挂载到 Node 上时,根据不同卷的特点可以设置额外的挂载参数。

现在支持挂载参数的卷有:AWSElasticBlockStore、AzureDisk、AzureFile、CephFS、Cinder、GCEPersistentDisk、Glusterfs、NFS、Quobyte Volumes、RBD(Ceph Block Device)、StorageOS、VsphereVolume、iSCSI。

由于挂载参数没有验证,所以如果其中的一个参数是无效的,那么挂载就会失败。

nodeAffinity(节点亲和性)

使用这个字段可以限制只有符合条件的 Node 才能访问存储卷,而使用这些存储卷的 Pod 也只会被调度到满足条件的 Node 上。

这个参数只能用于 Local 存储卷。对于公有云提供的存储卷(比如:AWS EBS、GCE PD、Azure Disk 等)是公有云自动完成节点亲和性设置,不需要手工设置。


2.2 持久存储卷声明(PersistentVolumeClaim)

PVC 是对 PV 的声明使用。

PVC 生命周期的 3 个阶段

  • Pending: 等待状态,还没有与 PV 绑定。
  • Bound: 绑定状态,已经与 PV 绑定。
  • Lost: 丢失状态,与 PV 失去了绑定关系。

它们之间的状态转换关系如下图所示:

image-vrueomkv.png

1:这里表示的是 Pending -> Bound,当集群找到符合条件的 PV 后,将 PV 与 PVC 绑定。
2:这里表示的是 Bound -> Lost,当 PV 被删除后,PVC 的状态变为 Lost。
3:这里表示的是 Lost -> Bound,被删除的 PV 又被添加回来,与 PVC 重新绑定。

PVC 详解

示例

PVC 是用户对存储资源的需求申请,下面是一个配置示例:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: myclaim
spec:
  accessModes: # 访问模式,设置为 ReadWriteOnce
    - ReadWriteOnce
  volumeMode: Filesystem # 存储卷模式,设置为 Filesystem
  resources:
    requests: # 资源请求,设置 storage 为 8GiB
      storage: 8Gi
  storageClassName: slow # 绑定的 StorageClass 的名称
  selector: # 选择条件
    matchLabels: # 符合标签
      release: "stable"
    matchExpressions: # 符合标签表达式
      - { key: environment, operator: In, values: [dev] }

这其中涉及到了比较多的字段选项,下面来一一详解。

accessModes(访问模式)

这个字段,PVC 与 PV 相同,也可以设置访问模式,作用也是描述应用对存储资源的访问权限。

volumeMode(存储卷模式)

这个字段,PVC 与 PV 也是相同的,描述的是希望使用的 PV 存储卷模式,可以配置的模式为文件系统和块设备。

resources(资源请求)

这个字段用于描述对存储资源的请求,目前仅支持设置 requests.storage,也就是对存储空间的设置。

storageClassName(绑定的 StorageClass 的名称,即:存储类别)

这个字段用于设置后端存储类别(即:绑定 StorageClass),可以减少对后端存储特性详细信息的依赖。分为两种情况:

  • 如果设置这个字段为某个 StorageClass,那么只有设置了相同 Class 的 PV 才会被选中,然后与 PVC 绑定。
  • 如果设置这个字段为空 storageClassName="",即不设置 Class。这时取决于系统是否启用名为 DefaultStorageClass 的 admission controller:
  • 没有启动 DefaultStorageClass:将选择没有设定 Class 的 PV 进行匹配和绑定。
  • 启用 DefaultStorageClass:管理员定义默认的 StorageClass,系统将使用默认 StorageClass 的后端存储创建一个 PV 并自动与 PVC 绑定。设置的具体方法为:在 StorageClass 的定义中添加一个 annotation "storageclass.kubernetes.io/is-default-class=true"。需要注意的是:只能定义一个默认的 StorageClass。

selector(选择条件)

这个字段可以对系统中已经存在的各种 PV 进行筛选,根据筛选条件选出最符合条件的 PV 进行绑定。这下面包含两个字段:

  • matchLabels:PV 必须有这个标签值。
  • matchExpressions:通过键值对和操作符指定标签选择器列表,操作符包括:In、Notln、Exists 和 DoesNotExist。

如果两个字段都设置了,就必须满足所有的条件才能完成匹配。

最后需要注意的是:Pod、PV 和 PVC 都必须在同一命名空间下才起作用。

另外,如果使用动态存储管理,即不预先定义 PV,只通过 StorageClass 交给系统自动完成 PV 的动态创建,那么 PVC 设置的 selector 就是无效的。如果用户删除了 PVC,与它绑定的 PV(默认回收策略为 Delete)也会被删除,可以在绑定成功后手动将 Delete 策略修改为 Retain。


2.3 静态存储实例

在这里使用一个简单的例子演示 PV、PVC 和 Pod 的搭配使用方式,我们创建 hostPath 类型的 PersistentVolume(也就是使用 Node 文件系统中的文件)。

在本地创建要映射进容器的文件:

$ mkdir tmp
$ echo "Hello Kubernetes" | tee -a /root/learning/tmp/index.html
Hello Kubernetes
$ scp tmp/index.html root@kubesphere02:/root/learning/tmp
$ scp tmp/index.html root@kubesphere03:/root/learning/tmp

定义PV,新建 pv-volume.yaml 文件,并向其中写入如下内容:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-volume
  labels:
    type: local
spec:
  storageClassName: manual # 这里需要定义 storageClassName
  capacity:
    storage: 10Gi # 设置存储空间大小为 10GiB
  accessModes:
    - ReadWriteOnce
  hostPath: # 为 hostPath 类型
    path: "/root/learning/tmp" # 路径为本地的 /root/learning/tmp

执行创建:

$ kubectl create -f pv-volume.yaml
persistentvolume/pv-volume created
# 现在 pv-volume 已经创建好了,状态为 Available
$ kubectl get pv
NAME              CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM     STORAGECLASS    REASON   AGE                                           
pv-volume         10Gi       RWO            Retain           Available             manual                   31s

接下来创建 PVC,新建 pv-claim.yaml 文件,并向其中写入如下内容:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pv-claim
spec:
  storageClassName: manual # 这里一定要设置 storageClassName 的名称与 PV 的定义中相同,否则会使用默认的 StorageClass 进行动态创建
  accessModes:
    - ReadWriteOnce
  resources: # 这里申请使用 3GiB 的存储空间
    requests:
      storage: 3Gi

执行创建:

$ kubectl create -f pv-claim.yaml
persistentvolumeclaim/pv-claim created

再一次查看 PV 的状态:

$ kubectl get pv pv-volume
NAME        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                STORAGECLASS   REASON   AGE
pv-volume   10Gi       RWO            Retain           Bound    user-test/pv-claim   manual                  3m51s

可以发现 pv-volume 的状态已经是 Bound。

查看 PVC 的状态:

# PVC 的状态也变为了 Bound
$ kubectl get pvc -n user-test
NAME        STATUS   VOLUME      CAPACITY   ACCESS MODES   STORAGECLASS    AGE
pv-claim    Bound    pv-volume   10Gi       RWO            manual          2m24s

创建一个 Pod 使用 pv-claim PersistentVolumeClaim 作为卷,新建 pv-pod.yaml 文件并向其中写入如下内容:

apiVersion: v1
kind: Pod
metadata:
  name: pv-pod
spec:
  volumes: # 为 Pod 设置卷
    - name: pv-storage # 卷名
      persistentVolumeClaim: # 引用 PVC 的名称
        claimName: pv-claim
  containers:
    - name: pv-container
      image: nginx:1.19
      ports:
        - containerPort: 80
          name: "http-server"
      volumeMounts: # 将卷挂载到容器中的 /usr/share/nginx/html 路径下
        - mountPath: "/usr/share/nginx/html"
          name: pv-storage

执行创建:

$ kubectl create -f pv-pod.yaml
pod/pv-pod created
$ kubectl get pod -o wide
NAME     READY   STATUS    RESTARTS   AGE    IP             NODE           NOMINATED NODE   READINESS GATES
pv-pod   1/1     Running   0          28s    10.20.138.50   kubesphere02   <none>           <none>

测试,查看 Pod 对应目录下是否存在文件:

$ kubectl exec -it pv-pod bash
root@pv-pod:/# cd /usr/share/nginx/html/
root@pv-pod:/usr/share/nginx/html# ls
index.html
root@pv-pod:/usr/share/nginx/html# cat index.html
Hello Kubernetes
root@pv-pod:/usr/share/nginx/html# echo 'Hello World' >> index.html
[root@kubesphere02 ~]# cat /root/learning/tmp/index.html
Hello Kubernetes
Hello World
0

评论区