Kubernetes 学习笔记:Volume 存储卷与 ConfigMap 配置管理

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.htmlerror.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-1webapp-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 环境验证,后续版本若有变更请参考官方文档。

相关推荐
梦梦代码精2 小时前
LikeShop 深度测评:开源电商的务实之选
java·前端·数据库·后端·云原生·小程序·php
A_QXBlms2 小时前
企销宝新版本技术解读新客运营多天计划与关键词自动化拉群实践
运维·自动化
yuanlaile2 小时前
NestJS实战商城与云原生落地指南
云原生·nestjs·nestjs学习指南
江畔何人初2 小时前
Kafka 消息队列概念及与RabbitMQ 的区别
运维·服务器·分布式·云原生·kafka·rabbitmq
木泽八2 小时前
PCIe虚拟化技术全景:从SR-IOV到云原生IO
云原生·pcie虚拟化
雪碧聊技术2 小时前
微服务实战:彻底解决子项目找不到父项目工具类、实体类的问题
微服务·云原生·架构
nLif2 小时前
linux-stable-sw-v4.19.180-sw64-2203.tar.gz 编译错误排查方法
linux·运维·服务器
周末也要写八哥2 小时前
前端三大类设计模式学习
学习·设计模式
Wmenghu2 小时前
Ubuntu 安装 RocketMQ 5.x + Dashboard 完整教程
linux·ubuntu·rocketmq