glusterfs如何在k8s中使用

bash 复制代码
[root@paas-controller-1:~]$ kubectl get sc -o yaml
apiVersion: v1
items:
- allowVolumeExpansion: true
  apiVersion: storage.k8s.io/v1
  kind: StorageClass
  metadata:
    annotations:
      createdBy: storageManager
      kubectl.kubernetes.io/last-applied-configuration: |
        {"allowVolumeExpansion":true,"apiVersion":"storage.k8s.io/v1","kind":"StorageClass","metadata":{"annotations":{"createdBy":"storageManager","storageClassType":"FileStorage"},"name":"gluster-csi-sc"},"parameters":{"replica":"2"},"provisioner":"fs.csi.gluster"}
      storageClassType: FileStorage
    creationTimestamp: "2026-04-15T03:59:47Z"
    name: gluster-csi-sc
    resourceVersion: "177163"
    uid: 5e31a968-7f99-4ff4-a5ac-4b544f4ef834
  parameters:
    replica: "2"
  provisioner: fs.csi.gluster
  reclaimPolicy: Delete
  volumeBindingMode: Immediate
kind: List
metadata:
  resourceVersion: ""

您提供的 kubectl get sc -o yaml 输出揭示了您当前 Kubernetes 集群中已有的存储生态,这与我们刚才讨论的 NFS 方案是完全不同的技术路径。

结论先行:

不需要 再手动配置 NFS 了。您的集群已经预置了 GlusterFS(也就是您之前一直在排查进程的那个服务)作为后端存储,并且配置好了 CSI 插件。

1. 如何在 K8s 中使用现有的 GlusterFS?

您不需要关心底层的 IP 或挂载路径,只需要通过 StorageClass 来申请存储,GlusterFS 的 CSI 插件会自动帮您在后端创建卷并挂载。

  • StorageClass 名称gluster-csi-sc
  • 创建方式:动态供应 (Dynamic Provisioning)
  • 数据副本数 :2 (由参数 replica: "2" 决定,保证了高可用)

操作步骤:

您只需创建 PVC,K8s 和 GlusterFS 就会自动完成剩下的工作:

yaml 复制代码
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: gluster-pvc
spec:
  storageClassName: gluster-csi-sc  # 指定使用 GlusterFS 的存储类
  accessModes:
    - ReadWriteMany                 # GlusterFS 支持多节点同时读写
  resources:
    requests:
      storage: 10Gi                # 申请 10G 空间
---
apiVersion: v1
kind: Pod
metadata:
  name: gluster-pod
spec:
  containers:
    - name: app
      image: nginx
      volumeMounts:
        - name: gluster-storage
          mountPath: /usr/share/nginx/html
  volumes:
    - name: gluster-storage
      persistentVolumeClaim:
        claimName: gluster-pvc

2. 关于您提供的网页解析内容

这两个 URL 是 Kubernetes 内部的 API 组标识,出现在 YAML 的 apiVersion 和注解中,您无需手动访问这些网址。

  • storage.k8s.io/v1
    • 含义 :这是 Kubernetes 存储相关资源(如 StorageClassVolumeAttachment)的 API 组和版本。
    • 出处 :您输出的 apiVersion: storage.k8s.io/v1
  • kubectl.kubernetes.io/last-applied-configuration
    • 含义 :这是一个注解(Annotation),Kubectl 用它来记录上一次创建或修改该资源时使用的完整配置(JSON 格式)。用于在执行 kubectl apply 时计算差异。
    • 出处 :您输出的 metadata.annotations 下方的长串 JSON。

3. 总结与建议

  • 不要重复造轮子:您已经有了 GlusterFS,它比 NFS 更适合容器云环境(去中心化、高可用、原生集成)。
  • 使用方式 :在部署应用(Deployment/StatefulSet)时,将 spec.template.spec.volumes 指向一个 PVC,且 PVC 的 storageClassName 设置为 gluster-csi-sc 即可。

这个sc(StorageClass)是如何创建的,要先安装provisioner: fs.csi.gluster?

是的,您的理解非常准确。在 Kubernetes 中,必须先安装对应的 Provisioner(存储制备器),然后才能创建使用该 Provisioner 的 StorageClass

结合您当前集群中 provisioner: fs.csi.gluster 的配置,以下是具体的创建逻辑和步骤:

1. 核心逻辑:先有 Provisioner,后有 StorageClass

