1. 简介
Kubernetes 作为主流的容器编排平台,提供了多种机制来突破容器本身临时性的限制。我们可以通过创建持久化存储卷,使得 Pod 和服务在重启后依然能保留数据,从而实现一致的行为和架构冗余。
然而,Kubernetes 原生对多个微服务共享同一个中心卷的支持并不完全。本文将探讨在 Kubernetes 集群中实现 Pod 间存储共享的几种方式:
- 回顾数据共享与微服务架构的基本理念
- 理解 Kubernetes 卷的类型与访问模式
- 通过示例演示无法跨 Pod 共享的卷(如
emptyDir
) - 展示可以跨 Pod 共享的卷类型(如
hostPath
) - 探讨基于 Linux 标准的卷(如 NFS)实现跨 Pod、跨节点共享
本文中的代码示例均在 Debian 12(Bookworm)环境下使用 GNU Bash 5.2.15 测试通过。除非特别说明,适用于大多数 POSIX 兼容环境。
2. 数据共享与架构设计
在微服务架构中,虽然技术上可以实现多个服务共享数据,但是否应该这么做是一个需要权衡的问题。
2.1 数据视角一致性
类似面向对象编程中通过接口封装数据的做法,微服务也应尽量封装自己的数据,并通过特定的读写接口来暴露数据。
这样做的好处是:
✅ 保证数据完整性
✅ 统一消费者视角
✅ 易于数据结构变更后的兼容处理
当多个服务直接读写同一份原始数据时,如果数据结构发生变化,每个服务都必须同步适配,否则可能导致错误。
2.2 原始数据共享的必要性
当然,某些场景下共享原始文件系统路径是不可避免甚至更优的选择:
✅ 日志聚合
✅ 临时缓存目录
✅ 多 Pod 协作任务
这时我们可以借助 Kubernetes 提供的卷机制,或底层操作系统支持的共享文件系统来实现。
3. Kubernetes 卷类型与访问模式
Kubernetes 支持多种卷类型,大致可分为:
- 临时卷(Ephemeral):生命周期与 Pod 一致,如
emptyDir
- 持久卷(Persistent):独立于 Pod 生命周期,如
hostPath
,NFS
,CephFS
等
3.1 卷访问模式
卷支持的访问模式决定了它可以被如何使用:
模式 | 说明 |
---|---|
ReadWriteOnce | 单节点读写 |
ReadOnlyMany | 多节点只读 |
ReadWriteMany | 多节点读写 ✅(适合共享) |
ReadWriteOncePod | 单 Pod 读写(Kubernetes 1.22+) |
3.2 主流持久卷支持情况
Volume Type | RWO | ROX | RWX | RWOP |
---|---|---|---|---|
AzureFile | ✅ | ✅ | ✅ | ❌ |
CephFS | ✅ | ✅ | ✅ | ❌ |
FC | ✅ | ✅ | ❌ | ❌ |
FlexVolume | ✅ | ✅ | ⚠️(依赖驱动) | ❌ |
HostPath | ✅ | ❌ | ❌ | ❌ |
iSCSI | ✅ | ✅ | ❌ | ❌ |
NFS | ✅ | ✅ | ✅ | ❌ |
RBD | ✅ | ✅ | ❌ | ❌ |
VsphereVolume | ✅ | ❌ | ❌ | ❌ |
PortworxVolume | ✅ | ❌ | ✅ | ❌ |
⚠️ 注意:一个卷挂载时只能使用一种访问模式。
✅ 推荐:NFS 是最常用且支持 RWX 的卷类型之一。
4. Kubernetes emptyDir
卷示例
emptyDir
是一种生命周期与 Pod 一致的临时卷,适用于 Pod 内部容器间共享数据。
创建 Pod 并挂载 emptyDir
卷
$ kubectl run -i pod0 --image=debian:latest --overrides='{
"apiVersion": "v1",
"spec": {
"containers": [
{
"name": "pod0",
"image": "debian:latest",
"command": ["bash"],
"stdin": true,
"stdinOnce": true,
"tty": true,
"volumeMounts": [
{
"mountPath": "/home/emptydir-vol0-attach-path",
"name": "emptydir-vol0"
}
]
}
],
"volumes": [
{
"name": "emptydir-vol0",
"emptyDir": {}
}
]
}
}'
emptyDir
卷特性
- 在 Pod 被调度到节点后创建
- 同一 Pod 内多个容器共享
- 容器重启数据保留
- Pod 被删除时卷一并销毁
⚠️ 缺点:不能跨 Pod 共享数据,即使两个 Pod 使用相同的 emptyDir
配置,它们的卷也是独立的。
5. Kubernetes hostPath
卷示例
hostPath
卷将节点本地路径挂载到 Pod 中,适用于单节点内共享数据。
创建 Pod 并挂载 hostPath
卷
$ kubectl run -i --rm pod0 --image=debian:latest --overrides='{
"apiVersion": "v1",
"spec": {
"containers": [
{
"name": "pod0",
"image": "debian:latest",
"command": ["bash"],
"stdin": true,
"stdinOnce": true,
"tty": true,
"volumeMounts": [
{
"mountPath": "/home/hostpath-vol0-attach-path",
"name": "hostpath-vol0"
}
]
}
],
"volumes": [
{
"name": "hostpath-vol0",
"hostPath": {
"path": "/mnt/hostpath-vol0-source-path",
"type": "DirectoryOrCreate"
}
}
]
}
}'
特性总结
- Pod 与节点共享路径
- 可在多个 Pod 中使用相同路径实现共享
- 只能在本地节点使用,不支持跨节点共享
- 存在安全风险,不推荐用于生产环境
示例:跨 Pod 共享
$ for podname in pod0 pod1; do
kubectl run $podname --image=debian:latest --overrides='{
"apiVersion": "v1",
"spec": {
"containers": [
{
"name": "'$podname'",
"image": "debian:latest",
"command": ["bash"],
"stdin": true,
"stdinOnce": true,
"tty": true,
"volumeMounts": [
{
"mountPath": "/home/hostpath-vol0-attach-path",
"name": "hostpath-vol0"
},
{
"mountPath": "/home/emptydir-vol0-attach-path",
"name": "emptydir-vol0"
}
]
}
],
"volumes": [
{
"name": "hostpath-vol0",
"hostPath": {
"path": "/mnt/hostpath-vol0-source-path",
"type": "DirectoryOrCreate"
}
},
{
"name": "emptydir-vol0",
"emptyDir": {}
}
]
}
}'
done
测试共享效果
$ echo 666 > /mnt/hostpath-vol0-source-path/hostfile
$ kubectl exec -it pod0 -- bash -c 'echo 667 > /home/hostpath-vol0-attach-path/podfile'
$ for podname in pod0 pod1; do
kubectl exec -it $podname -- bash -c 'cat /home/hostpath-vol0-attach-path/hostfile; cat /home/hostpath-vol0-attach-path/podfile'
done
输出结果:
666
667
666
667
✅ 成功实现跨 Pod 共享
❌ emptyDir
卷则无法共享,验证如下:
$ kubectl exec -it pod0 -- bash -c 'echo 668 > /home/emptydir-vol0-attach-path/pod0file'
$ kubectl exec -it pod1 -- bash -c 'cat /home/emptydir-vol0-attach-path/pod0file'
输出:
cat: /home/emptydir-vol0-attach-path/pod0file: No such file or directory
command terminated with exit code 1
6. Kubernetes 与 NFS 卷共享
如果需要实现跨节点、跨 Pod 的数据共享,推荐使用 NFS(Network File System),它是一个标准的 Linux 文件系统协议。
优势
✅ 支持多节点读写(RWX)
✅ 稳定、成熟
✅ 可集成外部存储服务
示例:挂载 NFS 卷
$ kubectl run pod0 --image=debian:latest --overrides='{
"apiVersion": "v1",
"spec": {
"containers": [
{
"name": "pod0",
"image": "debian:latest",
"command": ["bash"],
"stdin": true,
"stdinOnce": true,
"tty": true,
"volumeMounts": [
{
"mountPath": "/home/nfs-vol0-attach-path",
"name": "nfs-vol0"
}
]
}
],
"volumes": [
{
"name": "nfs-vol0",
"nfs": {
"server": "192.168.6.60",
"path": "/mnt/nfs/"
}
}
]
}
}'
测试 NFS 共享
在 NFS 服务器上写入数据:
$ echo 666 > /mnt/nfs/file
在 Pod 中查看:
$ kubectl exec -it pod0 -- cat /home/nfs-vol0-attach-path/file
666
✅ 成功读取共享数据
7. 总结
本文介绍了 Kubernetes 中实现 Pod 间数据共享的几种方式:
类型 | 是否跨 Pod | 是否跨节点 | 是否支持 RWX |
---|---|---|---|
emptyDir | ❌ | ❌ | ❌ |
hostPath | ✅(本地) | ❌ | ❌ |
NFS | ✅ | ✅ | ✅ |
✅ 推荐:若需跨节点共享数据,首选 NFS 卷。
⚠️ 警告:hostPath
虽然简单易用,但存在安全风险且不支持跨节点共享,不建议在生产环境中使用。
💡 小贴士:卷访问模式只能选择一种,挂载时务必确认使用正确的模式。