目录
docker有自己的持久化方案,我们的k8s集群中也有进行持久化的操作。
容器磁盘上的文件的生命周期是短暂的,这就使得在容器中运行重要应用时会出现一些问题。首先,当容器崩溃时,kubelet 会重启它,但是容器中的文件将丢失——容器以干净的状态(镜像最初的状态)重新启动。其次,在Pod 中同时运行多个容器时,这些容器之间通常需要共享文件。Kubernetes 中的 Volume 抽象就很好的解决了这些问题。
上面的跟我们在docker中还不一样哈,在docker中我们把启动命令加上一个restart always的话,那么docker重启之后并不会删除原有的数据,但是在k8s中,一旦崩溃,就会以一个干净的状态重新启动,因为如果不以干净的状态启动,我们的init C就无法启动。
Kubernetes 中的卷有明确的寿命 —— 与封装它的 Pod 相同。所f以,卷的生命比 Pod 中的所有容器都长,当这个容器重启时数据仍然得以保存。当然,当 Pod 不再存在时,卷也将不复存在。也许更重要的是,Kubernetes支持多种类型的卷,Pod 可以同时使用任意数量的卷。。。。
虽然我们的pod会重新启动,然后创建出新的容器,但是我们的volume卷一直没动,所以存储不会丢失。这个也是实现的原理。
volume卷的类型
Kubernetes 支持以下类型的卷:
下面介绍一些重要的,常用的卷进行分析:
emptyDir(空的目录卷)
当 Pod 被分配给节点时,首先创建 emptyDir 卷,并且只要该 Pod 在该节点上运行,该卷就会存在。正如卷的名字所述,它最初是空的。Pod 中的容器可以读取和写入 emptyDir 卷中的相同文件,尽管该卷可以挂载到每个容器中的相同或不同路径上。当出于任何原因从节点中删除 Pod 时, emptyDir 中的数据将被永久删除。注意容器崩溃不会从节点中移除pod,因此,emptyDir卷中的数据在容器崩溃时是安全的。
分析上图:当我们的pod确定要被运行的时候,它会先创建一个空的目录卷,空卷会与生成的Pause进行绑定,然后依次运行处C1和C2。(下面的示例有讲到,这个C1和C2数据共享)
emptyDir 的用法
1. 暂存空间,例如用于基于磁盘的合并排序
(也就是我们现在有很多的容器,它们有些数据要进行组合和合并的话,我们需要一个临时空间去存放这些数据)
2. 用作长时间计算崩溃恢复时的检查点
(比如说,我们有一些服务需要去运行,会有一些数据重载,这些数据可能在我们pod重启时发生重载,那么重载的时候如何进行数据的恢复呢,或者是有些数据不需要进行持久话的写入,或者是只恢复部分的数据)
3. Web服务器容器提供数据时,保存内容管理器容器提取的文件
(也就是我们的web服务器运行的时候,有一些文件需要存在,那么我们在初始化镜像的时候,从某个地方download一些文件放到我们当前的目录下,然后可以保存至空目录下去使用)
下面就是示例:
[root@master1 storge]# cat emptydir.yaml
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: nginx:alpine
name: test-container
volumeMounts:
- mountPath: /cache #挂载到容器的哪个目录下
name: cache-volume #通过哪个volume去挂载的
volumes:
- name: cache-volume
emptyDir: {}
[root@master1 storge]#
[root@master1 storge]# kubectl apply -f emptydir.yaml
pod/test-pd created
[root@master1 storge]# kubectl get pod
NAME READY STATUS RESTARTS AGE
test-pd 1/1 Running 0 17s
[root@master1 storge]# kubectl exec -it test-pd -- sh
/ # cd /cache/
/cache # ls -al
total 0
drwxrwxrwx 2 root root 6 Sep 4 13:52 .
drwxr-xr-x 1 root root 64 Sep 4 13:52 ..
/cache #
我们发现cache里面为空,其实我们常用的场景是一个pod里面多个容器共享emptyDir这个数据卷,下面我们演示两个容器的案例。
即在同一个pod里面的两个不同的容器C1和C2数据共享
[root@master1 storge]# cat emptydir.yaml
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: nginx:alpine
name: test-container
volumeMounts:
- mountPath: /cache #挂载到容器的哪个目录下
name: cache-volume #通过哪个volume去挂载的
- name: test-busybox
image: busybox:alpine
command: ["/bin/sh","-c","sleep 600s"]
volumeMounts:
- mountPath: /test #挂载到容器的哪个目录下
name: cache-volume
volumes:
- name: cache-volume
emptyDir: {}
[root@master1 storge]# kubectl apply -f emptydir.yaml
pod/test-pd created
[root@master1 storge]# kubectl get pod
NAME READY STATUS RESTARTS AGE
test-pd 2/2 Running 0 4s
不同的容器进行数据共享,上面的容器先是在挂载目录(cache)写入,然后下面的是另外的一个容器也可以读取到该容器挂载目录(test)的文件里面内容。
hostPath
hostPath 卷将主机节点的文件系统中的文件或目录挂载到集群中。。
hostPath 的用途如下:
1. 运行需要访问 Docker 内部的容器;使用 /var/lib/docker 的 hostPath。
意味着我们想要在本机获取容器内部的一些文件的话,可以使用该方案。
2. 在容器中运行 cAdvisor;使用 /dev/cgroups 的 hostPath。
3. 允许 pod 指定给定的 hostPath 是否应该在 pod 运行之前存在,是否应该创建,以及它应该以什么形式存在。
我们有一个nfs集群,然后我们把集群挂载至node节点的某个目录下,我们在启动pod的时候,我们让pod去挂载本机的那个目录。即只要存储服务能够挂载至我们的node节点,那么我们的pod可以通过hostPath方案实现外部存储的挂载。
除了所需的 path 属性之外,用户还可以为 hostPath 卷指定 type
使用这种卷类型是请注意,因为:
1. 由于每个节点上的文件都不同,具有相同配置(例如从 podTemplate 创建的)的 pod 在不同节点上的行为可能会有所不同。我们要确保每一个pod挂载到指定的本机哪个目录,因为pod会根据调度规则随机部署到node节点上,假如说,我们在yaml文件中指定了某个pod对应的目录,但是有些node节点没有这个目录,此时pod里面的内容就会发生变化。所以我们需要提前想好所有的节点(或者说pod对应的调度规则匹配的node)是否有符合yaml对应目录。
2. 当 Kubernetes 按照计划添加资源感知调度时,将无法考虑 hostPath 使用的资源。也就是hostPath本机的一些资源的动态是无法添加到我们的k8s资源管理器中去的。
3. 在底层主机上创建的文件或目录只能由 root 写入。您需要在特权容器中以 root 身份运行进程,或修改主机上的文件权限以便写入 hostPath 卷。也就意味着,在我们本机的某些文件或者目录上,如果是一些默认的权限的话,它是没有写入权的,这个时候可以把本机的某个目录调整为我们kubelete的权限即可。
hostPath的用法
[root@master1 storge]# mkdir /data
[root@master1 storge]# cat hostpath.yaml
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: nginx:alpine
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
[root@master1 storge]#
[root@master1 storge]# kubectl apply -f hostpath.yaml
pod/test-pd created
[root@master1 storge]# kubectl get pod
NAME READY STATUS RESTARTS AGE
test-pd 1/1 Running 0 3s
补充:对于上面的第一点如果还有点迷糊的话,像我们这里创建一个hostpath,挂载的宿主机目录是/data,由于我这里是单节点部署,所有只在master上面创建一个目录,如果是多节点的话,那么就得在其他节点上创建同样的目录。
下面看一下效果
同样我们互相写入数据也是能够在宿主机和pod里面看见的
总结:上面的这种方案有很大的灵活性,可以跟我们的任何存储进行对接,也就是开始讲的可以对接nfs集群。