StorageClass 本质上是一个"配置模板",它本身不具备创建存储的能力。它必须依赖集群中已经运行的 Provisioner 组件(通常是一个以 Pod 形式运行的控制器)来真正与底层存储(GlusterFS)进行通信并创建卷。

2. 如何安装 fs.csi.gluster Provisioner?

fs.csi.gluster 是一个基于 CSI(Container Storage Interface)标准的 GlusterFS 驱动。安装它通常有以下几种方式:

  • 通过 Helm 部署(推荐):GlusterFS 官方或相关社区通常提供 Helm Chart,可以通过一条命令一键部署 Provisioner 及其所需的 DaemonSet、RBAC 权限等组件。
  • 通过 YAML 清单部署 :手动 kubectl apply 一系列 YAML 文件,包括 Provisioner 的 Deployment、Node 插件、ServiceAccount 和 ClusterRole 等。
  • 通过 PaaS 平台自动化部署 :从您之前提供的 YAML 注解中可以看到 "createdBy": "storageManager""storageClassType": "FileStorage"。这强烈暗示您当前使用的是一个企业级 PaaS 平台(如 KubeSphere、Rancher 或自研平台)。在这个平台中,您很可能只是在 UI 界面上勾选了"启用 GlusterFS 存储",平台的后台控制器(storageManager)就自动为您完成了 Provisioner 的安装和 StorageClass 的创建。

3. 如何验证 Provisioner 是否已经安装并正常运行?

既然您的集群中已经存在这个 StorageClass,说明 Provisioner 大概率已经部署了。您可以通过以下命令验证:

  1. 检查 Provisioner Pod 是否运行

    bash 复制代码
    kubectl get pods -A | grep gluster

    您应该能看到类似 glusterfs-csi-provisioner-xxxglusterfs-csi-node-xxx 的 Pod 处于 Running 状态。

  2. 检查 CSI Driver 是否注册

    bash 复制代码
    kubectl get csidrivers

    输出列表中应该包含 fs.csi.gluster

4. 总结

如果您是在一个全新的集群中从零开始配置,您需要先查阅 GlusterFS CSI 的官方文档部署 Provisioner,然后再编写 YAML 创建 StorageClass。但鉴于您当前环境的特殊性(由 storageManager 自动创建),您无需手动安装 Provisioner ,直接使用这个现成的 gluster-csi-sc 来创建 PVC 即可。

fs.csi.gluster 是一个基于 CSI(Container Storage Interface)标准的 GlusterFS 驱动。 ---CSI标准是啥,具体有什么标准接口?

在 Kubernetes 存储生态中,CSI(Container Storage Interface,容器存储接口) 是一个极其重要的行业标准。它旨在将容器编排系统(如 Kubernetes)与底层存储系统解耦。

在 CSI 出现之前,Kubernetes 的存储驱动代码是硬编码在 Kubernetes 核心代码库中的(即"树内"插件)。这导致每次添加新的存储系统或修复存储 Bug,都需要修改 Kubernetes 核心代码并重新发版。CSI 的出现彻底改变了这一现状,它定义了一套标准的 gRPC 接口规范,使得存储提供商可以独立开发、部署和维护存储插件,而无需修改 Kubernetes 的核心代码。

CSI 的核心标准接口

CSI 规范将存储插件的职责划分为三个核心组件,每个组件负责暴露一组特定的 gRPC 服务接口:

1. Controller Service(控制器服务)

该服务通常作为一个独立的 Pod(Controller Plugin)运行,负责处理集群级别的存储卷管理操作。它不关心具体的节点,主要包含以下核心接口:

  • CreateVolume / DeleteVolume:在底层存储系统中创建或删除一个存储卷。
  • ControllerPublishVolume / ControllerUnpublishVolume:将卷挂载到特定节点,或从节点卸载(例如,将云盘附加到某台虚拟机)。
  • ValidateVolumeCapabilities:验证某个卷是否支持请求的访问模式(如 ReadWriteMany)。
  • ListVolumes / GetCapacity:列出当前存在的卷或查询底层存储的剩余容量。
2. Node Service(节点服务)

该服务作为一个 DaemonSet 运行在集群的每一个节点上,负责处理与具体节点相关的卷操作。主要接口包括:

  • NodeStageVolume / NodeUnstageVolume:在节点级别对卷进行全局格式化或挂载(例如,将云盘挂载到节点的全局路径并格式化文件系统)。
  • NodePublishVolume / NodeUnpublishVolume:将全局挂载点绑定(Bind Mount)到具体的容器 Pod 目录中。
  • NodeGetInfo:返回当前节点的唯一标识符(NodeID),供 Controller 识别。
