本篇介绍内容主要有

  • Volume
  • PersistenceVolume
  • PersistentVolumeClaim
  • PV的提供方式(Provisioning)
  • PVC与PV的绑定
  • 示例:使用PVC

参考文档

Volume

容器中的文件系统是不持久的,在容器中运行一些复杂的应用可能导致某些问题:第一,当容器崩溃,kubelet会重启它,但容器内的文件已经丢失了,因为每次当容器重启的时候,容器会以一个“干净”的初始状态启动。第二,当一组容器在同一个Pod上运行的时候,他们通常需要共享一些文件。k8s的Volume是为了解决这些问题而作的一个抽象。

On-disk files in a Container are ephemeral, which presents some problems for non-trivial applications when running in Containers. First, when a Container crashes, kubelet will restart it, but the files will be lost - the Container starts with a clean state. Second, when running Containers together in a Pod it is often necessary to share files between those Containers. The Kubernetes Volume abstraction solves both of these problems.
https://kubernetes.io/docs/concepts/storage/volumes/#background

Volume的生命周期跟Pod是一样的,所以Volume比任何在Pod中运行的容器的启动时间都早,所以当容器启动的时候,Volume就已经准备好了,当容器崩溃重启的时候,Volume也不会丢失。

k8s内置了许多Volume的类型:

  • awsElasticBlockStore
  • azureDisk
  • nfs
  • emptyDir
  • hostPath
  • nfs

完整列表可以查看官方文档
k8s通过一组抽象接口——CSI(Container Storage Interface) 与不同类型的“存储卷”进行通信,内置的Volume类型不仅包含像NFS这样的标准网络文件系统,还集成了许多云平台的存储服务,比如aws亚马逊云,GCE(Google Compute Engine)谷歌云等。这一组抽象的接口叫 。
换句话说,只要实现了CSI接口,就可以以Volume的方式被k8s所管理,比如阿里云,腾讯云,华为云等等的云存储卷均可以用k8s来管理(前提是要实现CSI接口)。

示例

在minikube环境下,以hostPath为例,hostPath表示将Volume挂载在Node节点上的文件系统上。
创建test-pd.yaml文件:

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: nginx:1.7.9
    name: test-container
    volumeMounts:
    - mountPath: /test-pd
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      # directory location on host
      path: /data
      # this field is optional
      type: Directory

该配置文件表示将容器中的/test-pd路径挂载到Node的目录/data
创建Pod:

$  kubectl create -f test-pd.yaml
pod "test-pd" created
$ kubectl get po
NAME      READY     STATUS              RESTARTS   AGE
test-pd   0/1       ContainerCreating   0          14s

接下来验证一下:
查看Node主机上的/data目录:

$ ls /data
minikube

验证Pod中/test-pd中的内容是否跟Node中/data目录的内容一致:

$ kubectl exec -it test-pd /bin/bash
root@test-pd:/# ls /test-pd
minikube

目录一致,说明Volume已经正确挂载了。

PersistenceVolume (PV)

通过上述的例子我们发现:Volume是定义在Pod上的,属于Pod——“计算资源”的一部分。而实际上,“网络资源”应当是相对于独立于“计算资源的”而存在的一种实体资源。
k8s提供PersistenceVolume来将“计算资源”和“网络资源”进行解耦。
PV跟Node一样都是k8s集群中的一种资源。PV与Volume很类似,但有以下区别:

  • PV只能是网络存储(区别于上述的hostPath本地存储),不属于任何Node,但可以在每个Node上访问。
  • PV并不是定义在Pod上的,而是独立于Pod之外定义。
  • PV的生命周期与Pod是独立的。

示例

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv0003
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: slow
  mountOptions:
    - hard
    - nfsvers=4.1
  nfs:
    path: /tmp
    server: 172.17.0.2

这个PV表示了一个使用NFS文件系统的,大小为5G的存储资源。
如果某个Pod想申请某种类型的PV,则首先需要定义一个PersistentVolumeClaim(PVC)。

关于accessModes的一些定义:

  • ReadWriteOnce – 该Volume只能被挂载在一个节点上,读写权限。
  • ReadOnlyMany – 该Volume可以被挂载在多个节点上,只读权限。
  • ReadWriteMany – 该Volume只能被挂载在多个节点上,读写权限。

PersistentVolumeClaim (PVC)

PVC是用户对“存储资源”(PV)的一种请求,它跟Pod类似。Pod消费Node资源,而PVC消费PV资源。对于用户而言,PVC屏蔽了Volume的一些细节——用户通过PVC请求具体大小和特定的access-mode的PV资源,而不需要关心底层Volume到底是哪一种类型。
但不同的Volume类型在使用上还是有一定差异,比如某些Volume类型是存储介质是SSD(固态硬盘),某些Volume类型的存储介质可能只是机械硬盘,所以对于用户来说,还需要对这些Volume做一个分类,而不能仅仅通过Volume的大小和access-mode来请求资源,毕竟请求到一块SSD硬盘和一块机械硬盘的区别还是很大的。
k8s提供StorageClass资源来处理这个问题:集群管理员可以根据Volume的类型,划分出不同的StorageClass类别,让用户自由选择。比如:StorageClass=ssd的表示SSD,StorageClass=hhd的表示机械硬盘。用户在请求PV的时候带上所需的StorageClass,就可以获取到匹配的Volume了。

示例

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: myclaim
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 8Gi
  storageClassName: slow
  selector:
    matchLabels:
      release: "stable"
    matchExpressions:
      - {key: environment, operator: In, values: [dev]}

