(十六)卷
由于容器中的文件默认写在容器可写层中,如果容器被删除了,那么这些文件也会随之消失,即使我们使用 kubelet 重启容器,新容器看到的也不一定还是原来的文件
但是应用运行时经常需要缓存文件、共享配置、保存日志,或者让同一个 Pod 中的多个容器交换数据,因此 Kubernetes 提供了 Volume,也就是卷
卷是一个抽象的概念,本质上是一套 把不同存储挂载到容器中的一个机制,也就是说它不具有物理上的概念;它可以来自节点目录、临时目录、ConfigMap、Secret、NFS,也可以来自云硬盘和 CSI 存储系统
卷和容器文件系统
Pod 中可以声明一个或多个卷,然后由容器通过 volumeMounts 决定把卷挂载到哪里
apiVersion: v1
kind: Pod
metadata:
name: volume-demo
spec:
containers:
- name: nginx
image: nginx:latest
volumeMounts:
- name: cache
mountPath: /cache
volumes:
- name: cache
emptyDir: {}
这里需要理解两个字段:
spec.volumes表示 Pod 有哪些卷,以及卷从哪里来- **
spec.containers.volumeMounts表示 卷在容器中的挂载位置
volumeMounts.name 必须与 volumes.name 对应,并且同一个卷可以挂载到 Pod 中的多个容器内,但不能跨 Pod 直接引用
挂载完成后,mountPath 原来能看到的文件会暂时被卷遮住,而不是合并到卷中,删除挂载以后,镜像中的原文件仍然存在
emptyDir 卷
emptyDir 是最简单的临时卷,Pod 被调度到某个节点时,kubelet 会为它创建一个空目录,emptyDir 的生命周期与 Pod 相同
- 在容器崩溃、容器重启时,emptyDir 中的数据仍然存在
- 当 Pod 被删除或重新创建时,原来的 emptyDir 也会被删除
因此当节点故障时,emptyDir 中的数据是无法恢复的,所以这就决定了 emptyDir 适合缓存、临时计算数据,以及在同一个 Pod 中多个容器共享文件,但不适合保存数据库等重要数据
使用文件目录作为 emptyDir
下面的 Pod 中,writer 容器向卷中写入时间,reader 容器读取同一份文件;别忘了 Pod 中可以存在多个容器
cat > emptydir.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: emptydir-demo
spec:
containers:
- name: writer
image: busybox:1.36
command: ["sh", "-c"]
args:
- while true; do date >> /data/time.txt; sleep 5; done
volumeMounts:
- name: shared-data
mountPath: /data
- name: reader
image: busybox:1.36
command: ["sh", "-c"]
args:
- tail -f /data/time.txt
volumeMounts:
- name: shared-data
mountPath: /data
volumes:
- name: shared-data
emptyDir: {}
EOF
kubectl apply -f emptydir.yaml
kubectl logs emptydir-demo -c reader
kubectl delete pod emptydir-demo
从输出可以看,两个容器各自独立的容器文件系统,但是通过共享一个卷,实现了 writer 写文件、reader 读文件

使用内存作为 emptyDir
如果把 medium 设置为 Memory,emptyDir 就会使用 tmpfs,此时数据主要保存在内存中
volumes:
- name: cache
emptyDir:
medium: Memory
sizeLimit: 128Mi
内存卷速度很快,适合保存少量临时数据;写入其中的数据会计入内存使用量,使用不当可能触发内存不足,因此最好设置 sizeLimit,并为容器配置合理的内存限制
hostPath 卷
hostPath 可以把节点上的文件或目录直接挂载到 Pod 中,效果与 Docker 的 bind mount
apiVersion: v1
kind: Pod
metadata:
name: hostpath-demo
spec:
containers:
- name: nginx
image: nginx:latest
volumeMounts:
- name: node-data
mountPath: /data
readOnly: true
volumes:
- name: node-data
hostPath:
path: /data/demo
type: Directory
hostPath 依赖 Pod 所在节点:同样的 /data/demo,在不同节点上是不同的目录。如果 Pod 被重新调度到另一个节点,它看到的数据也会改变
常见的 type 有:
- Directory:目录必须已经存在
- DirectoryOrCreate:目录不存在时由 kubelet 创建
- File:文件必须已经存在
- FileOrCreate:文件不存在时创建空文件,但不会自动创建父目录
- Socket:必须是 UNIX Socket
hostPath 能让容器接触节点文件系统、容器运行时 Socket 或 kubelet 凭据,具有较高的安全风险;普通业务 Pod 应尽量避免使用,确实需要时应限制路径并优先只读挂载
hostPath 比较常见于需要读取节点日志、设备或系统信息的 DaemonSet,而不是普通 Web 应用的数据存储
使用 subPath
默认情况下,volumeMounts 会把整个卷挂载到 mountPath。如果只想挂载卷中的某个子目录或文件,可以使用 subPath
volumeMounts:
- name: app-data
mountPath: /var/lib/app
subPath: data
这表示把 app-data 卷中的 data 子目录挂载到 /var/lib/app
需要注意,ConfigMap 或 Secret 使用 subPath 挂载时,后续更新不会自动同步到容器中;这部分会在下一章继续学习
获取 Git 仓库中的文件
早期 Kubernetes 曾经提供 gitRepo 卷,但它已经被弃用,并在新的 Kubernetes 版本中移除,所以下面这种配置
# 已经过时,不要使用
volumes:
- name: source
gitRepo:
repository: https://example.com/demo.git
现在可以使用 init Container 把代码克隆到 emptyDir,再由业务容器读取
apiVersion: v1
kind: Pod
metadata:
name: git-source-demo
spec:
initContainers:
- name: clone
image: alpine/git:latest
args:
- clone
- --depth=1
- https://github.com/kubernetes/website.git
- /work/repo
volumeMounts:
- name: source
mountPath: /work
containers:
- name: reader
image: busybox:1.36
command: ["sh", "-c"]
args: ["ls -lah /source && sleep 3600"]
volumeMounts:
- name: source
mountPath: /source
subPath: repo
volumes:
- name: source
emptyDir: {}
init Container 必须成功完成,业务容器才会启动;这种方式只会在 Pod 创建时克隆一次,如果仓库发生变化,Pod 中的内容不会自动更新
临时卷和持久卷
emptyDir 与 hostPath 都没有解决应用在不同节点之间长期保存数据的问题
emptyDir
-> 跟随 Pod 生命周期
-> Pod 删除后数据消失
hostPath
-> 跟随节点
-> Pod 换到其他节点后数据不同
持久存储
-> 独立于 Pod
-> 可以通过 PVC 交给 Pod 使用
对于数据库、用户上传文件等重要数据,通常需要 PersistentVolume、PersistentVolumeClaim、StorageClass 和 CSI 存储;后面的章节会继续学习
总结
Kubernetes 的卷由 volumes 定义来源,由 volumeMounts 决定容器中的挂载位置
emptyDir 适合 Pod 内的临时共享数据,hostPath 适合少量确实需要访问节点文件的系统组件;它们都不能代替真正的持久存储
早期的 gitRepo 卷已经过时,需要改用 init Container 加 emptyDir;使用 hostPath 时,也应首先考虑节点依赖和安全风险
这一章说实话,我没怎么理解到位,因为生成的资料没有逻辑感,而我又是头一次看,所以文章质量不怎么高
愿诸君顺遂