Kubernetes 学习笔记:Volume 存储卷与 ConfigMap 配置管理
本笔记基于课堂实验,系统梳理了 Kubernetes 中 Volume(存储卷)的常用类型与 PV/PVC 持久化存储架构,以及 ConfigMap 配置管理的最佳实践。所有示例代码均经过验证,可直接运行。
1. Kubernetes Volume 概述
Kubernetes 中的 Volume(卷)解决了容器文件系统临时性的问题。Pod 中的容器可以共享 Volume,且 Volume 的生命周期独立于单个容器。
常用 Volume 类型对比
| 类型 | 生命周期 | 数据持久性 | 典型场景 |
|---|---|---|---|
emptyDir |
随 Pod 创建,Pod 删除时销毁 | 否 | 临时缓存、多容器共享临时数据 |
hostPath |
独立于 Pod,Pod 删除后保留 | 是 | 访问宿主机文件系统、监控数据采集 |
nfs |
完全独立于 Pod 和 Node | 是 | 多 Pod 共享数据、持久化存储 |
2. 临时存储卷:emptyDir
emptyDir 在 Pod 分配到 Node 时创建,Pod 内所有容器均可读写。Pod 被删除时,emptyDir 中的数据永久丢失。
2.1 示例:多容器共享 emptyDir
yaml
# pod-emptyDir.yaml
apiVersion: v1
kind: Pod
metadata:
name: busybox
labels:
app: busybox
spec:
volumes:
- name: datavolume
emptyDir: {}
containers:
- name: busybox1
image: docker.io/library/busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
volumeMounts:
- mountPath: /data
name: datavolume
- name: busybox2
image: docker.io/library/busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
volumeMounts:
- mountPath: /data
name: datavolume
2.2 验证 emptyDir 行为
bash
kubectl apply -f pod-emptyDir.yaml
# 查看容器所在节点
kubectl describe pod busybox | grep Node:
# 在 busybox1 中创建文件
kubectl exec busybox -c busybox1 -- touch /data/b1-f1
# 在 busybox2 中查看文件(共享存储)
kubectl exec busybox -c busybox2 -- ls /data
# 输出:b1-f1
# 删除 Pod,数据随之删除
kubectl delete pod busybox --force
3. 节点存储卷:hostPath
hostPath 将宿主机(Node)上的目录挂载到 Pod 中。Pod 被删除后,宿主机上的数据依然保留。
3.1 示例:挂载宿主机目录
yaml
# pod-hostPath.yaml
apiVersion: v1
kind: Pod
metadata:
name: busybox
labels:
app: busybox
spec:
volumes:
- name: datavolume
hostPath:
path: /busyboxdir
containers:
- name: busybox1
image: docker.io/library/busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
volumeMounts:
- mountPath: /data
name: datavolume
- name: busybox2
image: docker.io/library/busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
volumeMounts:
- mountPath: /data
name: datavolume
readOnly: false # 默认为 false,可读写
3.2 验证 hostPath 持久性
bash
kubectl apply -f pod-hostPath.yaml
# 写入数据
kubectl exec busybox -c busybox1 -- touch /data/host-file
# 在 Node 上查看(需登录对应节点)
ls /busyboxdir/
# 输出:host-file
# 删除 Pod 后,宿主机目录数据仍存在
kubectl delete pod busybox --force
ls /busyboxdir/ # 文件依然存在
4. 网络存储卷:NFS
NFS 卷允许多个 Pod 同时挂载同一个网络共享目录,实现跨节点数据共享。
4.1 搭建 NFS 服务端(在 Master 节点)
bash
# 安装 NFS 服务
apt install -y nfs-kernel-server
# 创建共享目录并写入测试文件
mkdir -m 777 /nfsshares
echo "hello whisky" > /nfsshares/index.html
# 配置 exports,允许所有客户端访问
echo "/nfsshares *(rw)" > /etc/exports
# 重启服务
systemctl restart nfs-server.service
# 在所有 Worker 节点安装 NFS 客户端
apt install -y nfs-common
4.2 示例:Pod 挂载 NFS 共享
yaml
# pod-nfs.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
run: nginx
name: nginx
spec:
volumes:
- name: nfs
nfs:
server: 10.1.8.30 # NFS 服务器 IP
path: "/nfsshares" # 共享路径
containers:
- image: docker.io/library/nginx
name: nginx
volumeMounts:
- name: nfs
mountPath: "/usr/share/nginx/html"
4.3 验证 NFS 共享
bash
kubectl apply -f pod-nfs.yaml
POD_IP=$(kubectl get pod nginx -o jsonpath='{.status.podIP}')
curl http://$POD_IP
# 输出:hello whisky
# 在容器内写入文件
kubectl exec nginx -- touch /usr/share/nginx/html/test.html
# 在 NFS 服务端查看,文件已同步
ls /nfsshares/
# 输出:index.html test.html
# 删除 Pod,NFS 上的数据依然保留
kubectl delete pod nginx --force
ls /nfsshares/ # 文件仍在
5. 持久化存储:PV 与 PVC
Kubernetes 通过 PersistentVolume(PV) 和 PersistentVolumeClaim(PVC) 抽象存储资源,实现存储与应用的解耦。
- PV:集群级别的存储资源,由管理员预先创建或通过 StorageClass 动态制备。
- PVC:命名空间级别的存储请求,用户只需声明容量和访问模式。
5.1 创建 PV(基于 NFS)
yaml
# pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: web
spec:
capacity:
storage: 5Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
nfs:
path: /nfsshares
server: 10.1.8.30
5.2 创建 PVC 并绑定
yaml
# pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: webclaim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
5.3 Pod 使用 PVC
yaml
# pod-web.yaml
apiVersion: v1
kind: Pod
metadata:
name: web
spec:
containers:
- image: docker.io/library/httpd
name: web
volumeMounts:
- name: web-persistent-storage
mountPath: /usr/local/apache2/htdocs
volumes:
- name: web-persistent-storage
persistentVolumeClaim:
claimName: webclaim
5.4 验证流程
bash
# 创建 PV 和 PVC
kubectl apply -f pv.yaml
kubectl apply -f pvc.yaml
# 查看绑定状态
kubectl get pv
kubectl get pvc
# STATUS 应为 Bound
# 创建 Pod 并访问
kubectl apply -f pod-web.yaml
POD_IP=$(kubectl get pod web -o jsonpath='{.status.podIP}')
curl http://$POD_IP
# 输出:hello whisky
6. PV 关键属性详解
6.1 访问模式(accessModes)
| 缩写 | 全称 | 含义 |
|---|---|---|
| RWO | ReadWriteOnce | 单个节点读写挂载 |
| ROX | ReadOnlyMany | 多个节点只读挂载 |
| RWX | ReadWriteMany | 多个节点读写挂载 |
| RWOP | ReadWriteOncePod | 单个 Pod 独占读写(需 CSI 支持) |
常用存储后端支持情况:
- NFS:支持 RWO、ROX、RWX
- hostPath:仅支持 RWO
- 云厂商块存储(EBS/PD):仅支持 RWO
6.2 卷模式(volumeMode)
- Filesystem(默认):挂载为文件系统目录。
- Block:挂载为原始块设备,Pod 应用需自行处理块设备读写。
6.3 回收策略(persistentVolumeReclaimPolicy)
| 策略 | 行为 |
|---|---|
Retain(默认) |
PVC 删除后 PV 变为 Released 状态,需手动清理 claimRef 后才能被再次绑定 |
Recycle(已弃用) |
执行 rm -rf 清空数据后重新可用 |
Delete |
PVC 删除时自动删除 PV 及后端存储(需存储插件支持) |
6.4 Retain 策略手动回收示例
bash
# 删除 PVC 后 PV 进入 Released 状态
kubectl delete pvc webclaim
kubectl get pv # STATUS: Released
# 编辑 PV,删除 spec.claimRef 部分
kubectl edit pv web
# 删除以下内容:
# claimRef:
# name: webclaim
# namespace: default
# 此时新 PVC 可重新绑定该 PV
6.5 指定 PV 绑定特定 PVC
在 PV 定义中添加 claimRef 字段,实现一对一绑定:
yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: web
spec:
# ... 其他配置 ...
claimRef:
name: webclaim
namespace: test
7. ConfigMap 配置管理
ConfigMap 用于将非敏感的配置数据与容器镜像解耦,支持以环境变量或文件卷的形式注入 Pod。
7.1 创建 ConfigMap
方式一:从键值对创建
bash
kubectl create configmap mysql --from-literal=password=redhat
方式二:从文件创建
bash
echo "Hello World" > index.html
kubectl create configmap web1 --from-file=./index.html
方式三:从目录创建
bash
mkdir web2
cp index.html error.html web2/
kubectl create configmap web2 --from-file=./web2
7.2 以环境变量方式引用
yaml
# pod-cm-env.yaml
apiVersion: v1
kind: Pod
metadata:
name: mysql
spec:
containers:
- image: docker.io/library/mysql:latest
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
configMapKeyRef:
name: mysql
key: password
验证:
bash
kubectl exec -it mysql -- bash -c 'echo $MYSQL_ROOT_PASSWORD'
# 输出:redhat
7.3 以 Volume 方式挂载
挂载整个 ConfigMap
yaml
# pod-cm-volume-all.yaml
apiVersion: v1
kind: Pod
metadata:
name: web
spec:
containers:
- image: docker.io/library/nginx:latest
name: web
volumeMounts:
- name: webcontent
mountPath: "/usr/share/nginx/html"
volumes:
- name: webcontent
configMap:
name: web2 # 包含 index.html 和 error.html
挂载后容器内将看到两个文件:index.html 和 error.html。
挂载指定键(两种写法)
yaml
# 写法一:使用 items 筛选
volumes:
- name: webcontent
configMap:
name: web2
items:
- key: index.html
path: index.html
# 写法二:使用 subPath 挂载单文件
volumeMounts:
- name: webcontent
mountPath: "/usr/share/nginx/html/index.html"
subPath: index.html
7.4 ConfigMap 热更新
通过 Volume 挂载的 ConfigMap 内容会自动更新(kubelet 定期同步)。但需注意:
- 环境变量方式引用的 ConfigMap 不会自动更新,需重启 Pod。
- 使用
subPath挂载的文件也不会自动更新。
7.5 实战案例:修改 kube-proxy 工作模式
bash
# 查看 kube-proxy 的 ConfigMap
kubectl get cm -n kube-system kube-proxy -o yaml
# 编辑 ConfigMap,将 mode 改为 "ipvs"
kubectl edit cm -n kube-system kube-proxy
# 找到 mode: "" 改为 mode: "ipvs"
# 删除 kube-proxy Pod,DaemonSet 会自动重建并应用新配置
kubectl delete pod -n kube-system -l k8s-app=kube-proxy
# 验证日志
kubectl logs -n kube-system <new-kube-proxy-pod> | grep "Using ipvs"
8. 综合案例:HAProxy + Web 应用
需求描述
- 部署两个 Nginx Pod(
webapp-1、webapp-2),每个 Pod 的网页内容通过 ConfigMap 提供。 - 部署一个 HAProxy Pod,其配置文件通过 ConfigMap 挂载,将请求负载均衡到两个后端 Nginx。
8.1 创建 Web 应用及 ConfigMap
bash
# 创建 webapp-1 的 ConfigMap 和 Pod
kubectl create cm webapp-1 \
--from-literal=index.html="hello webapp-1" \
--from-literal=error.html="sorry, error."
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: webapp-1
spec:
containers:
- name: nginx
image: docker.io/library/nginx:latest
volumeMounts:
- name: config
mountPath: "/usr/share/nginx/html"
volumes:
- name: config
configMap:
name: webapp-1
EOF
# 创建 webapp-2(类似,略)
8.2 获取 Pod IP 并准备 HAProxy 配置
bash
# 查看 Pod IP
kubectl get pods -o wide
# webapp-1 10.224.84.80
# webapp-2 10.224.149.25
# 编写 haproxy.cfg
cat > haproxy.cfg <<EOF
global
daemon
maxconn 256
defaults
mode http
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
frontend http-in
bind *:8080
default_backend servers
backend servers
server app1 10.224.84.80:80 check
server app2 10.224.149.25:80 check
EOF
# 创建 ConfigMap
kubectl create cm haproxy.cfg --from-file=haproxy.cfg=./haproxy.cfg
8.3 部署 HAProxy Pod
yaml
# haproxy.yaml
apiVersion: v1
kind: Pod
metadata:
name: haproxy
spec:
containers:
- name: haproxy
image: docker.io/library/haproxy
volumeMounts:
- name: config
mountPath: "/usr/local/etc/haproxy"
volumes:
- name: config
configMap:
name: haproxy.cfg
8.4 验证负载均衡
bash
kubectl apply -f haproxy.yaml
HAPROXY_IP=$(kubectl get pod haproxy -o jsonpath='{.status.podIP}')
# 多次请求,轮流返回不同后端页面
curl http://$HAPROXY_IP:8080
# 输出:hello webapp-1
curl http://$HAPROXY_IP:8080
# 输出:hello webapp-2
9. 知识点速查表
9.1 Volume 类型对比
| Volume 类型 | 数据持久性 | 跨 Pod 共享 | 跨节点共享 | 典型用途 |
|---|---|---|---|---|
emptyDir |
Pod 内持久,Pod 删则无 | 同一 Pod 内多容器 | 否 | 临时缓存、暂存数据 |
hostPath |
宿主机持久 | 同一节点上的 Pod | 否 | 访问节点日志、Docker Socket |
nfs |
持久 | 是 | 是 | 多副本应用共享数据 |
persistentVolumeClaim |
持久 | 取决于后端存储 | 取决于后端存储 | 生产环境标准存储抽象 |
9.2 PV 关键字段速查
| 字段 | 说明 | 可选值 |
|---|---|---|
accessModes |
访问模式 | RWO, ROX, RWX, RWOP |
volumeMode |
卷模式 | Filesystem(默认), Block |
persistentVolumeReclaimPolicy |
回收策略 | Retain(默认), Recycle(弃用), Delete |
storageClassName |
存储类名称 | 自定义字符串,如 "fast" |
capacity.storage |
容量大小 | 如 1Gi, 10Gi |
9.3 ConfigMap 创建方式速查
| 创建方式 | 命令示例 |
|---|---|
| 键值对 | kubectl create cm my-cm --from-literal=key1=val1 --from-literal=key2=val2 |
| 单个文件 | kubectl create cm my-cm --from-file=./file.txt |
| 目录 | kubectl create cm my-cm --from-file=./config-dir/ |
| 指定 key 名称 | kubectl create cm my-cm --from-file=custom-key=./file.txt |
9.4 ConfigMap 引用方式速查
| 引用方式 | 配置示例 | 特点 |
|---|---|---|
| 环境变量(单值) | valueFrom.configMapKeyRef |
容器内通过 $ENV_NAME 访问,不自动更新 |
| Volume(整体) | volumes[].configMap.name |
每个键生成一个文件,支持热更新 |
| Volume(指定项) | volumes[].configMap.items |
筛选特定键并重命名文件 |
| Volume(单文件) | volumeMounts[].subPath |
挂载单个文件,不支持热更新 |
9.5 常用调试命令
| 场景 | 命令 |
|---|---|
| 查看 Pod 挂载详情 | kubectl describe pod <pod-name> |
| 进入容器查看挂载内容 | kubectl exec <pod> -- ls <mountPath> |
| 查看 ConfigMap 内容 | kubectl get cm <cm-name> -o yaml |
| 查看 PV/PVC 绑定状态 | kubectl get pv,pvc |
| 强制删除 Pod | kubectl delete pod <pod> --force --grace-period=0 |
笔记基于 Kubernetes v1.28 环境验证,后续版本若有变更请参考官方文档。