在 Pod 中使用临时卷和持久卷

KubernetesBeginner
立即练习

介绍

容器拥有可写的文件系统,但该存储与容器本身绑定。Kubernetes 卷为 Pod 提供了一种更清晰的文件处理方式。有些卷是临时的,旨在用于单个 Pod 内部的临时数据;而另一些卷是持久的,即使在 Pod 被删除并重建后,数据依然可以保持可用。

在本实验中,你将使用 emptyDir 卷来存储 Pod 本地的临时数据,然后创建一个 PersistentVolumeClaim(PVC)并将其挂载到两个不同的 Pod 中,以验证数据在 Pod 更替后依然存在。

准备卷练习命名空间

在此步骤中,你将准备一个干净的命名空间,并确认集群具备本实验所需的存储组件。

首先进入 /home/labex/project 目录。你将在该目录下编写 Pod 和 PVC 的清单文件。

mkdir -p /home/labex/project
cd /home/labex/project

检查 Kubernetes API 服务器是否可达。如果虚拟机刚启动且节点尚未就绪,请等待几秒钟后再运行该命令。

kubectl get nodes --request-timeout=5s

预期输出包含:

NAME         STATUS   ROLES           AGE   VERSION
labex-v135   Ready    control-plane   ...   v1.35...

等待集群网络 Pod 就绪:

kubectl wait --namespace kube-system --for=condition=Ready pod -l k8s-app=calico-node --timeout=120s

预期输出:

pod/calico-node-... condition met

本实验稍后会用到 PersistentVolumeClaim。确认默认的 standard StorageClass 存在,然后等待 minikube 存储配置器 Pod 就绪。

kubectl get storageclass standard
kubectl wait --namespace kube-system --for=condition=Ready pod/storage-provisioner --timeout=120s

预期输出包含:

NAME                 PROVISIONER                RECLAIMPOLICY   VOLUMEBINDINGMODE   ...
standard (default)   k8s.io/minikube-hostpath   Delete          Immediate           ...
pod/storage-provisioner condition met

创建一个名为 ckad-prep-volumes 的命名空间:

kubectl create namespace ckad-prep-volumes

将此命名空间设置为当前上下文的命名空间:

kubectl config set-context --current --namespace=ckad-prep-volumes

确认当前命名空间:

kubectl config view --minify --output 'jsonpath={..namespace}'; echo

预期输出:

ckad-prep-volumes

使用 emptyDir 存储 Pod 本地临时数据

在此步骤中,你将创建一个使用 emptyDir 卷的 Pod。

emptyDir 卷在 Pod 创建时为空。同一 Pod 中的容器可以将它用作共享的临时空间、缓存空间或临时工作目录。它不用于在 Pod 被移除后保留数据。

创建 emptydir-pod.yaml:

cat > emptydir-pod.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: scratch-worker
  labels:
    app: scratch-worker
spec:
  terminationGracePeriodSeconds: 0
  volumes:
  - name: scratch-space
    emptyDir: {}
  containers:
  - name: worker
    image: busybox:1.36
    command:
    - /bin/sh
    - -c
    - 'echo scratch-ready > /scratch/status.txt; sleep 3600'
    volumeMounts:
    - name: scratch-space
      mountPath: /scratch
EOF

应用清单:

kubectl apply -f emptydir-pod.yaml

预期输出:

pod/scratch-worker created

等待 Pod 就绪:

kubectl wait --for=condition=Ready pod/scratch-worker --timeout=120s

预期输出:

pod/scratch-worker condition met

从挂载的 emptyDir 卷中读取文件:

kubectl exec scratch-worker -c worker -- cat /scratch/status.txt

预期输出:

scratch-ready

检查 Pod 的卷类型。jsonpath 表达式会打印卷名称并确认其为 emptyDir。

kubectl get pod scratch-worker -o 'jsonpath={.spec.volumes[0].name}{" emptyDir\n"}'

预期输出:

scratch-space emptyDir

将数据写入 PersistentVolumeClaim

