介绍
容器拥有可写的文件系统,但该存储与容器本身绑定。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 更替后依然存在的数据。