PV的提供方式(Provisioning)

一共有两种提供PV的方式:静态(static)或者动态(dynamic)。

静态

集群管理员通过预先分配的方式,创建一些PV资源。

动态

当PVC请求PV资源的时候,由集群动态生成PV资源。这种分配方式是基于StorageClass的:PVC必须带StorageClass来请求动态生成的PV资源。同时,也需要对应的Volume类型能支持动态分配。

PVC与PV的绑定

PVC跟PV是一个一对一的绑定关系,当PV跟PVC绑定之后,才能被Pod引用。
每当新建一个PVC的时候,k8s都会尝试寻找一个符合条件的PV(静态或动态)与之绑定,这种绑定关系是一对一的。一旦绑定成功,PVC能够得到满足其最小要求的PV资源,也有可能得到超过预期的资源。
如果没有任何一个PV能够满足PVC的需求,则该PVC会一直处于未绑定状态,直到找到可用的PV与之绑定。
比如:一个提供50G存储容量的PV不能与一个请求100G的PVC绑定。当一个至少包含100G存储容量的PV被创建出来之后,PVC才会与之绑定。

示例

通过一个示例来说明一下PV和PVC的绑定过程:

  1. 创建一个1G的PV,监控其绑定状态。
  2. 创建一个请求2G的PVC,监控其绑定状态。
  3. 创建一个2G的PV。

打开三个控制台,分别为c1,c2,c3
c1控制台创建1G的PV:

# 1g-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: 1g-pv
spec:
  capacity:
    storage: 1Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: slow
  hostPath:
    # 本地测试路径
    path: /test

创建并监控PV的状态变化:

$ kubectl create -f  1g-pv.yaml
persistentvolume "1g-pv" created
$ get pv 1g-pvc -w
NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM     STORAGECLASS   REASON    AGE
1g-pv  	  1Gi        RWO            Recycle          Available             slow                     1m

c2控制台创建请求2G的PVC:

# 2g-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: 2g-pvc
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 2Gi
  storageClassName: slow

创建并监控状态:

$ kubectl create -f 2g-pvc.yaml
persistentvolumeclaim "2g-pvc" created
$ kubectl get pvc 2g-pvc -w
NAME       STATUS    VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS   AGE
2g-pvc     Pending                                       slow           1m

至此,我们创建了一个1G的PV,一个请求2G资源的PVC,由于1G的PV并不能满足2G的PVC的请求,所以此时该PV和PVC还未有绑定关系:

//c1控制台,1g-pv处于Available状态
$ kubectl get pv 1g-pv -w
NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM     STORAGECLASS   REASON    AGE
1g-pv     1Gi        RWO            Recycle          Available             slow                     1m
//c2控制台,2g-pvc处于Pending状态
$ kubectl get pvc 2g-pvc -w
NAME      STATUS    VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS   AGE
2g-pvc    Pending                                       slow           1m

接下来在c3控制台创建2G的PV:

# 2g-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: 2g-pv
spec:
  capacity:
    storage: 2Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: slow
  hostPath:
    # 本地测试路径
    path: /test

创建并且查看状态:

//c3控制台
$ create -f 2g-pv.yaml
persistentvolume "2g-pv" created
$  kubectl get pv 2g-pv -w
NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM     STORAGECLASS   REASON    AGE
2g-pv     2Gi        RWO            Recycle          Available             slow                     5s
2g-pv     2Gi       RWO       Recycle   Available   default/2g-pvc   slow                9s
2g-pv     2Gi       RWO       Recycle   Bound     default/2g-pvc   slow                9s

可以看到2g的PV创建出来没多久,就被绑定到2g-pvc上了。
同样的可以查看c2控制台:

NAME      STATUS    VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS   AGE
2g-pvc    Pending                                       slow           5s
2g-pvc    Pending   2g-pv     0                   slow      27s
2g-pvc    Bound     2g-pv     2Gi       RWO       slow      27s

2g-pvc已经跟2g-pv绑定上了。
由于1G的PV资源还是处于“无人认领”状态,所以查看c1控制台,发现1g-pv的状态是没有发生变化的:

NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM     STORAGECLASS   REASON    AGE
1g-pv     1Gi        RWO            Recycle          Available             slow                     30s

示例:使用PVC

Pod通过定义PVC来请求PV资源,下面通过一个例子加以说明。
创建test-pv.yamltest-pvc.yamltest-po.yaml文件,内容如下:

# test-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: test-pv
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: slow
  hostPath:
    # 本地测试路径
    path: /test
# test-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: myclaim
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 5Gi
  storageClassName: slow
# test-po.yaml
kind: Pod
apiVersion: v1
metadata:
  name: mypod
spec:
  containers:
    - name: myfrontend
      image: nginx:1.7.9
      volumeMounts:
      - mountPath: "/usr/share/nginx/html"
        name: mypd
  volumes:
    - name: mypd
      persistentVolumeClaim:
        claimName: myclaim

创建PV/PVC/POD:

$ kubectl create -f test-po.yaml
pod "mypod" created
$ kubectl create -f test-pv.yaml
persistentvolume "test-pv" created
$ kubectl create -f test-pvc.yaml
persistentvolumeclaim "myclaim" created

通过查询得到pod的IP地址是172.17.0.5,用curl验证是否绑定成功:

$ echo "hello word" > test.html
$ curl 172.17.0.5/test.html 
hello word
Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