3. Identity Service(身份服务)

这是所有 CSI 插件必须实现的基础服务,用于向 Kubernetes 声明自身的能力。主要接口包括:

  • GetPluginInfo :返回插件的名称和版本号(例如 fs.csi.gluster)。
  • GetPluginCapabilities:声明该插件支持哪些高级功能(例如是否支持动态制备、是否支持卷扩容等)。
  • Probe:供 Kubernetes 进行健康检查,确认插件是否处于正常工作状态。

结合您的 GlusterFS 场景

回到您集群中的 fs.csi.gluster,它就是一个严格遵循上述 CSI 标准开发的插件。当您创建一个 PVC 并指定 storageClassName: gluster-csi-sc 时,Kubernetes 会通过 gRPC 调用该插件的 CreateVolume 接口;当 Pod 被调度到某个节点时,Kubernetes 会调用该插件的 NodeStageVolumeNodePublishVolume 接口,最终将 GlusterFS 的分布式卷无缝挂载到您的容器内部。

这两个步骤详细解释一下

NodeStageVolume / NodeUnstageVolume:在节点级别对卷进行全局格式化或挂载(例如,将云盘挂载到节点的全局路径并格式化文件系统)。

NodePublishVolume / NodeUnpublishVolume:将全局挂载点绑定(Bind Mount)到具体的容器 Pod 目录中。

在 Kubernetes 的 CSI(容器存储接口)架构中,将存储卷挂载到 Pod 并非一步到位,而是被严谨地拆分为了**"全局准备(Stage)""挂载到容器(Publish)"**两个阶段。这种设计主要是为了解决块设备在 Linux 下只能挂载一次,但同一个卷可能被多个 Pod 共享的问题。

以下是这两个步骤的详细原理解析:

1. NodeStageVolume / NodeUnstageVolume(节点级全局准备)

这个阶段发生在存储卷被附加(Attach)到节点之后,但在挂载给具体 Pod 之前。它的主要职责是在宿主机(Node)级别对存储卷进行初始化和全局挂载。

  • 核心操作
    • 格式化与准备:如果这是一个全新的块存储设备,CSI 插件会在此阶段对其进行文件系统格式化(如 ext4、xfs)。
    • 全局挂载 :将格式化后的块设备挂载到宿主机上的一个临时全局目录(Global Path / Staging Path)。
  • 为什么需要这一步?
    因为底层块设备在 Linux 系统中通常只能被挂载一次。通过 NodeStageVolume 将其挂载到全局目录,相当于在宿主机上建立了一个统一的"数据中转站"。
  • 反向操作(NodeUnstageVolume):当节点上所有使用该卷的 Pod 都被销毁,且不再需要该卷时,CSI 会将该卷从这个全局目录中卸载,并清理相关的文件系统状态。

2. NodePublishVolume / NodeUnpublishVolume(容器级挂载)

这个阶段由 Kubelet 在 Pod 启动时触发,它的职责是将上一步准备好的全局数据,安全地映射到具体的容器(Pod)目录中。

  • 核心操作
    • Bind Mount(绑定挂载):CSI 插件利用 Linux 的 Bind Mount 技术,将上一步生成的"全局目录"直接映射到 Pod 内部指定的目标路径(Target Path)。
    • 权限与隔离 :在此阶段,还可以设置容器级别的挂载选项(如只读 ro、SELinux 标签等),确保不同 Pod 之间的数据访问权限相互隔离。
  • 为什么需要这一步?
    由于多个 Pod 可以共享同一个存储卷,通过 Bind Mount,Kubernetes 可以安全地将同一个全局目录映射给同一节点上的多个 Pod,而无需重复挂载底层块设备。
  • 反向操作(NodeUnpublishVolume):当 Pod 被删除或迁移时,CSI 会将该卷从 Pod 的目标目录中卸载,断开容器与存储的连接。

💡 总结:两步走的生命周期

您可以将这个过程理解为**"先修路,再开门"**:

  1. NodeStageVolume:把远端存储挂载到宿主机的全局目录,相当于"修好了一条通往数据的高速公路"。
  2. NodePublishVolume:把全局目录 Bind Mount 到 Pod 内部,相当于"为每个需要数据的房间(Pod)开了一扇专属的门"。

