一个pod内有多个容器,我们知道这些容器是共享网络栈的,但是文件系统却似乎还不能共享?如果一个容器运行了一段时间,崩溃重启了,里面保存的内容都丢失了,这又该如何解决?带着这些疑问我们一起来学习k8s中负责数据存储的组件:volume。
我是T型人小付,一位坚持终身学习的互联网从业者。喜欢我的博客欢迎在csdn上关注我,如果有问题欢迎在底下的评论区交流,谢谢。
Docker中的数据保存
docker启动容器,只要容器不被删除其内部的文件不会消失
[root@k8s-node1 ~]# docker run -it -d victor2019/test:v1
e64c2bc31ac04174c1c78f9223e01a2cdec371104c58b74bdfc0a439913bf2d3
之后往容器内写点东西,因为我这个测试镜像是nginx,所以写到主页面便于测试
[root@k8s-node1 ~]# docker exec e64c bash -c "echo hello > /usr/share/nginx/html/index.html"
查看一下容器的docker0网段的ip
[root@k8s-node1 ~]# docker inspect e64c | grep 172
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.2",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.2",
验证下写进去的内容
[root@k8s-node1 ~]# curl 172.17.0.2
hello
然后停掉容器再启动
[root@k8s-node1 ~]# docker kill e64c
e64c
[root@k8s-node1 ~]# docker start e64c
e64c
[root@k8s-node1 ~]# curl 172.17.0.2
hello
容器内的文件一直都还在。
所以直接用docker起容器的话,指定--restart=always就可以保证容器提供持续服务了。
K8s中的数据保存
但是k8s的机制不一样,每个pod都有自己完整的生命周期,即使没有被人为删除,如果pod因为其中某个容器崩溃重启,不仅pod的ip会变,里面所有容器保存的内容也会全部消失
那么有没有一种办法,可以让k8s的pod可以和docker里面的容器一样,除非是被人为删除,不然即使重启里面保存的东西一直都在。我们这一节要学习的volume就是为了解决这个问题而引入的。
需要注意,docker中也有volume的概念,不过docker中的volume是容器的内容在宿主机磁盘的持久化,即使容器删除也会一直在,和我们下一节要学习的PVC有点像,但是和这里k8s的volume是两个东西,要注意区分
k8s中的volume
volume就是一个可以被pod中所有容器使用的公共文件夹,每个容器可以将这个公共文件夹挂载到本容器内的一个目录(不同容器的目录可以不同),这样pod内容器就可以互相交换数据。volume的生命周期和pod一致,所以不管pod重启多少次volume内的内容和挂载目录都不会改变,但是如果pod被终止volume就会跟着消失。
注意这里的重启是指因为容器崩溃导致的重启,而不是手动删除pod然后因为重启策略而进行的重启
k8s支持很多种volume的类型,官方在官网中给出几十种媒介可供使用。其中的configMap和secret上一节我们已经接触过,这一节我们选其最常用的emptyDir和hostPath再来学习下。
实际操作
以下所有yaml文件托管在我的Github仓库
emptyDir操作
当一个pod被分配到node时,一个emptyDir volume首先被创建,并且其生命周期和pod一样长。正如其名字表示的那样,emptyDir一开始是空的,pod中的所有容器都可以在里面进行读写。如果pod在node上被删除,emptyDir中的数据也会被永久删除。
容器崩溃不会将pod从node上删除,而只是重启,所以emptyDir对容器崩溃来说是安全的
emptyDir通常用于声明一些临时空间,存放一些临时文件,尤其是多容器配合操作的时候。或者是容器从崩溃恢复时候的一些checkpoint。
两个配置字段在pod.spec.volumes.emptyDir中声明
| 字段 | 类型 | 说明 |
|---|---|---|
| medium | string | 默认是空字符串表示硬盘,还可以是Memory表示内存 |
| sizeLimit | string | 默认是nil表示不指定大小 |
通过下面这个yaml文件test-volume-emptydir.yaml来创建一个Deployment
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: test-volume-emptydir
spec:
replicas: 1
template:
metadata:
labels:
app: test-volume-emptydir
spec:
containers:
- name: mynginx
image: mynginx:v2
ports:
- containerPort: 80
volumeMounts:
- mountPath: /usr/share/nginx/html
name: test-emptydir
- name: myalpine
image: alpine
command: ["ping","8.8.8.8"]
volumeMounts:
- mountPath: /emptydir
name: test-emptydir
volumes:
- name: test-emptydir
emptyDir: {}
其中emptyDir: {}表示用默认配置,也就是不声明大小的硬盘空间。
创建成功以后进入myalpine容器,往emptyDir绑定的目录写入文件,之后再访问另一个mynginx容器时被显示
[root@k8s-master volume]# kubectl apply -f test-volume-emptydir.yaml
deployment.extensions/test-volume-emptydir created
[root@k8s-master volume]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-volume-emptydir-6bf8d675fc-ds2jx 2/2 Running 0 15m 10.244.1.117 k8s-node1 <none> <none>
[root@k8s-master volume]# kubectl exec test-volume-emptydir-6bf8d675fc-ds2jx -c myalpine -it -- /bin/sh -c "echo hello emptydir > /emptydir/index.html"
[root@k8s-master volume]# curl 10.244.1.117
hello emptydir
说明因为emptyDir的引入一个pod内的容器可以共享同一个存储卷。
hostPath操作
hostPath是将node中的文件或目录挂载成为pod的volume。
这里看起来和docker里面的volume很像,但是要注意这里node上的内容并不会持久保存,要注意区分
除了node本地的文件系统,还可以使用NFS或者MFS这种网络存储。只要是能被node锚定的存储设备都可以被hostPath绑定,所以hostPath的使用范围是非常广泛的。
两个配置字段在pod.spec.volumes.hostPath中声明
| 字段 | 类型 | 说明 |
|---|---|---|
| path | string | 必填,node上的目录 |
| type | string | 见下面单独表格 |
其中可选的type字段可选如下,摘自官方文档
| Value | Behavior |
|---|---|
| Empty string (default) is for backward compatibility, which means that no checks will be performed before mounting the hostPath volume. | |
| DirectoryOrCreate | If nothing exists at the given path, an empty directory will be created there as needed with permission set to 0755, having the same group and ownership with Kubelet. |
| Directory | A directory must exist at the given path |
| FileOrCreate | If nothing exists at the given path, an empty file will be created there as needed with permission set to 0644, having the same group and ownership with Kubelet. |
| File | A file must exist at the given path |
| Socket | A UNIX socket must exist at the given path |
| CharDevice | A character device must exist at the given path |
| BlockDevice | A block device must exist at the given path |
使用hostPath需要注意两点:
- 因为不确定pod会被分配到哪个node,所以所有的可能被分配pod的node都需要满足hostPath需要满足的条件
- 当k8s按照计划添加资源感知调度时,无法考虑hostPath使用的资源
- 要注意权限设置,node上的目录需要有kubelet的操作权限
通过下面的yaml文件test-volume-hostpath.yaml创建一个deployment
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: test-volume-hostpath
spec:
replicas: 1
template:
metadata:
labels:
app: test-volume-hostpath
spec:
containers:
- name: mynginx
image: mynginx:v2
ports:
- containerPort: 80
volumeMounts:
- mountPath: /usr/share/nginx/html
name: test-hostpath
- name: myalpine
image: alpine
command: ["ping","8.8.8.8"]
volumeMounts:
- mountPath: /hostpath
name: test-hostpath
volumes:
- name: test-hostpath
hostPath:
path: /tmp/hostpath
type: DirectoryOrCreate
这里将每个node的/tmp/hostpath目录做为volume,如果该目录不存在会被创建
创建以后发现pod被分配到k8s-node1上
[root@k8s-master volume]# kubectl apply -f test-volume-hostpath.yaml
deployment.extensions/test-volume-hostpath created
[root@k8s-master volume]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-volume-hostpath-7bb449ff9b-82gl5 2/2 Running 0 8s 10.244.1.118 k8s-node1 <none> <none>
去node上看,果然自动创建了一个目录。因为是用root用户运行的kubelet,所以这里目录的owner是root
[root@k8s-node1 ~]# cd /tmp
[root@k8s-node1 tmp]# ll
total 0
drwxr-xr-x. 2 root root 6 May 11 21:06 hostpath
往里面写点东西
[root@k8s-node1 tmp]# cd hostpath/
[root@k8s-node1 hostpath]# echo hello hostpath > index2.html
然后去pod上查看一下
[root@k8s-master volume]# curl 10.244.1.118/index2.html
hello hostpath
[root@k8s-master volume]# kubectl exec test-volume-hostpath-7bb449ff9b-82gl5 -c myalpine -it -- /bin/sh -c "cd /hostpath;ls -al;cat index2.html"
total 4
drwxr-xr-x 2 root root 25 May 11 13:10 .
drwxr-xr-x 1 root root 33 May 11 13:06 ..
-rw-r--r-- 1 root root 15 May 11 13:10 index2.html
hello hostpath
成功在pod的两个容器内都找到了相同的文件。
总结
总结下volume的一些知识点
- 和docker的volume有本质区别,不能混淆
- volume的周期和pod一样,容器的崩溃导致的pod重启不会影响volume的内容
- pod的删除会将volume的内容永久删除
- emptyDir用来存储一些临时内容,和node上的存储无关
- hostPath用来将node的文件系统做为volume,每个可能被分配的node都要准备好条件,尤其要注意kubelet的运行权限
volume虽然给pod内容器的文件共享带来很多便利,但是这只是解决了一个痛点。如果我想pod即使被删除其容器的内容也依然会被持久化,又该如何做呢?下一节要学习的Persistent Volume来对volume进行一次升级。
324



被折叠的 条评论
为什么被折叠?



