一、持久化Volumes

数据持久化:

容器部署过程中一般有以下三种数据:

  • 启动时需要的初始数据,例如配置文件;

  • 启动过程中产生的临时数据,该临时数据需要多个容器间共享;

  • 应用运行过程中产生的持久化数据,例如MySQL的data目录;

数据卷的分类:

  • 本地(hostPath,emptyDir等)
  • 网络(NFS,Ceph,GlusterFS等)
  • 公有云(阿里云OSS,NAS / AWS EBS等)
  • K8S资源(configmap,secret等)

二、emptyDir(临时存储卷)

  • 1、emptyDir存储卷是Pod对象生命周期中的一个临时目录,在Pod对象启动时即被创建,而在Pod对象被移除时会被彻底删除;
  • 2、不具有持久能力的emptyDir存储卷只能用于某些特殊场景中;
  • 3、emptyDir只是一个临时挂载的文件,pod删除后,该目录也会在node节点上被删除,但是容器崩溃时,该文件还存在;

案例一: 暂存空间,用作长时间计算崩溃恢复时的检查点

# 创建pod模板
[root@master01 1]# k run emptydir-nginx --image=registry.cn-hangzhou.aliyuncs.com/zq-demo/nginx:1.21.6  --dry-run=client -oyaml > emptydir-nginx.yaml

# 定义yaml文件
[root@master01 1]# vim emptydir-nginx.yaml
apiVersion: v1
kind: Pod
metadata:
  name: emptydir-nginx
  namespace: study
spec:
  containers:
  - image: registry.cn-hangzhou.aliyuncs.com/zq-demo/nginx:1.21.6
    name: emptydir-nginx
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - mountPath: /data/ #添加容器内部的挂载点
      name: emptydir-name
  volumes:
  - name: emptydir-name
    emptyDir: #创建一个空目录
      sizeLimit: 1G #目录容量大小为1G

# 应用
[root@master01 1]# kaf emptydir-nginx.yaml

# 查看
[root@master01 1]# kgp -owide -n study
NAME             READY   STATUS    RESTARTS   AGE     IP               NODE     NOMINATED NODE   READINESS GATES
emptydir-nginx   1/1     Running   0          3m55s   172.21.231.178   node02   <none>           <none>

# 进入容器并在容器中创建文件测试
[root@master01 1]# kubectl exec -it emptydir-nginx -n study /bin/bash
root@emptydir-nginx:/# echo $(date +%Y%m%d-%H:%M:%S) >> /data/time.log
root@emptydir-nginx:/# ls /data/time.log
/data/time.log

# 在node02节点上查看临时存储的文件位置
[root@node02 ~]# find / -name "time.log"
/var/lib/kubelet/pods/6ed328ee-72fe-44ca-9720-20c25708173d/volumes/kubernetes.io~empty-dir/emptydir-name/time.log

# 在node02节点上查看并验证文件内容
[root@node02 ~]# cat /var/lib/kubelet/pods/6ed328ee-72fe-44ca-9720-20c25708173d/volumes/kubernetes.io~empty-dir/emptydir-name/time.log
20250506-03:24:14

# 在master节点上删除pod
[root@master01 1]# k delete -f  emptydir-nginx.yaml

# 再次在node02节点上验证数据是否存在,观察到数据已经消失
[root@master01 ~]# ll /var/lib/kubelet/pods/6ed328ee-72fe-44ca-9720-20c25708173d/volumes/kubernetes.io~empty-dir/emptydir-name/time.log
ls: cannot access /var/lib/kubelet/pods/6ed328ee-72fe-44ca-9720-20c25708173d/volumes/kubernetes.io~empty-dir/emptydir-name/time.log: No such file or directory

# 环境复原
[root@master01 1]# k delete -f emptydir-nginx.yaml

案例二: Pod内的多个容器间文件的共享,或者作为容器数据的临时存储目录用于数据缓存系统等

# 创建pod模板
[root@master01 1]# k run producer-consumer --image=registry.cn-hangzhou.aliyuncs.com/abroad_images/busybox:1.28  --dry-run=client -oyaml > producer-consumer.yaml

# 定义yaml文件
[root@master01 1]# vim producer-consumer.yaml
apiVersion: v1
kind: Pod
metadata:
  name: producer-consumer
  namespace: study
spec:
  containers:
    - image: registry.cn-hangzhou.aliyuncs.com/abroad_images/busybox:1.28
      name: producer
      volumeMounts:
      - name: shared-volume
        mountPath: /producer_dir
      command: [ "/bin/sh", "-c" ]
      args:
      - while true; do
          echo $(hostname) $(date) >> /producer_dir/hello;
          sleep 5;
        done
    - name: consumer
      image: registry.cn-hangzhou.aliyuncs.com/abroad_images/busybox:1.28
      volumeMounts:
      - name: shared-volume
        mountPath: /consumer_dir
      args:
      - /bin/sh
      - -c
      - cat /consumer_dir/hello ; sleep 3600
  volumes:
  - name: shared-volume
    emptyDir: {}

# 应用
[root@master01 1]# kaf producer-consumer.yaml

# 查看
[root@master01 1]# kgp -n study -owide
NAME                READY   STATUS    RESTARTS   AGE   IP               NODE     NOMINATED NODE   READINESS GATES
producer-consumer   2/2     Running   0          73s   172.21.231.179   node02   <none>           <none>

# 在master01节点上新起两个窗口进行测试验证观察到两个容器 producer和consumer它们共享一个 Volume
[root@master01 1]# kubectl exec -it producer-consumer -c producer -n study -- tail -f /producer_dir/hello

