数据管理

1991/6/26 基础

# 1. 什么是存储卷(Volume)

容器和Pod生命周期可能很短,会被频繁地销毁和创建。容器销毁时,保存在容器内部文件系统中的数据都会被清除,为了持久化保存容器的数据,可以使用Kubernetes 中的卷(Volume);

通俗点讲,Volume就是一个目录,这一点与Docker Volume类似。当VolumemountPodPod中的所有容器都可以访问这个Volume

Volume的生命周期独立于容器,Pod中的容器可能被销毁和重建,但Volume会被保留。

# 2. 存储卷分类

Kubernetes支持非常丰富的存储卷类型,包括本地存储(节点)和网络存储,下面只列出一些常见的类型:

  • emptyDir: 用于存储临时数据的简单空目录。
  • hostPath: 用于将目录从工作节点的文件系统挂载到pod中。
  • persistentVolumeClaim: 用来将持久卷(PersistentVolume)挂载到 Pod 中。
  • ....

查看更多支持类型:https://kubernetes.io/zh-cn/docs/concepts/storage/volumes/

# 3. emptyDir 使用

emptyDir是最基础的Volume类型,emptyDir Volume 对于容器来说是持久的,对于Pod则不是。当Pod从节点删除时,Volume的内容也会被删除。但如果只是容器被销毁而Pod还在,则Volume不受影响。也就是说:emptyDir Volume的生命周期与Pod一致。

Pod中的所有容器都可以共享Volume它们可以指定各自的mount路径。下面通过例子来实践emptyDir,配置文件如下:

# 3.1 配置文件

文件:empty-volume-deploy.yaml

apiVersion: v1
kind: Pod
metadata:
  name: emptydir-demo
