k8s数据管理(八)

volume

我们经常会说:容器和 Pod 是短暂的。
其含义是它们的生命周期可能很短,会被频繁地销毁和创建。容器销毁时,保存在容器内部文件系统中的数据都会被清除。

为了持久化保存容器的数据,可以使用 Kubernetes Volume。

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

本质上,Kubernetes Volume 是一个目录,这一点与 Docker Volume 类似。当 Volume 被 mount 到 Pod,Pod 中的所有容器都可以访问这个 Volume。Kubernetes Volume 也支持多种 backend 类型,包括 emptyDir、hostPath、GCE Persistent Disk、AWS Elastic Block Store、NFS、Ceph 等,完整列表可参考 https://kubernetes.io/docs/concepts/storage/volumes/#types-of-volumes

Volume 提供了对各种 backend 的抽象,容器在使用 Volume 读写数据的时候不需要关心数据到底是存放在本地节点的文件系统中呢还是云硬盘上。对它来说,所有类型的 Volume 都只是一个目录。

emptyDir

emptyDir 是最基础的 Volume 类型。一个 emptyDir Volume 是 Host 上的一个空目录。

emptyDir Volume 对于容器来说是持久的,对于 Pod 则不是。当 Pod 从节点删除时,Volume 的内容也会被删除。但如果只是容器被销毁而 Pod 还在,则 Volume 不受影响。

也就是说:emptyDir Volume 的生命周期与 Pod 一致。

Pod 中的所有容器都可以共享 Volume,它们可以指定各自的 mount 路径。

下面通过例子来实践 emptyDir

第一步:配置文件如下:

k8s数据管理(八)

这里我们模拟了一个 producer-consumer 场景。Pod 有两个容器 producer和 consumer,它们共享一个 Volume。producer 负责往 Volume中写数据,consumer 则是从 Volume 读取数据。

① 文件最底部 volumes 定义了一个 emptyDir 类型的 Volume shared-volume。

② producer 容器将 shared-volume mount 到 /producer_dir 目录。

③ producer 通过 echo 将数据写到文件 hello 里。

④ consumer 容器将 shared-volume mount 到 /consumer_dir 目录。

⑤ consumer 通过 cat 从文件 hello 读数据。

第二步:执行如下命令创建 Pod:

[root@ken ~]# kubectl get pod
NAME                READY   STATUS    RESTARTS   AGE
producer-consumer   2/2     Running   0          2m1s
[root@ken ~]# kubectl logs producer-consumer consumer
hello world

注意:一个pod里面运行多个容器的时候,查看容器日志,需要指定pod名称及容器名

kubectl logs 显示容器 consumer 成功读到了 producer 写入的数据,验证了两个容器共享 emptyDir Volume。

因为 emptyDir 是 Docker Host 文件系统里的目录,其效果相当于执行了 docker run -v /producer_dir 和 docker run -v /consumer_dir。

第三步:通过 docker inspect 查看容器的详细配置信息,我们发现两个容器都 mount 了同一个目录:

k8s数据管理(八)

k8s数据管理(八)

这里 /var/lib/kubelet/pods/4dcc0366-245f-11e9-9172-000c292d5bb8/volumes/kubernetes.io~empty-dir/shared-volume 就是 emptyDir在 Host 上的真正路径。

emptyDir 是 Host 上创建的临时目录,其优点是能够方便地为 Pod 中的容器提供共享存储,不需要额外的配置。但它不具备持久性,如果 Pod 不存在了,emptyDir 也就没有了。根据这个特性,emptyDir 特别适合 Pod 中的容器需要临时共享存储空间的场景,比如前面的生产者消费者用例。

hostpath

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

比如 kube-apiserver 和 kube-controller-manager 就是这样的应用,通过

kubectl edit –namespace=kube-system pod kube-apiserver-k8s-master

查看 kube-apiserver Pod 的配置,下面是 Volume 的相关部分:

k8s数据管理(八)

这里定义了三个 hostPath volume k8s、certs 和 pki,分别对应 Host 目录 /etc/kubernetes、/etc/ssl/certs 和 /etc/pki。

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

示例:把节点上的tmp目录挂载到容器中

第一步:编写yml文件

[root@ken-node1 ~]# cat hostpath.yml 
apiVersion: v1
kind: Pod
metadata:
   name: myhostpath
spec:
  containers:
  - name: nginx
    image: nginx
    volumeMounts:
    - name: hostpath
      mountPath: /usr/share/nginx/html
  volumes:
  - name: hostpath
    hostPath:
      path: /tmp
      type: Directory

启动nginx,把主机的/tmp目录挂载到nginx容器的网站根目录

这里共享的是tmp目录,如果是挂载其他的目录要确保节点有这个目录

第二步:启动

[root@ken-node1 ~]# kubectl apply -f hostpath.yml

第三步:在tmp目录下编写测试文件