当删除数据时,流程则完全相反:先关门(NodeUnpublishVolume),等所有房间都不需要时,再拆除高速公路(NodeUnstageVolume)。

这里说的存储卷,全局目录, 指的是pv吗? 有10个pv,就相当于有10个存储卷,10个全局目录?

您的理解非常敏锐,但这里有一个非常关键的概念需要澄清:

"存储卷"和"全局目录"指的是底层的物理/逻辑存储实体(即 PV 背后的真实存储),而不是 PV 这个 K8s 对象本身。

我们可以这样梳理它们之间的关系:

1. PV 是什么?

PV(PersistentVolume)只是 Kubernetes 集群中的一个**"抽象对象"**(一个 API 资源)。它就像一张"提货券"或"说明书",记录了底层存储的容量、访问模式以及连接信息(比如 NFS 的 IP 和路径,或者云盘的 ID)。PV 本身并不存储数据,它只是指向了真正的存储。

2. 存储卷是什么?

"存储卷"指的是 PV 背后对应的真实物理或逻辑存储空间。比如一块真实的云硬盘、一个 NFS 共享目录,或者一个 GlusterFS 的分布式卷。

3. 全局目录是什么?

"全局目录"(Global Path / Staging Path)是宿主机(Node)上的一个本地临时文件夹。当 K8s 将底层的"存储卷"挂载到某台具体的机器上时,就会在这个机器上创建一个全局目录作为"中转站"。


回答您的疑问:有 10 个 PV,就相当于有 10 个存储卷,10 个全局目录吗?

答案是:有 10 个 PV,就一定有 10 个真实的"存储卷",但"全局目录"的数量取决于这些 PV 是否被调度到了同一台机器上。

具体场景如下:

  • 关于存储卷(10个 PV = 10 个存储卷)

    是的。如果您创建了 10 个 PV,就意味着 K8s 对接了 10 个真实的底层存储空间(例如 10 个独立的 GlusterFS 卷,或 10 块独立的云盘)。

  • 关于全局目录(数量不固定)

    全局目录是**按节点(Node)**生成的,而不是按 PV 生成的。

    • 情况 A :如果这 10 个 PV 对应的 Pod 被调度到了同一台 宿主机上,那么这台宿主机上就会生成 10 个全局目录。
    • 情况 B :如果这 10 个 Pod 被均匀调度到了 2 台 宿主机上,那么每台宿主机上只会生成 5 个全局目录。
    • 情况 C :如果某个 PV 还没有被任何 Pod 使用(即 PVC 还没绑定,或者 Pod 还没调度),那么它就不会产生全局目录。

💡 形象的比喻

  • PV:相当于您在京东上买的"10 个快递的物流单号"。
  • 存储卷:相当于仓库里真实的"10 个包裹"。
  • 全局目录 :相当于您小区楼下的"10 个快递暂存柜"。
    • 如果您买的 10 个包裹都被送到了同一个小区,那这个小区就会有 10 个暂存柜。
    • 如果快递员把包裹分别送到了您家和您父母家,那这两个地方会各自产生暂存柜。
  • Pod 挂载(NodePublishVolume):相当于您从楼下的暂存柜里,把包裹拿回自己家里的具体房间。

Linux 的 Bind Mount 技术 是什么,详细说说

Linux 的 Bind Mount(绑定挂载)是 Linux 虚拟文件系统(VFS)层提供的一种强大且轻量级的功能。它允许将一个已存在的目录或文件"镜像"或"重定向"到另一个路径。

结合我们之前讨论的 Kubernetes 和容器技术,Bind Mount 正是实现 NodePublishVolume(将全局存储挂载到具体容器)的底层核心技术。

以下是关于 Bind Mount 的详细解析:

1. 核心原理:不是复制,而是"视图"

Bind Mount 的本质是在内核级别创建一个新的"视图"。当执行绑定挂载时,Linux 内核并不会复制数据,也不会创建新的文件系统,而是让目标路径和源路径指向同一个底层文件系统对象(inode)

  • 双向实时同步:因为两者指向同一份物理数据,对源路径或目标路径的任何读写、修改、删除操作,都会立即在另一侧生效。
  • 对应用完全透明:与符号链接(Symlink)不同,Bind Mount 发生在文件系统级别。应用程序无法区分自己访问的是原始目录还是绑定目录,它看起来就像是真实存在于该路径下。

