Kubernetes(k8s)存储管理之数据卷volumes(一):volumes的引入和emptyDir数据卷
一.系统环境
服务器版本 | docker软件版本 | Kubernetes(k8s)集群版本 | CPU架构 |
---|---|---|---|
CentOS Linux release 7.4.1708 (Core) | Docker version 20.10.12 | v1.21.9 | x86_64 |
Kubernetes集群架构:k8scloude1作为master节点,k8scloude2,k8scloude3作为worker节点
服务器 | 操作系统版本 | CPU架构 | 进程 | 功能描述 |
---|---|---|---|---|
k8scloude1/192.168.110.130 | CentOS Linux release 7.4.1708 (Core) | x86_64 | docker,kube-apiserver,etcd,kube-scheduler,kube-controller-manager,kubelet,kube-proxy,coredns,calico | k8s master节点 |
k8scloude2/192.168.110.129 | CentOS Linux release 7.4.1708 (Core) | x86_64 | docker,kubelet,kube-proxy,calico | k8s worker节点 |
k8scloude3/192.168.110.128 | CentOS Linux release 7.4.1708 (Core) | x86_64 | docker,kubelet,kube-proxy,calico | k8s worker节点 |
二.前言
Kubernetes(k8s)数据卷volumes类型众多,本文介绍数据卷volumes之一emptyDir数据卷。
使用数据卷volumes的前提是已经有一套可以正常运行的Kubernetes集群,关于Kubernetes(k8s)集群的安装部署,可以查看博客《Centos7 安装部署Kubernetes(k8s)集群》https://www.cnblogs.com/renshengdezheli/p/16686769.html
三.Docker数据卷volumes
Docker 也有卷(Volume) 的概念,但对它只有少量且松散的管理。 Docker 卷是磁盘上或者另外一个容器内的一个目录。 Docker 提供卷驱动程序,但是其功能非常有限。
创建卷
[root@k8scloude1 ~]# docker volume create my-vol
查看卷
[root@k8scloude1 ~]# docker volume ls
local my-vol
查看卷属性
[root@k8scloude1 ~]# docker volume inspect my-vol
[
{
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/my-vol/_data",
"Name": "my-vol",
"Options": {},
"Scope": "local"
}
]
删除卷
[root@k8scloude1 ~]# docker volume rm my-vol
运行一个带有卷的容器,可以使用--mount参数或者-v参数。
注意:如果您使用尚不存在的卷启动容器,Docker 会为您创建该卷。
[root@k8scloude1 ~]# docker run -d --name devtest --mount source=my-vol,target=/app nginx:latest
或者
[root@k8scloude1 ~]# docker run -d --name devtest -v my-vol:/app nginx:latest
#查看Mounts部分,可以看到数据卷已经正确挂载
[root@k8scloude1 ~]# docker inspect devtest
......
"Mounts": [
{
"Type": "volume",
"Name": "my-vol",
"Source": "/var/lib/docker/volumes/my-vol/_data",
"Destination": "/app",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
四.Kubernetes 数据卷volumes
4.1 有状态容器和无状态容器
无状态容器应用stateless,意味着容器上应用所使用的历史数据或运行状态不需要进行持久化,重新拉起这个应用时,无需关注这些历史输入。简单来说,例如你要运行一个计算器(而且这个计算器不需要支持历史记录功能),当你重新拉起这个计算器时,之前的数据不需要重新被加载上来,计算器可以认为是一个无状态应用。其它类似的无状态容器应用还包括一些协议转换、请求转发等应用,大体都可以认为是无状态的。
有状态容器应用stateful,有状态容器应用的特征是应用中处理的历史请求或操作,对现在或未来的操作是有影响的,那历史数据就必须被记录下来,这种应用就被称之为有状态容器应用。最典型的有状态应用莫过于数据库了,当数据库重新拉起时,你当然“要求”之前写入数据库的记录必须能被正确无误地加载出来。其实同样地,很多的消息队列(例如RabbitMQ等),为了不使消息丢失,消息队列中间件也会将消息进行持久化。当然,随着容器应用的边界越发广阔,越来越多的有状态应用正在容器化,我们看到很多的AI、自动驾驶、HPC任务都在进行容器化。
4.2 Kubernetes 数据卷volumes解决的问题
容器Container 中的文件在磁盘上是临时存放的,这给 容器Container 中运行的较重要的应用程序带来一些问题。
- 问题之一是当容器崩溃时文件丢失。 kubelet 会重新启动容器,但容器会以干净的状态重启。
- 第二个问题会在同一 Pod 中运行多个容器并共享文件时出现。
Kubernetes 卷(Volume) 这一抽象概念能够解决这两个问题。
4.3 Kubernetes 数据卷volumes概述
Kubernetes 支持很多类型的卷。 Pod 可以同时使用任意数目的卷类型。 临时卷类型的生命周期与 Pod 相同,但持久卷可以比 Pod 的存活期长。 当 Pod 不再存在时,Kubernetes 也会销毁临时卷;不过 Kubernetes 不会销毁持久卷。 对于给定 Pod 中任何类型的卷,在容器重启期间数据都不会丢失。
卷的核心是一个目录,其中可能存有数据,Pod 中的容器可以访问该目录中的数据。 所采用的特定的卷类型将决定该目录如何形成的、使用何种介质保存数据以及目录中存放的内容。
使用卷时, 在 .spec.volumes 字段中设置为 Pod 提供的卷,并在 .spec.containers[*].volumeMounts 字段中声明卷在容器中的挂载位置。 容器中的进程看到的文件系统视图是由它们的容器镜像 的初始内容以及挂载在容器中的卷(如果定义了的话)所组成的。 其中根文件系统同容器镜像的内容相吻合。 任何在该文件系统下的写入操作,如果被允许的话,都会影响接下来容器中进程访问文件系统时所看到的内容。
卷挂载在镜像中的指定路径下。 Pod 配置中的每个容器必须独立指定各个卷的挂载位置。
卷不能挂载到其他卷之上(不过存在一种使用 subPath 的相关机制),也不能与其他卷有硬链接。
五.临时卷emptyDir
5.1 临时卷emptyDir使用场景
有些应用程序需要额外的存储,但并不关心数据在重启后是否仍然可用。 例如,缓存服务经常受限于内存大小,而且可以将不常用的数据转移到比内存慢的存储中,对总体性能的影响并不大。临时卷 就是为此类用例设计的。因为卷会遵从 Pod 的生命周期,与 Pod 一起创建和删除, 所以停止和重新启动 Pod 时,不会受持久卷在何处可用的限制。
5.2 临时卷emptyDir简介
当 Pod 分派到某个节点上时,emptyDir 卷会被创建,并且在 Pod 在该节点上运行期间,卷一直存在。 就像其名称表示的那样,卷最初是空的。 尽管 Pod 中的容器挂载 emptyDir 卷的路径可能相同也可能不同,这些容器都可以读写 emptyDir 卷中相同的文件。 当 Pod 因为某些原因被从节点上删除时,emptyDir 卷中的数据也会被永久删除。
说明:容器崩溃并不会导致 Pod 被从节点上移除,因此容器崩溃期间 emptyDir 卷中的数据是安全的。
emptyDir 的一些用途:
- 缓存空间,例如基于磁盘的归并排序。
- 为耗时较长的计算任务提供检查点,以便任务能方便地从崩溃前状态恢复执行。
- 在 Web 服务器容器服务数据时,保存内容管理器容器获取的文件。
emptyDir.medium 字段用来控制 emptyDir 卷的存储位置。 默认情况下,emptyDir 卷存储在该节点所使用的介质上; 此处的介质可以是磁盘、SSD 或网络存储,这取决于你的环境。 你可以将 emptyDir.medium 字段设置为 "Memory", 以告诉 Kubernetes 为你挂载 tmpfs(基于 RAM 的文件系统)。 虽然 tmpfs 速度非常快,但是要注意它与磁盘不同:tmpfs 在节点重启时会被清除, 并且你所写入的所有文件都会计入容器的内存消耗,受容器内存限制约束。
你可以通过emptyDir.sizeLimit为默认介质指定大小限制,来限制 emptyDir 卷的存储容量。 此存储是从节点临时存储中分配的。 如果来自其他来源(如日志文件或镜像分层数据)的数据占满了存储,emptyDir 可能会在达到此限制之前发生存储容量不足的问题。
说明:
当启用 SizeMemoryBackedVolumes 特性门控时, 你可以为基于内存提供的卷指定大小。 如果未指定大小,则基于内存的卷的大小为 Linux 主机上内存的 50%。
5.3 创建普通的pod
查看k8s状态
[root@k8scloude1 ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8scloude1 Ready control-plane,master 8d v1.21.0
k8scloude2 Ready <none> 8d v1.21.0
k8scloude3 Ready <none> 8d v1.21.0
创建一个目录存放yaml文件
[root@k8scloude1 ~]# mkdir volume
创建一个namespace
[root@k8scloude1 ~]# kubectl create ns volume
namespace/volume created
切换namespace到volume
[root@k8scloude1 ~]# kubens volume
Context "kubernetes-admin@kubernetes" modified.
Active namespace is "volume".
生成创建pod的yaml文件
[root@k8scloude1 ~]# cd volume/
[root@k8scloude1 volume]# pwd
/root/volume
#生成创建pod的yaml文件
[root@k8scloude1 volume]# kubectl run pod --image=nginx --image-pull-policy=IfNotPresent --dry-run=client -o yaml >pod.yaml
[root@k8scloude1 volume]# vim pod.yaml
[root@k8scloude1 volume]# cat pod.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: pod
name: pod
spec:
terminationGracePeriodSeconds: 0
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: pod
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
创建pod
[root@k8scloude1 volume]# kubectl apply -f pod.yaml
pod/pod created
#可以看到pod运行在k8scloude2节点
[root@k8scloude1 volume]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod 1/1 Running 0 11s 10.244.112.180 k8scloude2 <none> <none>
进入pod里面,创建container.txt文件
[root@k8scloude1 volume]# kubectl exec -it pod -- bash
root@pod:/# which nginx
/usr/sbin/nginx
root@pod:/# touch container.txt
root@pod:/# exit
exit
在pod里创建container.txt文件,对应的在k8scloude2节点上也有相应的文件
[root@k8scloude2 ~]# find / -name container.txt
/var/lib/docker/overlay2/909e91a50b6d90577e21c6c1ccb97d4571dddeecc296323934c28dfa3a26acf7/diff/container.txt
/var/lib/docker/overlay2/909e91a50b6d90577e21c6c1ccb97d4571dddeecc296323934c28dfa3a26acf7/merged/container.txt
删除pod
[root@k8scloude1 volume]# kubectl delete pod pod
pod "pod" deleted
k8scloude2节点上对应的文件也没了
[root@k8scloude2 ~]# find / -name container.txt
5.4 创建有emptyDir卷的pod
当我们删除pod之后,我们往pod里写的数据都被删除了,这样的容器不存储任何数据,称为无状态的stateless,有时候我们需要pod能够存储数据,这样的容器称为有状态的statefull,docker 配置数据卷为:docker run xxx -v /xxx:/yyy
下面配置emptyDir数据卷,emptyDir类型的卷在物理机随机生成一个目录
[root@k8scloude1 volume]# vim pod-emptydir.yaml
[root@k8scloude1 volume]# cat pod-emptydir.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: pod
name: pod
spec:
terminationGracePeriodSeconds: 0
#指定卷
volumes:
#卷的名字
- name: v1
#卷的类型为emptyDir
emptyDir: {}
- name: v2
emptyDir: {}
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: pod
resources: {}
#卷挂载,把v1卷挂载到/xx目录
volumeMounts:
- name: v1
mountPath: /xx
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
创建pod
[root@k8scloude1 volume]# kubectl apply -f pod-emptydir.yaml
pod/pod created
[root@k8scloude1 volume]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod 1/1 Running 0 5s 10.244.112.181 k8scloude2 <none> <none>
进入pod,在挂载目录/xx/下写入数据
[root@k8scloude1 volume]# kubectl exec -it pod -- bash
root@pod:/# ls /xx/
root@pod:/# df -h
Filesystem Size Used Avail Use% Mounted on
overlay 150G 4.2G 146G 3% /
tmpfs 64M 0 64M 0% /dev
tmpfs 1018M 0 1018M 0% /sys/fs/cgroup
/dev/sda1 150G 4.2G 146G 3% /xx
shm 64M 0 64M 0% /dev/shm
tmpfs 1018M 12K 1018M 1% /run/secrets/kubernetes.io/serviceaccount
tmpfs 1018M 0 1018M 0% /proc/acpi
tmpfs 1018M 0 1018M 0% /proc/scsi
tmpfs 1018M 0 1018M 0% /sys/firmware
root@pod:/# echo "hello w" >/xx/h.txt
root@pod:/# cat /xx/h.txt
hello w
root@pod:/# exit
exit
在k8scloude2节点上查看对应的docker容器,可以看到pod的容器名为82df167460ae
[root@k8scloude2 ~]# docker ps | grep pod
82df167460ae 605c77e624dd "/docker-entrypoint.…" About a minute ago Up About a minute k8s_pod_pod_volume_d8fda77c-1554-4110-9ca1-29b8f177cd1b_0
056dfa5153a8 registry.aliyuncs.com/google_containers/pause:3.4.1 "/pause" About a minute ago Up About a minute k8s_POD_pod_volume_d8fda77c-1554-4110-9ca1-29b8f177cd1b_0
查看容器属性的Mounts部分,可以看到emptyDir卷的目录和挂载目录
[root@k8scloude2 ~]# docker inspect 82df167460ae | grep -A10 Mounts
"Mounts": [
{
"Type": "bind",
"Source": "/var/lib/kubelet/pods/d8fda77c-1554-4110-9ca1-29b8f177cd1b/volumes/kubernetes.io~empty-dir/v1",
"Destination": "/xx",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
},
{
"Type": "bind",
查看对应的物理机目录:/var/lib/kubelet/pods/d8fda77c-1554-4110-9ca1-29b8f177cd1b/volumes/kubernetes.io~empty-dir/v1
#也存在相应的文件
[root@k8scloude2 ~]# cat /var/lib/kubelet/pods/d8fda77c-1554-4110-9ca1-29b8f177cd1b/volumes/kubernetes.io~empty-dir/v1/h.txt
hello w
删除pod
[root@k8scloude1 volume]# kubectl delete pod pod --force
warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely.
pod "pod" force deleted
当pod被删除之后,物理机上文件也没了
[root@k8scloude2 ~]# ls /var/lib/kubelet/pods/d8fda77c-1554-4110-9ca1-29b8f177cd1b/volumes/kubernetes.io~empty-dir/v1/
ls: 无法访问/var/lib/kubelet/pods/d8fda77c-1554-4110-9ca1-29b8f177cd1b/volumes/kubernetes.io~empty-dir/v1/: 没有那个文件或目录
emptyDir类型的数据卷默认是以内存为介质的,删除pod之后,物理机上对应的目录也没了
5.5 sidecar使用emptyDir卷共享数据
emptyDir一般用来共享数据,比如:一个pod里运行了两个容器,一个为nginx容器,nginx容器挂载一个目录a,日志生成了之后就放在目录a,另一个fluentd容器读取目录a进行日志分析,如下实例:
[root@k8scloude1 volume]# vim sharedir.yaml
#v1卷分别挂载到n1和s1容器
[root@k8scloude1 volume]# cat sharedir.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: pod
name: pod
spec:
terminationGracePeriodSeconds: 0
volumes:
- name: v1
emptyDir: {}
- name: v2
emptyDir: {}
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: n1
resources: {}
volumeMounts:
- name: v1
mountPath: /xx
- image: nginx
imagePullPolicy: IfNotPresent
name: s1
resources: {}
command: ["sh","-c","sleep 1000"]
volumeMounts:
- name: v1
mountPath: /yy
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
创建pod
[root@k8scloude1 volume]# kubectl apply -f sharedir.yaml
pod/pod created
[root@k8scloude1 volume]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod 2/2 Running 0 13s 10.244.112.182 k8scloude2 <none> <none>
进入pod里的容器n1,创建文件
[root@k8scloude1 volume]# kubectl exec -it pod -c n1 -- bash
root@pod:/# echo "nginx log" >/xx/nginx.log
root@pod:/# exit
exit
进入pod里的容器s1,可以看到文件nginx.log
[root@k8scloude1 volume]# kubectl exec -it pod -c s1 -- bash
root@pod:/# cat /yy/nginx.log
nginx log
root@pod:/# exit
exit
在k8scloude2节点上查看对应的容器
[root@k8scloude2 ~]# docker ps | grep pod
eb17cb0232c3 605c77e624dd "sh -c 'sleep 1000'" 47 seconds ago Up 46 seconds k8s_s1_pod_volume_69ebce38-cfc6-4fdb-bd83-a3b8ddf034cf_0
0537eb92e1dc 605c77e624dd "/docker-entrypoint.…" 47 seconds ago Up 46 seconds k8s_n1_pod_volume_69ebce38-cfc6-4fdb-bd83-a3b8ddf034cf_0
210302b1861f registry.aliyuncs.com/google_containers/pause:3.4.1 "/pause" 47 seconds ago Up 47 seconds k8s_POD_pod_volume_69ebce38-cfc6-4fdb-bd83-a3b8ddf034cf_0
查看两个容器的属性,可以看到/xx /yy 对应的物理机的目录是一样的
[root@k8scloude2 ~]# docker inspect eb17cb0232c3 | grep -A10 Mounts
"Mounts": [
{
"Type": "bind",
"Source": "/var/lib/kubelet/pods/69ebce38-cfc6-4fdb-bd83-a3b8ddf034cf/volumes/kubernetes.io~empty-dir/v1",
"Destination": "/yy",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
},
{
"Type": "bind",
[root@k8scloude2 ~]# docker inspect 0537eb92e1dc | grep -A10 Mounts
"Mounts": [
{
"Type": "bind",
"Source": "/var/lib/kubelet/pods/69ebce38-cfc6-4fdb-bd83-a3b8ddf034cf/volumes/kubernetes.io~empty-dir/v1",
"Destination": "/xx",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
},
{
"Type": "bind",
[root@k8scloude2 ~]# cat /var/lib/kubelet/pods/69ebce38-cfc6-4fdb-bd83-a3b8ddf034cf/volumes/kubernetes.io~empty-dir/v1/nginx.log
nginx log
删除pod
[root@k8scloude1 volume]# kubectl delete pod pod --force
warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely.
pod "pod" force deleted
删除pod之后,目录也消失了,emptyDir卷可以共享数据但是不可以永久存储,持久化存储请查看博客《Kubernetes(k8s)存储管理之数据卷volumes(二):hostPath数据卷 ,《Kubernetes(k8s)存储管理之数据卷volumes(三):NFS数据卷,《Kubernetes(k8s)存储管理之数据卷volumes(四):持久卷Persistent Volume,《Kubernetes(k8s)存储管理之数据卷volumes(五):动态制备-存储类StorageClass。
[root@k8scloude2 ~]# cat /var/lib/kubelet/pods/69ebce38-cfc6-4fdb-bd83-a3b8ddf034cf/volumes/kubernetes.io~empty-dir/v1/nginx.log
cat: /var/lib/kubelet/pods/69ebce38-cfc6-4fdb-bd83-a3b8ddf034cf/volumes/kubernetes.io~empty-dir/v1/nginx.log: 没有那个文件或目录