Kubernetes基于Volume存储数据
1 Volume介绍
1.1 Volume的作用
- 容器中的文件在磁盘上是临时存放的,这给容器中运行的特殊应用程序带来一些问题。 首先,当容器崩溃时,kubelet 将重新启动容器,容器中的文件将会丢失——因为容器会以干净的状态重建。 其次,当在一个 Pod 中同时运行多个容器时,常常需要在这些容器之间共享文件。 Kubernetes 抽象出 Volume 对象来解决这两个问题。
1.2 Volume的特点
-
Kubernetes 卷具有明确的生命周期——与包裹它的 Pod 相同。 因此,卷比 Pod 中运行的任何容器的存活期都长,在容器重新启动时数据也会得到保留。 当然,当一个 Pod 不再存在时,卷也将不再存在。 重要的是,Kubernetes 可以支持许多类型的卷,Pod 也能同时使用任意数量的卷。
-
卷的核心是包含一些数据的目录,Pod 中的容器可以访问该目录。 特定的卷类型可以决定这个目录如何形成,并能决定它支持何种介质,以及目录中存放什么内容。
-
容器中的进程能看到由它们的 Docker 镜像和卷组成的文件系统视图。 Docker 镜像位于文件系统层次结构的根部,并且任何 Volume 都挂载在镜像内的指定路径上。 卷不能挂载到其他卷,也不能与其他卷有硬链接。 Pod 中的每个容器必须独立地指定每个卷的挂载位置。
1.3 Volume的类型
- 卷的类型有很多种,具体请见Kubernetes官网说明文档,其中大部分是与云环境相关。本文主要介绍在非云环境中使用得较多的几种卷,主要包括:emptyDir、hostPath、configMap、secret、nfs、persistentVolumeClaim等。
2 Volume使用
2.1 特别说明
- 本文使用的 K8S 集群的搭建过程请见 基于CentOS 7.6安装Kubernetes 1.18.0单Master节点集群 。
2.2 emptyDir
-
作用说明:emptyDir 的作用是创建一个空卷,并挂载到 Pod中 的容器。Pod 删除则该卷也会被删除,一般用作临时目录、缓存、Pod 中容器之间的数据共享。
-
实战需求:同一个 Pod 中的两个容器基于 emptyDir 进行数据共享,其中一个容器往 emptyDir 中写数据,另一个容器从 emptyDir 中读数据。
-
编写yaml:在 Kubernetes 集群的 Master 节点中创建一个名为 empty-dir-pod.yaml 的文件,其内容如下所示:
apiVersion: v1 kind: Pod metadata: name: empty-dir-pod spec: containers: - name: write-shared-data image: busybox:latest command: ["/bin/sh", "-c", "echo 'hello world' >> /data/shared-data.txt"] volumeMounts: - name: shared-data mountPath: /data - name: read-shared-data image: busybox:latest command: ["/bin/sh", "-c", "cat /data/shared-data.txt"] volumeMounts: - name: shared-data mountPath: /data volumes: - name: shared-data emptyDir: {} restartPolicy: Never
-
yaml说明:上述yaml文件中创建了一个名称为 empty-dir-pod的Pod,该 Pod 中创建一个名称为 shared-data 的 emptyDir 类型的数据卷,然后使用 busybox:latest 镜像创建了两个容器,且这两个容器都将该数据卷挂载了容器内部的 /data 目录,名称为 write-shared-data 的容器会往 /data/shared-data.txt 文件中写一句 “hello world”,名称为 read-shared-data 的容器会把 /data/shared-data.txt 文件中的内容打印出来。
-
创建容器:在 Kubernetes 集群的 Master 节点中执行以下命令即可使用上述 yaml 容器创建容器,执行结果如下图所示:
kubectl apply -f empty-dir-pod.yaml
-
查看日志:上述容器执行成功之后,即可通过在 Kubernetes 集群的 Master 节点中执行以下命令查看 read-shared-data 容器的执行日志,结果如下图所示,则说明 write-shared-data 成功的将数据写入到了emptyDir 中,而且 read-shared-data 成功的从 emptyDir 中读取到了数据。
kubectl logs -f empty-dir-pod -c read-shared-data
2.3 hostPath
-
作用说明:hostPath 的作用是把 Node 节点上的文件或者目录挂载到 Pod中 的容器。由于 Pod 在重新生成时,可能会更换 Node 节点,从而导致原先的 hostPath 无法使用。
-
实战需求:Pod中 的容器访问宿主机(即 Node 节点)中的文件。
-
编写yaml:在 Kubernetes 集群的 Master 节点中创建一个名为 host-path-pod.yaml 的文件,其内容如下所示:
apiVersion: v1 kind: Pod metadata: name: host-path-pod spec: containers: - name: read-host-data image: busybox:latest command: ["/bin/sh", "-c", "cat /etc/docker/daemon.json"] volumeMounts: - name: host-data mountPath: /etc volumes: - name: host-data hostPath: path: /etc type: Directory restartPolicy: Never
-
yaml说明:上述yaml文件中创建了一个名称为 host-path-pod 的 Pod,该 Pod 中创建一个名称为 host-data 的 hostPath 类型的数据卷,该数据卷映射到 Node 节点的 /etc 目录,然后使用 busybox:latest 镜像创建了一个名称为 read-host-data 的容器,该容器将该数据卷挂载了容器内部的 /etc 目录,并且会把 /etc/docker/daemon.json 文件中的内容打印出来。
-
创建容器:在 Kubernetes 集群的 Master 节点中执行以下命令即可使用上述 yaml 容器创建容器,执行结果如下图所示:
kubectl apply -f host-path-pod.yaml
-
查看日志:上述容器执行成功之后,即可通过在 Kubernetes 集群的 Master 节点中执行以下命令查看 read-host-data 容器的执行日志,结果如下图所示,则说明 read-host-data 成功从 hostPath 中读取到了数据。
kubectl logs -f host-path-pod
2.4 configMap
- 作用说明:configMap 的作用是存储应用程序配置,例如配置文件,或配置项等,并通过环境变量或挂载的方式传给 Pod 中的容器使用,由于 configMap 对于存储的配置信息的安全性较低,因此,不建议把密码、OAuth Token 等敏感信息保存在 configMap 中。
- 详情链接:configMap 的详细应用请见 Kubernetes基于ConfigMap实现配置分离。
2.5 secret
- 作用说明:secret 的作用是存储密码、OAuth Token、SSH Key 等敏感信息。
- 详情链接:secret 的详细应用请见 Kubernetes基于Secret存储敏感数据。
2.6 nfs
-
作用说明:Kubernetes 除了可以使用 emptyDir、hostPath、configMap、secret 等类型的数据卷把数据存储在 Kubernetes 集群内部的的节点可 Pod 中,还可以使用 nfs 等类型的数据卷把数据存储在远程的 NFS 服务器中。
-
特别说明:本小节中使用的 NFS 服务器的搭建过程请见下面的第3节。
-
实战需求:Pod 中的容器将运行日志保存到 NFS 服务器中。
-
编写yaml:在 Kubernetes 集群的 Master 节点中创建一个名为 nfs-pod.yaml 的文件,其内容如下所示:
apiVersion: v1 kind: Pod metadata: name: nfs-pod spec: containers: - name: write-nfs-data image: busybox:latest command: ["/bin/sh", "-c", "echo 'hello world' >> /mnt/nfs/logs/log.txt"] volumeMounts: - name: nfs-data mountPath: "/mnt/nfs/logs" volumes: - name: nfs-data nfs: path: /mnt/nfs server: nfs.k8s.com restartPolicy: Never
-
yaml说明:上述 yaml 文件中创建了一个名称为 nfs-pod 的 Pod,该 Pod 中创建一个名称为 nfs-data 的 nfs 类型的数据卷,该数据卷映射到了域名为 nfs.k8s.com 的 NFS 服务器的 /mnt/nfs 目录,然后使用 busybox:latest 镜像创建了一个名称为 write-nfs-data 的容器,该容器将该数据卷挂载了容器内部的 /mnt/nfs/logs 目录,并模拟把容器的运行日志保存到容器内部的 /mnt/nfs/logs/log.txt 文件中,即保存到了 NFS 服务器中的 /mnt/nfs 目录中。
-
创建容器:在 Kubernetes 集群的 Master 节点中执行以下命令即可使用上述 yaml 容器创建容器,执行结果如下图所示:
kubectl apply -f nfs-pod.yaml
-
查看日志:上述容器执行成功之后,即可通过在 NFS 服务器中的 Shell 命令行客户端中执行以下命令查看 write-nfs-data 容器是否把运行日志保存到了 NFS 服务器的 /mnt/nfs 目录下,结果如下图所示,则说明 write-nfs-data 成功把运行日志保存到了 nfs 类型的数据卷中了。
ll /mnt/nfs cat /mnt/nfs/log.txt
2.7 persistentVolumeClaim
-
作用说明:Kubernetes 可以使用 PersistentVolumeClaim 持久卷声明类型的数据卷来保存数据,这种类型的数据卷只是一个数据卷声明,而并没有指定数据卷的具体实现方式,从而实现了数据卷的实现和使用解耦的目的。PersistentVolumeClaim 类型的数据卷的实现形式有很多种,具体请见 Kubernates官方说明文档。
-
使用说明:需要先创建 PersistentVolume 持化卷(表示具体的存储资源),然后再创建 PersistentVolumeClaim 持久卷声明(与具体的 PersistentVolume 持久卷绑定),最后再在 Pod 挂载 PersistentVolumeClaim 类型的持久卷并进行使用。
-
特别说明:本小节中使用的 NFS 服务器的搭建过程请见下面的第3节。
-
实战需求:Pod 中的容器通过持久化卷将运行日志保存在 NFS 服务器中。
2.7.1 创建persistentVolume
-
创建yaml:在 Kubernetes 集群的 Master 节点中创建一个名为 nfs-pv.yaml 的文件,其内容如下所示:
apiVersion: v1 kind: PersistentVolume metadata: name: nfs-pv spec: capacity: storage: 1Gi volumeMode: Filesystem accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Recycle storageClassName: slow nfs: path: /mnt/nfs server: nfs.k8s.com
-
yaml说明:上述 yaml 文件中创建了一个名称为 nfs-pv 的持久卷,该持久卷映射到了域名为 nfs.k8s.com 的 NFS 服务器的 /mnt/nfs 目录,并且容量大小为 1G、卷模式为 Filesystem、访问模式为 ReadWriteMany、回收策略为 Recycle。
-
创建持久卷:在 Kubernetes 集群的 Master 节点中执行以下命令即可使用上述 yaml 创建持久卷,执行结果如下图所示:
kubectl apply -f nfs-pv.yaml
-
查看持久卷:在 Kubernetes 集群的 Master 节点中执行以下命令即可查看创建成功的持久卷,此时该持久卷应该是 Available 的状态,结果如下图所示:
kubectl get pv
2.7.2 创建persistentVolumeClaim
-
创建yaml:在 Kubernetes 集群的 Master 节点中创建一个名为 nfs-pvc.yaml 的文件,其内容如下所示:
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: nfs-pvc spec: resources: requests: storage: 1Gi volumeMode: Filesystem accessModes: - ReadWriteMany storageClassName: slow
-
yaml说明:上述 yaml 文件中创建了一个名称为 nfs-pvc 的持化卷声明,该持化卷声明请求容量大小为 1G、卷模式为 Filesystem、访问模式为 ReadWriteMany 的持久卷,如果 Kubernetes 集群中在满足该条件且状态为 Available 的持久卷,则该持久卷声明会绑定到满足条件的持久卷,如果没有,则不会绑定。
-
创建持久卷声明:在 Kubernetes 集群的 Master 节点中执行以下命令即可使用上述 yaml 创建持久卷声明,执行结果如下图所示:
kubectl apply -f nfs-pvc.yaml
-
查看持久卷声明:在 Kubernetes 集群的 Master 节点中执行以下命令即可查看创建成功的持久卷声明,此时该持久卷声明的状态为 Bound,说明该持久卷已经绑定到了具体的持久卷上了,结果如下图所示:
kubectl get pvc
-
查看持久卷:在 Kubernetes 集群的 Master 节点中执行以下命令再次查看持久卷,此时在2.7.2节中创建的持久卷的状态为 Bound,说明该持久卷已经绑定到了某一持久卷声明上了,结果如下图所示:
kubectl get pv
2.7.3 使用persistentVolumeClaim
-
创建yaml:上述成功创建了持久卷和持久卷声明,且持久卷和持久卷声明成功绑定在一起了,则可以在 Pod 挂载持久卷声明,并使用持久卷了。在 Kubernetes 集群 Master 节点中创建一个名为 nfs-pvc-pod.yaml 的文件,其具体内容如下所示:
apiVersion: v1 kind: Pod metadata: name: nfs-pvc-pod spec: containers: - name: write-nfs-pvc-data image: busybox:latest command: ["/bin/sh", "-c", "echo 'hello world' >> /mnt/nfs/logs/pvc-log.txt"] volumeMounts: - name: nfs-pvc-data mountPath: /mnt/nfs/logs volumes: - name: nfs-pvc-data persistentVolumeClaim: claimName: nfs-pvc restartPolicy: Never
-
yaml说明:上述 yaml 文件中创建了一个名称为 nfs-pvc-pod 的 Pod,并将名称为 nfs-pvc 且类型为 persistentVolumeClaim 的持久卷声明挂载到了该 Pod 的 /mnt/nfs/logs 目录,并模拟把容器的运行日志保存到容器内部的 /mnt/nfs/logs/pvc-log.txt 文件中,即保存到了 NFS 服务器中。
-
创建容器:在 Kubernetes 集群的 Master 节点中执行以下命令即可使用上述 yaml 容器创建容器,执行结果如下图所示:
kubectl apply -f nfs-pvc-pod.yaml
-
查看日志:上述容器执行成功之后,即可通过在 NFS 服务器中的 Shell 命令行客户端中执行以下命令查看 nfs-pvc-data 容器是否把运行日志保存到了 NFS 服务器的 /mnt/nfs 目录下,结果如下图所示,则说明 nfs-pvc-data 成功把运行日志保存到了 nfs 类型的数据卷中了。
ll /mnt/nfs cat /mnt/nfs/pvc-log.txt
2.7.4 persistentVolume的补充说明
-
访问模式:PersistentVolume 有三种访问模式,分别是 ReadWriteOnce(持久卷可以被一个节点以读写方式挂载)、ReadWriteMany(持久卷可以被多个节点以读写方式挂载)、ReadOnlyMany(持久卷可以被多个节点以只读方式挂载)。
-
回收策略:当用户不再使用 PersistentVolume 时,可以将 PersistentVoumeClaim 对象删除,从而允许 PersistentVolume 资源被回收再利用。PersistentVolume 对象的回收策略告诉集群,当其被从 PersistentVoumeClaim 中释放时如何处理该持久卷。 持久卷的回收策略有三种,分别是 Retained(保留)、Recycled(回收)或 Deleted(删除)。
-
卷模式:针对 PersistentVolume 持久卷,Kuberneretes 支持两种卷模式:Filesystem(文件系统) 和 Block(块)。 默认的卷模式是 Filesystem,如果volumeMode 属性设置为 Filesystem ,则持久卷会被 Pod 挂载到某个目录。
3 Kubernetes使用NFS服务
3.1 环境说明
- 本文使用的 K8S 集群的搭建过程请见 基于CentOS 7.6安装Kubernetes 1.18.0单Master节点集群 。
- K8S集群中搭建Harbor私有镜像仓库的过程请见 Kubernetes使用Harbor私有镜像仓库。
- 本文要在上述K8S集群中搭建 NFS 服务,由于资源有限,所以在 Harbor 私有镜像仓库所在的服务器中部署 NFS 服务器,部署后的效果如下图所示。
3.2 NFS服务端搭建
-
安装软件:搭建 NFS 服务器,需要安装 nfs-utils 和 rpcbind 软件,由于 CentOS 7.6 已经安装了 rpcbind,所以只需要安装 nfs-utils 即可。在要安装 NFS 服务端的主机上(本文是在K8S集群中的k8s-harbor节点中安装)执行以下 Shell 命令即可安装 NFS 服务端。
yum install -y nfs-utils
-
创建目录:安装完成之后,需要创建一个目录,用作 NFS 共享目录,并且需要给该目录设置权限,执行 Shell 命令如下所示:
mkdir /mnt/nfs chmod -R a+w /mnt/nfs
-
修改配置:NFS 服务端安装成功之后,需要在 NFS 的配置文件 /etc/exports 中指定允许来自哪些 IP 地址的主机访问 NFS 服务,由于本文中的 Kubernetes 集群所有节点都在 192.168.1.0/24 网段中,所以需要在 /etc/exports 文件中添加以下内容。
/mnt/nfs/ 192.168.1.0/24(rw,sync,no_root_squash)
-
启动服务:执行以下 Shell 命令启动 nfs-server 服务。
systemctl start nfs-server.service
-
配置生效:执行以下 Shell 命令使用上述配置生效。
exportfs -r
-
检查配置:执行以下命令检查上述配置是否生效,执行命令后,会显示出已生效的配置,结果截图如下所示。
exportfs
-
开机启动:执行以下 Shell 命令使 nfs-server 服务开机自动启动。
systemctl enable nfs-server.service
-
特别说明:在使用 NFS 客户端连接 NFS 服务端的时候可能会报错 clnt_create: RPC: Port mapper failure - Unable to receive: errno 113 (No route to host) ,这是因为 NFS 服务端防火墙的问题,可以关闭防火墙来解决(生产环境不推荐这样解决)。
systemctl stop firewalld
3.3 NFS客户端搭建
-
添置域名映射:由于本文中的 Kubernetes 集群中的节点无法解析域名 nfs.k8s.com,所以需要在 Kubernetes 集群中所有的 Worker 节点的域名解析文件 /etc/hosts 中添加以下内容(192.168.1.18 为本文中的 NFS 服务器的 IP 地址)。
192.168.1.18 nfs.k8s.com
-
安装软件:这里安装 NFS 客户端,是为了检查 NFS 服务端的服务是否可以正常访问,因此,只需要安装 nfs-utils 软件,而不需要启动 nfs 服务。在 Kubernetes 集群中的某一 Worker 节点中执行以下 Shell 命令即可安装 nfs-utils 软件。
yum install -y nfs-utils
-
查看服务状态:nfs-utils 软件安装成功之后,即可执行以下 Shell 命令检查 NFS 服务是否可以正常访问,结果如下图所示则表示可以正常访问,如果 NFS 服务可以正常访问,则 Kubernetes 集群中就可以通过本文的 2.6 节和 2.6 节中介绍的两种方式使用 NFS 服务存储数据了。如果还需要主机中通过 nfs-client 客户端访问 nfs-server 服务端了,则需要继续完成后面的步骤。
showmount -e nfs.k8s.com
-
创建目录:在 nfs-client 客户端中创建一个目录,用于挂载 NFS 服务端的共享目录。
mkdir /mnt/nfs
-
挂载目录:在 nfs-client 客户端中执行以下 Shell 命令挂载 NFS 共享目录。
mount -t nfs nfs.k8s.com:/mnt/nfs /mnt/nfs
-
开机自动挂载:如果希望 nfs-client 客户端所在的主机在启动后自动挂载 NFS 共享目录,则可以在 nfs-client 客户端所在的主机中的配置文件 /etc/fstab 中加入以下内容。
nfs.k8s.com:/mnt/nfs /mnt/nfs nfs defaults,_netdev 0 0