producer-consumer Fri Nov 10 09:11:30 UTC 2023
...
...

[root@master01 11.4]# kubectl exec -it producer-consumer -c consumer -n study -- tail -f /consumer_dir/hello
producer-consumer Fri Nov 10 09:11:30 UTC 2023
...
...

# 环境复原
[root@master01 1]# k delete -f producer-consumer.yaml

案例流程:

这里我们模拟了一个 producer / consumer 场景。Pod 有两个容器 producer和 consumer,它们共享一个 Volume。producer 负责往 Volume 中写数据,其每隔5秒 生成一行信息追加到存储卷上的hello文件中, consumer 则是从 Volume 读取数据。

  • 1、文件最底部 volumes 定义了一个 emptyDir 类型的 Volume shared-volume。
  • 2、producer 容器将 shared-volume mount 到 /producer_dir 目录。
  • 3、producer 通过 echo 将数据写到文件 hello 里。
  • 4、consumer 容器将 shared-volume mount 到 /consumer_dir 目录。
  • 5、consumer 通过 cat 从文件 hello 读数据。

总结:

emptyDir 是Host上创建的临时目录;

优点:能够方便地为 Pod 中的容器提供共享存储,不需要额外的配置。

缺点:不具备持久性,如果 Pod 不存在了,emptyDir 也就没有了。

三、hostPath(节点存储卷)

hostPath卷是指将工作节点上某文件系统的目录或文件挂载于Pod中的一种存储卷,它可独立于Pod资源的生命周期,因而具有持久性。

支持的 type 值如下:

取值 解释
空字符串(默认) 在安装 hostPath 卷之前不会执行任何检查
DirectoryOrCreate 目录存在就使用,不存在就先创建,权限设置为0755
Directory 目录必须存在
FileOrCreate 文件存在就使用,不存在就先创建,权限设置为0644
File 文件必须存在
Socket(不常用) 在给定路径上必须存在的UNIX 套接字
CharDevice(不常用) 在给定路径上必须存在的字符设备
BlockDevice(不常用) 在给定路径上必须存在的块设备

案例一:

# 创建deploy模板
[root@master01 1]# k create deploy pod-hook-exec --image=registry.cn-hangzhou.aliyuncs.com/abroad_images/busybox:1.28 -r=1 -n study  --dry-run=client -oyaml > pod-hook-exec.yaml

# 定义yaml文件
[root@master01 1]# vim pod-hook-exec.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-hook-exec
  namespace: study
spec:
  replicas: 1
  selector:
    matchLabels:
      app: pod-hook-exec
  template:
    metadata:
      labels:
        app: pod-hook-exec
    spec:
      terminationGracePeriodSeconds: 8 # 设置8秒宽限时间默认是30s
      initContainers:
      - name: init-containers
        image: registry.cn-hangzhou.aliyuncs.com/abroad_images/busybox:1.28
        command: ["sh","-c","echo init-containers...|tee -a /tmp/pod-hook-exec.log;sleep 5s"]
        volumeMounts:
        - name: logs
          mountPath: /tmp/
      containers:
      - image: registry.cn-hangzhou.aliyuncs.com/abroad_images/busybox:1.28
        name: main-container
        command: ["sh","-c","echo main-container...|tee -a /tmp/pod-hook-exec.log;sleep 3600s"]
        volumeMounts:
        - name: logs
          mountPath: /tmp/
        startupProbe:
          exec:
            command: ["sh","-c","echo startupProbe...|tee -a /tmp/pod-hook-exec.log;sleep 5s"]
          timeoutSeconds: 10
        livenessProbe:
          exec:
            command: ["sh","-c","echo livenessProbe...|tee -a /tmp/pod-hook-exec.log;sleep 5s"]
          timeoutSeconds: 10
        readinessProbe:
          exec:
            command: ["sh","-c","echo readinessProbe...|tee -a /tmp/pod-hook-exec.log;sleep 5s"]
          timeoutSeconds: 10
        lifecycle:
          postStart:
            exec:
              command: ["sh","-c","echo postStart...|tee -a /tmp/pod-hook-exec.log;sleep 5s"]
          preStop: # 在pod停止之前执行这个命令
            exec:
              command: ["sh","-c","echo preStop...|tee -a /tmp/pod-hook-exec.log"]
      volumes:
      - name: logs #和上面保持一致 这是本地的文件路径上面是容器内部的路径
        hostPath:
          path: /tmp/logs/
          type: DirectoryOrCreate

# 应用
[root@master01 1]# kaf pod-hook-exec.yaml

# 查看
[root@master01 1]# kgp -n study -owide
NAME                             READY   STATUS    RESTARTS   AGE     IP               NODE     NOMINATED NODE   READINESS GATES
pod-hook-exec-75cb79786d-6f2zj   1/1     Running   0          2m37s   172.21.231.180   node02   <none>           <none>

# 在宿主机node02节点上查看应用的pod-hook-exec.log日志
[root@node02 logs]# more pod-hook-exec.log
init-containers...
main-container...
postStart...
startupProbe...
readinessProbe...
livenessProbe...
readinessProbe...
livenessProbe...
readinessProbe...
readinessProbe...
livenessProbe...
readinessProbe...
...
...
...

# 环境复原
[root@master01 1]# k delete -f pod-hook-exec.yaml

最终我们可以通过容器所在宿主机的 /tmp/logs/ 去查看应用的 pod-hook-exec.log 日志;