2. 基本用法

Bind Mount 通常通过 mount 命令来实现,需要 root 权限:

bash 复制代码
# 1. 确保源目录和目标目录都已存在
mkdir -p /data/source
mkdir -p /mnt/target

# 2. 执行绑定挂载
sudo mount --bind /data/source /mnt/target

执行后,访问 /mnt/target 就等同于访问 /data/source

3. Bind Mount 与其他机制的区别

  • 对比符号链接(Symlink) :Symlink 只是一个包含目标路径的快捷方式文件,应用可以通过 readlink 等命令感知到它的存在;而 Bind Mount 是内核级的重定向,对应用完全隐藏了映射关系。
  • 对比硬链接(Hardlink):硬链接只能用于单个文件,且不能跨文件系统;Bind Mount 支持整个目录树,并且可以跨越不同的文件系统(只要源文件系统已挂载)。
  • 对比普通挂载(Mount) :普通挂载需要将一个设备(如 /dev/sda1)或虚拟文件系统(如 tmpfs)关联到目录;Bind Mount 则是纯粹的"目录到目录"或"文件到文件"的映射,不涉及新设备。

4. 高级特性与进阶用法

  • 只读绑定(Read-Only Bind) :可以将源目录以只读方式映射到目标路径,防止应用意外修改源数据。

    bash 复制代码
    sudo mount --bind -o ro /data/source /mnt/target
  • 递归绑定(Recursive Bind) :如果源目录内部还嵌套了其他的挂载点(如 tmpfs 或其他 bind),普通的 --bind 不会映射这些子挂载点。需要使用 --rbind 才能完整镜像整个目录树。

  • 持久化配置 :通过 mount --bind 创建的映射在系统重启后会失效。若要永久生效,需将其写入 /etc/fstab 文件:

    text 复制代码
    /data/source  /mnt/target  none  bind  0  0

5. 在容器与 K8s 中的关键作用

Bind Mount 是现代容器化技术的基石:

  • Docker 数据持久化:Docker 允许将宿主机的目录通过 Bind Mount 挂载到容器内部,实现数据的持久化和宿主机与容器的通信。
  • Kubernetes 的 NodePublishVolume :正如我们之前探讨的,K8s 的 CSI 插件在执行 NodePublishVolume 时,正是利用了 Linux 的 Bind Mount 技术,将节点上的"全局存储目录"无缝映射到了具体的 Pod 容器内部。

⚠️ 注意事项

  • 卸载必须使用 umount :绝不能使用 rm -rf 去删除目标目录,否则会直接删除源目录下的真实数据。必须先使用 sudo umount /mnt/target 解除绑定。
  • 隐藏原有内容 :如果目标目录 /mnt/target 原本就有文件,绑定挂载后这些文件会被"隐藏"(不可见),直到卸载绑定后才会重新显示。
相关推荐
暮云星影1 小时前
个人总结 搭建Docker监控
docker·容器·grafana·prometheus
IT策士3 小时前
第49篇 k8s之服务网格入门:Istio 简介
容器·kubernetes·istio
张忠琳5 小时前
【client-go v0.36.1】LeaderElection 深度分析(上篇)— 模块定位、结构、LeaderElector 核心逻辑
云原生·kubernetes·client-go·leaderelection
牛奶咖啡136 小时前
k8s容器编排技术实践——K8s中服务发现ingress、ingress controller 应用实践
kubernetes·服务发现·ingress·ingress-nginx·部署ingress-nginx·部署ingress的三种方法·ingress的服务发现原理
l1t6 小时前
DeepSeek总结的使用 Docker 对 PostgreSQL 进行 Beta 测试
docker·postgresql·容器
张忠琳8 小时前
【client-go v0.36.1】tools/cache 深度分析(中篇)— 辅助组件逐行解析
云原生·kubernetes·cache·informer·client-go
暮云星影12 小时前
个人总结 docker 常用命令
docker·容器
张忠琳12 小时前
【client-go v0.36.1】WorkQueue 深度分析(下篇)— 限流队列、限流器、指标、并行化
云原生·kubernetes·informer·workqueue·client-go
极客先躯13 小时前
高级java每日一道面试题-2026年01月19日-实战篇[Docker]-如何配置镜像仓库的垃圾回收 (GC)?
java·运维·docker·容器