[root@ken-node1 ~]# echo "hostpath" > /tmp/index.html

第四步:查看podIP并尝试访问

[root@ken-node1 ~]# kubectl get pods -o wide
NAME                         READY   STATUS             RESTARTS   AGE     IP            NODE        NOMINATED NODE   READIN
myhostpath                   1/1     Running            0          8m8s    10.244.1.59   ken-node2   <none>           <none>

第五步:访问

[root@ken-node1 ~]# curl 10.244.1.59
hostpath

对接NFS

第一步:在控制节点部署NFS

[root@ken-node1 ~]# yum install nfs-utils rpcbind -y
[root@ken-node1 ~]# rm -rf /ken
[root@ken-node1 ~]# mkdir /ken
[root@ken-node1 ~]# chown -R nfsnobody: /ken
[root@ken-node1 ~]# echo "/ken *(rw)" > /etc/exports
[root@ken-node1 ~]# systemctl restart nfs rpcbind

第二步:编写yml文件

[root@ken-node1 ~]# cat nfs.yml 
apiVersion: v1
kind: Pod
metadata:
  name: nfs
spec:
  containers:
  - name: nginx
    image: nginx
    volumeMounts:
    - name: nfsback
      mountPath: /usr/share/nginx/html
  volumes:
    - name: nfsback
      nfs:
        path: /ken
        server: 192.168.163.132

第三步:节点需要下载nfs-utils去支持nfs文件类型,否则会报错

[root@ken-node2 ~]# yum install nfs-utils -y

第四步:执行yml文件

[root@ken-node1 ~]# kubectl apply -f nfs.yml

第五步:查看pod的IP

[root@ken-node1 ~]# kubectl get pods -o wide
NAME                         READY   STATUS             RESTARTS   AGE     IP            NODE        NOMINATED NODE   READINESS GATES
nfs                          1/1     Running            0          3m3s    10.244.2.61   ken-node3   <none>           <none>

第六步:编写测试文件在/ken共享目录下

[root@ken-node1 ~]# echo "nfs" > /ken/index.html

第七步: 访问测试

[root@ken-node1 ~]# curl 10.244.2.61
nfs

PV & PVC

PersistentVolume (PV) 是外部存储系统中的一块存储空间,由管理员创建和维护。与 Volume 一样,PV 具有持久性,生命周期独立于 Pod。

PersistentVolumeClaim (PVC) 是对 PV 的申请 (Claim)。PVC 通常由普通用户创建和维护。需要为 Pod 分配存储资源时,用户可以创建一个 PVC,指明存储资源的容量大小和访问模式(比如只读)等信息,Kubernetes 会查找并提供满足条件的 PV。

有了 PersistentVolumeClaim,用户只需要告诉 Kubernetes 需要什么样的存储资源,而不必关心真正的空间从哪里分配,如何访问等底层细节信息。这些 Storage Provider 的底层信息交给管理员来处理,只有管理员才应该关心创建 PersistentVolume 的细节信息。

Kubernetes 支持多种类型的 PersistentVolume,比如 AWS EBS、Ceph、NFS 等,完整列表请参考 https://kubernetes.io/docs/concepts/storage/persistent-volumes/#types-of-persistent-volumes

k8s数据管理(八)

NFS PV

第一步:需在 k8s-master 节点上搭建了一个 NFS 服务器,目录为 /nfsdata:

要更改目录属主,否则没有写入权限: chown -R nfsnobody.nfsnobody /nfsdata

注意:

  1. nfs可以安装在任意节点,不一定是在k8s集群当中的一台服务器
  2. k8s集群内的每台节点必须都要安装nfs-utils否则识别不到nfs类型
[root@ken ~]# showmount -e
Export list for ken:
/nfsdata *

第二步:创建一个 PV mypv1,配置文件 nfs-pv1.yml 如下:

[root@ken ~]# cat nfs-pv1.yml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mypv1
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  nfs:
    path: /nfsdata/pv1   #需要创建pv1目录,否则pod起不来
    server: 172.20.10.2

① capacity 指定 PV 的容量为 1G。

② accessModes 指定访问模式为 ReadWriteOnce,支持的访问模式有:

ReadWriteOnce – PV 能以 read-write 模式 mount 到单个节点。

ReadOnlyMany – PV 能以 read-only 模式 mount 到多个节点。

ReadWriteMany – PV 能以 read-write 模式 mount 到多个节点。

③ persistentVolumeReclaimPolicy 指定当 PV 的回收策略为 Recycle,支持的策略有:

Retain – 需要管理员手工回收。

