← 返回 Kubernetes图解 目录

💾 K8s存储与配置图解

Volume/PV/PVC/StorageClass/ConfigMap/Secret · SVG可视化 + 生活类比 + IDE源码窗口

① Volume类型全景

把K8s存储想象成公司里的各种空间
emptyDir = 临时办公桌(员工来了分配,走了清空,不保留任何东西)
hostPath = 靠墙的私人柜子(固定在大楼某个位置,只在那台机器上)
ConfigMap = 公告栏(大家都能看到的配置信息)
Secret = 保险柜(敏感信息,只有授权的人能看)
PV = 仓库(实实在在的存储空间)
PVC = 仓库申请单("我需要多大仓库、什么条件")
K8s Volume类型全景 — 公司空间类比 📦 Pod emptyDir 临时办公桌 Pod删了就没了 hostPath 靠墙私人柜 绑定某台Node ConfigMap 公告栏 非敏感配置 Secret 保险柜 密码/密钥/证书 容器A → /data 容器A → /host-data 容器 → /etc/config 容器 → /etc/secret 持久化存储:PV + PVC 📋 PVC(仓库申请单) storageClassName: "fast-ssd" accessModes: ["ReadWriteOnce"] resources.requests.storage: 10Gi selector: {matchLabels: {type: ssd}} "我要10G的SSD仓库,一个人用" 🏭 PV(仓库实体) capacity.storage: 20Gi accessModes: ["ReadWriteOnce"] storageClassName: "fast-ssd" nfs.server: "10.0.0.1" nfs.path: "/data/share" "我有20G的NFS仓库,等着被申请" 绑定
Volume类型全景:临时存储 vs 持久化存储 vs 配置存储
Volume类型生命周期类比典型场景
emptyDir同Pod临时办公桌Pod内多容器共享临时数据
hostPath同Node靠墙私人柜访问Node文件系统(调试用)
PV/PVC独立仓库/申请单持久化业务数据
ConfigMap同Namespace公告栏配置文件、环境变量
Secret同Namespace保险柜密码、Token、证书
emptyDir-pod.yaml
📂 k8s-yaml/
emptyDir-pod.yaml
pvc-demo.yaml
configmap-demo.yaml
secret-demo.yaml
# emptyDir:Pod内共享临时数据 apiVersion: v1 kind: Pod metadata: name: sidecar-demo spec: containers: - name: app # 主容器:写日志 image: nginx volumeMounts: - name: log-data mountPath: /var/log/app - name: sidecar # Sidecar:读日志转发 image: fluentd volumeMounts: - name: log-data mountPath: /var/log/app volumes: - name: log-data emptyDir: {} # Pod删除→数据消失

② PV与PVC绑定流程

PV和PVC就像仓库和仓库申请单
PV = 公司有一间间仓库(20G SSD / 50G HDD / 100G NFS),每间仓库有标签(类型、大小、访问方式)
PVC = 员工填一张申请单:"我要一个10G以上的SSD仓库,一个人用"
绑定 = 行政部门拿申请单去匹配仓库,找到最合适的就绑定
AccessModes = 仓库使用规则:ReadWriteOnce=独享 / ReadWriteMany=共享 / ReadOnlyMany=只读共享
PV-PVC绑定流程 — 仓库申请全流程 ① 用户提交PVC "我要10G SSD" ReadWriteOnce ② 控制器匹配 Size ≥ 10Gi? SC + AccessMode匹配? ③ 绑定成功 PVC → PV 状态: Bound 🏭 可用PV仓库池 PV-ssd-1 20Gi RWO SSD ✅ 匹配! PV-hdd-1 50Gi RWO HDD ❌ SC不匹配 PV-ssd-2 100Gi RWX SSD ❌ 太大/AccessMode不同 PV-ssd-3 5Gi RWO SSD ❌ 容量不够 AccessModes(仓库使用规则) ReadWriteOnce (RWO) 独享:只能一个Node挂载 类比:单人办公室 ReadWriteMany (RWX) 共享:多个Node同时读写 类比:开放办公区 ReadOnlyMany (ROX) 只读:多个Node只读访问 类比:阅览室
PV-PVC绑定三步流程:提交申请 → 控制器匹配 → 绑定成功
pv-pvc-demo.yaml
📂 storage/
pv-pvc-demo.yaml
pod-with-pvc.yaml
📂 config/
configmap.yaml
secret.yaml
# ① PV:仓库实体 apiVersion: v1 kind: PersistentVolume metadata: name: pv-ssd-20g spec: capacity: storage: 20Gi accessModes: ["ReadWriteOnce"] storageClassName: fast-ssd nfs: server: 10.0.0.1 path: /data/ssd-share --- # ② PVC:仓库申请单 apiVersion: v1 kind: PersistentVolumeClaim metadata: name: my-app-pvc spec: accessModes: ["ReadWriteOnce"] storageClassName: fast-ssd resources: requests: storage: 10Gi # 申请10G,匹配20G的PV --- # ③ Pod使用PVC apiVersion: v1 kind: Pod metadata: name: my-app spec: containers: - name: app image: nginx volumeMounts: - name: data mountPath: /usr/share/nginx/html volumes: - name: data persistentVolumeClaim: claimName: my-app-pvc
PVC绑定时不会自动找最小匹配,而是找第一个满足条件的PV。如果PV是20Gi而PVC申请10Gi,绑定的还是20Gi的PV——这20Gi就被占用了,别人用不了剩余的10Gi。所以PV大小最好按需创建。

