微前端架构如何改变企业的开发模式与效率提升
2267
2022-10-18
Kubernetes 本地存储卷 pod volume emptyDir
Volume
本节我们讨论 Kubernetes 的存储模型 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 等,完整列表可参考 提供了对各种 backend 的抽象,容器在使用 Volume 读写数据的时候不需要关心数据到底是存放在本地节点的文件系统中呢还是云硬盘上。对它来说,所有类型的 Volume 都只是一个目录。
我们将从最简单的 emptyDir 开始学习 Kubernetes Volume。
为什么需要存储卷
容器部署过程中一般有以下三种数据:
• 启动时需要的初始数据,例如配置文件
• 启动过程中产生的临时数据,该临时数据需要多个容器间共享
• 启动过程中产生的持久化数据,例如MySQL的data目录
数据卷概述
• Kubernetes中的Volume提供了在容器中挂载外部存储的能力
• Pod需要设置卷来源(spec.volume)和挂载点(spec.containers.volumeMounts)两个信息后才可以使用相应的Volume
数据卷类型大致分类
• 本地(hostPath,emptyDir等)
• 网络(NFS,Ceph,GlusterFS等)
• 公有云(AWS EBS等)
• K8S资源(configmap,secret等)
Kubernetes支持存储卷类型中,emptyDir存储卷的生命周期与其所属的Pod对象相同,它无法脱离Pod对象的生命周期提供数据存储功能,因此emptyDir通常仅用于数据缓存或临时存储。不过基于emptyDir构建的gitRepo存储卷可以在Pod对象的生命周期起始时从响应的Git仓库中复制相应的数据文件到底层的emptyDir中,从而使得它具有了一定意义上的持久性。
emptyDir存储卷
Kubernetes支持存储卷类型中,emptyDir存储卷的生命周期与其所属的Pod对象相同,它无法脱离Pod对象的生命周期提供数据存储功能,因此emptyDir通常仅用于数据缓存或临时存储。不过基于emptyDir构建的gitRepo存储卷可以在Pod对象的生命周期起始时从响应的Git仓库中复制相应的数据文件到底层的emptyDir中,从而使得它具有了一定意义上的持久性。
emptyDir存储卷是Pod对象生命周期中的一个临时目录,类似于Docker上的docker挂载卷,在Pod对象启动时即被创建,而在Pod对象被移除时会被一并删除。不具有持久能力的emptyDir存储卷只能用于某些特殊场景中,例如,用一Pod内的多个容器间文件的共享,或者作为容器数据的临时存储目录用于数据缓存系统等。
emptyDir存储卷则定义于.spec.volumes.emptyDir嵌套字段中,可用字段主要包含两个,具体如下:
•medium:此目录所在存储介质的类型,可取值为default或Memory,默认为default,表示使用节点的默认存储介质:Memory 表示基于RAM的临时文件系统tmpfs,空间受于内存,但性能非常好,通常用于为容器中的应用提供缓存空间
•sizeLimit:当前存储卷的空间限额,默认值为 nil,表示不限制;不过在 medium 字段为 Memory时,建议定义此限额。
emptyDir
emptyDir 是最基础的 Volume 类型。正如其名字所示,一个 emptyDir Volume 是 Host 上的一个空目录。
emptyDir Volume 对于容器来说是持久的,对于 Pod 则不是。当 Pod 从节点删除时,Volume 的内容也会被删除。但如果只是容器被销毁而 Pod 还在,则 Volume 不受影响。emptyDir一般用于测试,或者缓存场景。
也就是说:emptyDir Volume 的生命周期与 Pod 一致。
Pod 中的所有容器都可以共享 Volume,它们可以指定各自的 mount 路径。下面通过例子来实践 emptyDir,配置文件如下:
[root@k8s-master ~]# cat emptyDir.yml apiVersion: v1kind: Podmetadata: name: producer-consumerspec: containers: - name: producer image: busybox volumeMounts: - name: shared-volume mountPath: /producer_dir args: - /bin/sh - -c - echo "hello this is producer" > /producer_dir/hello ; sleep 3600 - name: consumer image: busybox volumeMounts: - name: shared-volume mountPath: /consumer_dir args: - /bin/sh - -c - cat /consumer_dir/hello ; sleep 3600 volumes: - name: shared-volume emptyDir: {}[root@k8s-master ~]# kubectl apply -f emptyDir.yml pod/producer-consumer created
这里我们模拟了一个 producer-consumer 场景。Pod 有两个容器 producer和 consumer,它们共享一个 Volume。producer 负责往 Volume 中写数据,consumer 则是从 Volume 读取数据。
文件最底部volumes 定义了一个emptyDir 类型的 Volumeshared-volume。producer 容器将shared-volume mount 到/producer_dir 目录。producer 通过echo 将数据写到文件hello 里。consumer 容器将shared-volume mount 到/consumer_dir 目录。consumer 通过cat 从文件hello 读数据。
[root@k8s-master ~]# kubectl logs producer-consumer consumerhello this is producer
kubectl logs 显示容器 consumer 成功读到了 producer 写入的数据,验证了两个容器共享 emptyDir Volume。
因为 emptyDir 是 Docker Host 文件系统里的目录,其效果相当于执行了 docker run -v /producer_dir 和 docker run -v /consumer_dir。通过 docker inspect 查看容器的详细配置信息,我们发现两个容器都 mount 了同一个目录:
"Mounts": [ { "Type": "bind", "Source": "/var/lib/kubelet/pods/620cd011-1d40-47af-8a1a-1beb131e135f/volumes/kubernetes.io~empty-dir/shared-volume", "Destination": "/consumer_dir", "Mode": "Z", "RW": true, "Propagation": "rprivate" }, { "Type": "bind", "Source": "/var/lib/kubelet/pods/620cd011-1d40-47af-8a1a-1beb131e135f/volumes/kubernetes.io~empty-dir/shared-volume", "Destination": "/producer_dir", "Mode": "Z", "RW": true, "Propagation": "rprivate" },
这里 /var/lib/kubelet/pods/620cd011-1d40-47af-8a1a-1beb131e135f/volumes/kubernetes.io~empty-dir/shared-volume 就是 emptyDir 在 Host 上的真正路径。
emptyDir 是 Host 上创建的临时目录,其优点是能够方便地为 Pod 中的容器提供共享存储,不需要额外的配置。但它不具备持久性,如果 Pod 不存在了,emptyDir 也就没有了。根据这个特性,emptyDir 特别适合 Pod 中的容器需要临时共享存储空间的场景,比如前面的生产者消费者用例。
[root@k8s-master ~]# kubectl describe pod producer-consumer Volumes: shared-volume: Type: EmptyDir (a temporary directory that shares a pod's lifetime)
下面是一个使用了emptyDir存储卷的简单示例
1.创建Pod对象配置清单
apiVersion: v1kind: Podmetadata: name: vol-emptydir-podspec: volumes: - name: html emptyDir: { } containers: - name: nginx image: nginx:latest volumeMounts: - name: html mountPath: /usr/share/nginx/html - name: pagegen image: alpine volumeMounts: - name: html mountPath: /html command: [ "/bin/sh", "-c" ] args: #定义循环,每10秒向/html/文件中追加写入当前主机名和时间 - while true; do echo $(hostname) $(date) >> /html/index.html; sleep 10; done
上面示例中定义的存储卷名称为html,挂载于容器nginx的 /usr/share/nginx/html目录,以及容器pagegen的/html目录。容器pagegen每隔10秒向存储卷上的index.html文件中追加一行信息,而容器nginx中的nginx进程则以其站点主页。如下图所示:
2.创建Pod对象
kubectl apply -f vol-emptydir.yaml
3.查看Pod状态 Pod对象的详细信息中会显示存储卷的相关状态,包括其是否创建成功(在Events字段中输出)、相关的类型及参数(在Volumes字段中输出)以及容器中挂载状态等信息(在Containers字段中输出),如下面命令所示:
kubectl describe pods/vol-emptydir-podContainers: nginx: Mounts: /usr/share/nginx/html from html (rw) /var/run/secrets/kubernetes.io/serviceaccount from default-token-xxqkj (ro) pagegen: Command: /bin/sh -c Args: while true; do echo $(hostname) $(date) >> /html/index.html; sleep 10; done Mounts: /html from html (rw) /var/run/secrets/kubernetes.io/serviceaccount from default-token-xxqkj (ro)
4.访问Pod中Nginx pagegen容器每隔10秒向 html/index.html 追加写入信息,Nginx容器挂载的也是此临时存储,所以Nginx的网页文件也是从这里获取。
curl Fri Jun 12 02:47:29 UTC 2020vol-emptydir-pod Fri Jun 12 02:47:39 UTC 2020vol-emptydir-pod Fri Jun 12 02:47:49 UTC 2020vol-emptydir-pod Fri Jun 12 02:47:59 UTC 2020vol-emptydir-pod Fri Jun 12 02:48:09 UTC 2020vol-emptydir-pod Fri Jun 12 02:48:19 UTC 2020
5.进入容器 以下分别进入Nginx容器以及pagegen容器查看其挂载
通过 -c 来指定容器名称进入指定容器
kubectl exec -it pods/vol-emptydir-pod -c nginx -- /bin/sh# ls /usr/share/nginx/htmlindex.html# head -3 /usr/share/nginx/html/index.htmlvol-emptydir-pod Fri Jun 12 02:47:29 UTC 2020vol-emptydir-pod Fri Jun 12 02:47:39 UTC 2020vol-emptydir-pod Fri Jun 12 02:47:49 UTC 2020
进入pagegen容器
kubectl exec -it pods/vol-emptydir-pod -c pagegen -- /bin/sh/ # ls /html/index.html/ # head -3 /html/index.htmlvol-emptydir-pod Fri Jun 12 02:47:29 UTC 2020vol-emptydir-pod Fri Jun 12 02:47:39 UTC 2020vol-emptydir-pod Fri Jun 12 02:47:49 UTC 2020/ # ps auxPID USER TIME COMMAND 1 root 0:00 /bin/sh -c while true; do echo $(hostname) $(date) >> /html/index.html; sleep 10; done 286 root 0:00 /bin/sh 303 root 0:00 sleep 10 304 root 0:00 ps aux
作为边车 (sidecar)的容器pagegen,其每隔10秒生成一行信息追加到存储卷上的index.html文件中,因此,通过主容器nginx的应用访问到文件内存也会处理不停的变动中。另外,emptyDir存储卷也可以基于RAM创建tmpfs文件系统的存储卷,常用于为容器的应用提高高性能缓存,下面是一个配置示例:
cat vol-emptydir.yamlapiVersion: v1kind: Podmetadata: name: vol-emptydir-podspec: volumes: - name: html emptyDir: medium: Memory #指定临时存储到内存 sizeLimit: 256Mi #给予的内存空间大小 containers: - name: nginx image: nginx:latest volumeMounts: - name: html mountPath: /usr/share/nginx/html - name: pagegen image: alpine volumeMounts: - name: html mountPath: /html command: [ "/bin/sh", "-c" ] args: - while true; do echo $(hostname) $(date) >> /html/index.html; sleep 10; done
emptyDir卷简单易用,但仅能用于临时存储,另外还存在一些类型的存储卷构建在emptyDir之上,并额外提供了emptyDir没有的功能。
如果还是不懂可以看下面这个例子
定义一个emptyDir存储大小为1G,将其挂载到redis的/data目录中
[root@k8s-master emptydir]# cat emptydir-redis.yaml apiVersion: v1kind: Podmetadata: name: emptydir-redis labels: volume: emptydir annotations: kubernetes.io/storage: emptyDirspec: containers: - name: emptydir-redis image: redis:latest imagePullPolicy: IfNotPresent ports: - name: redis-6379-port protocol: TCP containerPort: 6379 volumeMounts: #将定义的驱动emptydir-redis挂载到容器的/data目录,通过名字方式关联 - name: emptydir-redis mountPath: /data volumes: #定义一个存储,驱动类型为emptyDir,大小1G - name: emptydir-redis emptyDir: sizeLimit: 1Gi
生成redis pod,并查看describe pod的详情信息
[root@k8s-master emptydir]# kubectl apply -f emptydir-redis.yaml pod/emptydir-redis created[root@k8s-master emptydir]# kubectl describe pod emptydir-redis #执行kubectl describe pods emptydir-redis查看容器的存储挂载信息[root@k8s-master emptydir]# kubectl describe pod emptydir-redisName: emptydir-redisNamespace: defaultPriority: 0Node: k8s-master/192.168.179.99Start Time: Thu, 29 Oct 2020 15:43:57 +0800Labels: volume=emptydirAnnotations: kubernetes.io/storage: emptyDirStatus: RunningIP: 10.244.0.15IPs: IP: 10.244.0.15Containers: emptydir-redis: Container ID: docker://7e568233b435ad661971463cf7bb4acb87de7df99fc53b451732e81ec8cb0e7c Image: redis:latest Image ID: docker-pullable://redis@sha256:a0494b60a0bc6de161d26dc2d2f9d2f1c5435e86a9e5d48862a161131affa6bd Port: 6379/TCP Host Port: 0/TCP State: Running Started: Thu, 29 Oct 2020 15:44:52 +0800 Ready: True Restart Count: 0 Environment:
向redis中写入数据
[root@k8s-master emptydir]# kubectl get pod emptydir-redis -o wideNAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATESemptydir-redis 1/1 Running 0 11m 10.244.0.15 k8s-master
登陆到pod中,可以直接kill redis-server进程,进程一般为1,进程被kill后kubelet会自动将进程重启
[root@k8s-master emptydir]# kubectl exec -it emptydir-redis -- /bin/sh# kill 1# command terminated with exit code 137
pod异常重启后,再次登录redis并查看redis中的数据内容,发现数据没有丢失。
[root@k8s-master ~]# kubectl get podNAME READY STATUS RESTARTS AGEemptydir-redis 0/1 Completed 1 27m[root@k8s-master ~]# kubectl get podNAME READY STATUS RESTARTS AGEemptydir-redis 1/1 Running 2 30m#pod重启后,再次登录redis并查看redis中的数据内容,发现数据没有丢失。10.244.0.15:6379> keys *1) "volume"2) "username"
emptyDir实际是宿主机上创建的一个目录,将目录以bind mount的形势挂载到容器中,跟随容器的生命周期。查看存储内容如下:
[root@k8s-master ~]# docker inspect d64ee8367b49 "Mounts": [ { "Type": "bind", "Source": "/var/lib/kubelet/pods/28800e94-3474-4a0e-9e35-d5585b60a133/volumes/kubernetes.io~empty-dir/emptydir-redis", "Destination": "/data", "Mode": "", "RW": true, "Propagation": "rprivate" },
查看目录的信息:
[root@k8s-master ~]# cd /var/lib/kubelet/pods/28800e94-3474-4a0e-9e35-d5585b60a133/volumes/kubernetes.io~empty-dir/emptydir-redis[root@k8s-master emptydir-redis]# lsdump.rdb
Pod删除后,volume的信息也随之删除
[root@k8s-master emptydir]# kubectl delete -f emptydir-redis.yaml pod "emptydir-redis" deleted[root@k8s-master emptydir]# cd /var/lib/kubelet/pods/28800e94-3474-4a0e-9e35-d5585b60a133/volumes/kubernetes.io~empty-dir/emptydir-redis-bash: cd: /var/lib/kubelet/pods/28800e94-3474-4a0e-9e35-d5585b60a133/volumes/kubernetes.io~empty-dir/emptydir-redis: No such file or directory
总结
emptyDir是host上定义的一块临时存储,通过bind mount的形式挂载到容器中使用,容器重启数据会保留,容器删除则volume会随之删除。
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~