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 虽然简单易用,但存在安全风险且不支持跨节点共享,不建议在生产环境中使用。
💡 小贴士:卷访问模式只能选择一种,挂载时务必确认使用正确的模式。


原始标题:Kubernetes Pods Storage and Volume Sharing