Skip to content

Kubernetes 使用 GlusterFS 做持久化存储

GlusterFS 是一个开源的分布式文件系统,提供高性能和高可用性的集群存储解决方案。在 Kubernetes 环境中,GlusterFS 可以通过三种方式使用:Native Client(节点手动挂载)、Heketi(动态管理)、CSI Driver(原生集成)。本文详细介绍三种方案的架构和部署配置。

架构介绍

GlusterFS 概述

GlusterFS 是一个开源的软件定义存储系统,将多个存储服务器整合成单一的分布式文件系统。其核心特点是:去中心化架构,无单点故障;支持横向扩展,最大可扩展到数 PB 容量;提供弹性卷(Elastic Volume),可根据需求动态调整;支持多种访问协议(POSIX、NFS、CIFS)。

三种挂载方式对比

挂载方式是否需要节点手动挂载动态卷创建维护复杂度推荐场景
Native Client需要(所有节点)固定存储
Heketi不需要中小规模
CSI Driver不需要生产环境(推荐)

方案一:Native Client(节点手动挂载)

在 Kubernetes Node 上直接挂载 GlusterFS 卷,然后通过 HostPath 使用。

优点:性能好,延迟低 缺点:需要预先在所有节点上手动配置,不是真正的动态存储

┌──────────────────────────────────────┐
│       Kubernetes Cluster              │
│                                     │
│  ┌─────────┐  ┌─────────┐          │
│  │ Node 1  │  │ Node 2  │          │
│  │  mount  │  │  mount  │          │
│  │ /data   │  │ /data   │          │
│  └────┬────┘  └────┬────┘          │
│       │            │                │
└───────┼────────────┼────────────────┘
        │            │
        └──────────┼┘

        ┌──────────▼──────────┐
        │    GlusterFS          │
        │   Storage Cluster    │
        │                     │
        │  brick1 brick2 ...  │
        └────────────────────┘

方案二:Heketi(动态卷管理)

通过 Heketi(GlusterFS 的 RESTful 管理接口)自动管理 GlusterFS 卷。

优点:真正的动态存储,可按需创建 PVC 缺点:需要额外部署 Heketi 组件

方案三:CSI Driver(推荐)

使用 GlusterFS CSI Driver,Kubernetes 原生支持的标准化接口。

优点:标准化 CSI 接口;Kubernetes 原生支持;自动挂载 缺点:需要 CSI 驱动支持

方案一:Native Client(节点手动挂载)

这种方法需要在所有 Kubernetes 节点上手动安装和挂载 GlusterFS。

1. 部署 GlusterFS 集群(宿主机)

假设已有 GlusterFS 集群,节点信息如下:

主机名IP存储路径
glusterfs-1192.168.1.11/brick1
glusterfs-2192.168.1.12/brick2
glusterfs-3192.168.1.13/brick3

1.1 安装 GlusterFS(在所有宿主机上)

bash
# CentOS/RHEL 安装
sudo yum install -y glusterfs-server glusterfs-fuse
sudo systemctl enable glusterd
sudo systemctl start glusterd

# 配置 hosts
echo "192.168.1.11 glusterfs-1" | sudo tee -a /etc/hosts
echo "192.168.1.12 glusterfs-2" | sudo tee -a /etc/hosts
echo "192.168.1.13 glusterfs-3" | sudo tee -a /etc/hosts

# 信任主机
sudo gluster peer probe glusterfs-2
sudo gluster peer probe glusterfs-3

1.2 创建 GlusterFS 卷

bash
# 创建分布式复制卷
gluster volume create data-volume \
  replica 3 \
  glusterfs-1:/brick1 \
  glusterfs-2:/brick2 \
  glusterfs-3:/brick3 \
  force

gluster volume start data-volume

# 设置认证
gluster volume set data-volume auth.allow 192.168.1.*

# 验证
gluster volume info data-volume

2. 在 K8s 所有节点安装客户端

必须在所有 Kubernetes 节点上执行(管理节点 + 所有计算节点):

bash
# CentOS/RHEL
sudo yum install -y glusterfs-fuse

# 验证安装
mount.glusterfs --version