③ StorageClass动态供给

StorageClass = 仓库分类标准。就像公司把仓库分成"SSD仓""HDD仓""NFS仓",员工只需在申请单上写"我要SSD仓的10G",行政部门自动分配——不用提前建好每间仓库。
静态供给 = 管理员先建好仓库(手动创建PV),等PVC来匹配
动态供给 = 管理员只定好仓库模板(StorageClass),PVC来了自动创建PV
动态供给 vs 静态供给 静态供给(手动建仓库) 管理员 手动创建PV PV-1 等PVC来匹配 用户提交PVC Bound! ⚠️ 管理员得提前估算容量 ⚠️ PV多了浪费,少了不够 ⚠️ 扩容需要手动操作 动态供给(自动建仓库) StorageClass 仓库模板/分类 用户提交PVC 自动创建PV Provisioner干的事 ✅ 省心! 管理员只管定义StorageClass模板 PVC来了自动按模板创建PV 容量精确匹配,不浪费
静态供给 vs 动态供给:手动建仓库 vs 自动按模板创建
storageclass.yaml
📂 storage/
pv-pvc-demo.yaml
storageclass.yaml
📂 config/
configmap.yaml
secret.yaml
# StorageClass:仓库分类模板 apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: fast-ssd provisioner: kubernetes.io/aws-ebs # 谁来建仓库 parameters: type: gp3 iopsPerGB: "50" reclaimPolicy: Delete # PVC删了,PV也删 volumeBindingMode: WaitForFirstConsumer --- # PVC引用StorageClass(自动创建PV) apiVersion: v1 kind: PersistentVolumeClaim metadata: name: auto-pvc spec: storageClassName: fast-ssd # 引用SC accessModes: ["ReadWriteOnce"] resources: requests: storage: 10Gi
reclaimPolicyPVC删除后类比适用场景
RetainPV保留,需手动清理离职了工位还在生产环境数据保护
DeletePV和底层存储一起删离职了工位拆了临时/开发环境
Recycle清空数据后PV可重用(已废弃)工位打扫后给下个人旧版K8s

④ ConfigMap与Secret

ConfigMap = 公司公告栏:数据库地址、日志级别、功能开关——不怕人看到的配置。
Secret = 公司保险柜:数据库密码、API密钥、TLS证书——绝不能让人随便看到的敏感数据。

使用方式有两种
环境变量 = 口头告知(简单,但重启才更新)
挂载文件 = 贴在工位上(更新后自动刷新,不用重启Pod)
ConfigMap vs Secret — 公告栏 vs 保险柜 📋 ConfigMap(公告栏) DB_HOST: "mysql.prod.svc" LOG_LEVEL: "info" FEATURE_FLAG: "true" 使用方式 环境变量 envFrom: configMapRef 挂载文件 volume: configMap ✅ 明文存储,可随时查看 ✅ 挂载文件方式更新→自动刷新 ⚠️ 不要放敏感数据! 🔐 Secret(保险柜) DB_PASSWORD: "c2VjcmV0" ← Base64 API_KEY: "YWJjMTIz" ← Base64 tls.crt: "LS0tLS1..." ← 证书 安全措施 Base64编码 不是加密!是编码 RBAC控制 谁有权限查看 ⚠️ Base64 ≠ 加密!只是编码 ⚠️ 生产务必开启EncryptionConfiguration ✅ 配合RBAC + 审计日志
ConfigMap vs Secret:公告栏 vs 保险柜,两种使用方式
configmap-secret.yaml
📂 storage/
pv-pvc-demo.yaml
storageclass.yaml
📂 config/
configmap-secret.yaml
# ConfigMap:公告栏 apiVersion: v1 kind: ConfigMap metadata: name: app-config data: DB_HOST: "mysql.prod.svc" LOG_LEVEL: "info" application.yml: | server: port: 8080 spring: datasource: url: jdbc:mysql://${DB_HOST}:3306/mydb --- # Secret:保险柜 apiVersion: v1 kind: Secret metadata: name: db-credentials type: Opaque data: # 注意:值必须是Base64编码 username: YWRtaW4= # echo -n 'admin' | base64 password: c2VjcmV0 # echo -n 'secret' | base64 --- # Pod挂载ConfigMap和Secret apiVersion: v1 kind: Pod metadata: name: my-app spec: containers: - name: app image: my-app:latest # 方式1:环境变量 envFrom: - configMapRef: name: app-config - secretRef: name: db-credentials # 方式2:挂载文件 volumeMounts: - name: config-volume mountPath: /etc/config readOnly: true volumes: - name: config-volume configMap: name: app-config
Secret的Base64不是加密!任何人 `kubectl get secret xxx -o yaml` 然后 `echo 'c2VjcmV0' | base64 -d` 就能看到明文。生产环境必须:
1. 开启 EncryptionConfiguration(etcd加密存储)
2. 配置RBAC限制Secret访问权限
3. 使用外部密钥管理(Vault/AWS Secrets Manager)