spec:
  containers:
  - image: busybox
    name: write-box
    volumeMounts:
    - mountPath: /write-dir # 写容器将emptydir-volume挂载到 /write-dir
      name: emptydir-volume
    args:
    - /bin/sh
    - -c
    - echo "hello world" > /write-dir/hello; sleep 30000 # 写数据到文件
  - image: busybox
    name: read-box
    volumeMounts:
    - mountPath: /read-dir # 读容器将emptydir-volume挂载到 /read-dir
      name: emptydir-volume
    args:
    - /bin/sh
    - -c
    - cat /read-dir/hello; sleep 30000 # 从文件中读出

  volumes: # 定义一个名字为:emptydir-volume的emptyDir类型卷
  - name: emptydir-volume
    emptyDir: {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 3.2 发布验证

# 发布资源
$ kubectl apply -f empty-volume-deploy.yaml
pod/emptydir-demo created
# 查看读容器是否度到数据
$ kubectl logs emptydir-demo read-box
hello world
1
2
3
4
5
6

# 4. hostPath 使用

hostPath Volume的作用是将节点(宿主机)文件系统中已存在的目录挂载给Pod的容器。大部分应用都不会使用hostPath Volume,因为这实际上增加了Pod与节点的耦合,限制了Pod的使用。不过那些需要访问KubernetesDocker内部数据(配置文件和二进制库)的应用则需要使用hostPath

如果Pod被销毁了,hostPath对应的目录还是会被保留,从这一点来看,hostPath的持久性比emptyDir强。不过一旦Host崩溃,hostPath也就无法访问了。

# 4.1 配置文件

文件: hostpaht-volume-deploy.yaml

apiVersion: v1
kind: Pod
metadata:
  name: hostpath-demo
spec:
  containers:
  - image: busybox
    name: go-box
    volumeMounts:
    - mountPath: /home/go 
      name: hostpath-volume
    args:
    - /bin/sh
    - -c
    - echo "hello world" > /home/go/hello; sleep 30000 # 写数据到文件

  volumes: # 定义hostPath类型卷
  - name: hostpath-volume
    hostPath: 
      path: /home/go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 4.2 发布验证

# 发布资源
$ kubectl apply -f hostpaht-volume-deploy.yaml
pod/hostpath-demo created
# 查看pod 所在节点IP
$  kubectl describe pod hostpath-demo
Name:         hostpath-demo
Namespace:    default
Priority:     0
Node:         node2/192.168.148.132 # 这里看到节点IP
Start Time:   Fri, 02 Sep 2022 19:02:35 +0800
...
# 到对应的节点查看文件信息
[root@node2 go]$ cat /home/go/hello
hello world
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 5. PV和PVC

# 5.1 概念

Volume虽然提供了非常好的数据持久化方案,但是管理性上还有不足。

Pod通常是由应用的开发人员维护,而Volume则通常是由存储系统的管理员维护。开发人员要获得上面的信息,要么询问管理员,要么自己就是管理员。

这样就带来一个管理上的问题: 应用开发人员和系统管理员的职责耦合在一起了。如果系统规模较小或者对于开发环境,这样的情况还可以接受,当集群规模变大,特别是对于生成环境,考虑到效率和安全性,这就成了必须要解决的问题。

为了解决上面的问题,Kubernetes给出的解决方案是PersistentVolume(PV)PersistentVolumeClaim(PVC)

PersistentVolume(PV)PersistentVolumeClaim(PVC)说明:

  • PersistentVolume(PV):是外部存储系统中的一块存储空间,由管理员创建和维护。与Volume一样,PV具有持久性,生命周期独立于Pod
  • PersistentVolumeClaim (PVC): 是对PV的申请(Claim)。PVC通常由普通用户创建和维护。需要为Pod分配存储资源时,用户可以创建一个PVC,指明存储资源的容量大小和访问模式(比如只读)等信息,Kubernetes会查找并提供满足条件的PV

# 5.2 关系图

# 6.PV & PVC初使用

# 6.1 创建PV

下面示例是基于NFS文件共享系统为存储空间,至于NFS文件系统安装,此处省略。

# 6.1.1 配置文件

apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv
spec:
  capacity:
    storage: 1Gi # 指定PV的容量为1G
  accessModes: 
    - ReadWriteOnce # 访问模式
  persistentVolumeReclaimPolicy: Retain # 指定PV的回收策略
  storageClassName: nfs # 指定PV的class为nfs
  nfs:
    path: /data/pv1  #  指定PV在NFS服务器上的对应的目录
    server: 192.168.148.130 # NFS服务器IP
1
2
3
4
5
6
7
8
9
10
11
12
13
14

配置参数说明:

accessModes:访问模式

  • ReadWriteOnce(RWO): 仅允许单个节点挂载读写;
  • ReadOnlyMany(ROX): 允许多个节点挂载只读 ;
  • ReadWriteMan(RWX): 允许多个节点挂载读写 ;

persistentVolumeReclaimPolicy:回收策略

  • Retain : 需要手工回收;
  • Recycle:自动回收,即删除存储卷目录下的所有文件(包括子目录和隐藏文件),效果相当于执行rm -rf/xx/*,目前仅NFShostPath支持此操作;
  • Delete: 删除存储卷,仅部分云端存储系统支持,如AWS EBS、GCE PD、Azure Disk和Cinder

# 6.1.2 创建&发布

# 创建资源
$ kubectl apply -f nfs-pv.yaml
persistentvolume/nfs-pv created
# 查询状态 
$ kubectl get pv
NAME     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS     STORAGECLASS  .. 
nfs-pv   1Gi        RWO            Retain           Available     nfs        .. 
1
2
3
4
5
6
7

PV状态说明

  • Available:可用状态的自由资源,尚未被PVC绑定。
  • Bound:已经绑定至某PVC
  • Released:绑定的PVC已经被删除,但资源尚未被集群回收。
  • Failed:因自动回收资源失败而处于的故障状态。

# 6.2 创建PVC

# 6.2.1 配置文件

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-pvc1
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: nfs
1
2
3
4
5
6
7
8
9
10
11

# 6.2.2 创建&绑定

# 创建PVC
$ kubectl apply -f nfs-pvc1.yaml
persistentvolumeclaim/nfs-pvc1 created
# 查看PVC状态
$ kubectl get pvc
NAME       STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
nfs-pvc1   Bound    nfs-pv   1Gi        RWO            nfs            4s
# 查看PV状态
$ kubectl get pv
NAME   CAPACITY  ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM            STORAGECLASS ...    
nfs-pv  1Gi        RWO            Retain          Bound    default/nfs-pvc1   nfs       ...
1
2
3
4
5
6
7
8
9
10
11

通过PVPVC的状态都是Bound,则说明已经绑定成功。

# 6.3 使用PVC

经过上面几个步骤,已经成功创建了持久卷,接下来就可以在Pod中使用存储了。

# 6.3.1 配置文件

apiVersion: v1
kind: Pod
metadata:
  name: nfs-demo
spec:
  containers:
  - name: pvc-pod
    image: busybox
    volumeMounts:
    - mountPath: /pvc 
      name: pvc-volume
    args:
    - /bin/sh
    - -c
    - echo "hello world" > /pvc/hello-pvc; sleep 30000 # 写数据到文件
  volumes:  # 使用以下方式关联pvc
  - name: pvc-volume
    persistentVolumeClaim:
      claimName: nfs-pvc1 # 和之前创建的pvc名称一致
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 6.3.2 发布 & 验证

# 发布资源
$ kubectl apply -f pvc-pod.yaml
# 查看nfs服务文件
$ tree -l data
data
└── pv1
    └── hello-pvc
# 进入pod,查看pod内部文件
$ kubectl exec -it nfs-demo /bin/sh
/ # ls
bin   dev   etc   home  proc  pvc   root  sys   tmp   usr   var
/ # cd pvc/
/pvc # ls
hello-pvc
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 7. PV动态供给(StorageClass)

在前面的例子中,我们提前创建了PV,然后通过PVC申请PV并在Pod中使用,这种方式叫作静态供给(Static Provision)。

与之对应的是动态供给(Dynamical Provision),即如果没有满足PVC条件的PV,会动态创建PV。相比静态供给,动态供给有明显的优势:不需要提前创建PV,减少了管理员的工作量,效率高。

动态供给是通过StorageClass实现的,每个StorageClass 都包含 provisionerparametersreclaimPolicy 字段, 这些字段会在 StorageClass 需要动态分配 PersistentVolume 时会使用到。

StorageClass 支持多种类型的卷插件(Provisioner),不同的Provisoner的创建方法各有不同,参数也会有所区别,下面以NFS卷插件为例,进行学习。其他插件可参见文档: https://kubernetes.io/zh-cn/docs/concepts/storage/storage-classes/#aws-ebs

# 7.1 NFS驱动安装

因为Kubernetes 不包含内部 NFS 驱动。所以需要使用外部驱动为 NFS 创建 StorageClass,创建文档参考https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner

# 7.1.1 使用helm安装

# 添加仓库
$ helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/

# 安装
$ helm install nfs-client nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
     --set nfs.server=192.168.148.130 \ # nfs服务器IP
     --set nfs.path=/data/share \ # nfs共享目录
     --set image.repository=eipwork/nfs-subdir-external-provisioner \ # 更换镜像地址
     --set storageClass.name=nfs-client \
     --set storageClass.defaultClass=true # 开启默认使用的nfs的storageclass
1
2
3
4
5
6
7
8
9
10

# 7.1.2 查看deployment

$ kubectl get deploy
NAME                                         READY   UP-TO-DATE   AVAILABLE   AGE
nfs-client-nfs-subdir-external-provisioner   1/1     1            1           2m28s
1
2
3

# 7.1.3 查看StorageClass

[root@master ~]# kubectl get sc
NAME					PROVISIONER													RECLAIMPOLICY	VOLUMEBINDINGMODE	ALLOWVOLUMEEXPANSION	AGE	
nfs-client (default)	cluster.local/nfs-client-nfs-subdir-external-provisioner	Delete			Immediate			true					4m17s
1
2
3

# 7.2 绑定PVC

# 7.2.1 配置文件 nfs-pvc.yaml

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: nfs-pvc
spec:
  storageClassName: nfs-client
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1Mi
1
2
3
4
5
6
7
8
9
10
11

# 7.2.2 发布

$ kubectl apply -f nfs-pvc.yaml
persistentvolumeclaim/nfs-pvc created
# 查看状态发现状态 已经变成绑定
$ kubectl get pvc
NAME      STATUS   VOLUME   CAPACITY  ACCESS MODES   STORAGECLASS   AGE
nfs-pvc   Bound    pvc-...   1Mi        RWX           nfs-client     4s

# 虽然我们没有创建PV,但会自动创建
$ kubectl get pv
NAME  CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM           STORAGECLASS     
pvc-...   1Mi        RWX           Delete        Bound    default/nfs-pvc   nfs-client
1
2
3
4
5
6
7
8
9
10
11

# 7.3 Pod使用

# 7.3.1 配置文件nfs-pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: nfs-pod-demo
spec:
  containers:
  - name: pvc-pod
    image: busybox
    volumeMounts:
    - mountPath: /pvc 
      name: pvc-volume
    args:
    - /bin/sh
    - -c
    - echo "hello world..." > /pvc/hello-pvc; sleep 30000 # 写数据到文件
  volumes:  # 使用以下方式关联pvc
  - name: pvc-volume
    persistentVolumeClaim:
      claimName: nfs-pvc # 和之前创建的pvc名称一致
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 7.3.2 发布 & 验证

# 发布资源
$ kubectl apply -f nfs-pod.yaml
pod/nfs-pod-demo created
1
2
3
[root@node1 share]# pwd
/data/share
[root@node1 share]# ls
default-nfs-pvc-pvc-71043bd1-5fef-4b6d-8cf5-dbfdbif19a75
[root@node1 share]# cd default-nfs-pvc-pvc-71043bd1-5feef-4b6d-8cf5-dbfdb1f19a75/
[root@node1 default-nfs-pvc-pvc-71043bd1-5fef-4b6d-8cf5-dbfdb1f19a75]# ls
hello-pvc
[root@node1 default-nfs-pvc-pvc-71043bd1-5fef-4b6d-8cf55-dbfdb1f19a75]# cat hello-pvc
hello world..
1
2
3
4
5
6
7
8
9