3. 手动挂载(方式一:直接 mount)

在所有节点上执行:

bash
# 创建挂载点
sudo mkdir -p /mnt/glusterfs

# 临时挂载测试
sudo mount -t glusterfs glusterfs-1:/data-volume /mnt/glusterfs

# 验证
df -h | grep glusterfs

# 卸载(如需要)
sudo umount /mnt/glusterfs

4. 持久挂载(方式二:/etc/fstab)

在所有节点上执行:

bash
# 添加挂载条目
echo "glusterfs-1:/data-volume /mnt/glusterfs glusterfs defaults,_netdev 0 0" | sudo tee -a /etc/fstab

# 验证配置
sudo mount -a

# 检查挂载
df -h | grep glusterfs

5. 创建 PV

yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: glusterfs-pv
spec:
  capacity:
    storage: 100Gi
  accessModes:
    - ReadWriteMany
  hostPath:
    path: /mnt/glusterfs/data
  storageClassName: glusterfs-native
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: glusterfs-pvc
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 50Gi
  storageClassName: glusterfs-native

6. StorageClass

yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: glusterfs-native
provisioner: kubernetes.io/hostpath
volumeBindingMode: Immediate

方案二:Heketi(动态卷管理)

Heketi 是 GlusterFS 的 RESTful 管理接口,实现动态卷创建。

1. 部署 GlusterFS 集群

(同方案一的步骤 1.1-1.2)

2. 安装 Heketi

2.1 命名空间

yaml
apiVersion: v1
kind: Namespace
metadata:
  name: glusterfs

2.2 ServiceAccount

yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: heketi-service-account
  namespace: glusterfs
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: heketi-client
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:persistent-volume-provisioner
subjects:
- kind: ServiceAccount
  name: heketi-service-account
  namespace: glusterfs

2.3 Heketi ConfigMap

yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: heketi-config
  namespace: glusterfs
data:
  heketi.json: |
    {
      "_port_range": "20000-20100",
      "executor": "kubernetes",
      "dbpath": "/var/lib/heketi",
      "logfile": "/var/log/heketi/heketi.log",
      "loglevel": "debug"
    }

2.4 Heketi Deployment

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: heketi
  namespace: glusterfs
spec:
  replicas: 1
  selector:
    matchLabels:
      app: heketi
  template:
    metadata:
      labels:
        app: heketi
    spec:
      serviceAccountName: heketi-service-account
      containers:
      - name: heketi
        image: heketi/heketi:10.0.0
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8080
        name: http
        env:
        - name: HEKETI_EXECUTOR
          value: "kubernetes"
        - name: HEKETI_KUBERNETES_API_SERVER
          value: "https://kubernetes.default.svc"
        - name: HEKETI_KUBERNETES_CA_CERT
          value: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
        - name: HEKETI_KUBERNETES_POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        volumeMounts:
        - name: heketi-data
          mountPath: /var/lib/heketi
        - name: heketi-config
          mountPath: /etc/heketi
        livenessProbe:
          httpGet:
            path: /hello
            port: 8080
          initialDelaySeconds: 60
          periodSeconds: 30
      volumes:
      - name: heketi-data
        emptyDir: {}
      - name: heketi-config
        configMap:
          name: heketi-config

2.5 Heketi Service

yaml
apiVersion: v1
kind: Service
metadata:
  name: heketi
  namespace: glusterfs
spec:
  ports:
  - port: 8080
    targetPort: 8080
    name: http
  selector:
    app: heketi

3. 创建 StorageClass

yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: glusterfs-sc
provisioner: kubernetes.io/glusterfs
parameters:
  resturl: "http://heketi.glusterfs.svc.cluster.local:8080"
  restauthenabled: "true"
  restuser: admin
  userid: admin
  secretName: heketi-secret
  secretNamespace: glusterfs
  volumetype: "replicate:2"
allowVolumeExpansion: true
reclaimPolicy: Retain
volumeBindingMode: Immediate

4. 添加 GlusterFS 集群

bash
# 通过 CLI 添加拓扑
kubectl exec -it heketi-pod -n glusterfs -- \
  heketi-cli topology-load --jsonfile=/etc/heketi/topology.json