⑤ 面试速答

emptyDir和hostPath有什么区别?各适合什么场景?
emptyDir:Pod级别临时存储,Pod删除数据消失。适合:Sidecar共享日志、临时计算中间结果。

hostPath:挂载Node本地路径,Pod删除数据还在(但换了Node就找不到)。适合:访问Docker socket(/var/run/docker.sock)、系统日志(/var/log)。

关键区别:emptyDir随Pod生灭,hostPath绑定Node。
PV和PVC是什么关系?绑定规则是什么?
PV是集群级资源(仓库实体),PVC是命名空间级资源(仓库申请单)。

绑定规则:
1. storageClassName必须一致
2. PV的capacity ≥ PVC的requests
3. PV的accessModes必须包含PVC要求的模式
4. 如果PVC有selector,PV的labels必须匹配

注意:一个PV只能绑定一个PVC(1:1关系)。
StorageClass的动态供给是什么?比静态供给好在哪里?
动态供给:用户创建PVC时,K8s根据StorageClass模板自动创建PV并绑定。
静态供给:管理员手动创建PV,等PVC来匹配。

动态供给优势:
1. 按需创建:不浪费存储空间
2. 自动匹配:不需要管理员手动计算容量
3. 解耦:开发不用关心底层存储细节

关键配置:provisioner(谁来建)+ parameters(建什么样的)+ reclaimPolicy(PVC删了怎么办)。
ConfigMap和Secret有什么区别?Secret真的安全吗?
ConfigMap:存储非敏感配置,明文存储。
Secret:存储敏感数据,Base64编码。

Secret并不真正安全!
1. Base64是编码不是加密,`base64 -d` 一秒解码
2. 默认存储在etcd中是明文的
3. 有RBAC权限的人可以直接 `kubectl get secret`

生产环境安全措施:
• 开启EncryptionConfiguration(etcd加密)
• 配置RBAC限制访问
• 使用Vault等外部密钥管理系统
• 开启审计日志记录Secret访问
ConfigMap更新后Pod里的配置会自动生效吗?
取决于使用方式

环境变量方式(envFrom):❌ 不会自动更新。Pod的环境变量在启动时注入,之后不会变。必须重启Pod。

挂载文件方式(volume):✅ 会自动更新!kubelet会定期同步(默认60秒),Pod内文件内容自动刷新。但应用本身要支持热加载(比如Spring Boot的@RefreshScope)。

最佳实践:需要热更新的配置用挂载文件方式,启动时一次性配置用环境变量。
PVC删除后PV会怎样?reclaimPolicy有什么影响?
取决于PV的reclaimPolicy

Retain:PV保留,数据不删,需管理员手动清理后才能重新绑定。生产环境推荐。
Delete:PV和底层存储一起删除。开发/测试环境可用。
Recycle(已废弃):执行rm -rf后PV可重用。

生产环境建议用Retain,防止误删PVC导致数据丢失。
ReadWriteOnce/ReadWriteMany/ReadOnlyMany分别支持什么存储后端?
RWO(ReadWriteOnce):单Node读写。AWS EBS、GCE PD、Azure Disk都支持。
RWX(ReadWriteMany):多Node同时读写。只有NFS、CephFS、GlusterFS等网络存储支持。
ROX(ReadOnlyMany):多Node只读。NFS、CephFS、托管存储支持。

关键记忆:块存储(EBS)只支持RWO,文件存储(NFS/CephFS)支持RWX。选存储后端时要先确认AccessMode需求。
volumeBindingMode的WaitForFirstConsumer是什么意思?
Immediate(默认):PVC一创建就绑定PV,不管Pod调度到哪个Node。
WaitForFirstConsumer:等到第一个使用此PVC的Pod调度完成后,才在那个Node上创建/绑定PV。

为什么需要?
比如AWS EBS是区域绑定的——如果PVC在us-east-1a创建了PV,但Pod被调度到us-east-1b的Node上,PV就挂不上了。

WaitForFirstConsumer让PV在Pod实际运行的Zone创建,避免跨Zone挂载失败。
如何给Spring Boot应用注入K8s的ConfigMap配置?
推荐方式:挂载为文件 + Spring Cloud Kubernetes

1. ConfigMap中定义application.yml
2. 挂载到/config目录
3. Spring Cloud Kubernetes自动读取

简单方式:环境变量注入
envFrom.configMapRef注入所有key为环境变量
Spring Boot用${DB_HOST}引用

注意:环境变量方式修改ConfigMap需要重启Pod才能生效;挂载文件方式kubelet会自动同步(约60秒延迟),但应用需要支持热加载。
核心三原则速记

K8s存储配置三原则:① PV 是集群资源,PVC 是用户申请,StorageClass 自动动态创建 → ② ConfigMap 存非敏感配置,Secret 存密码证书(Base64 不是加密) → ③ 持久化数据用 PVC,临时数据用 emptyDir