临时存储卷

K8S支持存储卷类型中,emptyDir存储卷的生命周期与其所属的Pod对象相同,它无法脱离Pod对象的生命周期提供数据存储功能,因此emptyDir通常仅用于数据缓存或临时存储。不过,基于emptyDir构建的girRepo存储卷可以在Pod对象的生命周期起始时从相应的git仓库中复制相应的数据文件到底层的emptyDir中,从而使得它具有了一定意义上的持久性。

emptyDir存储卷

emptyDir存储卷是Pod对象生命周期中的一个临时目录,类似于docker上的”docker挂载卷”,在Pod对象启动时即被创建,而在Pod对象被移除时会被一并删除。不具有持久能力的emptyDir存储卷只能用于某些特殊场景中,例如,同一Pod内的多个容器间的共享,或者作为容器数据的临时存储目录用于数据缓存等 emptyDit存储卷则定义于.spec.volumes.emptyDir字段中,可用字段主要包含两个,具体如下

  • medium: 此目录所在的存储介质的类型,可取值为”default”或”memory”,默认为default,表示使用节点的默认存储介质;memeory表示使用基于RAM的临时文件系统tmpfs,空间受限于内存,但性能非常好,通常用于为容器中的应用提供缓存空间
  • sizeLimit: 当前存储卷的空间限额,默认值为nil,表示不限制;不过,在medium字段值为”memory”时建议务必定义此限额
    下面是一个使用了emptyDir存储卷的简单示例。示例中定义的存储卷的名称为html,挂载于容器nginx-emptydir的/usr/share/nginx/html目录,以及容器pagegen的/html目录。容器pagegen每过10秒向存储卷上的index.html文件中追加一行信息,而容器nginx-emptydir中的nginx进程则以其为站点主页。如下图所示
    存储卷与数据持久化(二)之临时存储卷 - 图1
  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: vol-nginx-emptydir
  5. spec:
  6. replicas: 2
  7. selector:
  8. matchLabels:
  9. app: myapp-emptydir
  10. template:
  11. metadata:
  12. labels:
  13. app: myapp-emptydir
  14. spec:
  15. dnsPolicy: ClusterFirstWithHostNet
  16. containers:
  17. #定义第一个容器为nginx
  18. - name: nginx-emptydir
  19. image: docker.io/nginx:1.13.0-alpine
  20. #定义容器暴露的端口为80
  21. ports:
  22. - containerPort: 80
  23. #定义存储卷的挂载点
  24. volumeMounts:
  25. - name: html
  26. mountPath: /usr/share/nginx/html
  27. #定义第二个容器为pagegen
  28. - name: pagegen
  29. image: docker.io/alpine:3.12.0
  30. volumeMounts:
  31. - name: html
  32. mountPath: /mnt/html
  33. command: ["/bin/sh","-c","while true; do echo '1' >> /mnt/html/index.html; sleep 10; done"]
  34. #定义一个类型为emptyDir类型的存储卷
  35. volumes:
  36. - name: html
  37. emptyDir: {}

资源配置清单配置完成之后便可创建deployment控制器开始创建Pod

  1. [root@k8s-master01 emptydir]# kubectl apply -f vol-emptydir.yaml
  2. deployment.apps/vol-nginx-emptydir created
  3. [root@k8s-master01 emptydir]#
  4. [root@k8s-master01 emptydir]# kubectl get pods -l "app=myapp-emptydir" -o wide
  5. NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
  6. vol-nginx-emptydir-86558457b9-cjlxs 2/2 Running 0 55s 10.244.38.6 172.18.15.114 <none> <none>
  7. vol-nginx-emptydir-86558457b9-tstdm 2/2 Running 0 57s 10.244.59.7 172.18.15.113 <none> <none>
  8. [root@k8s-master01 emptydir]#

