K8s Volume 类型大全 + subPath / 多视角
K8s 提供了 十几种 volume 类型,每个有自己的语义、生命周期、视角问题。这一篇把它们全部覆盖——并对每一个都给"应用看到啥、节点 host 看到啥"的对照。
Volume 类型选错 = 数据丢失 / 性能踩坑 / 配置失效。
这篇要回答什么
- emptyDir / hostPath / configMap / secret / projected / downwardAPI / PVC / ephemeral 各是干什么的?
- subPath / subPathExpr 解决什么问题?什么时候用?
- configMap 改了为啥 pod 看不到?什么时候才能看到?
- Secret 是不是真的"加密"?K8s 给你的"安全感"有多少?
- projected volume 是怎么把 ConfigMap + Secret + token 合一起的?
downwardAPI怎么把 pod label / annotation 注入容器?- CSI volume 在 K8s 是怎么运转的(简介,详见 03-pv-pvc-storageclass.md)
1. Volume 全景图
mindmap
root((K8s Volume))
临时类
emptyDir
tmpfs
ephemeral
Host 节点类
hostPath
Local PV
配置类
configMap
secret
downwardAPI
projected
持久类
PVC (CSI)
NFS
iSCSI
Ceph RBD/FS
特殊
gitRepo (废)
按生命周期分类(这是选型的根本):
| 类别 | 生命周期 | 例子 |
|---|---|---|
| Pod 级 | 跟 pod 同存亡 | emptyDir / configMap / secret / projected / downwardAPI |
| 节点级 | 跟节点同存亡(pod 走了数据还在) | hostPath |
| 集群级 | 独立生命周期、跨 pod / 跨节点 | persistentVolumeClaim (CSI / NFS / Ceph) |
第一原则:emptyDir / hostPath 不是"持久"
emptyDir pod 删 = 数据没。 hostPath 节点上跑 = 数据在;节点重启 / pod 重调度到别的节点 = 看不到旧数据。
要持久 → PVC。
2. emptyDir —— Pod 级临时空间
spec:
volumes:
- name: cache
emptyDir: {}
containers:
- name: app
volumeMounts:
- name: cache
mountPath: /cache
视角对照
| 视角 | 路径 |
|---|---|
| 应用代码 | /cache/foo |
| 容器 mount ns | /cache/foo |
| 节点 host | /var/lib/kubelet/pods/⟨pod-uid⟩/volumes/kubernetes.io~empty-dir/cache/foo |
| 物理盘 | 节点根分区(默认)/ 或 tmpfs(medium: Memory) |
medium: Memory 内存型 emptyDir
volumes:
- name: shm
emptyDir:
medium: Memory
sizeLimit: 1Gi
放内存里、不占盘 → 但占 pod 的 memory limit。
# 节点上看
$ findmnt /var/lib/kubelet/pods/⟨uid⟩/volumes/kubernetes.io~empty-dir/shm
TARGET SOURCE FSTYPE OPTIONS
/var/lib/kubelet/pods/.../shm tmpfs tmpfs rw,relatime,size=1024000k
典型场景:
- 共享内存(
/dev/shm替代)—— 多容器 IPC - 计算中间结果(不需持久)
- 解压临时空间
sizeLimit —— 生产必加
emptyDir:
sizeLimit: 5Gi
不设的话 pod 写满节点根分区 → 节点 NotReady。给所有 emptyDir 都加 sizeLimit——超过会被 evict。
反面教材
把 emptyDir 当持久缓存
spec:
containers:
- name: redis
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
emptyDir: {}
Redis 数据放 emptyDir → pod 重启容器 OK;pod 删除 / 重调度 → 数据没。
业务"反正能从源数据重建"用 emptyDir 没问题(如缓存)。真要持久 → PVC。
3. hostPath —— 节点本地路径
spec:
containers:
- name: app
volumeMounts:
- name: logs
mountPath: /app/logs
volumes:
- name: logs
hostPath:
path: /var/log/myapp
type: DirectoryOrCreate
type 字段
type | 含义 |
|---|---|
"" (默认) | 不检查 |
DirectoryOrCreate | 不存在就创建(目录) |
Directory | 必须存在、必须是目录 |
FileOrCreate | 不存在就创建(文件) |
File | 必须存在、必须是文件 |
Socket | 必须是 Unix socket |
CharDevice | 字符设备 |
BlockDevice | 块设备 |
hostPath 的本质 + 三个核心问题
hostPath = 容器直接挂宿主路径。
3 个生产坑:
- 节点之间内容不一致:pod 调度到不同节点看到的
/var/log/myapp完全不同 - 节点重启 / 维护:pod 飘到别的节点 = 看到空目录或别的数据
- 安全风险:容器写宿主路径 = 容器可以影响宿主(特别
/etc//proc//var/lib/...)
生产 99% 场景不要 hostPath。例外:
- 监控 agent 看节点
/proc/sys(需要 readOnly) - CSI driver pod 看节点
/var/lib/kubelet(需要 propagation) - 节点级日志收集(fluentd / promtail 读
/var/log/containers)
替代方案:Local PV
要"本地存储 + K8s 调度感知" → 用 Local PV(详见 03-pv-pvc-storageclass.md):
# Local PV - 比 hostPath 安全多了
apiVersion: v1
kind: PersistentVolume
metadata:
name: local-pv-m1
spec:
capacity:
storage: 100Gi
volumeMode: Filesystem
accessModes: [ReadWriteOnce]
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
local:
path: /mnt/disks/ssd1
nodeAffinity: # ← 关键:绑定到特定节点
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values: [m1]
差别:
- hostPath:任意节点都尝试 mount /var/log/myapp(即使没有这个目录 / 内容不一样)
- Local PV:K8s 知道这个数据只在 m1、把 pod 调度到 m1
4. configMap —— 配置文件投射
两种使用方式
spec:
containers:
- name: app
volumeMounts:
- name: cfg
mountPath: /app/config
volumes:
- name: cfg
configMap:
name: my-config
items: # 可选:选 ConfigMap 里的特定 key
- key: app.yaml
path: application.yaml # 改个文件名
- key: log.conf
path: log/app.log.conf # 子目录
容器里:
$ kubectl exec app -- ls /app/config
application.yaml
log/
$ kubectl exec app -- cat /app/config/application.yaml
# ConfigMap 里 app.yaml 的内容
containers:
- name: app
env:
- name: DB_HOST # 单个 key
valueFrom:
configMapKeyRef:
name: my-config
key: db.host
envFrom: # 批量注入
- configMapRef:
name: my-config
envFrom 把 ConfigMap 所有 key 都变成环境变量。注意 key 名必须合法环境变量名(不能含 . -)。
视角对照
volumes:
- name: cfg
configMap:
name: my-config
| 视角 | 路径 |
|---|---|
| 应用代码 | /app/config/app.yaml |
| 容器 mount ns | /app/config/app.yaml |
| 节点 host | /var/lib/kubelet/pods/⟨uid⟩/volumes/kubernetes.io~configmap/cfg/app.yaml |
| 实际存储 | 节点 tmpfs 文件(在内存里) |
ConfigMap 更新与同步(反直觉)
K8s 的 kubelet 周期性同步 ConfigMap 到 pod。默认 sync 周期 1 分钟:
# 改 ConfigMap
$ kubectl edit configmap my-config
# Pod 里多久能看到?
$ kubectl exec app -- cat /app/config/app.yaml
# 0-60 秒内更新
subPath 时永远不更新
volumeMounts:
- name: cfg
mountPath: /etc/app.yaml
subPath: app.yaml # ← subPath 时不会自动更新
subPath 用 bind mount 单个文件 → ConfigMap 更新时 bind 源是新文件、但 bind 目标没重新指向。
修法:
- 不用 subPath——挂整个目录
- 或者依赖 Helm chart 重启 pod(annotation hash)
详见 §8 subPath 章节。
应用要"感知"配置更新怎么办
K8s 不会通知应用 ConfigMap 改了。三种做法:
- 应用自己 watch 文件(inotify)—— 多数应用不支持
- 应用 SIGHUP reload(nginx / sshd 风格)—— 但 K8s 不会发 SIGHUP
- 改 ConfigMap → rollout restart deployment(最常见)
# 改 ConfigMap 之后
kubectl rollout restart deployment my-app
Helm chart 的高级套路:
spec:
template:
metadata:
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
ConfigMap 改 → annotation 变 → 强制 rolling update。
5. secret —— 配置敏感数据
用法跟 ConfigMap 几乎一样
volumes:
- name: tls
secret:
secretName: my-tls
items:
- key: tls.crt
path: server.crt
- key: tls.key
path: server.key
mode: 0400 # 只 owner 可读
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-creds
key: password
Secret 的"安全感" —— 真相
$ kubectl get secret my-secret -o yaml
apiVersion: v1
kind: Secret
data:
password: cGFzc3dvcmQxMjM= # ← base64 编码!
$ echo cGFzc3dvcmQxMjM= | base64 -d
password123
Secret = base64 编码、不是加密。任何有 secret get 权限的人都能解码。
K8s 提供的"安全感":
| 机制 | 提供 |
|---|---|
| RBAC | 限制谁能 get secret |
| 挂载用 tmpfs | 不落盘磁盘(看下面) |
| etcd 加密 | etcd 里 Secret 加密存储(需要显式开) |
| 绝对没有的 | base64 不是加密 |
etcd 加密 —— 生产必开
# /etc/kubernetes/encryption-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: <32 字节 base64 编码的 key>
- identity: {} # 兜底(明文)
启动时 apiserver --encryption-provider-config=...。
不开 etcd 加密 → 拿到 etcd 备份 = 拿到所有 Secret。K8s 默认不开。
详见 kubeadm.md。
视角对照
volumes:
- name: tls
secret:
secretName: my-tls
| 视角 | 路径 |
|---|---|
| 应用代码 | /etc/tls/server.crt |
| 节点 host | /var/lib/kubelet/pods/⟨uid⟩/volumes/kubernetes.io~secret/tls/server.crt |
| 物理存储 | tmpfs(内存里、不落盘) |
$ findmnt /var/lib/kubelet/pods/⟨uid⟩/volumes/kubernetes.io~secret/tls
TARGET SOURCE FSTYPE OPTIONS
/var/lib/kubelet/.../tls tmpfs tmpfs rw,relatime
K8s 故意把 Secret 放 tmpfs → 节点崩溃 / 重启 = 内存里 secret 没了 = 不会泄漏到盘。
Sealed Secret / External Secret —— 真正的"加密"方案
Sealed Secret:用集群里的 controller 公钥加密后存 git 仓库。只有 controller 能解开成 K8s Secret。
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: my-secret
spec:
encryptedData:
password: AgBxK4... # 加密的数据、可以放公开 git
External Secret:Secret 实际存 Vault / AWS Secrets Manager / GCP Secret Manager,K8s 通过 controller 拉到 cluster 里。
生产推荐其中一种。
6. projected —— 多源合一
把 configMap + secret + downwardAPI + token 投射到同一个目录。
volumes:
- name: all-config
projected:
sources:
- configMap:
name: app-config
items:
- key: app.yaml
path: config/app.yaml
- secret:
name: tls
items:
- key: tls.crt
path: tls/server.crt
- downwardAPI:
items:
- path: meta/labels
fieldRef:
fieldPath: metadata.labels
- serviceAccountToken:
path: token
expirationSeconds: 3600
audience: api
容器里:
$ kubectl exec app -- ls /etc/all-config/
config/
meta/
tls/
token
$ kubectl exec app -- cat /etc/all-config/config/app.yaml
# ConfigMap 内容
现代 K8s 已经默认用 projected token
$ kubectl exec my-pod -- mount | grep serviceaccount
... /var/run/secrets/kubernetes.io/serviceaccount type tmpfs (...) ...
$ kubectl exec my-pod -- ls /var/run/secrets/kubernetes.io/serviceaccount/
ca.crt
namespace
token
K8s 1.20+ 默认 ServiceAccount token 用 projected——含 audience + expiration、自动刷新。
老版(自动挂的 secret-based token)已 deprecated。
7. downwardAPI —— 把 pod 元数据注入容器
让容器访问自己的 pod 信息(label / annotation / 资源 limit 等)。
两种用法
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NS
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: CPU_LIMIT
valueFrom:
resourceFieldRef:
containerName: app
resource: limits.cpu
volumes:
- name: pod-info
downwardAPI:
items:
- path: labels
fieldRef:
fieldPath: metadata.labels # 整个 label map
- path: annotations
fieldRef:
fieldPath: metadata.annotations
- path: cpu-limit
resourceFieldRef:
containerName: app
resource: limits.cpu
可暴露的字段
| 来源 | 可用字段 |
|---|---|
| metadata | name / namespace / uid / labels / annotations |
| spec | nodeName / serviceAccountName |
| status | podIP / podIPs / hostIP |
| resource | limits.cpu / limits.memory / requests.cpu / requests.memory / limits.ephemeral-storage |
典型用例
# 应用日志 / metric 标记 pod 名(用 downwardAPI POD_NAME)
# 应用根据 limit 自动调 GC heap (Java -Xmx 用 CPU_LIMIT)
# 应用知道自己是哪个 ns (POD_NS) 决定行为
8. subPath / subPathExpr —— 最容易踩的坑
没 subPath 的默认行为:挂整个目录
volumeMounts:
- name: cfg
mountPath: /etc/nginx
volumes:
- name: cfg
configMap:
name: nginx-config
/etc/nginx 整个被替换成 ConfigMap 里的内容。
$ kubectl exec nginx -- ls /etc/nginx
nginx.conf # 来自 ConfigMap
fastcgi_params # 来自 ConfigMap
# 但是 /etc/nginx/conf.d/* /modules-enabled/* 都看不见了!被遮盖
典型问题:nginx 镜像里 /etc/nginx/conf.d/default.conf 被 ConfigMap 遮盖、nginx 启动失败找不到。
subPath:只挂 volume 里的一个子路径到一个特定文件
volumeMounts:
- name: cfg
mountPath: /etc/nginx/nginx.conf # 挂成一个文件、不是目录
subPath: nginx.conf # ConfigMap 里的 nginx.conf
volumes:
- name: cfg
configMap:
name: nginx-config
容器内:
$ ls /etc/nginx/
nginx.conf # 来自 ConfigMap(subPath 挂的)
conf.d/ # 原镜像里的、保留
modules-enabled/ # 原镜像里的、保留
fastcgi_params # 原镜像里的
只替换了 nginx.conf、其它原镜像内容保留。
subPath 的多视角
| 视角 | 路径 |
|---|---|
| 应用 | /etc/nginx/nginx.conf |
| 容器 mount | /etc/nginx/nginx.conf 是一个 bind mount |
| 节点 host | /var/lib/kubelet/pods/⟨uid⟩/volume-subpaths/<vol>/<container>/<idx> |
注意第三个——subPath 在节点上的存储位置和直接挂 volume 不一样:
$ ls /var/lib/kubelet/pods/⟨uid⟩/volume-subpaths/
cfg/ # 一个目录
└── nginx/0/ # 容器名/index
└── nginx.conf # ← subPath 挂的源
::: caution subPath 的两大坑
坑 1:subPath 时 ConfigMap 更新不同步
volumeMounts:
- name: cfg
mountPath: /etc/nginx.conf
subPath: nginx.conf
# 改 ConfigMap
kubectl edit configmap my-config
# Pod 内 cat /etc/nginx.conf
# 内容**没变**!
K8s 不会更新 subPath 挂的文件。pod 重启之后才会。
修:
- 不用 subPath,整个目录挂(但要解决"遮盖"问题)
- 或者依赖 deployment 重启(Helm checksum annotation)
坑 2:subPath 必须存在
volumeMounts:
- name: cfg
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf # ConfigMap 里没这个 key
→ pod 启动失败:
Error: failed to start container:
failed to mount: no such file or directory
修:确认 ConfigMap 里 key 存在。
:::
subPathExpr —— 用变量做 subPath
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
volumeMounts:
- name: logs
mountPath: /app/logs
subPathExpr: $(POD_NAME) # ← 用变量
volumes:
- name: logs
hostPath:
path: /var/log/k8s
效果:每个 pod 挂到 /var/log/k8s/⟨pod-name⟩/(不互相覆盖)。
适合 StatefulSet 多副本 + 节点本地日志收集。
9. persistentVolumeClaim —— 持久存储
spec:
containers:
- name: app
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: my-pvc
详见 03-pv-pvc-storageclass.md。这里只看视角:
| 视角 | 路径 |
|---|---|
| 应用 | /data/foo.dat |
| 容器 mount | /data/foo.dat |
| 节点 CSI mount 点 | /var/lib/kubelet/plugins/.../mount/foo.dat |
| 节点 bind 给 pod | /var/lib/kubelet/pods/⟨uid⟩/volumes/kubernetes.io~csi/⟨pv-name⟩/mount/foo.dat |
| 底层 | 云盘 / NFS server / Ceph RBD |
PVC 是两层 bind——CSI 先挂到全局位置、kubelet 再 bind 给 pod。
CSI volume 在节点上的实际位置
$ ls /var/lib/kubelet/plugins/⟨csi-driver⟩/
... global mount 点
$ ls /var/lib/kubelet/pods/⟨pod-uid⟩/volumes/kubernetes.io~csi/
... 每个 PVC 的 bind 点
排查 PV 挂载问题 → 看这两个位置。
Generic Ephemeral Volume(K8s 1.21+)
如果你想要"PV 的能力 + emptyDir 的生命周期":
spec:
volumes:
- name: scratch
ephemeral:
volumeClaimTemplate:
spec:
accessModes: [ReadWriteOnce]
storageClassName: fast-ssd
resources:
requests:
storage: 50Gi
K8s 自动创建一个 PVC(pod 删则删)+ 用 StorageClass 动态供应 PV。
适合"临时大数据处理"——比 emptyDir 大、自动清理、性能可配。
10. 其它 volume 类型(简介)
nfs —— 直接挂 NFS
volumes:
- name: shared
nfs:
server: nfs.example.com
path: /exports/shared
readOnly: false
不经过 PVC、直接挂。生产慎用——节点上 NFS server 挂了所有 pod 卡 D 状态。
详见 04-nfs-deep.md。
iscsi / cephfs / rbd —— 直接挂分布式存储
volumes:
- name: data
iscsi:
targetPortal: 10.0.0.100:3260
iqn: iqn.2024-01.com.example:storage
lun: 0
K8s 1.30+ 这些 in-tree provisioner 已 deprecated → 都改用 CSI driver。
gitRepo —— 已废弃
不要用。用 init container git clone 到 emptyDir 替代。
csi —— 未来
所有外部存储都通过 CSI 接入。详见 03-pv-pvc-storageclass.md。
11. 完整 volume 类型对照表
| Volume | 生命周期 | 多 pod 共享 | 持久 | 物理位置 |
|---|---|---|---|---|
emptyDir | Pod | 同 pod 容器 | ❌ | 节点 / tmpfs |
emptyDir{medium:Memory} | Pod | 同 pod 容器 | ❌ | 节点内存 |
hostPath | 节点 | ✅ | 节点级 | 节点指定路径 |
configMap | Pod | 同 pod | ❌ | tmpfs(节点) |
secret | Pod | 同 pod | ❌ | tmpfs(节点) |
projected | Pod | 同 pod | ❌ | tmpfs(节点) |
downwardAPI | Pod | 同 pod | ❌ | tmpfs(节点) |
persistentVolumeClaim | 独立 | ✅(看 accessMode) | ✅ | CSI / NFS / ... |
ephemeral (Generic) | Pod | 同 pod | ❌(pod 删则删) | CSI dynamic |
nfs (in-tree) | 集群 | ✅ | ✅ | NFS server |
csi (volume) | Pod | 同 pod | 看 CSI | CSI driver |
12. 反面教材合集
反面 1:ConfigMap 当 hot-reload 配置中心
# 改 ConfigMap 期望应用立刻看到
kubectl edit configmap my-config
K8s 同步周期默认 1 分钟、应用不会自动感知。要 hot-reload 用 Vault / Consul / 应用层 watch。
K8s ConfigMap 是 "启动时配置 + 偶尔更新" 的工具、不是 hot-reload 配置中心。
反面 2:把 Secret 当 KMS 用
type: Opaque
data:
api-key: bm90LXNlY3JldA== # base64
base64 不是加密。真敏感用:
- HashiCorp Vault + Vault Agent Injector / External Secrets
- Sealed Secrets(git 里也安全)
- 云 KMS + Secrets Manager(AWS / GCP / Aliyun)
K8s Secret 是"分发凭证给 pod"的方式、不是"安全存储凭证"的方案。
反面 3:用 subPath 挂目录
volumeMounts:
- name: data
mountPath: /data
subPath: subdir/ # ← 期望挂目录
subPath 挂的单个路径(文件或目录)。挂目录"看起来 work"、但和直接挂 volume 区别不大、还有缺点(不自动更新)。
目录就直接挂 volume、文件用 subPath。
反面 4:在 ReadWriteOnce PV 上扩多副本
spec:
replicas: 3
template:
spec:
volumes:
- name: data
persistentVolumeClaim:
claimName: my-pvc # RWO
RWO PVC 只能给一个 node 上一个 pod用。3 副本调度到不同节点 → 第 2/3 副本起不来:
MountVolume failed: volume is already mounted by node X
修:
- 要多副本 + 共享存储 → 用 RWX(NFS / CephFS / EFS)
- 要多副本 + 独立存储 → 用 StatefulSet + volumeClaimTemplate(每个 pod 自己 PVC)
反面 5:emptyDir 没 sizeLimit + 应用写爆
volumes:
- name: cache
emptyDir: {} # 没 sizeLimit
应用日志 / 缓存写满 → 节点根分区满 → 节点 DiskPressure → kubelet 驱逐 pod / 节点 NotReady。
永远加 sizeLimit:
emptyDir:
sizeLimit: 5Gi
K8s 超过 sizeLimit 会驱逐这个 pod(不会让节点撑爆)。
反面 6:hostPath 没 readOnly 暴露宿主
volumes:
- name: rootfs
hostPath:
path: /
containers:
- volumeMounts:
- name: rootfs
mountPath: /host
# 没设 readOnly
容器拿到宿主根目录 + 可写——等于宿主 root。CI / 监控类有时这么做、绝对加 readOnly:
volumeMounts:
- name: rootfs
mountPath: /host
readOnly: true
业务 pod 永远不要挂 /。
反面 7:用 gitRepo volume
volumes:
- name: code
gitRepo:
repository: https://github.com/...
gitRepo 已 deprecated。安全问题 / 不可维护。
替代:init container clone 到 emptyDir:
initContainers:
- name: clone
image: alpine/git
command: ["git", "clone", "https://...", "/code"]
volumeMounts:
- name: code
mountPath: /code
volumes:
- name: code
emptyDir: {}
13. 排查 cheatsheet
"Pod 起不来、ContainerCreating 卡"
$ kubectl describe pod my-pod
Events:
Warning FailedMount ... Unable to attach or mount volumes: unmounted volumes=[data], unattached volumes=[data]: timed out waiting for the condition
逐层排查:
# 1. PVC 状态
$ kubectl get pvc my-pvc
# 应 Bound
# 2. PV 状态
$ kubectl get pv
# 3. 节点上看是否真挂
$ ssh m4 'ls /var/lib/kubelet/pods/⟨uid⟩/volumes/'
$ ssh m4 'findmnt | grep ⟨pod-uid⟩'
# 4. CSI driver 日志
$ kubectl logs -n kube-system ⟨csi-driver-pod⟩
# 5. K8s events 看具体错
$ kubectl get events --sort-by=".lastTimestamp" | tail -20
"configMap 改了 pod 没生效"
# 1. 用了 subPath 吗?
$ kubectl get pod my-pod -o yaml | grep subPath
# 用了 subPath → 不会自动更新
# 2. 没用 subPath、看 sync 周期
# kubelet 默认 1 分钟 sync
# 3. 触发 rolling restart
$ kubectl rollout restart deploy my-deploy
"Secret 看起来变了应用没用上"
# 1. Secret 真改了吗
$ kubectl get secret my-secret -o yaml
# 2. Pod 里看到的是不是新值
$ kubectl exec pod -- cat /etc/secret/token
# 3. 应用是不是缓存了
# (启动时读、改了不感知 → 重启 pod)
14. 下一步
| 篇 | 内容 |
|---|---|
| 00-storage-mental-model.md | 存储心智模型 |
| 01-container-volumes.md | 容器挂载完整指南 |
| 本篇 | K8s Volume 类型大全 |
| 03-pv-pvc-storageclass.md | PV / PVC / CSI 深入(下一篇必读) |
| 04-nfs-deep.md | NFS |
| 05-distributed-storage.md | Ceph / Longhorn |
| 06-storage-troubleshooting.md | 故障排查 runbook |