Recycle – 清除 PV 中的数据,效果相当于执行 rm -rf /thevolume/*。

Delete – 删除 Storage Provider 上的对应存储资源,例如 AWS EBS、GCE PD、Azure Disk、OpenStack Cinder Volume 等。

④ storageClassName 指定 PV 的 class 为 nfs。相当于为 PV 设置了一个分类,PVC 可以指定 class 申请相应 class 的 PV。

⑤ 指定 PV 在 NFS 服务器上对应的目录。

第三步:创建 mypv1:

[root@ken ~]# kubectl apply -f nfs-pv1.yml
persistentvolume/mypv1 created

第四步:查看pv

[root@ken ~]# kubectl get pv
NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
mypv1   1Gi        RWO            Recycle          Available           nfs                     48s

STATUS 为 Available,表示 mypv1 就绪,可以被 PVC 申请。

第五步:接下来创建 PVC mypvc1,配置文件 nfs-pvc1.yml 如下:

注意:

1. 各个节点都需要下载nfs-utils

[root@ken-node1 ~]# cat pvc.yml 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mypvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
   requests:
     storage: 1Gi
  volumeName: mypv   #pv名字

PVC 就很简单了,只需要指定 PV 的容量,访问模式和 class。

[root@ken ~]# kubectl apply -f nfs-pvc1.yml
persistentvolumeclaim/mypvc1 created

第六步:查看pvc

[root@ken ~]# kubectl get pvc
NAME     STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mypvc1   Bound    mypv1    1Gi        RWO            nfs            112s
[root@ken ~]# kubectl get pv
NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM            STORAGECLASS   REASON   AGE
mypv1   1Gi        RWO            Recycle          Bound    default/mypvc1   nfs                     6m5s

从 kubectl get pvc 和 kubectl get pv 的输出可以看到 mypvc1 已经 Bound 到 mypv1,申请成功。

第七步:接下来就可以在 Pod 中使用存储了,Pod 配置文件 pod1.yml 如下:

k8s数据管理(八)

与使用普通 Volume 的格式类似,在 volumes 中通过 persistentVolumeClaim 指定使用 mypvc1 申请的 Volume。

第八步:创建 mypod1:

[root@ken ~]# kubectl apply -f pod1.yml
pod/mypod1 created

[root@ken ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mypod1 1/1 Running 0 51s 10.244.1.59 host1 <none> <none>

第九步:验证 PV 是否可用:

[root@ken ~]# kubectl exec mypod1 touch /mydata/hello

[root@ken ~]# ls /nfsdata/pv1/
hello

可见,在 Pod 中创建的文件 /mydata/hello 确实已经保存到了 NFS 服务器目录 /nfsdata/pv1 中。

回收PV

当 PV 不再需要时,可通过删除 PVC 回收。

k8s数据管理(八)

当 PVC mypvc1 被删除后,Kubernetes 启动了一个新 Pod recycler-for-mypv1,这个 Pod 的作用就是清除 PV mypv1 的数据。此时 mypv1 的状态为 Released,表示已经解除了与 mypvc1 的 Bound,正在清除数据,不过此时还不可用。

当数据清除完毕,mypv1 的状态重新变为 Available,此时则可以被新的 PVC 申请。

k8s数据管理(八)

因为 PV 的回收策略设置为 Recycle,所以数据会被清除,但这可能不是我们想要的结果。如果我们希望保留数据,可以将策略设置为 Retain。

第一步:将策略设置为 Retain。

[root@ken ~]# cat nfs-pv1.yml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mypv1
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: nfs
  nfs:
    path: /nfsdata/pv1
    server: 172.20.10.2

第二步:通过 kubectl apply 更新 PV:

root@ken ~]# kubectl apply -f nfs-pv1.yml
persistentvolume/mypv1 configured
[root@ken ~]# kubectl get pv
NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM            STORAGECLASS   REASON   AGE
mypv1   1Gi        RWO            Retain           Available                        nfs                     65m

回收策略已经变为 Retain,通过下面步骤验证其效果:

k8s数据管理(八)

① 重新创建 mypvc1

② 在 mypv1 中创建文件 hello

③ mypv1 状态变为 Released

④ Kubernetes 并没有启动 Pod recycler-for-mypv1

⑤ PV 中的数据被完整保留。

另外使用retain虽然删除pod后的数据得到了保留,但其 PV 状态会一直处于 Released,不能被其他 PVC 申请。为了重新使用存储资源,可以删除并重新创建 PV。删除操作只是删除了 PV 对象,存储空间中的数据并不会被删除。

k8s数据管理(八)

新建的 mypv1 状态为 Available,已经可以被 PVC 申请。

小结

本章我们讨论了 Kubernetes 如何管理存储资源。

emptyDir 和 hostPath 类型的 Volume 很方便,但可持久性不强,Kubernetes 支持多种外部存储系统的 Volume。

PV 和 PVC 分离了管理员和普通用户的职责,更适合生产环境。

原创文章,作者:Maggie-Hunter,如若转载,请注明出处:https://blog.ytso.com/53499.html

(0)
上一篇 2021年8月6日
下一篇 2021年8月6日

相关推荐

发表回复

登录后才能评论