# 或通过 REST API
curl -X POST http://heketi.glusterfs.svc.cluster.local:8080/clusters \
  -H "Content-Type: application/json" \
  -d '{"name":"kubernetes-cluster"}'

方案三:CSI Driver(推荐)

使用 GlusterFS CSI Driver 是 Kubernetes 原生支持的标准化方式,无需手动挂载

1. 安装 CSI Driver

1.1 RBAC

yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: glusterfs-csi-controller
  namespace: glusterfs
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: glusterfs-csi-role
rules:
- apiGroups: [""]
  resources: ["persistentvolumes"]
  verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
  resources: ["persistentvolumeclaims"]
  verbs: ["get", "list", "watch", "update"]
- apiGroups: [""]
  resources: ["events"]
  verbs: ["list", "watch", "create", "update", "patch"]
- apiGroups: ["storage.k8s.io"]
  resources: ["csidinodes"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["storage.k8s.io"]
  resources: ["csidrivers"]
  verbs: ["get", "list", "watch", "create", "update"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: glusterfs-csi-binding
subjects:
- kind: ServiceAccount
  name: glusterfs-csi-controller
  namespace: glusterfs
roleRef:
  kind: ClusterRole
  name: glusterfs-csi-role
  apiGroup: rbac.authorization.k8s.io

1.2 CSI Driver Deployment

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: glusterfs-csi-driver
  namespace: glusterfs
spec:
  replicas: 2
  selector:
    matchLabels:
      app: glusterfs-csi-driver
  template:
    metadata:
      labels:
        app: glusterfs-csi-driver
    spec:
      serviceAccountName: glusterfs-csi-controller
      containers:
      - name: csi-provisioner
        image: quay.io/k8scsi/csi-provisioner:v3.5.0
        args:
        - "--provisioner=glusterfs.csi.driver"
        - "--csiendpoint=$(ADDRESS)"
        - "--v=5"
        env:
        - name: ADDRESS
          value: /csi/csi.sock
        volumeMounts:
        - name: socket-dir
          mountPath: /csi
      - name: driver-registrar
        image: quay.io/k8scsi/csi-node-driver-registrar:v1.3.0
        args:
        - "--csi-address=$(ADDRESS)"
        - "--kubelet-registration-path=$(REGISTRATION_PATH)"
        env:
        - name: ADDRESS
          value: /csi/csi.sock
        - name: REGISTRATION_PATH
          value: /var/lib/kubelet/plugins/glusterfs.csi.driver/csi.sock
        volumeMounts:
        - name: socket-dir
          mountPath: /csi
        - name: registration-dir
          mountPath: /var/lib/kubelet/plugins
      - name: glusterfs-plugin
        image: redhatgluster/glusterfs-csi-driver:v2.0.0
        args:
        - "--nodeid=$(NODE_ID)"
        - "--endpoint=$(CSI_ENDPOINT)"
        - "--resturl=$(REST_URL)"
        env:
        - name: NODE_ID
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        - name: CSI_ENDPOINT
          value: unix:/csi/csi.sock
        - name: REST_URL
          value: "http://glusterfs-rest.mgmt:4800"
        volumeMounts:
        - name: socket-dir
          mountPath: /csi
        - name: mount-dir
          mountPath: /var/lib/kubelet/pods
          mountPropagation: Bidirectional
      volumes:
      - name: socket-dir
        emptyDir: {}
      - name: registration-dir
        hostPath:
          path: /var/lib/kubelet/plugins
      - name: mount-dir
        hostPath:
          path: /var/lib/kubelet/pods

1.3 CSI Driver DaemonSet(节点插件)

yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: glusterfs-csi-node
  namespace: glusterfs
spec:
  selector:
    matchLabels:
      app: glusterfs-csi-driver
  template:
    metadata:
      labels:
        app: glusterfs-csi-driver
    spec:
      serviceAccountName: glusterfs-csi-controller
      hostNetwork: true
      containers:
      - name: driver-registrar
        image: quay.io/k8scsi/csi-node-driver-registrar:v1.3.0
        args:
        - "--csi-address=/csi/csi.sock"
        - "--kubelet-registration-path=/var/lib/kubelet/plugins/glusterfs.csi.driver/csi.sock"
        volumeMounts:
        - name: plugin-dir
          mountPath: /var/lib/kubelet/plugins
        - name: socket-dir
          mountPath: /csi
      - name: gcest-nse-plugin
        image: redhatgluster/glusterfs-csi-driver:v2.0.0
        securityContext:
          privileged: true
        args:
        - "--nodeid=$(NODE_ID)"
        - "--endpoint=$(CSI_ENDPOINT)"
        env:
        - name: NODE_ID
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        - name: CSI_ENDPOINT
          value: unix:/csi/csi.sock
        volumeMounts:
        - name: plugin-dir
          mountPath: /var/lib/kubelet/plugins
        - name: socket-dir
          mountPath: /csi
        - name: mount-dir
          mountPath: /var/lib/kubelet/pods
          mountPropagation: Bidirectional
      volumes:
      - name: plugin-dir
        hostPath:
          path: /var/lib/kubelet/plugins
      - name: socket-dir
        emptyDir: {}
      - name: mount-dir
        hostPath:
          path: /var/lib/kubelet/pods

2. 创建 CSI StorageClass

yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: glusterfs-csi
provisioner: glusterfs.csi.driver
parameters:
  resturl: "http://glusterfs-rest.mgmt:4800"
  clusterid: "gluster-cluster-id"
  volumetype: "replicate:3"
  restauthenabled: "true"
  restuser: "admin"
  secrettype: "clusteradmin"
allowVolumeExpansion: true
reclaimPolicy: Retain
volumeBindingMode: Immediate

3. 创建 Secret

yaml
apiVersion: v1
kind: Secret
metadata:
  name: glusterfs-secret
  namespace: glusterfs
type: Opaque
stringData:
  key: admin-password

使用示例

创建 PVC(通用)

yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-data-pvc
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 10Gi
  storageClassName: glusterfs-csi  # 使用 CSI

在 Pod 中使用

yaml
apiVersion: v1
kind: Pod
metadata:
  name: my-app
spec:
  containers:
  - name: app
    image: nginx:1.21
    volumeMounts:
    - name: data
      mountPath: /usr/share/nginx/html
  volumes:
  - name: data
    persistentVolumeClaim:
      claimName: my-data-pvc

在 Deployment 中使用

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: app
        image: nginx:1.21
        volumeMounts:
        - name: data
          mountPath: /usr/share/nginx/html
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: my-data-pvc

部署步骤总结

方案一:Native Client

bash
# 1. 部署 GlusterFS 集群(宿主机)
# 2. 在所有 K8s 节点安装客户端
sudo yum install -y glusterfs-fuse
# 3. 在所有节点手动挂载
sudo mount -t glusterfs glusterfs-1:/data-volume /mnt/glusterfs
# 或写入 /etc/fstab
# 4. 创建 PV/PVC
kubectl apply -f pv.yaml

方案二:Heketi

bash
# 1. 命名空间
kubectl apply -f namespace.yaml
# 2. Heketi
kubectl apply -f heketi-deployment.yaml
# 3. 验证
kubectl wait --for=condition=available deployment/heketi -n glusterfs
# 4. StorageClass
kubectl apply -f storageclass.yaml
# 5. 创建 PVC(自动创建卷)
kubectl apply -f pvc.yaml

方案三:CSI Driver

bash
# 1. RBAC + CSI Driver
kubectl apply -f csi-driver.yaml
# 2. StorageClass
kubectl apply -f csi-storageclass.yaml
# 3. 创建 PVC(自动挂载)
kubectl apply -f pvc.yaml

三种方案对比总结

特性Native ClientHeketiCSI Driver
节点手动挂载需要不需要不需要
动态卷创建
CSI 标准
推荐场景固定存储中小规模生产环境

推荐

生产环境推荐使用 CSI Driver 方案,因为:

  1. 无需手动操作节点
  2. Kubernetes 原生支持
  3. 标准 CSI 接口
  4. 自动挂载和管理