存储与配置
Volume、PV/PVC、StorageClass、ConfigMap、Secret 完整详解。
1. Volume(卷)概述
卷是独立于容器之外的一块存储区域,通过挂载方式供 Pod 中的容器使用。
1.1 使用场景
| 场景 | 说明 |
|---|---|
| 多容器共享数据 | 同一个 Pod 中的容器可以共享卷 |
| 持久存储 | 数据存储在外部存储上,Pod 删除后数据保留 |
| 备份与迁移 | 独立于容器的数据更易于备份和迁移 |
1.2 卷的三种分类
| 类型 | 生命周期 | 典型用途 |
|---|---|---|
| 临时卷(Ephemeral) | 与 Pod 同生命周期 | emptyDir、ConfigMap、Secret |
| 持久卷(Persistent) | 独立于 Pod | hostPath、local、NFS、Ceph |
| 投射卷(Projected) | 映射多个卷到同一目录 | projected |
2. 临时卷
2.1 emptyDir
创建一个初始为空的目录,存储来自本地 kubelet 根目录(磁盘)或内存。
apiVersion: v1
kind: Pod
metadata:
name: redis-pod
spec:
containers:
- name: redis
image: redis
volumeMounts:
- mountPath: /data/redis
name: redis-storage
volumes:
- name: redis-storage
emptyDir: {} # 磁盘存储
# emptyDir:
# medium: "Memory" # 内存存储
# sizeLimit: 1Gi # 大小限制| 用途 | 说明 |
|---|---|
| 缓存空间 | 磁盘型,适合临时文件 |
| 日志暂存 | 供边车容器读取处理 |
| 内存存储 | 设置为 Memory 时使用 tmpfs,速度快但 Pod 重启数据丢失 |
2.2 ConfigMap 卷与 Secret 卷
kubelet 引用 ConfigMap/Secret 中的内容,在 Pod 所在节点生成临时卷。删除 Pod 后临时卷也会被删除,但 ConfigMap/Secret 对象仍然存在。
临时卷位于 Pod 所在节点的 /var/lib/kubelet/pods 目录下。
3. 持久卷(PV)与持久卷声明(PVC)
3.1 概念
管理员创建 PV → 用户创建 PVC → 集群绑定 PV 与 PVC → Pod 使用 PVC 作为存储卷| 对象 | 类比 | 说明 |
|---|---|---|
| PV(PersistentVolume) | 虚拟硬盘 | 集群级别的存储资源,不属于任何 namespace |
| PVC(PersistentVolumeClaim) | 申请单 | 用户对存储的请求,属于 namespace |
| StorageClass | 硬盘类型 | 定义存储的类型,支持动态创建 PV |
3.2 创建 StorageClass
# local-storage.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: local-storage
provisioner: kubernetes.io/no-provisioner # local 卷不支持动态创建
volumeBindingMode: Immediate3.3 创建 PV(静态创建)
apiVersion: v1
kind: PersistentVolume
metadata:
name: example-pv
spec:
capacity:
storage: 2Gi # 容量
volumeMode: Filesystem # 卷模式:Filesystem / Block
accessModes:
- ReadWriteOnce # 访问模式
persistentVolumeReclaimPolicy: Delete # 回收策略
storageClassName: local-storage
local:
path: /mnt/disks/ssd1 # 此目录需手动创建
nodeAffinity: # local 卷必须指定节点亲和性
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- work-node-1🚨 注意:
local卷不支持动态创建,必须手动创建 PV。当节点不可用时,Pod 和卷都会失效,数据可能丢失。生产环境建议使用集群外部存储或云存储。
3.4 创建 PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: task-pv-claim
spec:
storageClassName: local-storage
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi3.5 在 Pod 中使用 PVC
spec:
containers:
- name: mysql
image: mysql:8.0.31
volumeMounts:
- mountPath: /var/lib/mysql
name: data-volume
volumes:
- name: data-volume
persistentVolumeClaim:
claimName: task-pv-claim3.6 绑定机制
- PVC 与 PV 的绑定是一对一的排他关系
- PVC 可匹配比声明容量大的 PV,但不能匹配小的
- 找不到满足要求的 PV 时,PVC 会处于 Pending 状态
- 多个较小 PV 加起来的容量再大,也无法满足一个大容量 PVC
3.7 PV 状态
| 状态 | 说明 |
|---|---|
| Available | 空闲,尚未绑定到任何 PVC |
| Bound | 已绑定到某个 PVC |
| Released | PVC 已被删除,但 PV 尚未被回收 |
| Failed | 自动回收操作失败 |
3.8 访问模式
| 模式 | 说明 | 缩写 |
|---|---|---|
| ReadWriteOnce | 单个节点读写挂载 | RWO |
| ReadOnlyMany | 多个节点只读挂载 | ROX |
| ReadWriteMany | 多个节点读写挂载 | RWX |
| ReadWriteOncePod | 单个 Pod 读写挂载(K8s ≥1.22,仅 CSI) | RWOP |
3.9 卷模式
| 模式 | 说明 |
|---|---|
| Filesystem | 默认,作为文件系统挂载 |
| Block | 作为原始块设备使用 |
3.10 回收策略
| 策略 | 行为 |
|---|---|
| Delete(默认) | 删除 PVC 时自动删除关联的 PV |
| Retain | 删除 PVC 时 PV 保留,数据仍在,需手动处理 |
| Recycle | ⚠️ 已废弃,不建议使用 |
3.11 存储类(StorageClass)详解
一个集群可存在多个 StorageClass,管理不同类型的存储。
| 参数 | 说明 |
|---|---|
| provisioner(制备器) | 决定用哪个卷插件制备 PV(必填) |
| volumeBindingMode | 卷绑定模式:Immediate(立即)/ WaitForFirstConsumer(延迟) |
| reclaimPolicy | 回收策略 |
卷绑定模式对比:
| 模式 | 行为 | 适用场景 |
|---|---|---|
Immediate |
创建 PVC 后立即创建 PV 并绑定 | 全局可用的存储 |
WaitForFirstConsumer |
Pod 被创建时才创建 PV | 节点亲和性、拓扑感知存储 |
3.12 K3s 的 local-path StorageClass
K3s 自带 local-path 存储类,支持动态创建基于 hostPath 或 local 的持久卷。
- 创建 PVC 后自动创建 PV,无需手动创建
- 删除 PVC 后 PV 自动删除
# 查看集群中的 StorageClass
kubectl get sc
# 输出示例:
# NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE
# local-path (default) rancher.io/local-path Delete WaitForFirstConsumer3.13 静态创建 vs 动态创建
| 方面 | 静态创建 | 动态创建 |
|---|---|---|
| 创建方式 | 管理员手动创建 PV | 按需自动创建 |
| 灵活性 | 低,需提前规划 | 高 |
| 资源利用 | 可能浪费(PVC 匹配到更大的 PV) | 精确分配 |
| 支持范围 | 所有存储类型 | 需 StorageClass 支持 |
💡 最佳实践:结合使用。管理员手动创建一些常用规格的 PV 以备急用,同时配置 StorageClass 支持动态创建。
4. hostPath 卷
将主机节点上的文件或目录挂载到 Pod 中。
volumes:
- name: data-volume
hostPath:
path: /home/mysql/data
type: DirectoryOrCreatehostPath type 值
| type | 行为 |
|---|---|
DirectoryOrCreate |
目录不存在则自动创建 |
Directory |
挂载已存在的目录,不存在时报错 |
FileOrCreate |
文件不存在则自动创建(需确保父目录存在) |
File |
挂载已存在的文件,不存在时报错 |
Socket |
挂载 UNIX 套接字(如 /var/run/docker.sock) |
🚨 警告:
hostPath仅限于单节点测试使用。多节点集群中 Pod 重新调度到其他节点后无法访问原来节点上的数据,应使用local卷替代。
5. ConfigMap 详解
5.1 基本用法
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-config
data:
mysql.cnf: |
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_general_ci
init-connect="SET NAMES utf8mb4"
[client]
default-character-set=utf8mb4
[mysql]
default-character-set=utf8mb45.2 使用方式
| 方式 | 说明 |
|---|---|
| 环境变量 | valueFrom.configMapKeyRef |
| 命令行参数 | 配合环境变量使用 |
| 存储卷挂载 | 挂载为文件到容器目录 |
# 作为存储卷挂载(最常用)
containers:
- name: mysql
image: mysql:8.0.31
volumeMounts:
- mountPath: /etc/mysql/conf.d
name: conf-volume
readOnly: true
volumes:
- name: conf-volume
configMap:
name: mysql-config5.3 更新与热加载
修改 ConfigMap 后,以卷方式挂载的文件会自动更新(有一定的 kubelet 同步延迟,通常在 1 分钟左右)。
但应用程序不一定会自动重新加载配置。需要:
- 应用自身支持热加载(如监听文件变化)
- 或手动重启 Pod:
kubectl rollout restart deploy <name>
# 编辑 ConfigMap
kubectl edit cm mysql-config5.4 通过命令行创建
# 从文件创建
kubectl create cm my-config --from-file=app.conf
# 从目录创建(每个文件为一个 key)
kubectl create cm my-config --from-file=/path/to/config/dir
# 从字面量创建
kubectl create cm my-config --from-literal=key1=value1 --from-literal=key2=value26. Secret 详解
6.1 基本用法
apiVersion: v1
kind: Secret
metadata:
name: mysql-password
type: Opaque
data:
PASSWORD: MTIzNDU2Cg== # Base64 编码的值(echo -n "123456" | base64)
---
# 或使用 stringData(明文,创建时自动编码)
apiVersion: v1
kind: Secret
metadata:
name: mysql-password
type: Opaque
stringData:
PASSWORD: "123456" # 直接写明文6.2 在 Pod 中使用
# 通过环境变量引用
containers:
- name: mysql
image: mysql:8.0.31
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-password
key: PASSWORD
optional: false6.3 Secret 类型
| 类型 | 用途 |
|---|---|
| Opaque | 通用 Secret(默认) |
| kubernetes.io/dockerconfigjson | 镜像仓库认证 |
| kubernetes.io/tls | TLS 证书(需 tls.crt 和 tls.key 两个 key) |
| kubernetes.io/service-account-token | ServiceAccount 令牌 |
6.4 Secret vs ConfigMap
| 特性 | ConfigMap | Secret |
|---|---|---|
| 存储格式 | 明文 | Base64 编码(⚠️ 非加密) |
| 用途 | 普通配置 | 密码、令牌、密钥 |
| 大小限制 | ≤ 1 MiB | ≤ 1 MiB |
| RBAC 隔离 | 通常较宽松 | 应给予更严格的访问控制 |
| 静态加密 | 不加密 | 可配置 etcd 静态加密 |
🚨 安全警告:Secret 的 Base64 是编码而非加密。任何人只要能访问 Secret 对象就能解码。生产环境必须结合 RBAC、静态加密和外部密钥管理系统(如 HashiCorp Vault)。
6.5 创建 Secret 的命令行方式
# 从文件创建
kubectl create secret generic my-secret --from-file=password.txt
# 从字面量创建
kubectl create secret generic my-secret --from-literal=password=123456
# 创建镜像仓库 Secret
kubectl create secret docker-registry regcred \
--docker-server=<registry> \
--docker-username=<user> \
--docker-password=<pass>