在此步骤中,你将创建一个 PersistentVolumeClaim(PVC)并将其挂载到一个写入者 Pod 中。

PersistentVolumeClaim(即 PVC)是应用程序对存储的请求。在本实验中,standard StorageClass 会为该声明动态提供存储。你的 Pod 将该声明挂载到 /data 目录并在其中写入一个文件。

创建 notes-pvc.yaml:

cat > notes-pvc.yaml <<'EOF'
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: notes-pvc
spec:
  accessModes:
  - ReadWriteOnce
  storageClassName: standard
  resources:
    requests:
      storage: 1Mi
EOF

应用 PVC:

kubectl apply -f notes-pvc.yaml

预期输出:

persistentvolumeclaim/notes-pvc created

等待 PVC 变为 Bound(已绑定)状态:

kubectl wait --for=jsonpath='{.status.phase}'=Bound pvc/notes-pvc --timeout=120s

预期输出:

persistentvolumeclaim/notes-pvc condition met

创建一个挂载了该 PVC 的写入者 Pod:

cat > pvc-writer-pod.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: pvc-writer
  labels:
    app: pvc-writer
spec:
  terminationGracePeriodSeconds: 0
  volumes:
  - name: notes-storage
    persistentVolumeClaim:
      claimName: notes-pvc
  containers:
  - name: writer
    image: busybox:1.36
    command:
    - /bin/sh
    - -c
    - 'echo persistent-note > /data/note.txt; sleep 3600'
    volumeMounts:
    - name: notes-storage
      mountPath: /data
EOF

应用写入者 Pod 并等待其就绪:

kubectl apply -f pvc-writer-pod.yaml
kubectl wait --for=condition=Ready pod/pvc-writer --timeout=120s

预期输出:

pod/pvc-writer created
pod/pvc-writer condition met

从 PVC 挂载点读取文件:

kubectl exec pvc-writer -c writer -- cat /data/note.txt

预期输出:

persistent-note

从新 Pod 中复用 PVC

在此步骤中,你将删除写入者 Pod,创建一个挂载相同 PVC 的新读取者 Pod,并确认文件依然存在。

这就是 Pod 本地临时数据与持久化存储之间的实际区别。写入者 Pod 可以被移除,但 PVC 依然存在。一个新的 Pod 可以挂载相同的声明并读取数据。

删除写入者 Pod:

kubectl delete pod pvc-writer

预期输出:

pod "pvc-writer" deleted

创建一个挂载相同 PVC 的读取者 Pod:

cat > pvc-reader-pod.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: pvc-reader
  labels:
    app: pvc-reader
spec:
  terminationGracePeriodSeconds: 0
  volumes:
  - name: notes-storage
    persistentVolumeClaim:
      claimName: notes-pvc
  containers:
  - name: reader
    image: busybox:1.36
    command:
    - /bin/sh
    - -c
    - 'cat /data/note.txt; sleep 3600'
    volumeMounts:
    - name: notes-storage
      mountPath: /data
EOF

应用读取者 Pod 并等待其就绪:

kubectl apply -f pvc-reader-pod.yaml
kubectl wait --for=condition=Ready pod/pvc-reader --timeout=120s

预期输出:

pod/pvc-reader created
pod/pvc-reader condition met

从新 Pod 中读取文件:

kubectl exec pvc-reader -c reader -- cat /data/note.txt

预期输出:

persistent-note

检查 PVC。即使原始的写入者 Pod 已经消失,它应该仍然处于 Bound 状态。

kubectl get pvc notes-pvc

预期输出包含:

NAME        STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
notes-pvc   Bound    ...      1Mi        RWO            standard       <unset>                 ...

总结

你使用了 emptyDir 卷来存储 Pod 本地的临时数据,创建了一个用于持久化存储的 PVC,通过一个 Pod 写入数据,删除了该 Pod,并从挂载了相同声明的新 Pod 中读取了相同的数据。现在你已经了解了何时使用临时存储来处理 Pod 的临时数据,以及何时请求持久化存储来保存那些需要在 Pod 更替后依然存在的数据。