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-1 | 192.168.1.11 | /brick1 |
| glusterfs-2 | 192.168.1.12 | /brick2 |
| glusterfs-3 | 192.168.1.13 | /brick3 |
1.1 安装 GlusterFS(在所有宿主机上)
# 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-31.2 创建 GlusterFS 卷
# 创建分布式复制卷
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-volume2. 在 K8s 所有节点安装客户端
必须在所有 Kubernetes 节点上执行(管理节点 + 所有计算节点):
# CentOS/RHEL
sudo yum install -y glusterfs-fuse
# 验证安装
mount.glusterfs --version3. 手动挂载(方式一:直接 mount)
在所有节点上执行:
# 创建挂载点
sudo mkdir -p /mnt/glusterfs
# 临时挂载测试
sudo mount -t glusterfs glusterfs-1:/data-volume /mnt/glusterfs
# 验证
df -h | grep glusterfs
# 卸载(如需要)
sudo umount /mnt/glusterfs4. 持久挂载(方式二:/etc/fstab)
在所有节点上执行:
# 添加挂载条目
echo "glusterfs-1:/data-volume /mnt/glusterfs glusterfs defaults,_netdev 0 0" | sudo tee -a /etc/fstab
# 验证配置
sudo mount -a
# 检查挂载
df -h | grep glusterfs5. 创建 PV
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-native6. StorageClass
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 命名空间
apiVersion: v1
kind: Namespace
metadata:
name: glusterfs2.2 ServiceAccount
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: glusterfs2.3 Heketi ConfigMap
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
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-config2.5 Heketi Service
apiVersion: v1
kind: Service
metadata:
name: heketi
namespace: glusterfs
spec:
ports:
- port: 8080
targetPort: 8080
name: http
selector:
app: heketi3. 创建 StorageClass
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: Immediate4. 添加 GlusterFS 集群
# 通过 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
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.io1.2 CSI Driver Deployment
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/pods1.3 CSI Driver DaemonSet(节点插件)
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/pods2. 创建 CSI StorageClass
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: Immediate3. 创建 Secret
apiVersion: v1
kind: Secret
metadata:
name: glusterfs-secret
namespace: glusterfs
type: Opaque
stringData:
key: admin-password使用示例
创建 PVC(通用)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-data-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Gi
storageClassName: glusterfs-csi # 使用 CSI在 Pod 中使用
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 中使用
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
# 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
# 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
# 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 Client | Heketi | CSI Driver |
|---|---|---|---|
| 节点手动挂载 | 需要 | 不需要 | 不需要 |
| 动态卷创建 | 否 | 是 | 是 |
| CSI 标准 | 否 | 否 | 是 |
| 推荐场景 | 固定存储 | 中小规模 | 生产环境 |
推荐
生产环境推荐使用 CSI Driver 方案,因为:
- 无需手动操作节点
- Kubernetes 原生支持
- 标准 CSI 接口
- 自动挂载和管理