Pod资源的详细信息中会显示存储卷的相关状态,包括其是否成功创建(在events字段中输出)、相关的类型及参数(在volumes字段中输出)以及容器中的挂载状态信息(在containers字段中输出)。如下面的命令结果所示

  1. [root@k8s-master01 emptydir]# kubectl describe deployment vol-nginx-emptydir
  2. Name: vol-nginx-emptydir
  3. Namespace: default
  4. CreationTimestamp: Wed, 01 Jul 2020 18:55:27 +0800
  5. Labels: <none>
  6. Annotations: deployment.kubernetes.io/revision: 1
  7. kubectl.kubernetes.io/last-applied-configuration:
  8. {"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"name":"vol-nginx-emptydir","namespace":"default"},"spec":{"repli...
  9. Selector: app=myapp-emptydir
  10. Replicas: 2 desired | 2 updated | 2 total | 2 available | 0 unavailable
  11. StrategyType: RollingUpdate
  12. MinReadySeconds: 0
  13. RollingUpdateStrategy: 25% max unavailable, 25% max surge
  14. Pod Template:
  15. Labels: app=myapp-emptydir
  16. Containers:
  17. nginx-emptydir:
  18. Image: docker.io/nginx:1.13.0-alpine
  19. Port: 80/TCP
  20. Host Port: 0/TCP
  21. Environment: <none>
  22. Mounts:
  23. /usr/share/nginx/html from html (rw)
  24. pagegen:
  25. Image: docker.io/alpine:3.12.0
  26. Port: 80/TCP
  27. Host Port: 0/TCP
  28. Command:
  29. /bin/sh
  30. -c
  31. Args:
  32. while true; do echo ${hostname} ${date} >> /html/index.html sleep 10; done
  33. Environment: <none>
  34. Mounts:
  35. /html from html (rw)
  36. Volumes:
  37. html:
  38. Type: EmptyDir (a temporary directory that shares a pod's lifetime)
  39. Medium:
  40. SizeLimit: <unset>
  41. Conditions:
  42. Type Status Reason
  43. ---- ------ ------
  44. Available True MinimumReplicasAvailable
  45. Progressing True NewReplicaSetAvailable
  46. OldReplicaSets: <none>
  47. NewReplicaSet: vol-nginx-emptydir-588bff574c (2/2 replicas created)
  48. Events:
  49. Type Reason Age From Message
  50. ---- ------ ---- ---- -------
  51. Normal ScalingReplicaSet 4m18s deployment-controller Scaled up replica set vol-nginx-emptydir-588bff574c to 2
  52. [root@k8s-master01 emptydir]#

而后,可以为其创建Service资源并进行访问测试,或者在集群中直接对Pod的地址发起访问请求,以测试两个容器通过emptyDir卷共享数据的结果状态

  1. [root@k8s-master01 emptydir]# kubectl get pods -l "app=myapp-emptydir" -o wide
  2. NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
  3. vol-nginx-emptydir-86558457b9-cjlxs 2/2 Running 0 55s 10.244.38.6 172.18.15.114 <none> <none>
  4. vol-nginx-emptydir-86558457b9-tstdm 2/2 Running 0 57s 10.244.59.7 172.18.15.113 <none> <none>
  5. [root@k8s-master01 emptydir]#
  6. [root@k8s-node01 ~]# curl 10.244.38.6
  7. 1
  8. 1
  9. 1
  10. 1
  11. 1
  12. 1
  13. 1
  14. 1
  15. 1
  16. [root@k8s-node01 ~]#

作为边车(sidecar)的容器pagegen,每隔10秒生成一行信息追加到存储卷上的index.html文件中,因此,通过主容器nginx的应用访问到的内容也会处于不停的变动中。另外,emptyDir存储卷也可以基于RAM创建tmpfs文件系统的存储卷,常用于为容器的应用提供高性能缓存,下面是一个配置示例

  1. volumes:
  2. - name: cache
  3. emptyDir:
  4. medium: memory
  5. sizeLimit: 1G

虽然 tmpfs 速度非常快,但是要注意它与磁盘不同。 tmpfs 在节点重启时会被清除,并且您所写入的所有文件都会计入容器的内存消耗,受容器内存限制约束。 emptyDir卷简单易用,但仅能用于临时存储。另外还存在一些类型的存储卷建构在emptyDir之上,并额外提供了它所没有的功能

gitRepo存储卷

gitRepo存储卷可以看作是emptyDir存储卷的一种实际应用,使用该存储卷的Pod资源可以通过挂载目录访问指定的代码仓库中的数据。使用gitRepo存储卷的Pod资源在创建时,会首先创建一个空目录(emptyDir)并克隆(clone)一份指定的git仓库中的数据至该目录,而后再创建容器并挂载存储卷 定义gitRepo类型的存储卷时,其可嵌套使用的字段具体包含如下三个。其字段在.spec.volumes.gitRepo

  • directory:目标目录名称,名称中不能包含”..”字符,”.”表示将仓库中的数据直接复制到卷目录中,否则,即为复制到卷目录中以用户指定的字符串为名称的子目录中
  • repository:表示需要获取的git代码仓库的地址
  • revision: 特定revision的提交哈希码
    注意:使用gitRepo存储卷的Pod资源运行的工作节点上必须安装有git程序,否则克隆仓库的操作将无法完成。另外,从K8S 1.12起,gitrepo存储卷基本已经被废弃了,如果需要在容器中提供 git 仓库,请将一个EmptyDir卷挂载到 InitContainer 中,使用 git 命令完成仓库的克隆操作,然后将 EmptyDir 卷挂载到 Pod 的容器中。
    下面示例中我们还是使用直接创建的deployment控制器,然后修改资源清单配置文件,增加一个gitRepo存储卷的配置,将指定Git仓库[https://github.com/iKubernetes/k8s_book.git](https://github.com/iKubernetes/k8s_book.git)中的数据复制一份,直接保存于此目录中,而后将此目录创建为存货就git,最后由Nginx将此目录存储卷挂载于/usr/share/nginx/html/git目录上
  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: vol-nginx-emptydir
  5. spec:
  6. replicas: 2
  7. selector:
  8. matchLabels:
  9. app: myapp-emptydir
  10. template:
  11. metadata:
  12. labels:
  13. app: myapp-emptydir
  14. spec:
  15. dnsPolicy: ClusterFirstWithHostNet
  16. containers:
  17. #定义第一个容器为nginx
  18. - name: nginx-emptydir
  19. image: docker.io/nginx:1.13.0-alpine
  20. ports:
  21. - containerPort: 80
  22. volumeMounts:
  23. - name: html
  24. mountPath: /usr/share/nginx/html
  25. - name: git
  26. mountPath: /usr/share/nginx/html/git
  27. #定义第二个容器为pagegen
  28. - name: pagegen
  29. image: docker.io/alpine:3.12.0
  30. volumeMounts:
  31. - name: html
  32. mountPath: /mnt/html
  33. command: ["/bin/sh","-c","while true; do echo ${date} >> /mnt/html/index.html; sleep 10; done"]
  34. volumes:
  35. - name: html
  36. emptyDir: {}
  37. - name: git
  38. gitRepo:
  39. repository: https://github.com/iKubernetes/k8s_book.git
  40. directory: .
  41. revision: master

资源清单文件修改完成后,并可以直接使用kubectl apply进行应用。可以通过下面的输出信息看到,Pod一直处于ContainerCreating状态,我们可以使用命令kubectl describe pods podname查看信息进行排错 因为gitRepo类型的我们在实际环境当中使用的并不多,所以这里不作演示 不过,gitRepo存储卷在其创建完成后不会再与其指定的仓库进行同步操作,这就意味着在Pod资源运行期间,如果仓库中的数据发生了变化,那么gitRepo存储卷不会同步到这些内容。当然,此时可以为Pod资源创建一个专门边车容器用于执行此类的同步操作,尤其是数据来源于私有仓库时,通过边车容器完成其复制就更为必要 gitRepo存储卷构建与emptyDir存储卷之上,它的生命周期与隶属的Pod对象相同,因此使用时不建议在此类存储卷中保存由容器生成的重要数据。另外,gitrepo存储卷基本已经被废弃了,所在之后的版本中若要使用它配置Pod对象,可以借助初始化容器(InitContainer)将仓库中的数据复制到emptyDir存储卷之上,并在容器中使用此存储卷

节点存储卷

hostPath类型的存储卷是指将工作节点上某文件系统的目录或文件挂载于Pod中的一种存储卷,它可以独立于Pod资源的生命周期,因而具有持久性。但它是工作节点本地的存储空间,仅适用于特定情况下的存储卷使用需求,例如,将工作节点上的文件系统关联为Pod的存储卷,从而使得容器访问节点文件系统上的数据。这一点在运行有管理任务的系统级Pod资源需要访问节点上的文件时尤为有用 配置hostPath存储卷的嵌套字段总共有两个:

  • .spec.volumes.hostPath.path: 用于指定工作节点上的目录路径的必选字段
  • .spec.volumes.hostPath.type: 用于指定使用的卷类型,它支持使用的卷类型包含如下几种
  • DirectoryOrCreate:指定的路径不存在自动将其创建为权限是0755的空目录,属主和属组均为kebelet
  • Directory:给定的路径上必须存在的目录
  • FileOrCreate:指定的路径不存在自动将其创建为权限是0644的空文件,属主和属组均为kebelet
  • File:给定的路径上必须存在的文件
  • Socket:给的的路径上必须存在的套接字
  • CharDevice:给定的路径上必须存在的字符设备
  • BlockDevice:给定的路径上必须存在的块设备
    使用这种类型的存储卷时需要注意,具有相同配置(例如,PodTemplate创建)的多个Pod会由于节点上文件的不同而在不同节点上有不同的行为。当K8S按照计划添加资源感知的调度时,这类调度机制将无法考虑由hostPath使用的资源。基础主机上创建的文件或目录只能由root用户写入。需要在特权容器中以root身份运行进程,或者修改主机上的文件权限以便容器能够写入hostPath卷
    下面是定义hostPath类型存储卷的示例,我们将宿主机的/var/log/目录挂载到容器nginx-emptydir的/var/log/nginx目录下面
  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: vol-nginx-emptydir
  5. spec:
  6. replicas: 2
  7. selector:
  8. matchLabels:
  9. app: myapp-emptydir
  10. template:
  11. metadata:
  12. labels:
  13. app: myapp-emptydir
  14. spec:
  15. dnsPolicy: ClusterFirstWithHostNet
  16. containers:
  17. #定义第一个容器为nginx
  18. - name: nginx-emptydir
  19. image: docker.io/nginx:1.13.0-alpine
  20. ports:
  21. - containerPort: 80
  22. volumeMounts:
  23. - name: html
  24. mountPath: /usr/share/nginx/html
  25. - name: varlog
  26. mountPath: /var/log/nginx/
  27. #定义第二个容器为pagegen
  28. - name: pagegen
  29. image: docker.io/alpine:3.12.0
  30. volumeMounts:
  31. - name: html
  32. mountPath: /mnt/html
  33. command: ["/bin/sh","-c","while true; do echo ${date} >> /mnt/html/index.html; sleep 10; done"]
  34. volumes:
  35. - name: html
  36. emptyDir: {}
  37. - name: varlog
  38. hostPath:
  39. path: /var/log
  40. type: DirectoryOrCreate

资源清单文件准备完成之后,使用kubectl apply更新pod

  1. [root@k8s-master01 emptydir]# kubectl apply -f vol-emptydir.yaml
  2. deployment.apps/vol-nginx-emptydir configured
  3. [root@k8s-master01 emptydir]#
  4. [root@k8s-master01 emptydir]# kubectl get pods -l "app=myapp-emptydir" -o wide
  5. NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
  6. vol-nginx-emptydir-675794f4ff-mhv7z 2/2 Running 0 53s 10.244.59.8 172.18.15.113 <none> <none>
  7. vol-nginx-emptydir-675794f4ff-xn2dv 2/2 Running 0 49s 10.244.38.8 172.18.15.114 <none> <none>
  8. [root@k8s-master01 emptydir]#

进入容器查看是否成功挂载。确认挂载的目录里面的文件是否与工作节点的一致

  1. [root@k8s-master01 emptydir]# kubectl exec -ti vol-nginx-emptydir-675794f4ff-mhv7z -- /bin/sh
  2. Defaulting container name to nginx-emptydir.
  3. Use 'kubectl describe pod/vol-nginx-emptydir-675794f4ff-mhv7z -n default' to see all of the containers in this pod.
  4. / # cd /var/log
  5. /var/log # ls
  6. nginx
  7. /var/log # cd nginx/
  8. /var/log/nginx # ls
  9. access.log cron-20200607 maillog pods spooler-20200628
  10. anaconda cron-20200614 maillog-20200607 rhsm tallylog
  11. audit cron-20200621 maillog-20200614 secure tuned
  12. boot.log cron-20200628 maillog-20200621 secure-20200607 vmware-network.1.log
  13. boot.log-20200526 dmesg maillog-20200628 secure-20200614 vmware-network.2.log
  14. boot.log-20200527 dmesg.old messages secure-20200621 vmware-network.3.log
  15. boot.log-20200528 error.log messages-20200607 secure-20200628 vmware-network.log
  16. btmp firewalld messages-20200614 spooler vmware-vgauthsvc.log.0
  17. btmp-20200701 grubby messages-20200621 spooler-20200607 vmware-vmsvc.log
  18. containers grubby_prune_debug messages-20200628 spooler-20200614 wtmp
  19. cron lastlog ntpstats spooler-20200621 yum.log
  20. /var/log/nginx # pwd
  21. /var/log/nginx
  22. /var/log/nginx #

在使用hostPath存储卷时一定要注意,不同节点上的文件或许会完全不同,于是,那些要求实现必须存在的文件或者目录的满足状态也可能会有所不同;另外,基于资源可用状态的调度器调度Pod时,hostPath资源的可用状态不会被考虑在内;再者,在节点中创建的文件或目录默认仅允许root可写,若期望容器内的进程拥有可写权限,则要么将它运行为特权容器,要么修改节点上目录路径的权限。 那些并非执行系统管理任务的且不受限于DaemonSet控制器的无状态应用在Pod资源被重新调度至其他节点运行时,此前创建的文件或目录大多都不会存在。因此,hostPath存储卷虽然能持久存储数据,但对于被调度器按需调度的应用来说并不使用,这时需要用到的是独立于集群节点的持久性存储卷,即网络存储卷