第一部分 分布式存储基础
1.1 单机存储的瓶颈:为什么需要分布式存储
传统负载均衡集群的存储层架构:
客户端 -> 调度器 -> 服务器集群 -> (数据库:MySQL Cluster 或读写分离)(memcache)(NAS、NFS)
引入 GlusterFS 之后的架构:
客户端 -> 调度器 -> 服务器集群 -> 存储集群(GlusterFS)
通过副本可以设置容错性。集群常见类型有:
| 集群类型 | 全称 | 作用 |
|---|---|---|
| HA | High Availability | 高可用集群,消除单点故障 |
| LB | Load Balance | 负载均衡集群,分摊请求压力 |
| HPC | High Performance Computing | 高性能计算集群 |
| Storage | Storage Cluster | 存储集群,本文主题 |
单机存储(单台服务器挂一堆盘 + NFS 共享)的瓶颈:
| 瓶颈维度 | 具体表现 |
|---|---|
| 容量上限 | 单机槽位有限,单卷受文件系统与 RAID 卡限制,扩容必须停机加盘 |
| 可用性 | 服务器宕机、系统盘损坏、网卡故障即全站不可用,是典型单点 |
| 性能上限 | 所有 IO 汇聚到一台机器的总线、RAID 卡、网卡,带宽与 IOPS 无法横向扩展 |
| 数据可靠性 | RAID 只能容忍有限磁盘故障,无法容忍整机/整机柜故障 |
| 扩展成本 | 纵向扩展(Scale-Up)越到后期单位成本越高 |
| 运维窗口 | 加盘、扩容、迁移都需要停机窗口 |
分布式存储用横向扩展(Scale-Out)解决上述问题:加机器即加容量、加带宽、加冗余,且理论上无容量上限(GlusterFS 可轻松达到 PB 级)。
1.2 DAS、NAS、SAN 的区别
| 对比项 | DAS | NAS | SAN |
|---|---|---|---|
| 全称 | Direct Attached Storage 直连存储 | Network Attached Storage 网络附加存储 | Storage Area Network 存储区域网络 |
| 典型形态 | 服务器内置盘、SCSI/SAS 外挂磁盘柜 | NFS/CIFS 文件服务器、群晖等 NAS 设备 | FC-SAN、IP-SAN(iSCSI)磁盘阵列 |
| 连接方式 | SCSI、SAS、SATA 线缆直连 | 以太网 TCP/IP | FC 光纤网络 或 以太网(iSCSI) |
| 对外暴露 | 块设备(/dev/sdb) |
文件系统目录(共享) | 块设备(LUN) |
| 使用协议 | SCSI 指令 | NFS、CIFS/SMB、FTP | FC 协议、iSCSI |
| 文件系统在哪 | 服务器端 | 存储设备端 | 服务器端 |
| 是否可多机共享 | 一般不可(独占) | 可,天然多机共享 | 可,但需集群文件系统(GFS2、OCFS2)才能并发写 |
| 性能 | 高(无网络开销) | 中(受网络与协议开销) | 高(FC 低延迟) |
| 成本 | 低 | 低到中 | 高(FC 交换机、HBA 卡昂贵) |
| 扩展性 | 差 | 中 | 好 |
| 典型场景 | 单机数据盘、系统盘 | 部门文件共享、Web 静态资源共享 | 数据库、虚拟化平台后端存储 |
详见 09-02-NFS网络文件系统.md(NAS 侧 NFS 实现)
一句话记忆:DAS 是"我自己的盘",SAN 是"网络上的盘",NAS 是"网络上的目录"。
1.3 块存储、文件存储、对象存储
| 对比项 | 块存储 Block | 文件存储 File | 对象存储 Object |
|---|---|---|---|
| 数据抽象 | 裸块(sector/LBA) | 文件 + 目录树(层级命名空间) | 对象(扁平的 Bucket/Key + 元数据) |
| 访问接口 | SCSI、iSCSI、FC、RBD、virtio-blk | POSIX、NFS、CIFS、FUSE | HTTP RESTful(S3、Swift API) |
| 是否可 mount | 可(需先格式化文件系统) | 可(直接挂载成目录) | 一般不可(SDK/HTTP 访问,或用 s3fs 等勉强挂载) |
| 是否支持随机改写 | 支持(任意偏移读写) | 支持 | 一般不支持,只能整对象覆盖 |
| 元数据能力 | 无(对存储而言只有块号) | 有限(权限、时间戳) | 强(可自定义任意 KV 元数据) |
| 一致性/性能特点 | 延迟最低、IOPS 最高 | 中等,支持共享语义 | 吞吐好、延迟高、并发扩展性极强 |
| 典型产品 | Ceph RBD、iSCSI 阵列、云硬盘 | NFS、GlusterFS、CephFS、HDFS | S3、OSS、MinIO、Ceph RGW、Swift |
| 适用场景 | 数据库、虚拟机磁盘、需要裸设备的应用 | 多机共享目录、代码/日志/共享上传目录、大文件 | 图片视频音频、备份归档、静态资源、海量小文件 |
| 不适用场景 | 多机同时读写同一 LUN(需集群 FS) | 海量小文件(元数据压力大) | 需要 POSIX 语义、随机改写的应用 |
FastDFS 严格来说属于"轻量级分布式文件存储 + HTTP 访问",介于文件存储与对象存储之间,但不提供 POSIX 挂载;GlusterFS 属于典型的文件存储(可挂载)。
1.4 CAP 理论在存储系统中的体现
CAP:一个分布式系统不可能同时完全满足以下三点。
| 字母 | 含义 | 在存储系统中的体现 |
|---|---|---|
| C Consistency | 一致性 | 任一时刻从任意副本读到的数据都相同;写入返回成功后立刻可读到新值 |
| A Availability | 可用性 | 任何请求都能在有限时间得到非错误响应,部分节点故障也能继续服务 |
| P Partition tolerance | 分区容错性 | 网络分区(节点之间通信中断)时系统仍能继续工作 |
在真实的分布式存储中,网络分区(P)无法回避,因此实际是在 C 与 A 之间取舍:
- CP 取向:网络分区时宁可拒绝服务,也不返回不一致数据。GlusterFS 开启 quorum(仲裁)后就是 CP:少数派分区变为只读或拒绝写,从而避免脑裂。适合对数据正确性要求高的场景。
- AP 取向:网络分区时两侧都继续接受写入,事后再合并/修复。GlusterFS 不开 quorum 的两副本卷就偏 AP,代价是可能产生 split-brain(脑裂),需要人工介入修复。
- FastDFS 的同组 Storage 之间是异步同步(最终一致),偏 AP:上传成功后另一节点可能短时间内还没有该文件,这正是需要 fastdfs-nginx-module 的根本原因。
结论:GlusterFS 建议三副本,或两副本 + 仲裁盘(arbiter),即在可用性与一致性之间取得工程平衡;FastDFS 需要在应用层容忍短时的同步延迟。
1.5 常见分布式存储方案对比
| 方案 | 定位/类型 | 元数据设计 | 擅长文件大小 | 访问方式 | 复杂度 | 备注 |
|---|---|---|---|---|---|---|
| GlusterFS | 通用分布式文件系统 | 无元数据服务器,弹性哈希算法定位 | 中大文件(MB~GB 及以上) | FUSE 挂载、NFS、CIFS、libgfapi | 中 | 部署简单、无单点;小文件性能一般 |
| Ceph | 统一存储(块+文件+对象) | CRUSH 算法 + MON/MDS 集群 | 大小皆可,全场景 | RBD / CephFS / RGW(S3) | 高 | 功能最全,运维门槛最高 |
| FastDFS | 轻量级中小文件存储 | Tracker 只做调度,不存文件索引;file_id 自带定位信息 | 中小文件(4KB~500MB) | 客户端 API、HTTP(配 Nginx) | 低 | 不支持挂载;适合图片、文档 |
| HDFS | 大数据批处理文件系统 | NameNode 集中式元数据 | 超大文件(GB~TB),怕小文件 | Java API、hdfs 命令、WebHDFS | 中高 | 一次写入多次读取,高吞吐低随机性 |
| MinIO | 对象存储(S3 兼容) | 无独立元数据服务,纠删码分片 | 中小到大文件皆可 | S3 HTTP API | 低 | 云原生首选,K8s 友好 |
| MooseFS / MFS | 分布式文件系统 | Master 集中式元数据 | 中小文件 | FUSE 挂载 | 中 | Master 是单点(社区版) |
| TFS | 淘宝海量小文件方案 | 有中心节点(NameServer) | 极小文件(图片) | API | 中 | 淘宝开源,适应海量小文件存储 |
详见 16-03-HDFS分布式文件系统.md
有中心节点 vs 无中心节点,这是分布式文件系统最重要的分类维度:
- 有中心节点(有元数据服务器):性能好、角色清晰、易实现强一致与目录语义。例如 TFS、HDFS、MFS、FastDFS(Tracker 为调度中心)。缺点是元数据服务器可能成为性能瓶颈和单点,需要额外做 HA。
- 无中心节点:GlusterFS 是代表。好处是健壮性强、无单点、无元数据瓶颈,任意节点都可提供完整视图;缺点是目录遍历(
ls大目录)等操作需要向所有 brick 广播,小文件与元数据密集型负载性能一般。
其它相关名词:
- DFS:分布式存储的统称(Distributed File System)。
- MFS:MooseFS,有中心节点。
- TFS:淘宝的方案,适应海量小文件存储,开源方案。
第二部分 GlusterFS 分布式文件系统
2.1 GlusterFS 简介
GlusterFS 具备 PB 级容量、高可用性、良好的读/写性能,基于文件系统级别共享,是分布式的。
GlusterFS(GNU Cluster File System)是一种全对称的开源分布式文件系统。所谓全对称,是指 GlusterFS 采用弹性哈希算法,没有中心节点,所有节点全部平等。GlusterFS 配置方便、稳定性好,可以轻松达到 PB 级容量,支持数千个节点。2011 年被红帽收购,之后推出了基于 GlusterFS 的 Red Hat Storage Server,增加了针对 KVM 的许多特性,可用作 KVM 存储 IMAGE 的存储集群,也可以为 LB 或 HA 提供存储。
- GlusterFS 是一个开源的分布式文件系统,是 Scale-Out 存储的核心,能够处理千数量级的客户端。在传统的解决方案中,GlusterFS 能够灵活地结合物理的、虚拟的和云资源,体现高可用和企业级的性能存储。
- GlusterFS 通过 TCP/IP 或 InfiniBand RDMA 网络链接将客户端的存储资源块聚集在一起,使用单一的全局命名空间来管理数据、磁盘和内存资源。
- GlusterFS 基于堆叠的用户空间设计(Translator 堆栈),可以为不同的工作负载提供高优的性能。
- GlusterFS 支持运行在任何标准 IP 网络上标准应用程序的标准客户端,用户可以在全局统一的命名空间中使用 NFS/CIFS 等标准协议来访问应用数据。
2.2 重要特性
- 扩展性和高性能:Scale-Out 横向扩展,加节点即加容量与带宽。
- 高可用:通过副本卷/分散卷提供冗余,节点故障不影响数据访问。
- 全局统一命名空间:所有 brick 聚合为一个统一的目录树,客户端看到单一挂载点。
- 弹性 hash 算法:无需元数据服务器即可定位文件,消除单点与性能瓶颈。
- 弹性卷管理:在线创建、扩容、缩容、修改卷类型与参数,业务不中断。
- 基于标准协议:支持 NFS、CIFS、FUSE、libgfapi。
2.3 无元数据服务器设计与弹性哈希算法
传统有中心节点的文件系统必须先问元数据服务器"文件在哪台机器上",这带来两个问题:元数据服务器是单点,且是性能瓶颈(每次访问都要多一次网络往返和一次集中查表)。
GlusterFS 的做法:用算法代替查表。
- 对文件的文件名使用 Davies-Meyer 哈希函数计算出一个 32 位整数(哈希值)。
- 卷内每个 brick(准确说是 DHT 子卷)被分配一段互不重叠的哈希区间(layout range),这些区间共同覆盖 0 ~ 2^32-1 的完整空间。
- 文件哈希值落在哪个区间,文件就存到对应 brick 上。
- 每个目录在每个 brick 上都存在,并在扩展属性中记录本 brick 负责的哈希区间(
trusted.glusterfs.dht),因此布局信息是分布式存储在各 brick 的扩展属性里的,而不是集中在某台服务器。
弹性哈希算法的优势:
| 优势 | 说明 |
|---|---|
| 消除单点 | 没有元数据服务器,任何节点故障都不会让整个命名空间不可用 |
| 消除性能瓶颈 | 定位文件是本地计算,不需要额外网络往返,客户端直接连到目标 brick |
| 线性扩展 | 增加 brick 只需重新划分哈希区间(fix-layout),不需要重建集中索引 |
| 客户端直连 | 数据路径上没有中间代理,带宽随节点数线性增长 |
代价与注意点:
- 文件名改变会导致哈希值改变,因此
rename后文件可能"不在应该在的 brick 上",GlusterFS 用 linkto 文件(大小为 0、带trusted.glusterfs.dht.linkto属性的指针文件)解决,读的时候多一跳。 - 扩容后老文件仍在原 brick,必须执行 rebalance 才会重新分布。
- 大目录
ls -l需要向所有 brick 发请求再合并,目录条目多时较慢。
2.4 核心术语
| 概念 | 说明 |
|---|---|
| brick | GlusterFS 的存储基本单元,以节点服务器目录形式展现,表示为 主机名:/导出目录,例如 node1.com:/opt/gluster/exp1。 |
| Volume(卷) | 多个 bricks 的逻辑集合,是客户端挂载和管理的对象。 |
| Node(节点) | 运行 glusterd 的一台服务器,可以提供一个或多个 brick。 |
| Peer(对等端) | 加入了同一个 trusted storage pool(信任存储池)的其它节点,通过 gluster peer probe 加入。 |
| Trusted Storage Pool | 信任存储池,由互相 probe 成功的一组节点组成,是创建卷的前提。 |
| GFID | GlusterFS 为每个文件/目录分配的 128 位全局唯一标识(类似 inode 号),存于扩展属性 trusted.gfid,.glusterfs 隐藏目录中按 GFID 建立硬链接索引。副本修复、脑裂判定都依赖 GFID。 |
| Translator(xlator) | GlusterFS 的功能模块,以堆叠(stack)方式组合,每层实现一种能力(DHT 分布、AFR 复制、EC 纠删、io-cache 缓存、write-behind 等)。卷类型本质上就是 translator 堆栈的不同组合,配置保存在 volfile 中。 |
| Metadata | 元数据,用于描述文件、目录等的信息。GlusterFS 不设集中元数据服务器,元数据随文件存放在 brick 的扩展属性中。 |
| Self-heal | 自愈,用于后台运行监测文件和目录的不一致问题,并且解决。 |
| FUSE | FileSystem in Userspace,是一个可加载的内核模块,支持非特权用户创建自己的文件系统而不需要修改内核代码。GlusterFS 原生客户端基于 FUSE 实现。 |
| GlusterFS Server | 数据存储服务器,即组成 GlusterFS 存储集群的节点,运行 glusterd 管理进程和每 brick 一个的 glusterfsd 进程。 |
| GlusterFS Client | 使用 GlusterFS 存储服务的客户。例如 KVM 存储池、OpenStack 的后端存储、LB RealServer、HA Node。 |
| volfile | 卷配置文件,描述 translator 堆栈,客户端挂载时从服务端拉取。 |
| op-version | 集群操作版本号,决定可用的特性集合,滚动升级时需要注意。 |
2.5 GlusterFS 的七种卷模式
2.5.1 分布式卷(Distribute Volume)
将文件以 hash 的方式存储到不同的卷(brick)中。文件通过 hash 算法随机分布到由 bricks 组成的卷上,卷中资源仅在一台服务器上存储,采用非镜像、非条带模式。使用分布式卷主要用于扩展存储,冗余需要由其他硬件/软件层提供。
流程图:
file(abcd) --> hash --> 卷1
file(123) --> hash --> 卷2 (无规则存放进不同的卷)
file(ab12) --> hash --> 卷2
- 数据分布:整个文件不切分,完整存放在某一个 brick 上。
- Brick 数量要求:>= 1,任意个,无倍数要求。
- 容量利用率:100%(所有 brick 容量之和)。
- 冗余能力:无冗余。任一 brick 损坏,该 brick 上的文件全部丢失(其他文件仍可访问)。
- 读写性能:并发访问不同文件时性能好(请求被分散到不同节点);单个大文件的读写只受单块盘/单节点带宽限制,不会加速。
- 适用场景:以扩容为唯一目的、底层已有 RAID 或不在意数据丢失的缓存类数据、可重建的数据。
2.5.2 复制卷(Replica Volume)
将插入的文件以副本的形式存放进每一个卷中,类似 RAID 1。replica 数必须等于 volume 中 brick 所包含的存储服务器数,可用性高。创建一个两两互为备份的卷,存储池中一块硬盘损坏不会影响到数据的使用,最少需要两台服务器才能创建分布镜像卷。
流程图:
abcd --> 分布式卷1(abcd)
file(abcd) --> 存入
abcd --> 分布式卷2(abcd)
- 数据分布:每个 brick 上都保存文件的完整副本。
- Brick 数量要求:等于 replica 数(如
replica 2需 2 个 brick,replica 3需 3 个)。 - 容量利用率:
1/replica。2 副本为 50%,3 副本约 33%。 - 冗余能力:可容忍
replica-1个 brick 故障。 - 读写性能:读性能好(可从任一副本读,客户端可并行分担);写性能差(客户端需同时写所有副本,写带宽被放大 replica 倍)。
- 适用场景:对可靠性要求高的关键数据、虚拟机镜像、配置文件、需要节点级容错的场合。
2.5.3 条带卷(Stripe Volume)
将文件切割成若干块,然后存放进不同的卷中,类似 RAID 0。stripe 数必须等于 volume 中 brick 所包含的存储服务器数。文件被分成数据块,以 Round Robin 的方式存储在 bricks 中,并发粒度是数据块,大文件性能好。为达到最佳效果,应在高并发环境下访问非常大的文件时使用条带卷。
流程图:
ab --> 存入 --> 分布式卷1(ab)
file(abcd) --> 拆分
cd --> 存入 --> 分布式卷2(cd)
- 数据分布:单个文件被切成固定大小的条带块,轮询写入各 brick。
- Brick 数量要求:等于 stripe 数。
- 容量利用率:100%,但单个文件的最大大小可突破单 brick 容量限制。
- 冗余能力:无冗余,且更危险——任一 brick 损坏,卷中所有被条带化的文件全部损坏(因为每个文件都缺了一块)。
- 读写性能:单个超大文件的顺序读写吞吐可叠加多节点带宽;小文件性能反而更差(一次访问要跨多个节点)。
- 适用场景:极少数超大文件、高并发大文件顺序读写(如视频编辑素材)。
重要提示(版本适用性):stripe(条带)功能自 GlusterFS 6 起已被废弃(deprecated),并在更高版本中移除。官方推荐用 dispersed(分散卷/纠删码卷) 或 sharding(分片特性
features.shard) 来替代条带卷处理大文件。本文保留条带卷相关内容用于原理理解与老版本(GlusterFS 3.x/4.x/5.x,如 CentOS 7 默认源附带版本)的实验复现;新建生产环境不要再使用条带卷。
2.5.4 分布式复制卷(Distributed-Replica Volume)
即模式 1+2。创建多个分布式卷,并以两个(或多个)卷组合为单个复制卷;使用 hash 将文件插入到指定的单个复制卷中,每份文件将保存在该复制组的所有卷中。兼顾分布式和复制式的功能,volume 中 brick 所包含的存储服务器数必须是 replica 的倍数(>=2 倍)。分布式复制卷在大多数环境下还能提供更好的读取性能,是推荐使用的模式。
流程图:
--> 卷1(abcd)
--> 卷2(abcd)
file(abcd) --> hash --> 存入
file(1234) --> hash --> 存入
--> 卷3(1234)
--> 卷4(1234)
- 数据分布:先按 hash 选出一个复制组,再在组内所有 brick 上各存一份完整文件。
- Brick 数量要求:
replica × N(N >= 2)。命令行中 brick 的书写顺序决定分组:前 replica 个为第一组,接下来 replica 个为第二组,依此类推——因此必须让同一组的 brick 落在不同物理节点上。 - 容量利用率:
1/replica。 - 冗余能力:每个复制组内可容忍
replica-1个 brick 故障。 - 读写性能:读性能好(多组并行 + 组内多副本),写性能受 replica 放大影响,但比纯复制卷可扩展。
- 适用场景:生产环境首选,需要同时具备容量扩展与节点级冗余的通用场景。
2.5.5 分布式条带卷(Distributed-Stripe Volume)
即模式 1+3。将文件拆分为若干块,并以 hash 方式分布到不同的条带组中。volume 中 brick 所包含的存储服务器数必须是 stripe 的倍数(>=2 倍),兼顾分布式和条带式的功能。每个文件分布在多台共享服务器上,通常用于大文件访问处理,最少需要 4 台服务器才能创建分布式条带卷。
流程图:
--> 卷1(ab)
ab --> 卷2
file(abcd) --> 拆分 hash
cd --> 卷3(cd)
--> 卷4
- 数据分布:先 hash 选条带组,再在组内把文件切块轮询存放。
- Brick 数量要求:
stripe × N(N >= 2),最少 4 个 brick。 - 容量利用率:100%。
- 冗余能力:无冗余,且组内任一 brick 故障会损坏该组的所有文件。
- 读写性能:大文件吞吐好,且可通过多组并发扩展。
- 适用场景:超大文件 + 需要容量扩展;已废弃,新环境改用 dispersed 或 shard。
2.5.6 条带复制卷(Stripe-Replica Volume)
即模式 2+3。将文件切割成若干块,每个数据块都在复制组中保留副本。在高并发环境下并行访问非常大的文件、且性能至关重要时使用。
流程图:
--> 卷1 (ab)(cd)
ab --> 卷2 (ab)(cd)
file(abcd) --> 拆分
cd --> 卷3 (cd)(ab)
--> 卷4 (cd)(ab)
- 数据分布:文件先切块,每块再在复制组内做多副本。
- Brick 数量要求:
stripe × replica(如 stripe 2 replica 2 需 4 个 brick)。 - 容量利用率:
1/replica。 - 冗余能力:每个条带位置的复制组内可容忍
replica-1个 brick 故障。 - 读写性能:大文件读写吞吐高且有冗余;写放大明显(切块 + 副本)。
- 适用场景:超大文件且要求冗余;已废弃,新环境改用 dispersed。
2.5.7 分布式条带复制卷(Distributed-Stripe-Replica Volume)
即模式 1+2+3。将文件切割为若干块,以 hash 的形式存放在随机条带组中,由于使用了复制卷,不同的卷中都将保留对应的文件块副本。服务器数目是 stripe 与 replica 乘积的 2 倍及以上。
流程图:
--> 1 a1
a1 --> 复制 --> 2 a2
--> 条带 a2 --> 复制 --> 3 a1
a.txt --> 分布式 --> a.txt --> 条带 --> 4 a2
b.txt --> 分布式 --> b.txt --> 条带 --> 5 b1
--> 条带 b1 --> 复制 --> 6 b2
b2 --> 复制 --> 7 b1
--> 8 b2
- Brick 数量要求:
stripe × replica × N(N >= 2),最少 8 个 brick(stripe 2 replica 2 时)。 - 容量利用率:
1/replica。 - 适用场景:超大规模 + 超大文件 + 冗余;结构最复杂,运维困难,已废弃。
2.5.8 分散卷(Dispersed Volume,纠删码 EC)
分散卷是 GlusterFS 3.6 引入的纠删码(Erasure Coding)卷类型,是条带卷的现代替代品,也是大容量场景下比复制卷更省空间的方案。
原理:把文件切分成 k 个数据片,再计算出 m 个冗余片,共 k+m = n 片分散写到 n 个 brick 上;任意 k 个片即可还原原始文件,因此可容忍 m 个 brick 同时故障。GlusterFS 中用 disperse n redundancy m 表达。
file --> 编码(EC) --> D1 D2 D3 D4 P1 P2 (disperse 6 redundancy 2)
| | | | | |
b1 b2 b3 b4 b5 b6
任意 4 个 brick 存活即可完整读出文件
- 数据分布:数据片 + 校验片分散在所有 brick 上。
- Brick 数量要求:
n = k + m,m通常取 1~4,且要求m < n/2;常用组合disperse 6 redundancy 2、disperse 4 redundancy 1、disperse 12 redundancy 4。 - 容量利用率:
k/n,例如 6+2 为 75%,远高于 3 副本的 33%。 - 冗余能力:可容忍
m个 brick 故障,冗余能力可媲美多副本。 - 读写性能:顺序大文件吞吐好;CPU 开销大(编解码),小文件与随机小 IO 性能差,修复(heal)时读放大明显。
- 适用场景:冷数据、备份归档、大文件、对空间利用率敏感的大容量池。不适合小文件密集、随机写频繁(如数据库、虚拟机镜像)的场景。
2.5.9 七种卷类型对照汇总表
| 卷类型 | 参数写法 | Brick 数量要求 | 数据分布方式 | 容量利用率 | 冗余能力 | 性能特点 | 适用场景 | 状态 |
|---|---|---|---|---|---|---|---|---|
| 分布卷 Distributed | 不加参数 | 任意 >=1 | 整文件 hash 到某个 brick | 100% | 无 | 多文件并发好,单文件不加速 | 纯扩容、可重建数据 | 推荐可用 |
| 复制卷 Replicated | replica N |
= N | 每 brick 一份完整副本 | 1/N | 容 N-1 个 brick 故障 | 读好写差 | 关键小规模数据、VM 镜像 | 推荐可用 |
| 条带卷 Striped | stripe N |
= N | 文件切块 Round Robin | 100% | 无,且损坏影响所有文件 | 大文件吞吐高,小文件差 | 超大文件 | 已废弃 |
| 分布复制卷 Distributed-Replicated | replica N + N×M 个 brick |
N 的倍数(>=2 倍) | 先 hash 选组,组内复制 | 1/N | 每组容 N-1 故障 | 综合最佳 | 生产首选 | 推荐可用 |
| 分布条带卷 Distributed-Striped | stripe N + N×M 个 brick |
N 的倍数,最少 4 | 先 hash 选组,组内切块 | 100% | 无 | 大文件吞吐 + 容量扩展 | 超大文件 | 已废弃 |
| 条带复制卷 Striped-Replicated | stripe N replica M |
N×M(最少 4) | 切块后每块做副本 | 1/M | 每位置容 M-1 故障 | 大文件 + 冗余 | 高并发超大文件 | 已废弃 |
| 分布条带复制卷 Distributed-Striped-Replicated | stripe N replica M + 更多 brick |
N×M 的倍数(最少 8) | hash 选组 → 切块 → 副本 | 1/M | 每位置容 M-1 故障 | 三者兼顾,最复杂 | 超大规模超大文件 | 已废弃 |
| 分散卷 Dispersed(EC) | disperse n redundancy m |
n = k+m | 数据片 + 校验片分散 | k/n(如 75%) | 容 m 个 brick 故障 | 大文件好,小文件/随机写差,CPU 高 | 冷数据、归档、大容量池 | 推荐可用 |
| 分布分散卷 Distributed-Dispersed | disperse n redundancy m + n×M 个 brick |
n 的倍数 | hash 选 EC 组 | k/n | 每组容 m 故障 | 大容量 + 可扩展 | 大规模归档池 | 推荐可用 |
选择建议一句话:通用场景选分布复制卷(replica 3 或 replica 2 + arbiter);大容量冷数据选分布分散卷;不要再用任何带 stripe 的卷类型。
2.6 实验环境准备
2.6.1 环境与主机规划
4 台虚拟机,每台 512MB 内存,每台机器添加 3 块 2GB 硬盘(实验目的 2GB 硬盘即可)。
| IP | 主机名 | 角色 | 数据盘规划 |
|---|---|---|---|
| 192.168.224.10 | server.com | 客户端,使用 Gluster | 无(仅挂载) |
| 192.168.224.11 | node1.com | Gluster Node1 | /dev/sdb、/dev/sdc、/dev/sdd 各 2GB |
| 192.168.224.12 | node2.com | Gluster Node2 | /dev/sdb、/dev/sdc、/dev/sdd 各 2GB |
| 192.168.224.13 | node3.com | Gluster Node3 | /dev/sdb、/dev/sdc、/dev/sdd 各 2GB |
操作系统:CentOS 7(本文实验基于 4 台 CentOS 7 虚拟机完成)。CentOS 6 的差异会在相应位置标注。
2.6.2 准备独立数据盘(分区、格式化、挂载)
重要原则:不要用系统盘(
/所在分区)做 Brick。 原因:
- Brick 写满会导致根分区写满,系统直接不可用(日志无法写、无法登录)。
- 无法独立扩容、独立更换故障盘。
- GlusterFS 默认会拒绝在根分区上创建 brick,必须加
force才能绕过——这正是提醒你"你在做一件不该做的事"。- 生产建议:每块物理盘(或每个 RAID 组的 LV)对应一个 brick,用 XFS 格式化并加
inode64挂载选项。
在 node1~node3 上对每块数据盘执行(以 /dev/sdb 为例):
# 1. 查看磁盘
lsblk
fdisk -l
# 2. 分区(生产也可不分区直接用整盘/或用 LVM)
fdisk /dev/sdb
# 依次输入:n -> p -> 1 -> 回车 -> 回车 -> w
# 3. 格式化为 XFS(GlusterFS 官方推荐 XFS,inode size 512)
mkfs.xfs -i size=512 /dev/sdb1
# 4. 创建挂载点并挂载
mkdir -p /data/brick1
mount -o inode64,noatime /dev/sdb1 /data/brick1
# 5. 写入 /etc/fstab 实现开机自动挂载
echo '/dev/sdb1 /data/brick1 xfs defaults,inode64,noatime 0 0' >> /etc/fstab
mount -a
df -hT
推荐使用 LVM(便于后续扩容和快照,GlusterFS 快照功能强制要求 brick 位于 LVM 精简卷上):
pvcreate /dev/sdb
vgcreate vg_brick1 /dev/sdb
# 创建精简池(thin pool),快照功能必需
lvcreate -L 1.8G -T vg_brick1/thinpool
lvcreate -V 1.5G -T vg_brick1/thinpool -n lv_brick1
mkfs.xfs -i size=512 /dev/vg_brick1/lv_brick1
mkdir -p /data/brick1
mount -o inode64,noatime /dev/vg_brick1/lv_brick1 /data/brick1
echo '/dev/vg_brick1/lv_brick1 /data/brick1 xfs defaults,inode64,noatime 0 0' >> /etc/fstab
关键细节:brick 目录不要直接用挂载点,而应在挂载点下再建一层子目录(如 /data/brick1/gv0)。这样即使数据盘没挂上,GlusterFS 也会因为找不到子目录而拒绝启动 brick,避免把数据写进本地根分区。
mkdir -p /data/brick1/gv0
详见 03-01-磁盘管理与文件系统.md
2.6.3 绑定主机名与 hosts
全部服务器都要绑定主机名。
CentOS 7 及以上(推荐):
hostnamectl set-hostname node1.com --static
CentOS 6(仅适用于 CentOS 6):
vim /etc/sysconfig/network
NETWORKING=yes
HOSTNAME=node1.com
hostname node1.com # 立即生效,不需重启
原始文档写的是
vim /etc/sysconfig/hostname,这在 CentOS 6 中实际应为/etc/sysconfig/network的HOSTNAME项;CentOS 7 中主机名文件为/etc/hostname,推荐用hostnamectl修改。
写入 hosts 文件(全部服务器,包括客户端):
vim /etc/hosts
192.168.224.10 server.com
192.168.224.11 node1.com
192.168.224.12 node2.com
192.168.224.13 node3.com
必须双向可解析:每个节点都要能解析出其它所有节点的主机名,且解析出的 IP 是节点之间真正互通的那张网卡的地址。这是后面 peer probe 最常见的坑(详见 2.7.1)。
2.6.4 时间同步
GlusterFS 依赖文件时间戳判断副本新旧(脑裂"按修改时间"修复策略、self-heal 判定),节点间时间不同步会导致数据修复行为异常。
CentOS 7(chrony):
yum -y install chrony
systemctl start chronyd
systemctl enable chronyd
chronyc sources -v
timedatectl set-timezone Asia/Shanghai
CentOS 6(ntp):
yum -y install ntp
service ntpd start
chkconfig ntpd on
ntpq -p
# 或用定时任务
echo '*/5 * * * * /usr/sbin/ntpdate ntp1.aliyun.com &>/dev/null' >> /var/spool/cron/root
详见 08-03-时间同步与Xinetd.md
2.6.5 防火墙与 SELinux
GlusterFS 需要放行的端口:
| 端口 | 协议 | 用途 |
|---|---|---|
| 24007 | TCP | glusterd 管理进程(客户端获取 volfile、节点间管理通信)必开 |
| 24008 | TCP | RDMA 管理端口(使用 InfiniBand/RDMA 时需要) |
| 49152 起 | TCP | 每个 brick 一个端口,从 49152 开始依次递增(GlusterFS 3.4 之前是 24009 起)。有 N 个 brick 就要放行 49152~49152+N-1,建议直接放行 49152-49251 |
| 38465-38467 | TCP | 内置 gluster-NFS 服务(仅在启用 NFS 导出时需要) |
| 111 | TCP/UDP | rpcbind/portmap(使用 NFS 时需要) |
| 2049 | TCP | NFS-Ganesha(使用时需要) |
| 24009 起 | TCP | 老版本(<3.4)brick 端口 |
CentOS 7(firewalld):
# 精细放行(推荐生产做法)
firewall-cmd --permanent --add-port=24007-24008/tcp
firewall-cmd --permanent --add-port=49152-49251/tcp
firewall-cmd --permanent --add-service=glusterfs # 高版本 firewalld 自带此服务定义
firewall-cmd --reload
firewall-cmd --list-all
# 实验环境可直接关闭
systemctl stop firewalld
systemctl disable firewalld
CentOS 6(iptables,仅适用于 CentOS 6):
# 精细放行
iptables -I INPUT -p tcp --dport 24007:24008 -j ACCEPT
iptables -I INPUT -p tcp --dport 49152:49251 -j ACCEPT
service iptables save
service iptables restart
# 实验环境直接清空规则(源文档做法)
iptables -F
service iptables save
# 或彻底关闭
service iptables stop
chkconfig iptables off
SELinux(两个版本通用):
# 临时关闭
setenforce 0
getenforce
# 永久关闭:编辑配置文件
vim /etc/selinux/config
SELINUX=disabled
也可保留 SELinux 但放开 gluster 相关布尔值(生产更规范的做法):
setsebool -P rsync_full_access on
setsebool -P samba_load_libgfapi on # 与 Samba 集成时
semanage fcontext -a -t glusterd_brick_t "/data/brick1(/.*)?"
restorecon -Rv /data/brick1
详见 07-03-防火墙与SELinux.md
2.6.6 配置 yum 源并安装软件包
在所有机器上执行:
yum -y install epel-release
在 node1~node3(存储节点)上执行:
yum install -y centos-release-gluster
yum install -y glusterfs glusterfs-server glusterfs-fuse glusterfs-rdma
systemctl start glusterd
systemctl enable glusterd
netstat -tunlp | grep glusterd
yum -y install tree
各软件包作用:
| 包名 | 作用 |
|---|---|
| centos-release-gluster | 提供 CentOS SIG 的 gluster yum 源,可选择具体版本(如 centos-release-gluster9) |
| glusterfs | 核心库与公共文件 |
| glusterfs-server | 服务端,含 glusterd 管理进程、glusterfsd brick 进程、gluster 命令行 |
| glusterfs-fuse | FUSE 原生客户端(mount.glusterfs),服务端也建议装,便于本机挂载测试 |
| glusterfs-rdma | RDMA/InfiniBand 传输支持,普通以太网环境可不装 |
| glusterfs-cli | 命令行工具(通常随 server 安装) |
| glusterfs-geo-replication | 异地容灾(geo-rep)功能,按需安装 |
| tree | 实验中用于查看 brick 目录结构 |
CentOS 7 启动服务:
systemctl start glusterd
systemctl enable glusterd
systemctl status glusterd
CentOS 6 启动服务(仅适用于 CentOS 6):
service glusterd start
chkconfig glusterd on
service glusterd status
验证:
gluster --version
netstat -tunlp | grep glusterd
# 应看到 0.0.0.0:24007 处于 LISTEN
ps -ef | grep gluster
2.7 集群与卷管理命令逐项详解
2.7.1 组建信任存储池:peer 相关命令
在 node1 上执行,将 node2、node3 加入集群:
gluster peer probe node2.com
peer probe: success.
gluster peer probe node3.com
peer probe: success.
查看集群状态:
gluster peer status
Number of Peers: 2
Hostname: node2.com
Uuid: ccc69d36-b7ec-457a-a6fa-5a313dbeed10
State: Peer in Cluster (Connected)
Hostname: node3.com
Uuid: 02679d5d-735f-4f01-ac0f-375cb12cdbef
State: Peer in Cluster (Connected)
查看存储池列表(含本机):
gluster pool list
移除节点:
gluster peer detach node3.com
# 节点上还有 brick 时会被拒绝,需先 remove-brick;确实要强制移除:
gluster peer detach node3.com force
peer 命令一览:
| 命令 | 作用 |
|---|---|
gluster peer probe <IP/主机名> |
添加集群成员(探测并加入信任存储池) |
gluster peer status |
查看集群状态(不含本机) |
gluster pool list |
列出存储池所有节点(含本机) |
gluster peer detach <主机名> |
将节点移出信任存储池 |
gluster peer detach <主机名> force |
强制移出(节点已宕机或仍有 brick 时) |
gluster peer help |
查看子命令帮助 |
坑:添加节点必须双向可解析。
gluster peer probe node2.com 是从 node1 主动发起的,成功后 node2 会反向连接 node1。如果 node2 上没有 node1 的 hosts 记录,或解析到错误的 IP,会出现以下典型现象:
- node1 上
gluster peer status显示 node2 为Connected,但 node2 上gluster peer status显示对端主机名是 IP 地址而不是node1.com; - 或者一侧显示
State: Peer Rejected (Connected)/Disconnected; - 后续用主机名创建卷时报
Host node1.com is not in 'Peer in Cluster' state。
正确做法与排查:
# 1. 所有节点(包括第一个节点自己)的 /etc/hosts 都必须写全所有节点
# 2. 双向验证解析
ping -c 2 node1.com
getent hosts node1.com
# 3. 在被 probe 的节点上反向 probe 回去,可修正主机名显示
# 在 node2 上执行:
gluster peer probe node1.com
# 4. 检查每个节点的 UUID 是否唯一(克隆虚拟机最常见问题!)
cat /var/lib/glusterd/glusterd.info
# 若两台机器 UUID 相同(克隆导致),停服务、删除该文件、重启即可重新生成
systemctl stop glusterd
rm -f /var/lib/glusterd/glusterd.info
rm -rf /var/lib/glusterd/peers/*
systemctl start glusterd
其他常见 probe 失败原因:24007 端口未放行、glusterd 未启动、两端 GlusterFS 大版本不一致、Peer Rejected 需要删除 /var/lib/glusterd/vols 后重启同步配置。
2.7.2 卷的创建:gluster volume create 完整语法
gluster volume create <NEW-VOLNAME> \
[stripe <COUNT>] \
[replica <COUNT> [arbiter <COUNT>]] \
[disperse [<COUNT>]] [redundancy <COUNT>] \
[disperse-data <COUNT>] \
[transport <tcp|rdma|tcp,rdma>] \
<NEW-BRICK> <NEW-BRICK> ... \
[force]
创建卷的通用形式(源文档给出的精简形式):
gluster volume create <卷名> [stripe <N>] [replica <N>] transport tcp <brick1> <brick2> ... [force]
参数详解:
| 参数 | 含义与要点 |
|---|---|
<NEW-VOLNAME> |
卷名,客户端挂载时使用。不要用 -/特殊字符开头,建议如 gv0、data-vol |
stripe <COUNT> |
条带数,文件被切成 COUNT 块。brick 总数必须是 COUNT 的整数倍。已废弃 |
replica <COUNT> |
副本数。brick 总数必须是 COUNT 的整数倍;replica 2 有脑裂风险,推荐 replica 3 |
arbiter <COUNT> |
仲裁 brick 数量,通常配合 replica 3 arbiter 1:3 个 brick 中第 3 个只存元数据不存数据,占空间极小,用于防脑裂 |
disperse <COUNT> |
分散卷(EC)的总 brick 数 n |
redundancy <COUNT> |
分散卷的冗余片数 m,即可容忍故障的 brick 数。disperse 6 redundancy 2 表示 4 数据 + 2 校验 |
disperse-data <COUNT> |
直接指定数据片数 k(与 redundancy 二者给其一即可推出另一个) |
transport tcp\|rdma\|tcp,rdma |
传输协议,默认 tcp。RDMA 需要 InfiniBand 硬件与 glusterfs-rdma 包 |
<NEW-BRICK> |
形式为 主机名:/绝对路径。书写顺序决定复制组/条带组/EC 组的分组,务必让同组 brick 跨不同物理节点 |
force |
强制创建,跳过安全检查 |
为何常常需要 force? GlusterFS 在创建卷时会做一系列安全检查,任一不满足就报错,加 force 可跳过:
- brick 位于根分区(
/所在文件系统)——最常见原因。报错:volume create: xxx: failed: The brick node1.com:/opt/gluster/exp1 is being created in the root partition. It is recommended that you don't use the system's root partition for storage backend. Or use 'force' at the end of the command if you want to override this behavior.实验环境用/opt目录做 brick 就属于这种情况。 - 同一个复制组中的多个 brick 在同一台主机上——违反跨节点冗余原则,报错
Multiple bricks of a replicate volume are present on the same server。 - brick 目录已存在且非空、或曾属于其它卷(残留
trusted.glusterfs.volume-id扩展属性)。 - 复制卷副本数为 2(部分版本会警告脑裂风险)。
生产环境应尽量不用 force:先把数据盘挂到独立分区、正确规划 brick 顺序,让检查自然通过。
清理"曾属于其它卷"的 brick 目录(复用 brick 时必做):
# 报错:... or a prefix of it is already part of a volume
setfattr -x trusted.glusterfs.volume-id /data/brick1/gv0
setfattr -x trusted.gfid /data/brick1/gv0
rm -rf /data/brick1/gv0/.glusterfs
# 或者干脆删掉目录重建
rm -rf /data/brick1/gv0 && mkdir -p /data/brick1/gv0
各类型卷创建语法总结:
# 1. 分布式卷(不加任何参数)
gluster volume create gv0 transport tcp node1:/data/b1 node2:/data/b1 node3:/data/b1 force
# 2. 复制卷(3 副本)
gluster volume create gv1 replica 3 transport tcp node1:/data/b2 node2:/data/b2 node3:/data/b2 force
# 3. 条带卷(已废弃)
gluster volume create gv2 stripe 3 transport tcp node1:/data/b3 node2:/data/b3 node3:/data/b3 force
# 4. 分布式复制卷(推荐,replica 2 × 2 组 = 4 brick)
gluster volume create gv3 replica 2 transport tcp \
node1:/data/b4 node2:/data/b4 node3:/data/b4 node4:/data/b4 force
# 4b. 复制卷 + 仲裁盘(推荐替代 replica 2)
gluster volume create gv3a replica 3 arbiter 1 transport tcp \
node1:/data/b4 node2:/data/b4 node3:/data/arb force
# 5. 分布式条带卷(stripe 2 × 2 组 = 4 brick,已废弃)
gluster volume create gv4 stripe 2 transport tcp \
node1:/data/s1 node1:/data/s2 node2:/data/s1 node2:/data/s2 force
# 6. 条带复制卷(stripe 2 × replica 2 = 4 brick,已废弃)
gluster volume create gv5 stripe 2 replica 2 transport tcp \
node1:/data/r1 node1:/data/r2 node2:/data/r1 node2:/data/r2 force
# 7. 分布式条带复制卷(已废弃)
gluster volume create gv6 replica 2 stripe 2 transport tcp \
node1:/data/d1 node1:/data/d2 node1:/data/d3 \
node2:/data/d4 node2:/data/d5 node2:/data/d6 \
node3:/data/d7 node3:/data/d8 force
# 8. 分散卷(EC 4+2)
gluster volume create gv7 disperse 6 redundancy 2 transport tcp \
node1:/data/e1 node1:/data/e2 node2:/data/e1 node2:/data/e2 node3:/data/e1 node3:/data/e2 force
2.7.3 卷的启动、停止、删除
gluster volume start <卷名> # 启动卷(创建后必须启动才能挂载)
gluster volume start <卷名> force # 强制启动(用于个别 brick 进程未起来时重新拉起)
gluster volume stop <卷名> # 停止卷(客户端会立即无法访问,需确认 y)
gluster volume delete <卷名> # 删除卷(必须先 stop;不会删除 brick 目录里的数据)
gluster volume list # 列出所有卷名
注意:
delete只删除卷的元数据配置,brick 目录下的文件仍然存在。要彻底清理需手动删除 brick 目录内容和.glusterfs,否则该目录无法被新卷复用(见 2.7.2 的清理命令)。
2.7.4 卷信息与状态查看
gluster volume info # 查看所有卷的配置信息
gluster volume info <卷名> # 查看指定卷
gluster volume info all
gluster volume status # 查看卷运行状态(brick 进程、端口、PID)
gluster volume status <卷名>
gluster volume status <卷名> detail # 详细信息:磁盘空间、inode、设备名、挂载选项
gluster volume status <卷名> clients # 连接到该卷的客户端列表
gluster volume status <卷名> mem # 内存使用
gluster volume status <卷名> inode # inode 表
gluster volume status <卷名> fd # 打开的文件描述符
gluster volume status <卷名> callpool # 挂起的调用
gluster volume top <卷名> read # 热点文件统计(读)
gluster volume top <卷名> write
gluster volume profile <卷名> start # 开启性能剖析
gluster volume profile <卷名> info
gluster volume profile <卷名> stop
volume status 输出中最重要的是 Online 列:必须全部为 Y。任一 brick 为 N 说明该 brick 的 glusterfsd 进程没起来,需要去对应节点看 brick 日志(见 2.13 排错)。
2.7.5 卷参数调优:volume set / get / reset
gluster volume set <卷名> <参数> <值> # 设置参数
gluster volume get <卷名> <参数> # 查看单个参数当前值
gluster volume get <卷名> all # 查看所有参数及默认值(排错利器)
gluster volume reset <卷名> <参数> # 重置单个参数为默认
gluster volume reset <卷名> # 重置全部参数
gluster volume reset <卷名> force
常用调优与功能参数:
| 参数 | 默认 | 说明 |
|---|---|---|
performance.cache-size |
32MB | 客户端读缓存大小,可调到 1GB~4GB |
performance.io-thread-count |
16 | brick 侧 IO 线程数,SSD 可调 32~64 |
performance.write-behind |
on | 写聚合,提升小写性能 |
performance.write-behind-window-size |
1MB | 写缓冲窗口,可调 4MB~8MB |
performance.read-ahead |
on | 顺序读预读,随机读场景建议 off |
performance.quick-read |
on | 小文件一次性读完(<64KB),小文件场景务必 on |
performance.io-cache |
on | 读缓存 translator |
performance.stat-prefetch |
on | 预取 stat 信息,减少 lookup 次数 |
performance.client-io-threads |
off(复制卷) | 客户端 IO 线程,分布卷建议 on |
performance.parallel-readdir |
off | 并行读目录,大目录 ls 加速 |
network.ping-timeout |
42 秒 | 客户端认定 brick 失联的超时,详见 2.12 |
cluster.quorum-type |
none/auto | 客户端仲裁类型:none/auto/fixed |
cluster.quorum-count |
- | fixed 时的最小副本数 |
cluster.server-quorum-type |
none | 服务端仲裁:server/none |
cluster.server-quorum-ratio |
50% | 服务端仲裁比例 |
cluster.self-heal-daemon |
on | 自愈守护进程开关 |
cluster.data-self-heal-algorithm |
- | full(整文件复制)/diff(差异复制) |
cluster.background-self-heal-count |
8 | 后台并发自愈数 |
cluster.readdir-optimize |
off | 优化 readdir |
cluster.lookup-optimize |
on(高版本) | 优化 lookup,减少广播 |
cluster.min-free-disk |
10% | brick 剩余空间低于此值时新文件写往其它 brick |
features.shard |
off | 分片特性,大文件切成固定大小分片(替代 stripe) |
features.shard-block-size |
64MB | 分片大小 |
features.quota |
off | 配额开关(用 volume quota enable 打开) |
nfs.disable |
on(高版本) | 关闭内置 gluster-NFS 导出 |
auth.allow |
* |
允许挂载的客户端 IP 白名单,如 192.168.224.* |
auth.reject |
none | 拒绝挂载的 IP 黑名单 |
client.ssl / server.ssl |
off | 开启 SSL 传输加密 |
diagnostics.brick-log-level |
INFO | brick 日志级别,排错时调 DEBUG |
diagnostics.client-log-level |
INFO | 客户端日志级别 |
示例:
gluster volume set gv0 performance.cache-size 1GB
gluster volume set gv0 performance.io-thread-count 32
gluster volume set gv0 network.ping-timeout 20
gluster volume set gv0 auth.allow 192.168.224.*
gluster volume set gv0 cluster.quorum-type auto
gluster volume get gv0 performance.cache-size
gluster volume reset gv0 network.ping-timeout
常用命令速查(源文档整理):
| 命令 | 作用 |
|---|---|
gluster peer probe <IP/主机名> |
添加集群成员 |
gluster peer status |
查看集群状态 |
gluster volume info [卷名] |
查看卷信息 |
gluster volume status [卷名] |
查看卷状态 |
gluster volume create ... |
创建卷 |
gluster volume start <卷名> |
启动卷 |
gluster volume stop <卷名> |
停止卷 |
gluster volume delete <卷名> |
删除卷 |
gluster volume add-brick <卷名> <brick> [force] |
添加 brick(扩容) |
gluster volume remove-brick <卷名> <brick> start |
开始删除 brick(缩容) |
gluster volume remove-brick <卷名> <brick> status |
查看删除 brick 的进度 |
gluster volume remove-brick <卷名> <brick> commit |
提交删除 brick 操作 |
gluster volume rebalance <卷名> start |
开启卷数据均衡 |
2.8 实验一:创建分布式卷
2.8.1 创建数据存储目录
在 node1、node2、node3 上分别操作:
mkdir -p /opt/gluster/exp1 # node1
mkdir -p /opt/gluster/exp2 # node2
mkdir -p /opt/gluster/exp3 # node3
2.8.2 创建分布式卷
在 node1 上操作即可:
gluster volume create test-volume node1.com:/opt/gluster/exp1 node2.com:/opt/gluster/exp2 node3.com:/opt/gluster/exp3 force
volume create: test-volume: success: please start the volume to access data
2.8.3 查看卷信息
gluster volume info
Volume Name: test-volume
Type: Distribute
Volume ID: e2248fcf-a37c-44af-92ce-9e31a3a98764
Status: Created
Snapshot Count: 0
Number of Bricks: 3
Transport-type: tcp
Bricks:
Brick1: node1.com:/opt/gluster/exp1
Brick2: node2.com:/opt/gluster/exp2
Brick3: node3.com:/opt/gluster/exp3
Options Reconfigured:
transport.address-family: inet
nfs.disable: on
2.9 实验二:创建复制卷
2.9.1 创建数据存储目录
在 node1、node2、node3 上分别操作:
mkdir /opt/gluster/exp4 # node1
mkdir /opt/gluster/exp5 # node2
mkdir /opt/gluster/exp6 # node3
2.9.2 创建复制卷
在 node1 上操作即可:
gluster volume create repl-volume replica 3 transport tcp node1.com:/opt/gluster/exp4 node2.com:/opt/gluster/exp5 node3.com:/opt/gluster/exp6 force
volume create: repl-volume: success: please start the volume to access data
2.9.3 查看卷信息
gluster volume info repl-volume
Volume Name: repl-volume
Type: Replicate
Volume ID: 37c5200f-75f7-4f53-aca3-0a733a192708
Status: Created
Snapshot Count: 0
Number of Bricks: 1 x 3 = 3
Transport-type: tcp
Bricks:
Brick1: node1.com:/opt/gluster/exp4
Brick2: node2.com:/opt/gluster/exp5
Brick3: node3.com:/opt/gluster/exp6
Options Reconfigured:
transport.address-family: inet
nfs.disable: on
performance.client-io-threads: off
Number of Bricks: 1 x 3 = 3的含义是:1 个复制组 × 每组 3 个副本 = 3 个 brick。这个表达式是判断卷结构最直观的方式。
2.10 实验三:创建条带卷(RAID 0)
适用版本提示:本实验需要支持 stripe 的版本(GlusterFS 5 及以下)。GlusterFS 6+ 执行
stripe参数会报不支持。
2.10.1 创建数据存储目录
在 node1、node2、node3 上分别操作:
mkdir /opt/gluster/exp7 # node1
mkdir /opt/gluster/exp8 # node2
mkdir /opt/gluster/exp9 # node3
2.10.2 创建条带卷
在 node1 上操作即可:
gluster volume create raid0-volume stripe 3 transport tcp node1.com:/opt/gluster/exp7 node2.com:/opt/gluster/exp8 node3.com:/opt/gluster/exp9 force
volume create: raid0-volume: success: please start the volume to access data
2.10.3 查看卷信息
gluster volume info raid0-volume
Volume Name: raid0-volume
Type: Stripe
Volume ID: 61a654be-74c5-4514-80b3-ee2072df4c89
Status: Created
Snapshot Count: 0
Number of Bricks: 1 x 3 = 3
Transport-type: tcp
Bricks:
Brick1: node1.com:/opt/gluster/exp7
Brick2: node2.com:/opt/gluster/exp8
Brick3: node3.com:/opt/gluster/exp9
Options Reconfigured:
transport.address-family: inet
nfs.disable: on
2.11 启动卷
创建后的卷处于 Created 状态,必须启动后才能使用。
2.11.1 查看启动状态
gluster volume status
Volume repl-volume is not started
Volume raid0-volume is not started
Volume test-volume is not started
2.11.2 启动卷
gluster volume start repl-volume
gluster volume start raid0-volume
gluster volume start test-volume
volume start: repl-volume: success
volume start: raid0-volume: success
volume start: test-volume: success
2.11.3 再次查看卷信息
gluster volume info
Volume Name: repl-volume
Type: Replicate
Volume ID: 37c5200f-75f7-4f53-aca3-0a733a192708
Status: Started
Snapshot Count: 0
Number of Bricks: 1 x 3 = 3
Transport-type: tcp
Bricks:
Brick1: node1.com:/opt/gluster/exp4
Brick2: node2.com:/opt/gluster/exp5
Brick3: node3.com:/opt/gluster/exp6
Options Reconfigured:
transport.address-family: inet
nfs.disable: on
performance.client-io-threads: off
Volume Name: raid0-volume
Type: Stripe
Volume ID: 61a654be-74c5-4514-80b3-ee2072df4c89
Status: Started
Snapshot Count: 0
Number of Bricks: 1 x 3 = 3
Transport-type: tcp
Bricks:
Brick1: node1.com:/opt/gluster/exp7
Brick2: node2.com:/opt/gluster/exp8
Brick3: node3.com:/opt/gluster/exp9
Options Reconfigured:
transport.address-family: inet
nfs.disable: on
Volume Name: test-volume
Type: Distribute
Volume ID: e2248fcf-a37c-44af-92ce-9e31a3a98764
Status: Started
Snapshot Count: 0
Number of Bricks: 3
Transport-type: tcp
Bricks:
Brick1: node1.com:/opt/gluster/exp1
Brick2: node2.com:/opt/gluster/exp2
Brick3: node3.com:/opt/gluster/exp3
Options Reconfigured:
transport.address-family: inet
nfs.disable: on
启动后可以看到 Status: Started,同时 gluster volume status 会显示每个 brick 的 TCP 端口(49152 起)与 PID。
2.12 客户端挂载:三种访问方式
GlusterFS 提供三种客户端访问方式,各有取舍。
2.12.1 FUSE 原生客户端(推荐)
安装客户端(在 Server 192.168.224.10 上操作):
yum install -y centos-release-gluster
yum install -y glusterfs glusterfs-fuse glusterfs-rdma
yum install -y glusterfs-client
创建挂载目录:
mkdir /mnt/a1 /mnt/a2 /mnt/a3
挂载三种基础卷:
mount.glusterfs node1.com:/test-volume /mnt/a1/
mount.glusterfs node1.com:/repl-volume /mnt/a2/
mount.glusterfs node1.com:/raid0-volume /mnt/a3/
等价写法(-t glusterfs):
mount -t glusterfs node1.com:/test-volume /mnt/a1
mount -t glusterfs node1.com:test-volume /mnt/a1 # 卷名前的斜杠可省略
关键要点:backup-volfile-servers 避免挂载单点。
挂载时写的 node1.com 只用于获取 volfile 配置,数据路径依然是客户端直连所有 brick——但如果 node1.com 宕机,客户端重启后就无法再挂载(拿不到配置)。解决办法是指定备用 volfile 服务器:
mount -t glusterfs -o backup-volfile-servers=node2.com:node3.com \
node1.com:/test-volume /mnt/a1
老版本参数名为 backupvolfile-server(只能指定一个):
mount -t glusterfs -o backupvolfile-server=node2.com node1.com:/test-volume /mnt/a1
其它常用挂载选项:
| 选项 | 说明 |
|---|---|
backup-volfile-servers=h1:h2:h3 |
备用 volfile 服务器列表,冒号分隔,生产必配 |
log-level=WARNING |
客户端日志级别 |
log-file=/var/log/gluster-a1.log |
指定客户端日志文件 |
ro |
只读挂载 |
acl |
启用 POSIX ACL |
selinux |
启用 SELinux 标签支持 |
direct-io-mode=disable |
关闭 direct IO,一般用于提升小文件读性能 |
use-readdirp=yes |
使用 readdirp 加速目录读取 |
fetch-attempts=N |
获取 volfile 的重试次数 |
_netdev |
fstab 中必加,声明这是网络设备 |
写入 /etc/fstab 实现开机自动挂载(正确写法):
# 设备 挂载点 类型 选项 dump fsck
node1.com:/test-volume /mnt/a1 glusterfs defaults,_netdev,backup-volfile-servers=node2.com:node3.com 0 0
node1.com:/repl-volume /mnt/a2 glusterfs defaults,_netdev,backup-volfile-servers=node2.com:node3.com 0 0
关于 _netdev 的重要性:
- 不加
_netdev,系统会在网络就绪之前尝试挂载,必然失败,可能导致开机卡在 emergency mode。 - CentOS 7 还需保证网络相关服务已启动,可额外加
x-systemd.requires=glusterd.service(如果 brick 也在本机)或x-systemd.automount。 - CentOS 6 需要
netfs服务开机自启:chkconfig netfs on。
验证并处理:
mount -a
df -hT
# 卸载
umount /mnt/a1
umount -l /mnt/a1 # 忙时懒卸载
2.12.2 NFS 方式
GlusterFS 有两套 NFS 实现:
(1)内置 gluster-NFS(NFSv3):老版本默认开启,新版本默认关闭(nfs.disable: on,正是 volume info 中常见的那一行)。
# 服务端开启
gluster volume set test-volume nfs.disable off
# 需要 rpcbind
systemctl start rpcbind && systemctl enable rpcbind # CentOS 7
service rpcbind start && chkconfig rpcbind on # CentOS 6
# 查看导出
gluster volume status test-volume # 可看到 NFS Server 条目
showmount -e node1.com
# 客户端挂载(必须 vers=3,gluster-NFS 不支持 v4)
mount -t nfs -o vers=3,mountproto=tcp,nolock node1.com:/test-volume /mnt/nfs1
(2)NFS-Ganesha(NFSv3/v4,推荐的现代方案):用户态 NFS 服务器,通过 libgfapi 直连 GlusterFS,支持 NFSv4、pNFS、并可配合 pacemaker 做高可用(HA)。
yum -y install nfs-ganesha nfs-ganesha-gluster
vim /etc/ganesha/ganesha.conf
EXPORT {
Export_Id = 1;
Path = "/test-volume";
FSAL {
name = GLUSTER;
hostname = "192.168.224.11";
volume = "test-volume";
}
Access_type = RW;
Squash = No_root_squash;
Disable_ACL = TRUE;
Pseudo = "/test-volume";
Protocols = "3", "4";
Transports = "UDP", "TCP";
SecType = "sys";
}
systemctl restart nfs-ganesha
systemctl enable nfs-ganesha
# 客户端
mount -t nfs -o vers=4 node1.com:/test-volume /mnt/nfs1
详见 09-02-NFS网络文件系统.md
2.12.3 Samba / CIFS 方式
用于 Windows 客户端访问。两种做法:
(1)先在 Linux 上用 FUSE 挂载,再用 Samba 共享该目录(简单但多一跳):
yum -y install samba
mount -t glusterfs node1.com:/test-volume /mnt/gv0
vim /etc/samba/smb.conf
[global]
workgroup = WORKGROUP
security = user
map to guest = Bad User
[gluster-share]
comment = GlusterFS Share
path = /mnt/gv0
browseable = yes
writable = yes
guest ok = no
valid users = @smbgrp
(2)使用 vfs_glusterfs 模块直连(性能更好,无需先挂载):
yum -y install samba samba-vfs-glusterfs
setsebool -P samba_load_libgfapi on
[gluster-gv0]
comment = GlusterFS via libgfapi
vfs objects = glusterfs
glusterfs:volume = test-volume
glusterfs:volfile_server = node1.com
glusterfs:logfile = /var/log/samba/glusterfs-gv0.log
path = /
read only = no
guest ok = yes
kernel share modes = no
systemctl restart smb nmb # CentOS 7
service smb restart # CentOS 6
# Windows 客户端访问 \\192.168.224.11\gluster-gv0
详见 09-04-Samba文件共享.md
2.12.4 三种访问方式对照表
| 对比项 | FUSE 原生客户端 | NFS(gluster-NFS / Ganesha) | Samba / CIFS |
|---|---|---|---|
| 客户端要求 | 需安装 glusterfs-fuse | 系统自带 NFS 客户端即可 | 系统自带 CIFS 客户端 |
| 是否感知集群拓扑 | 是,客户端直连所有 brick | 否,所有流量经过一个 NFS 服务节点 | 否,经过一个 Samba 节点 |
| 高可用 | 天生高可用(配 backup-volfile-servers 即可) | 需额外做 HA(Ganesha + pacemaker/keepalived 或 VIP) | 需额外 VIP/DRBD 做 HA |
| 单点瓶颈 | 无 | 有,NFS 网关是带宽与故障单点 | 有 |
| 大文件吞吐 | 好(带宽可叠加多节点) | 受网关单机网卡限制 | 受网关限制 |
| 小文件/元数据性能 | 一般(每次 lookup 要访问多 brick) | 有时反而更快(网关侧缓存元数据) | 一般 |
| POSIX 语义完整度 | 最完整(ACL、xattr、锁) | v3 语义弱(无状态锁需 nolock),v4 较好 | Windows 语义映射,部分特性受限 |
| 支持客户端平台 | Linux(含 macOS 需自行编译) | Linux/Unix/Windows(NFS 客户端) | Windows、macOS、Linux |
| 配置复杂度 | 低 | 中(Ganesha 高) | 中 |
| 适用场景 | 绝大多数 Linux 生产场景(首选) | 无法装客户端的旧系统、需要 NFS 协议对接的设备 | Windows 桌面/办公文件共享 |
为什么推荐 FUSE 原生客户端? 三个决定性理由:
- 无单点、无瓶颈:客户端从 volfile 得知完整拓扑后直接与每个 brick 建立连接,读写流量不经过任何网关,带宽随节点数线性增长;配上
backup-volfile-servers后连挂载阶段都没有单点。 - 故障自动切换:某个 brick/节点宕机,复制卷客户端会自动切到其它副本,业务无感知;NFS 网关宕机则所有客户端全断。
- POSIX 语义最完整:支持 ACL、扩展属性、文件锁、self-heal 触发,是 GlusterFS 功能的完整体现。NFS/CIFS 只是为兼容性提供的"翻译层"。
代价是 FUSE 有用户态-内核态上下文切换开销,小文件密集场景性能不如内核态文件系统——这属于 GlusterFS 的固有短板(见 2.19 性能调优)。
2.13 实验四:验证三种基础卷的数据分布
2.13.1 查看挂载
df -hT
Filesystem Type Size Used Avail Use% Mounted on
/dev/mapper/centos-root xfs 18G 3.9G 14G 23% /
devtmpfs devtmpfs 479M 0 479M 0% /dev
tmpfs tmpfs 489M 0 489M 0% /dev/shm
tmpfs tmpfs 489M 6.8M 483M 2% /run
tmpfs tmpfs 489M 0 489M 0% /sys/fs/cgroup
/dev/sda1 xfs 497M 125M 373M 26% /boot
tmpfs tmpfs 98M 0 98M 0% /run/user/0
node1.com:/test-volume fuse.glusterfs 53G 13G 40G 24% /mnt/a1
node1.com:/repl-volume fuse.glusterfs 18G 4.5G 14G 26% /mnt/a2
node1.com:/raid0-volume fuse.glusterfs 53G 13G 40G 24% /mnt/a3
注意容量差异,这直接印证了卷类型的容量利用率:分布式卷与条带卷是 3 个 brick 容量之和(53G),复制卷只有单个 brick 的容量(18G)。
2.13.2 写入测试内容
echo abc > /mnt/a1/test1.txt # 写入分布式卷(exp1 exp2 exp3)
echo aaa > /mnt/a1/test2.txt
echo aaa > /mnt/a2/test3.txt # 写入复制卷(exp4 exp5 exp6)
echo aaa > /mnt/a3/test4.txt # 写入条带卷(exp7 exp8 exp9)
2.13.3 在各存储节点查看结果
在各节点用 tree /opt/gluster/ 查看 brick 目录的实际内容。
node1:
/opt/gluster/
├── exp1
│ └── test2.txt
├── exp4
│ └── test3.txt
└── exp7
└── test4.txt
node2:
/opt/gluster/
├── exp2
│ └── test1.txt
├── exp5
│ └── test3.txt
└── exp8
└── test4.txt
node3:
/opt/gluster/
├── exp3
├── exp6
│ └── test3.txt
└── exp9
└── test4.txt
结论:
/mnt/a1/test1.txt和/mnt/a1/test2.txt分布在不同的 brick 中(test2.txt 在 exp1,test1.txt 在 exp2,exp3 恰好没分到文件——哈希是随机的,文件少时分布不均很正常);/mnt/a2/test3.txt在所有 brick 中都有相同的副本(exp4、exp5、exp6 都有);/mnt/a3/test4.txt以条带的方式存储在不同 brick 中(exp7、exp8、exp9 都有同名文件,但每个只含一部分内容;文件太小时其中一些块为空洞,用ls -l看大小一致但du看实际占用不同)。
三种基础卷的总结:
- 分布式卷:将不同数据按照 hash 算法分布在不同 bricks 上;
- 复制卷:顾名思义,将插入的数据以副本的形式写入所有 bricks 中;
- 条带卷:将数据分成不同的块,写入不同的 bricks 中(切分)。
重要纪律:绝对不要直接在 brick 目录里增删改文件。 所有读写都必须通过客户端挂载点进行。直接改 brick 会导致 GFID 缺失、副本不一致、脑裂,GlusterFS 无法自动修复。到 brick 目录下 tree/ls 只读查看是允许的,这也是本实验的做法。
验证条带效果的更好方法(写入足够大的文件):
# 在客户端写入一个 100MB 的文件
dd if=/dev/zero of=/mnt/a3/big.dat bs=1M count=100
# 到各节点查看实际占用(注意用 du 而不是 ls)
du -sh /opt/gluster/exp7/big.dat /opt/gluster/exp8/big.dat /opt/gluster/exp9/big.dat
# 条带卷下每个 brick 约占 33MB;复制卷下每个 brick 都是 100MB;分布卷下只有一个 brick 有该文件
2.14 实验五:分布式复制卷(推荐)
2.14.1 创建数据存储目录
在 node1、node2、node3 上分别操作(每台节点都要有 exp10、exp11):
mkdir /opt/gluster/exp10 /opt/gluster/exp11 # node1
mkdir /opt/gluster/exp10 /opt/gluster/exp11 # node2
mkdir /opt/gluster/exp10 /opt/gluster/exp11 # node3
2.14.2 创建分布式复制卷
在 node1 上操作即可,replica 3 表示每个复制组 3 副本:
gluster volume create dreplica-volume replica 3 transport tcp \
node1.com:/opt/gluster/exp10/ node2.com:/opt/gluster/exp10/ node3.com:/opt/gluster/exp10/ \
node1.com:/opt/gluster/exp11/ node2.com:/opt/gluster/exp11/ node3.com:/opt/gluster/exp11/ force
volume create: dreplica-volume: success: please start the volume to access data
注意 brick 顺序:前 3 个(三台机器的 exp10)构成第一个复制组,后 3 个(三台机器的 exp11)构成第二个复制组。这样每组的 3 个副本分别落在 3 台不同机器上,任一台机器宕机数据仍完整。这就是"brick 书写顺序决定分组"的实际应用。
2.14.3 启动卷
gluster volume start dreplica-volume
volume start: dreplica-volume: success
2.14.4 客户端挂载测试
在 Server 上操作:
mkdir /mnt/dreplica
mount.glusterfs node1.com:/dreplica-volume /mnt/dreplica/
df -hT
Filesystem Type Size Used Avail Use% Mounted on
/dev/mapper/centos-root xfs 18G 3.9G 14G 23% /
devtmpfs devtmpfs 479M 0 479M 0% /dev
tmpfs tmpfs 489M 0 489M 0% /dev/shm
tmpfs tmpfs 489M 6.8M 483M 2% /run
tmpfs tmpfs 489M 0 489M 0% /sys/fs/cgroup
/dev/sda1 xfs 497M 125M 373M 26% /boot
tmpfs tmpfs 98M 0 98M 0% /run/user/0
node1.com:/test-volume fuse.glusterfs 53G 13G 40G 24% /mnt/a1
node1.com:/repl-volume fuse.glusterfs 18G 4.5G 14G 26% /mnt/a2
node1.com:/raid0-volume fuse.glusterfs 53G 13G 40G 24% /mnt/a3
node1.com:/dreplica-volume fuse.glusterfs 18G 4.5G 14G 26% /mnt/dreplica
可先清空之前测试的文件:
rm -rf /mnt/a1/*
rm -rf /mnt/a2/*
rm -rf /mnt/a3/*
写入内容:
echo 1 > /mnt/dreplica/1.txt
echo 1 > /mnt/dreplica/2.txt
echo 1 > /mnt/dreplica/3.txt
echo 1 > /mnt/dreplica/4.txt
2.14.5 查看结果
在 node1、node2、node3 上查看,三个节点结果一致:
/opt/gluster/
├── exp10
│ └── 4.txt
└── exp11
├── 1.txt
├── 2.txt
└── 3.txt
总结:该模式为先复制,再分布式。即:文件先按 hash 选定一个复制组(exp10 组或 exp11 组),然后在组内所有 brick 上各存一份完整副本。所以三台机器看到的目录内容完全相同,而 4 个文件在两个组之间是分散的(4.txt 落在 exp10 组,1/2/3.txt 落在 exp11 组)。
2.15 实验六:分布式条带卷
2.15.1 创建数据存储目录
在 node1、node2 上分别操作:
mkdir /opt/gluster/dstripe1 /opt/gluster/dstripe2 # node1
mkdir /opt/gluster/dstripe1 /opt/gluster/dstripe2 # node2
2.15.2 创建分布式条带卷
在 node1 上操作即可,stripe 2 表示每个条带组 2 个 brick:
gluster volume create dstripe-volume stripe 2 transport tcp \
node1.com:/opt/gluster/dstripe1 node1.com:/opt/gluster/dstripe2 \
node2.com:/opt/gluster/dstripe1 node2.com:/opt/gluster/dstripe2 force
volume create: dstripe-volume: success: please start the volume to access data
2.15.3 启动卷
gluster volume start dstripe-volume
volume start: dstripe-volume: success
2.15.4 客户端挂载测试
在 Server 上操作:
mkdir /mnt/dstripe-volume
mount.glusterfs node1.com:/dstripe-volume /mnt/dstripe-volume/
df -hT
清空上次测试文件并写入内容:
rm -rf /mnt/dreplica/*
echo 1 > /mnt/dstripe-volume/1.txt
echo 1 > /mnt/dstripe-volume/2.txt
echo 1 > /mnt/dstripe-volume/3.txt
echo 1 > /mnt/dstripe-volume/4.txt
2.15.5 查看结果
node1:
/opt/gluster/
├── dstripe1
│ └── 4.txt
└── dstripe2
└── 4.txt
node2:
/opt/gluster/
├── dstripe1
│ ├── 1.txt
│ ├── 2.txt
│ └── 3.txt
└── dstripe2
├── 1.txt
├── 2.txt
└── 3.txt
结论:文件以条带方式分布式存储在多个不同位置。因为文件内容太少,无法完全条带化(每个文件只有 2 字节,不足一个条带块,实际上第一个 brick 有内容、第二个是空文件)。要看到真正的条带效果,需用 dd 写入几十 MB 以上的文件再用 du 对比。
总结:该模式为先分布式,再条带。node1 的两个 brick 构成第一个条带组,node2 的两个 brick 构成第二个条带组;文件先 hash 选组,再在组内切块。
注意此例中同一条带组的两个 brick 都在同一台机器上(node1:dstripe1 + node1:dstripe2),这在实验中可行,但生产上意味着一台机器宕机该组文件全废,是反面示例。正确做法是让同组 brick 跨机器。
2.16 实验七:条带复制卷(复合型)
2.16.1 创建数据存储目录
在 node1、node2 上分别操作:
mkdir /opt/gluster/rstripe1 /opt/gluster/rstripe2 # node1
mkdir /opt/gluster/rstripe1 /opt/gluster/rstripe2 # node2
2.16.2 创建条带复制卷
在 node1 上操作即可:
gluster volume create rstripe-volume stripe 2 replica 2 transport tcp \
node1.com:/opt/gluster/rstripe1 node1.com:/opt/gluster/rstripe2 \
node2.com:/opt/gluster/rstripe1 node2.com:/opt/gluster/rstripe2 force
volume create: rstripe-volume: success: please start the volume to access data
2.16.3 启动卷
gluster volume start rstripe-volume
volume start: rstripe-volume: success
2.16.4 客户端挂载测试
在 Server 上操作:
mkdir /mnt/rstripe-volume
mount.glusterfs node1.com:/rstripe-volume /mnt/rstripe-volume/
df -hT
清空上次测试文件并写入内容:
rm -rf /mnt/dstripe-volume/*
echo 1 > /mnt/rstripe-volume/1.txt
echo 1 > /mnt/rstripe-volume/2.txt
echo 1 > /mnt/rstripe-volume/3.txt
echo 1 > /mnt/rstripe-volume/4.txt
2.16.5 查看结果
在 node1、node2 上查看,两个节点结果一致:
/opt/gluster/
├── rstripe1
│ ├── 1.txt
│ ├── 2.txt
│ ├── 3.txt
│ └── 4.txt
└── rstripe2
├── 1.txt
├── 2.txt
├── 3.txt
└── 4.txt
总结:该模式为先条带,再复制。所有 4 个文件都被切块,块在复制组内保留副本,因此每个 brick 目录里都能看到所有文件名(各自持有不同的条带块及其副本)。因为没有分布层,文件不再被 hash 分散到不同组。
2.17 实验八:分布式条带复制卷
2.17.1 创建数据存储目录
在 node1、node2、node3 上分别操作:
mkdir /opt/gluster/rdstripe1 /opt/gluster/rdstripe2 /opt/gluster/rdstripe3 # node1
mkdir /opt/gluster/rdstripe4 /opt/gluster/rdstripe5 /opt/gluster/rdstripe6 # node2
mkdir /opt/gluster/rdstripe7 /opt/gluster/rdstripe8 # node3
2.17.2 创建分布式条带复制卷
在 node1 上操作即可,replica 2 stripe 2:
gluster volume create rdstripe-volume replica 2 stripe 2 transport tcp \
node1.com:/opt/gluster/rdstripe1 node1.com:/opt/gluster/rdstripe2 node1.com:/opt/gluster/rdstripe3 \
node2.com:/opt/gluster/rdstripe4 node2.com:/opt/gluster/rdstripe5 node2.com:/opt/gluster/rdstripe6 \
node3.com:/opt/gluster/rdstripe7 node3.com:/opt/gluster/rdstripe8 force
volume create: rdstripe-volume: success: please start the volume to access data
共 8 个 brick = stripe 2 × replica 2 × 2 个分布组,正好满足"服务器数目是 stripe 与 replica 乘积的 2 倍及以上"。
2.17.3 启动卷
gluster volume start rdstripe-volume
volume start: rdstripe-volume: success
2.17.4 客户端挂载测试
在 Server 上操作:
mkdir /mnt/rdstripe
mount.glusterfs node1.com:/rdstripe-volume /mnt/rdstripe/
df -hT
清空上次测试文件并批量创建文件:
rm -rf /mnt/rstripe-volume/*
touch /mnt/rdstripe/a{1..10}.txt
2.17.5 查看结果
node1:
/opt/gluster/
├── rdstripe1
│ ├── a10.txt
│ ├── a2.txt
│ ├── a4.txt
│ ├── a5.txt
│ ├── a6.txt
│ └── a7.txt
├── rdstripe2
│ ├── a10.txt
│ ├── a2.txt
│ ├── a4.txt
│ ├── a5.txt
│ ├── a6.txt
│ └── a7.txt
└── rdstripe3
├── a10.txt
├── a2.txt
├── a4.txt
├── a5.txt
├── a6.txt
└── a7.txt
node2:
/opt/gluster/
├── rdstripe4
│ ├── a10.txt
│ ├── a2.txt
│ ├── a4.txt
│ ├── a5.txt
│ ├── a6.txt
│ └── a7.txt
├── rdstripe5
│ ├── a1.txt
│ ├── a3.txt
│ └── a8.txt
└── rdstripe6
├── a1.txt
├── a3.txt
└── a8.txt
node3:
/opt/gluster/
├── rdstripe7
│ ├── a1.txt
│ ├── a3.txt
│ └── a8.txt
└── rdstripe8
├── a1.txt
├── a3.txt
└── a8.txt
可以看出:a2/a4/a5/a6/a7/a10 落在第一个分布组(rdstripe1~4),a1/a3/a8 落在第二个分布组(rdstripe5~8);组内每个文件又被条带化并保留副本,所以组内每个 brick 都能看到组内所有文件名。(a9 未出现,说明它被 hash 到某组后条带块恰好为空,或本次哈希分布如此——文件数少时分布不均是正常现象。)
总结:该模式为先分布式,然后条带,最后复制。
2.18 卷的扩容与缩容
参考官方文档:https://gluster.readthedocs.io/en/latest/Administrator%20Guide/Managing%20Volumes/
2.18.1 客户端先写入数据
在 Server 上向分布式卷写入一批文件:
touch /mnt/a1/{10..19}.txt
ll /mnt/a1/
total 1
-rw-r--r-- 1 root root 0 Sep 18 11:39 10.txt
-rw-r--r-- 1 root root 0 Sep 18 11:39 11.txt
-rw-r--r-- 1 root root 0 Sep 18 11:39 12.txt
-rw-r--r-- 1 root root 0 Sep 18 11:39 13.txt
-rw-r--r-- 1 root root 0 Sep 18 11:39 14.txt
-rw-r--r-- 1 root root 0 Sep 18 11:39 15.txt
-rw-r--r-- 1 root root 0 Sep 18 11:39 16.txt
-rw-r--r-- 1 root root 0 Sep 18 11:39 17.txt
-rw-r--r-- 1 root root 0 Sep 18 11:39 18.txt
-rw-r--r-- 1 root root 0 Sep 18 11:39 19.txt
-rw-r--r-- 1 root root 4 Sep 18 10:36 test1.txt
2.18.2 创建目录并添加 brick(add-brick)
在 node1 上操作:
mkdir /opt/gluster/exp12
gluster volume add-brick test-volume node1.com:/opt/gluster/exp12/ force
volume add-brick: success
查看扩容后的卷信息:
gluster volume info test-volume
Volume Name: test-volume
Type: Distribute
Volume ID: e2248fcf-a37c-44af-92ce-9e31a3a98764
Status: Started
Snapshot Count: 0
Number of Bricks: 4
Transport-type: tcp
Bricks:
Brick1: node1.com:/opt/gluster/exp1
Brick2: node2.com:/opt/gluster/exp2
Brick3: node3.com:/opt/gluster/exp3
Brick4: node1.com:/opt/gluster/exp12
Options Reconfigured:
transport.address-family: inet
nfs.disable: on
此时新 brick 上还没有数据,因为没有开启均衡:
tree /opt/gluster/exp12/
/opt/gluster/exp12/
0 directories, 0 files
这是正常现象,不是故障! 弹性哈希算法在扩容后需要重新划分哈希区间,已有文件仍在原 brick 上;新写入的文件才可能落到新 brick。要让老数据重新分布,必须执行 rebalance。
复制卷扩容的特殊要求:Brick 数必须成倍增加。
# 复制卷 replica 2,扩容必须一次加 2 个 brick(构成一个新的完整复制组)
gluster volume add-brick dreplica-volume node4:/data/b1 node5:/data/b1 force
# 只加 1 个 brick 会报错:
# volume add-brick: failed: Incorrect number of bricks supplied 1 with count 2
# 也可以通过增加副本数来扩容(把 replica 2 变 replica 3)
gluster volume add-brick dreplica-volume replica 3 node4:/data/b1 node5:/data/b1 force
# replica 3 的卷扩容必须一次加 3 个(或其倍数)
# disperse 6 的卷扩容必须一次加 6 个
规律:扩容时新增 brick 数必须是"一个完整冗余组"的整数倍(replica N 加 N 个,disperse n 加 n 个,分布卷任意个)。
2.18.3 开启数据均衡(rebalance)
gluster volume rebalance test-volume start
volume rebalance: test-volume: success: Rebalance on test-volume has been started successfully. Use rebalance status command to check status of the rebalance process.
ID: bcb58321-2f43-4b09-96a1-5833d020b7b2
再次验证,新 brick 上已经有数据被迁移过来:
tree /opt/gluster/exp12/
/opt/gluster/exp12/
├── 11.txt
├── 14.txt
└── 16.txt
rebalance 的两个阶段:fix-layout 与 migrate-data
| 阶段 | 命令 | 做什么 | 耗时 | 影响 |
|---|---|---|---|---|
| 阶段一 fix-layout | gluster volume rebalance <卷> fix-layout start |
只重新计算并写入每个目录的哈希区间(trusted.glusterfs.dht 扩展属性),使新 brick 开始承接新写入的文件。不搬动任何已有数据 |
快(只遍历目录) | 影响小;老文件访问时通过 linkto 指针文件多一跳定位 |
| 阶段二 migrate-data | gluster volume rebalance <卷> start |
先做 fix-layout,然后遍历所有文件,把不在正确 brick 上的文件真正搬过去 | 慢(与数据量成正比,TB 级可能数天) | 大量 IO 与网络流量,业务可能明显变慢 |
执行顺序:fix-layout 必须先于 migrate-data(rebalance start 已隐含包含 fix-layout,无需单独执行)。
# 完整命令集
gluster volume rebalance <卷名> fix-layout start # 仅修布局(推荐扩容后立即执行,代价小)
gluster volume rebalance <卷名> start # 修布局 + 迁移数据
gluster volume rebalance <卷名> start force # 强制迁移(即使目标 brick 空间更紧张)
gluster volume rebalance <卷名> status # 查看进度
gluster volume rebalance <卷名> stop # 停止(已迁移的数据不会回滚,可安全中断)
rebalance status 输出解读:
Node Rebalanced-files size scanned failures skipped status run time in h:m:s
--------- ---------------- ---- ------- -------- ------- --------- -----------------
localhost 3 0Bytes 11 0 0 completed 0:00:01
node2.com 0 0Bytes 11 0 0 completed 0:00:01
node3.com 0 0Bytes 11 0 0 completed 0:00:01
| 字段 | 含义 |
|---|---|
| Rebalanced-files | 已迁移文件数 |
| size | 已迁移数据量 |
| scanned | 已扫描文件数 |
| failures | 迁移失败数,非 0 需查 rebalance 日志 |
| skipped | 跳过数(空间不足、文件被锁、正在被写等) |
| status | in progress / completed / stopped / failed |
生产实践建议:
- 扩容后先只做
fix-layout(几分钟),观察业务正常后,在业务低峰期再做migrate-data。 - 迁移期间不要做
remove-brick、replace-brick、版本升级等操作。 - 若中途需要恢复业务性能,可以
rebalance stop,下次再start(会重新扫描但已迁移的不会重复搬)。
2.18.4 删除 brick(缩容)
在任意一个 node 节点上操作均可。必须使用 start → status → commit 三段式流程。
(1)开始删除 brick
gluster volume remove-brick test-volume node1.com:/opt/gluster/exp12 start
Running remove-brick with cluster.force-migration enabled can result in data corruption. It is safer to disable this option so that files that receive writes during migration are not migrated.
Files that are not migrated can then be manually copied after the remove-brick commit operation.
Do you want to continue with your current cluster.force-migration settings? (y/n) y
volume remove-brick start: success
ID: 248846f3-c47d-4097-bcd1-6fc625d95d66
start 阶段做的事:把要移除的 brick 上的数据迁移到其余 brick,同时把该 brick 标记为"退役中"(不再接收新文件)。
(2)查看删除进度
gluster volume remove-brick test-volume node1.com:/opt/gluster/exp12 status
输出字段与 rebalance status 相同。必须等到 status 为 completed 且 failures 为 0、skipped 为 0 才能进行下一步。
(3)提交删除操作
gluster volume remove-brick test-volume node1.com:/opt/gluster/exp12 commit
commit 阶段做的事:正式把 brick 从卷的拓扑中摘除,更新 volfile。
(4)验证数据
被删除的 brick 目录 exp12 中的文件已被迁移到其余 brick,此时可以确认客户端挂载点下文件数量与内容完好,然后再清理该目录:
# 在客户端确认文件齐全
ls /mnt/a1/ | wc -l
# 在 node1 上确认 exp12 已空(或只剩 .glusterfs 元数据目录)
tree -a /opt/gluster/exp12/
# 确认无误后清理
rm -rf /opt/gluster/exp12
严重陷阱:跳过 start/status 直接 commit 会丢数据!
# 危险!!!不要这样做
gluster volume remove-brick test-volume node1.com:/opt/gluster/exp12 commit
直接 commit(或在 status 未 completed 时就 commit)会立即把 brick 从卷中摘除,而该 brick 上尚未迁移的文件全部从命名空间中消失——客户端 ls 看不到、读不到,数据只剩在那个已脱离集群的目录里,只能人工从 brick 目录一个个拷回挂载点。老版本还有 remove-brick force 子命令,效果同样是"不迁移直接摘除",除非该 brick 的磁盘已经彻底损坏、数据已确认无价值,否则绝不要用。
正确/危险操作对照:
| 操作 | 是否迁移数据 | 结果 |
|---|---|---|
remove-brick ... start → status completed → commit |
是 | 安全,数据完整保留 |
remove-brick ... commit(直接) |
否 | 未迁移的数据从卷中消失,需人工抢救 |
remove-brick ... force |
否 | 同上,仅用于磁盘已损坏的情况 |
remove-brick ... stop |
- | 中止迁移,brick 回到正常服务状态 |
复制卷缩容同样要按组:
# replica 2 的分布复制卷,必须一次移除一个完整复制组(2 个 brick)
gluster volume remove-brick dreplica-volume node4:/data/b1 node5:/data/b1 start
gluster volume remove-brick dreplica-volume node4:/data/b1 node5:/data/b1 status
gluster volume remove-brick dreplica-volume node4:/data/b1 node5:/data/b1 commit
# 减少副本数(replica 3 -> replica 2),这种情况数据无需迁移,可直接 force
gluster volume remove-brick repl-volume replica 2 node3.com:/opt/gluster/exp6 force
2.18.5 replace-brick 换盘流程
用于磁盘损坏或计划更换硬件的场景,把某个 brick 替换为新 brick(数据由 self-heal 从其它副本重建)。
# 语法
gluster volume replace-brick <卷名> <旧BRICK> <新BRICK> commit force
# 示例:node2 的 exp5 盘坏了,换到新盘 /data/newbrick
# 1. 在 node2 上准备新 brick 目录(新盘已格式化挂载)
mkdir -p /data/newbrick/gv1
# 2. 执行替换
gluster volume replace-brick repl-volume node2.com:/opt/gluster/exp5 node2.com:/data/newbrick/gv1 commit force
# 3. 触发全量自愈,把数据从其它副本同步到新 brick
gluster volume heal repl-volume full
# 4. 观察自愈进度
gluster volume heal repl-volume info
gluster volume heal repl-volume statistics
gluster volume status repl-volume # 确认新 brick Online 为 Y
注意事项:
replace-brick只适用于有冗余的卷(复制卷、分散卷)。纯分布卷上替换会丢该 brick 的数据(此时应改用 add-brick + remove-brick start/commit 组合)。- 新 brick 必须是空目录且不带残留扩展属性。
- 老版本的
replace-brick ... start/status/commit三段式已在新版本移除,现在统一用commit force。 - 原地换盘的替代方案(brick 路径不变,只是把盘换了):
# 停止该 brick 所在节点的 glusterd,换盘、格式化、挂到原路径
systemctl stop glusterd
# 换盘、mkfs、mount 到 /opt/gluster/exp5
# 从其它副本节点复制 volume-id 扩展属性,让 GlusterFS 认可这个空目录
getfattr -n trusted.glusterfs.volume-id -e hex /opt/gluster/exp4 # 在健康节点上执行
setfattr -n trusted.glusterfs.volume-id -v 0x<上面取到的值> /opt/gluster/exp5
systemctl start glusterd
gluster volume heal repl-volume full
2.19 副本修复与一致性
2.19.1 自愈机制(self-heal)
复制卷的一致性由 AFR(Automatic File Replication) translator 保证。当某个副本因节点宕机、网络中断而落后时,AFR 通过扩展属性中的 *changelog(`trusted.afr.`)** 记录"谁欠谁的数据",随后由自愈机制补齐。
三种触发自愈的途径:
| 途径 | 说明 |
|---|---|
| 客户端触发(读修复) | 客户端访问文件时发现副本不一致,就地触发修复。缺点:不被访问的文件永远不修复 |
| self-heal daemon(shd) | 每个节点上的 glustershd 守护进程,默认每 600 秒扫描一次 .glusterfs/indices/xattrop 索引目录,自动修复待修复文件。这是主力机制 |
| 手动触发 | gluster volume heal <卷名> 立即触发一轮,或 full 做全量扫描 |
相关命令:
gluster volume heal <卷名> # 触发一次增量自愈
gluster volume heal <卷名> full # 全量自愈(遍历所有文件,慢,换盘后使用)
gluster volume heal <卷名> info # 列出待修复文件(每个 brick 分别列出)
gluster volume heal <卷名> info summary # 汇总数量(高版本)
gluster volume heal <卷名> info split-brain # 列出处于脑裂状态的文件
gluster volume heal <卷名> info healed # 已修复列表(部分版本)
gluster volume heal <卷名> info heal-failed # 修复失败列表
gluster volume heal <卷名> statistics # 自愈统计
gluster volume heal <卷名> statistics heal-count # 待修复计数
gluster volume heal <卷名> enable # 启用自愈守护进程
gluster volume heal <卷名> disable # 禁用(大批量迁移时可临时关闭)
gluster volume set <卷名> cluster.self-heal-daemon on
gluster volume set <卷名> cluster.heal-timeout 600 # shd 扫描间隔(秒)
gluster volume set <卷名> cluster.background-self-heal-count 8
gluster volume set <卷名> cluster.data-self-heal-algorithm diff # full 整文件 / diff 只传差异块
gluster volume status <卷名> # Self-heal Daemon 行应显示 Online: Y
模拟与验证自愈:
# 1. 停掉 node3 的 glusterd 与 brick 进程(模拟宕机)
systemctl stop glusterfsd glusterd # 在 node3 上
# 2. 客户端继续写入
echo new > /mnt/a2/heal-test.txt
# 3. 查看待修复列表(在 node1 上)
gluster volume heal repl-volume info
# 会显示 heal-test.txt 待修复
# 4. 启动 node3
systemctl start glusterd # 在 node3 上
# 5. 等待自动修复,或手动触发
gluster volume heal repl-volume
gluster volume heal repl-volume info # 应变为 Number of entries: 0
# 6. 验证 node3 的 brick 上已有该文件
cat /opt/gluster/exp6/heal-test.txt
2.19.2 脑裂(split-brain)的成因与解决
什么是脑裂:复制卷的两个(或多个)副本都被独立修改过,GlusterFS 无法判断哪个是"正确的版本",于是拒绝提供服务。客户端访问该文件时报:
Input/output error
日志中出现:
[afr-common.c:xxx] E [MSGID: 108008] [afr-read-txn.c:90:afr_read_txn_refresh_done] 0-gv0-replicate-0:
Failing READ on gfid xxxxx: split-brain observed. [Input/output error]
成因:
| 成因 | 说明 |
|---|---|
| 网络分区(最典型) | 两副本之间网络断开,但两侧都有客户端在写。恢复后两边 changelog 都指责对方,无法裁决 |
| 两副本卷(replica 2)+ 未开 quorum | 只有两票,无法形成多数派,天然易脑裂。这是 replica 2 最大的问题 |
| 直接在 brick 目录里改文件 | 绕过 GlusterFS 修改,扩展属性不匹配 |
| 双方交替宕机 | A 宕机时写了 B,B 宕机时又写了 A,两边各有对方缺失的修改 |
| 时间不同步 | 影响新旧判定,加剧问题 |
脑裂的三种类型:
- data split-brain:文件内容不一致。
- metadata split-brain:权限、属主、时间戳不一致。
- entry / gfid split-brain:同名文件在不同副本上有不同的 GFID(最难处理,需手工删除一侧的文件与
.glusterfs硬链接)。
发现脑裂:
gluster volume heal <卷名> info split-brain
Brick node1.com:/opt/gluster/exp4
/split-file.txt
Status: Connected
Number of entries in split-brain: 1
Brick node2.com:/opt/gluster/exp5
/split-file.txt
Status: Connected
Number of entries in split-brain: 1
也可用 gluster volume heal <卷名> info 中标记为 Is in split-brain 的条目,或在客户端遇到 Input/output error 时排查。
查看各副本的 changelog 扩展属性(人工判断依据):
# 在每个 brick 所在节点执行
getfattr -d -m . -e hex /opt/gluster/exp4/split-file.txt
trusted.afr.gv1-client-0=0x000000000000000000000000
trusted.afr.gv1-client-1=0x000000020000000000000000 <-- 非零表示"我认为 client-1 那份数据是脏的"
trusted.gfid=0x8f7c...
若两侧都指责对方非零,即为脑裂。
三种解决策略(GlusterFS 3.6+ 提供 heal split-brain 子命令):
策略一:按文件大小(保留较大的那份)
gluster volume heal <卷名> split-brain bigger-file /split-file.txt
适用场景:文件是追加写的(日志、不断增长的数据文件),大的那份必然包含小的那份的内容。路径是相对于卷根的路径。
策略二:按修改时间(保留最新的那份)
gluster volume heal <卷名> split-brain latest-mtime /split-file.txt
适用场景:以"最后写入者胜出"为业务语义可接受时。前提是节点间时间必须同步,否则会挑错版本——这正是 2.6.4 强调时间同步的原因。
策略三:手工指定源 Brick(最可控,推荐)
# 指定单个文件,以 node1 的副本为准
gluster volume heal <卷名> split-brain source-brick node1.com:/opt/gluster/exp4 /split-file.txt
# 不带文件路径:把该 brick 作为整个卷所有脑裂文件的源(批量处理,谨慎使用)
gluster volume heal <卷名> split-brain source-brick node1.com:/opt/gluster/exp4
处理前应先人工对比两份文件内容再决定以哪边为准:
# 在 node1 与 node2 上分别查看
md5sum /opt/gluster/exp4/split-file.txt # node1
md5sum /opt/gluster/exp5/split-file.txt # node2
ls -l /opt/gluster/exp4/split-file.txt
diff /opt/gluster/exp4/split-file.txt /tmp/copy-from-node2.txt
修复后验证:
gluster volume heal <卷名> info split-brain # 应为 0 entries
cat /mnt/a2/split-file.txt # 客户端能正常读取
GFID 脑裂的手工处理(heal split-brain 无法处理时的最后手段):
# 1. 在决定舍弃的那个 brick 上,找到文件的 GFID
getfattr -n trusted.gfid -e hex /opt/gluster/exp5/split-file.txt
# 输出如 0x8f7c1a2b3c4d5e6f7a8b9c0d1e2f3a4b
# 2. 删除该 brick 上的文件与 .glusterfs 下的硬链接
rm -f /opt/gluster/exp5/split-file.txt
rm -f /opt/gluster/exp5/.glusterfs/8f/7c/8f7c1a2b-3c4d-5e6f-7a8b-9c0d1e2f3a4b
# 3. 触发自愈,从健康副本重建
gluster volume heal <卷名>
警告:手工删除 brick 内文件是高危操作,务必先备份、确认 GFID 与目标 brick 无误。
如何预防脑裂:见下一节的 quorum 机制。
2.19.3 quorum 仲裁机制
quorum(仲裁)的思想:当存活副本不足以构成多数派时,主动拒绝写入,宁可不可写也不产生分歧数据。这是 CAP 中选择 C 而牺牲 A。
(1)客户端仲裁(client-side quorum,cluster.quorum-type)
作用范围:单个复制组。由客户端判断本组可写副本是否达到 quorum,不达到则该组变为只读。
gluster volume set <卷名> cluster.quorum-type none # 不做仲裁(默认对 replica 2,最易脑裂)
gluster volume set <卷名> cluster.quorum-type auto # 自动:需要 > 50% 的副本存活;副本数为偶数时,需第一个 brick 参与
gluster volume set <卷名> cluster.quorum-type fixed # 固定:配合 quorum-count
gluster volume set <卷名> cluster.quorum-count 2 # fixed 时至少需要 2 个副本存活才允许写
gluster volume get <卷名> cluster.quorum-type
auto 在不同副本数下的效果:
| 副本数 | auto 的 quorum 要求 | 可容忍故障数(仍可写) |
|---|---|---|
| replica 2 | 需要 2 个中至少 1 个,且必须包含第一个 brick | 只能挂第二个 brick |
| replica 3 | 需要 3 个中至少 2 个 | 可挂 1 个 |
| replica 4 | 需要 4 个中至少 3 个(偶数需含第一个) | 可挂 1 个 |
| replica 5 | 需要 5 个中至少 3 个 | 可挂 2 个 |
(2)服务端仲裁(server-side quorum,cluster.server-quorum-type)
作用范围:整个 trusted pool。由 glusterd 之间互相判断,若存活节点数不足比例,则主动杀掉本节点上的 brick 进程,使少数派彻底停止服务,从根本上防止两侧同时写。
gluster volume set <卷名> cluster.server-quorum-type server # 启用(none 为关闭)
gluster volume set all cluster.server-quorum-ratio 51% # 全局设置,需 > 50%
gluster volume get <卷名> cluster.server-quorum-type
注意 server-quorum-ratio 是集群全局参数(用 volume set all),且必须大于 50% 才有意义(设为 50% 时可能两边都认为自己是多数派)。
(3)为什么建议三副本,或两副本 + 仲裁盘
| 方案 | 空间开销 | 防脑裂能力 | 可用性 | 评价 |
|---|---|---|---|---|
| replica 2,不开 quorum | 2 倍 | 无,极易脑裂 | 高(任一存活都能写) | 不推荐 |
| replica 2,开 quorum auto | 2 倍 | 有 | 差:第一个 brick 挂了整组就只读 | 勉强可用 |
| replica 3 | 3 倍 | 强(2/3 多数派) | 好(可挂 1 个仍读写) | 推荐 |
| replica 3 arbiter 1 | 约 2 倍(第三个只存元数据) | 强(有第三票裁决) | 好(可挂 1 个数据 brick) | 强烈推荐:三副本的防脑裂能力 + 两副本的空间成本 |
仲裁盘(arbiter)的原理:第三个 brick 只存储文件的元数据(文件名、权限、GFID、changelog)而不存文件内容,占用空间极小(通常几 GB 即可服务几十 TB 数据),但它拥有完整的"投票权",能在两个数据副本发生分歧时提供决定性的第三票。
创建仲裁卷:
gluster volume create gv-arb replica 3 arbiter 1 transport tcp \
node1.com:/data/brick1/gv-arb \
node2.com:/data/brick1/gv-arb \
node3.com:/data/arbiter/gv-arb force
gluster volume start gv-arb
gluster volume info gv-arb # Type 显示 Replicate,Number of Bricks: 1 x (2 + 1) = 3
把已有的 replica 2 卷转换为 replica 3 arbiter 1:
gluster volume add-brick gv3 replica 3 arbiter 1 node3.com:/data/arbiter/gv3 force
gluster volume heal gv3 full
仲裁盘的限制:仲裁 brick 上没有数据,因此不能从仲裁 brick 读取文件内容;当两个数据 brick 中只剩一个时,为防止脑裂,该组变为只读(这是设计意图,不是故障)。
2.20 配额与快照
2.20.1 配额(quota)
限制目录(包括卷根)的容量使用,防止某个业务目录写满整个卷。
# 1. 启用配额功能(卷级开关,启用后会遍历统计已有数据,大卷需要时间)
gluster volume quota <卷名> enable
gluster volume quota <卷名> disable
# 2. 设置目录容量限制(路径是相对于卷根的路径,必须已存在)
gluster volume quota <卷名> limit-usage / 10GB
gluster volume quota <卷名> limit-usage /project-a 5GB
gluster volume quota <卷名> limit-usage /project-b 2GB 90% # 第三个参数为软限阈值百分比
# 3. 设置目录文件数(inode)限制
gluster volume quota <卷名> limit-objects /project-a 10000
# 4. 查看配额
gluster volume quota <卷名> list
gluster volume quota <卷名> list /project-a
gluster volume quota <卷名> list-objects
# 5. 移除限制
gluster volume quota <卷名> remove /project-a
gluster volume quota <卷名> remove-objects /project-a
# 6. 相关参数
gluster volume set <卷名> features.quota-deem-statfs on # 让客户端 df 显示配额值而非物理容量(强烈建议开启)
gluster volume quota <卷名> soft-timeout 60
gluster volume quota <卷名> hard-timeout 5
gluster volume quota <卷名> alert-time 1d
gluster volume quota <卷名> default-soft-limit 80%
quota list 输出示例:
Path Hard-limit Soft-limit Used Available Soft-limit exceeded? Hard-limit exceeded?
-----------------------------------------------------------------------------------------------------------------------------
/ 10.0GB 80%(8.0GB) 2.1GB 7.9GB No No
/project-a 5.0GB 80%(4.0GB) 4.5GB 0.5GB Yes No
注意点:
- 配额统计是异步的,短时间内
list的值可能滞后。 - 开启
features.quota-deem-statfs on后客户端df -h才会显示配额上限,否则用户会看到整个卷的容量而误判。 - 超过硬限后写入报
Disk quota exceeded。 - 高版本对 quota 的支持在弱化,超大规模场景建议用独立卷做隔离而非依赖 quota。
2.20.2 快照(snapshot)
GlusterFS 快照基于底层 LVM 精简卷(thin-provisioned LV)的写时复制实现。
前提条件(硬性要求):
- 每个 brick 必须位于一个独立的 LVM 精简卷(thin LV)上,普通 LV 或裸分区不支持快照。
- 精简池(thin pool)需预留足够空间存放快照的 COW 数据。
- 卷必须处于
Started状态。 - 所有节点都需在线。
准备 thin LV(参见 2.6.2 的 LVM 示例),然后:
# 1. 创建快照(不指定名字会自动生成)
gluster snapshot create <快照名> <卷名> [no-timestamp] [description "备注"]
gluster snapshot create snap1 gv0 no-timestamp description "before upgrade"
# 2. 查看快照
gluster snapshot list
gluster snapshot list <卷名>
gluster snapshot info
gluster snapshot info snap1
gluster snapshot status
gluster snapshot status snap1
# 3. 激活快照(创建后默认未激活,激活后才能挂载访问)
gluster snapshot activate snap1
gluster snapshot deactivate snap1
# 4. 以只读方式挂载快照查看内容(推荐的"取数据"方式,不影响原卷)
mount -t glusterfs node1.com:/snaps/snap1/gv0 /mnt/snap1
ls /mnt/snap1
# 5. 回滚到快照(危险操作)
gluster volume stop gv0 # 必须先停卷!
gluster snapshot restore snap1 # 回滚后该快照本身被消耗(消失)
gluster volume start gv0
# 6. 删除快照
gluster snapshot delete snap1
gluster snapshot delete all
gluster snapshot delete volume <卷名> # 删除某卷的所有快照
# 7. 克隆快照为一个新的可写卷(不影响原卷与快照)
gluster snapshot clone <新卷名> snap1
gluster volume start <新卷名>
# 8. 配置
gluster snapshot config # 查看配置
gluster snapshot config <卷名> snap-max-hard-limit 100 # 单卷最大快照数
gluster snapshot config snap-max-soft-limit 90%
gluster snapshot config <卷名> auto-delete enable # 超过软限自动删最老的
gluster snapshot config <卷名> activate-on-create enable # 创建即激活
注意:snapshot restore 会消耗(删除)被用于恢复的那个快照,且必须先 volume stop,业务会中断。生产上更常用的做法是 挂载快照 → 拷贝需要的文件回原卷,或 clone 成新卷 后切换业务。
2.21 性能调优
2.21.1 缓存与 IO 相关参数
# 读缓存:客户端 io-cache translator 的缓存大小,默认 32MB
gluster volume set gv0 performance.cache-size 1GB
gluster volume set gv0 performance.cache-max-file-size 4MB # 只缓存小于此大小的文件
gluster volume set gv0 performance.cache-min-file-size 0
gluster volume set gv0 performance.cache-refresh-timeout 10 # 缓存有效期(秒)
# IO 线程:brick 侧处理请求的线程数,默认 16
gluster volume set gv0 performance.io-thread-count 32 # SSD/NVMe 可到 32~64
gluster volume set gv0 performance.high-prio-threads 32
gluster volume set gv0 performance.normal-prio-threads 32
gluster volume set gv0 performance.low-prio-threads 32
gluster volume set gv0 performance.least-prio-threads 1
# 写聚合(write-behind):把多次小写合并成大写后异步下发,显著提升小写性能
gluster volume set gv0 performance.write-behind on
gluster volume set gv0 performance.write-behind-window-size 8MB # 默认 1MB
gluster volume set gv0 performance.flush-behind on # flush 也异步(有丢数据风险)
gluster volume set gv0 performance.strict-write-ordering off
# 预读(read-ahead):顺序读时提前读取后续块
gluster volume set gv0 performance.read-ahead on # 顺序读大文件时开
gluster volume set gv0 performance.read-ahead-page-count 8
# 随机读为主(数据库、VM 镜像)时建议关闭,否则预读的数据用不上,纯浪费带宽
gluster volume set gv0 performance.read-ahead off
# quick-read:小文件在一次 lookup 中就把内容一起读回来,省掉 open+read+close 三次往返
gluster volume set gv0 performance.quick-read on # 小文件场景务必开启
gluster volume set gv0 performance.qr-cache-timeout 1
# 元数据加速
gluster volume set gv0 performance.stat-prefetch on # 预取 stat(md-cache)
gluster volume set gv0 performance.md-cache-timeout 600
gluster volume set gv0 network.inode-lru-limit 200000
gluster volume set gv0 performance.parallel-readdir on # 并行读目录,大目录 ls 提速
gluster volume set gv0 performance.readdir-ahead on
gluster volume set gv0 performance.rda-cache-limit 128MB
gluster volume set gv0 cluster.lookup-optimize on # 减少 DHT 的全 brick 广播查找
# 客户端 IO 线程(分布卷建议开,复制卷默认 off)
gluster volume set gv0 performance.client-io-threads on
2.21.2 network.ping-timeout 的含义与调小的风险
gluster volume set gv0 network.ping-timeout 42 # 默认 42 秒
含义:客户端(或 brick 之间)通过心跳检测对端存活。若在 ping-timeout 时间内没有收到对端响应,就认为该 brick 已失联,断开连接、释放该连接上所有已持有的锁与文件描述符,并把该副本标记为需要修复。
默认 42 秒的意义:这不是"响应慢"的超时,而是"确认对端真的死了"的判定时间。断开连接的代价很高:
- 需要重新建立 TCP 连接、重新拉取 volfile、重新获取所有锁(fd 重建);
- 该 brick 恢复后需要 self-heal 补齐差异;
- 对正在进行的大量并发 IO 来说,重建连接的开销可能达到数秒到数十秒。
调小的风险(重要):
| 风险 | 说明 |
|---|---|
| 误判抖动为宕机 | 网络瞬时抖动、节点短暂高负载(GC、大量 flush、备份任务)都可能让心跳延迟几秒。调到 5 秒会导致频繁误判 |
| 连接反复重建 | 每次误判都触发断开+重连+锁重建,这个过程本身比等待 42 秒更慢,会造成 IO 抖动甚至雪崩 |
| 触发无谓的 self-heal | 每次误判后该副本被标记为脏,产生大量不必要的自愈流量,进一步加重网络负担 |
| 加剧脑裂风险 | 频繁的"分区/恢复"切换增加了副本分歧的机会 |
建议:
- 万兆内网、网络稳定:可适度调小到 20~30 秒,缩短故障切换时间。
- 千兆或网络质量一般:保持默认 42 秒。
- 需要秒级故障切换的场景,正确做法不是把这个值调到极小,而是在应用层做重试与超时控制,或改用更适合的存储方案。
- 绝不要设为 0(表示永不超时)或 1~5 这种极小值。
2.21.3 小文件性能差的根因与优化方向
根因分析(每一个都是真实开销):
| 根因 | 说明 |
|---|---|
| FUSE 上下文切换 | 每次系统调用要在内核态与用户态之间来回,小文件请求数多,切换开销占比极高 |
| 多次网络往返 | 一次 open+read+close 至少 3 次 RPC;复制卷还要对所有副本加锁、写、解锁 |
| LOOKUP 风暴 | DHT 定位文件时,若哈希未命中或存在 linkto,需要向所有 brick 广播 LOOKUP |
| 元数据分散 | 没有集中元数据服务器,stat 一个文件需要访问对应 brick,ls -l 大目录要访问所有 brick 并合并 |
| 写放大 | 复制卷写一次要写 N 份;EC 卷还要做编码计算 |
| 磁盘随机 IO | 大量小文件在底层文件系统上是随机 IO,HDD 上 IOPS 极低 |
优化方向:
# 1. 元数据缓存(收益最大)
gluster volume set gv0 performance.stat-prefetch on
gluster volume set gv0 performance.md-cache-timeout 600
gluster volume set gv0 network.inode-lru-limit 200000
gluster volume set gv0 performance.cache-invalidation on
gluster volume set gv0 features.cache-invalidation on
gluster volume set gv0 features.cache-invalidation-timeout 600
# 2. 小文件读取路径优化
gluster volume set gv0 performance.quick-read on
gluster volume set gv0 performance.io-cache on
gluster volume set gv0 performance.cache-size 2GB
# 3. 目录遍历优化
gluster volume set gv0 performance.parallel-readdir on
gluster volume set gv0 performance.readdir-ahead on
gluster volume set gv0 cluster.lookup-optimize on
gluster volume set gv0 cluster.readdir-optimize on
# 4. 写路径优化
gluster volume set gv0 performance.write-behind-window-size 8MB
gluster volume set gv0 performance.client-io-threads on
架构层面的优化(比调参更有效):
- 换硬件:小文件性能高度依赖 IOPS,用 SSD/NVMe 做 brick 是最有效的手段(比任何调参都管用);元数据密集时把 XFS 的日志放到 SSD。
- 应用层聚合:把海量小文件打包成大文件(tar、SequenceFile)后再存,或改用对象存储/FastDFS 这类专为小文件设计的方案——这也是本文第三部分 FastDFS 的价值所在。
- 前置缓存:应用层加 Redis/Memcached 缓存热点小文件内容,或用 Nginx/CDN 做静态资源缓存。
- libgfapi 直连:应用改用
libgfapiAPI(绕过 FUSE 与内核,减少上下文切换),可提升 30%~100%。 - 减少单目录文件数:控制单目录条目数在几千以内,用多级目录分散(如按日期/哈希前缀分目录)。
- 不要用 EC 卷存小文件:分散卷的编解码开销在小文件上无法摊薄。
2.21.4 网络:万兆网与 RDMA
| 网络方案 | 带宽 | 延迟 | 说明 |
|---|---|---|---|
| 千兆以太网 | 125MB/s 理论 | 高 | 单块 SATA 盘就能打满,是最常见的性能瓶颈。仅适合实验和低负载 |
| 万兆以太网 | 1.25GB/s | 中 | 生产环境最低要求。建议双口做 bond(mode=4 LACP 或 mode=1 主备) |
| 25G/40G/100G | 更高 | 低 | 大规模集群、全闪存池 |
| InfiniBand + RDMA | 40G~200G | 极低(微秒级) | 绕过内核 TCP/IP 栈,CPU 开销极低。需 glusterfs-rdma 包与 IB 硬件;创建卷时 transport rdma 或 tcp,rdma |
# 使用 RDMA 传输
gluster volume create gv-rdma replica 2 transport rdma node1:/data/b1 node2:/data/b1 force
# 同时支持两种传输,客户端可选择
gluster volume create gv-both replica 2 transport tcp,rdma node1:/data/b1 node2:/data/b1 force
mount -t glusterfs -o transport=rdma node1:/gv-both /mnt/gv
网络规划建议:存储流量与业务流量物理隔离(分开的网卡与交换机/VLAN),因为副本写、rebalance、self-heal 都会产生巨大的东西向流量。
注意:RDMA 支持在部分 GlusterFS 版本中维护不足,上生产前需充分测试。
2.21.5 Brick 数量与磁盘规划建议
| 建议项 | 说明 |
|---|---|
| 一盘一 brick | 不要用大 RAID 组做单个巨大 brick;一盘一 brick 让故障域最小、并发度最高。若必须用 RAID,建议 RAID 6(EC 卷)或 RAID 10 |
| brick 大小 | 单 brick 建议不超过 10~30TB,太大则 self-heal / rebalance 时间无法接受 |
| brick 数量 | 单节点 brick 数不宜过多(每个 brick 一个 glusterfsd 进程,各占内存与端口)。建议单节点 不超过 12~24 个 |
| 节点数 | 复制卷建议节点数为 replica 的整数倍,且 >= 3;EC 卷节点数 >= n(让每个 EC 片落在不同物理节点上) |
| 文件系统 | XFS(官方推荐),mkfs.xfs -i size=512,挂载加 inode64,noatime,nodiratime |
| 挂载路径 | brick 放在挂载点的子目录中(如 /data/brick1/gv0),防止盘未挂载时写进根分区 |
| RAID 卡 | 开启 writeback 缓存(需带 BBU 电池),关闭单盘的 write cache |
| 系统盘 | 独立,不与 brick 共用 |
| 预留空间 | 设置 cluster.min-free-disk 10%,避免 brick 写满导致整卷异常 |
| 同构性 | 同一个卷内所有 brick 的容量与性能应尽量一致,否则小的 brick 先写满,木桶效应明显 |
| 内存 | 每 brick 预留约 1GB 内存较为宽裕;客户端 cache-size 要考虑总内存 |
| CPU | EC 卷(纠删码)对 CPU 敏感,需要更多核心 |
gluster volume set gv0 cluster.min-free-disk 10%
gluster volume set gv0 cluster.min-free-inodes 5%
2.22 日常运维与排错
2.22.1 日志位置与用途
所有日志位于 /var/log/glusterfs/:
| 日志文件 | 用途 |
|---|---|
glusterd.log |
管理进程日志。peer probe、卷创建/启动/停止、集群通信问题首先看这个 |
bricks/<挂载路径转义>.log |
每个 brick 一个日志,如 bricks/opt-gluster-exp1.log。brick 无法启动、磁盘错误、xattr 问题看这里 |
<挂载点转义>.log |
客户端日志,如 mnt-a1.log。挂载失败、IO 错误、脑裂报错看这里 |
glustershd.log |
自愈守护进程日志。self-heal 进展、脑裂检测 |
<卷名>-rebalance.log |
rebalance / remove-brick 迁移日志。迁移失败原因在此 |
cli.log |
gluster 命令行本身的日志 |
nfs.log |
内置 gluster-NFS 服务日志 |
ganesha.log、ganesha-gfapi.log |
NFS-Ganesha 日志 |
quotad.log |
配额守护进程日志 |
snaps/ |
快照相关日志目录 |
geo-replication/ |
异地复制日志目录 |
statedump/(/var/run/gluster/) |
statedump 输出,深度排错用 |
调整日志级别与采集 statedump:
gluster volume set gv0 diagnostics.brick-log-level DEBUG
gluster volume set gv0 diagnostics.client-log-level DEBUG
gluster volume set gv0 diagnostics.brick-sys-log-level WARNING
# 排错完成后务必调回,DEBUG 日志量极大
gluster volume reset gv0 diagnostics.brick-log-level
# 生成 statedump(分析内存泄漏、锁挂起)
gluster volume statedump gv0
ls /var/run/gluster/
# 日常快速查看
tail -f /var/log/glusterfs/glusterd.log
grep -iE 'error|warning|failed' /var/log/glusterfs/glusterd.log | tail -50
2.22.2 peer 状态 Disconnected 的排查
现象:gluster peer status 显示 State: Peer in Cluster (Disconnected)。
排查顺序:
# 1. 对端 glusterd 是否在跑
systemctl status glusterd # CentOS 7
service glusterd status # CentOS 6
ps -ef | grep glusterd
# 2. 24007 端口是否可达(在本机 telnet 对端)
telnet node2.com 24007
nc -zv node2.com 24007
ss -tunlp | grep 24007
# 3. 防火墙
firewall-cmd --list-all # CentOS 7
iptables -L -n --line-numbers # CentOS 6
# 4. 主机名解析是否双向正确
getent hosts node2.com
ping -c2 node2.com
# 在对端也执行 getent hosts node1.com
# 5. UUID 是否冲突(克隆虚拟机的经典问题)
cat /var/lib/glusterd/glusterd.info
ls /var/lib/glusterd/peers/ # 文件名即对端 UUID
cat /var/lib/glusterd/peers/*
# 6. 版本是否一致
gluster --version # 两端对比
# 7. 时间是否同步(差异过大会导致握手失败)
date; ssh node2.com date
# 8. 看日志
tail -100 /var/log/glusterfs/glusterd.log
Peer Rejected (Connected) 的处理(配置版本不一致导致):
# 在被拒绝的那个节点上执行
systemctl stop glusterd
# 备份并删除本地卷配置,让它从集群重新同步
mv /var/lib/glusterd/vols /var/lib/glusterd/vols.bak
# 保留 glusterd.info(UUID 不能变)
systemctl start glusterd
gluster peer status # 应变为 Peer in Cluster (Connected)
gluster volume info # 配置应已同步回来
2.22.3 卷无法启动 / Brick 进程未起来
定位第一步:看 volume status 的 Online 列。
gluster volume status gv0
Gluster process TCP Port RDMA Port Online Pid
------------------------------------------------------------------------------
Brick node1.com:/opt/gluster/exp1 49152 0 Y 1234
Brick node2.com:/opt/gluster/exp2 49153 0 Y 1256
Brick node3.com:/opt/gluster/exp3 N/A N/A N N/A <-- 问题在这里
Self-heal Daemon on localhost N/A N/A Y 1300
Online: N 表示该 brick 的 glusterfsd 进程没有启动。到该 brick 所在节点排查:
# 1. 看该 brick 的专属日志(最直接)
tail -100 /var/log/glusterfs/bricks/opt-gluster-exp3.log
# 日志文件名 = brick 路径把 / 换成 -
# 2. 常见原因逐项检查
# (a) 数据盘没挂载 —— 最常见
df -hT | grep /opt/gluster
mount -a
lsblk
# (b) brick 目录不存在或被误删
ls -ld /opt/gluster/exp3
# (c) brick 目录缺少 volume-id 扩展属性(目录被重建过)
getfattr -d -m . -e hex /opt/gluster/exp3
# 日志报:failed to get 'trusted.glusterfs.volume-id'
# 修复:从健康节点取 volume-id 后 setfattr(见 2.18.5)
# (d) 磁盘满了 / inode 用尽
df -h; df -i
# (e) 端口被占用或端口范围被防火墙拦
ss -tunlp | grep 4915
# (f) 磁盘只读(文件系统错误)
dmesg | tail -50
touch /opt/gluster/exp3/.testwrite
xfs_repair /dev/sdb1 # 需先卸载
# (g) SELinux 拦截
ausearch -m avc -ts recent | grep gluster
setenforce 0 # 验证是否为 SELinux 问题
# 3. 尝试强制拉起 brick 进程
gluster volume start gv0 force
# 或重启该节点的 glusterd(不影响已有 brick 的数据面)
systemctl restart glusterd
gluster volume status gv0
整卷无法启动(volume start 失败)的额外检查:
# 报 "Volume gv0 already started" —— 用 force
gluster volume start gv0 force
# 报 "Another transaction is in progress" —— 有未结束的操作,等待或重启 glusterd
gluster volume status # 看是否有 rebalance/heal 在跑
# 报 "Staging failed on nodeX" —— 到 nodeX 看 glusterd.log,通常是 brick 路径问题
2.22.4 客户端挂载失败的排查流程
# 1. 手动挂载看具体报错,并加详细日志
mount -t glusterfs -o log-level=DEBUG,log-file=/tmp/mnt.log node1.com:/gv0 /mnt/gv0
tail -100 /tmp/mnt.log
# 或看默认客户端日志
tail -100 /var/log/glusterfs/mnt-gv0.log
# 2. 客户端软件包是否安装(最常见的低级问题)
rpm -qa | grep glusterfs
# 必须有 glusterfs-fuse,否则报 "unknown filesystem type 'glusterfs'"
yum install -y glusterfs glusterfs-fuse
# 3. FUSE 模块是否加载
lsmod | grep fuse
modprobe fuse
# 4. 端口连通性:24007(拉 volfile)+ 49152 起(每个 brick)
nc -zv node1.com 24007
for p in 49152 49153 49154; do nc -zv node1.com $p; done
# 只放行 24007 却不放行 brick 端口 => 能挂上但读写全部报错,这是极典型的坑
# 5. 主机名解析:客户端也必须能解析所有节点的主机名!
# volfile 中记录的是创建卷时用的主机名,客户端要按这些主机名去连每个 brick
getent hosts node1.com node2.com node3.com
cat /etc/hosts
# 6. 卷是否已启动
gluster volume info gv0 | grep Status # 必须是 Started
gluster volume status gv0 # Online 必须全 Y
# 7. 版本不匹配
gluster --version # 服务端
glusterfs --version # 客户端
# 客户端版本 > 服务端版本 会被拒绝;跨大版本(如客户端 9 连服务端 3.12)易失败
# 报错示例:Server and Client lk-version numbers are not same /
# requested op-version xx is greater than the max supported
# 解决:统一大版本,或调整服务端 op-version
gluster volume get all cluster.op-version
gluster volume set all cluster.op-version 70000
# 8. auth.allow 白名单是否放行了客户端 IP
gluster volume get gv0 auth.allow
gluster volume set gv0 auth.allow '192.168.224.*'
# 9. SELinux
getenforce
setsebool -P use_fusefs_home_dirs on
# 10. 挂载点是否已被占用 / 是否有残留挂载
mount | grep /mnt/gv0
umount -l /mnt/gv0
报错速查:
| 报错 | 原因 |
|---|---|
unknown filesystem type 'glusterfs' |
未安装 glusterfs-fuse |
Mount failed. Please check the log file for more details. |
通用错误,必须看日志 |
failed to get the 'volume file' from server |
24007 不通 / 卷名写错 / 卷未启动 |
Transport endpoint is not connected |
见下节 |
No such file or directory |
卷名拼写错误 |
Permission denied |
auth.allow 未放行,或 SELinux |
Server and Client lk-version numbers are not same |
版本不匹配(多数情况是无害的 INFO,但版本差距大时会真失败) |
2.22.5 Transport endpoint is not connected
ls: cannot access /mnt/gv0: Transport endpoint is not connected
df: '/mnt/gv0': Transport endpoint is not connected
含义:客户端与服务端(brick 或 volfile server)的连接已断开,FUSE 挂载点变成"僵死"状态。这是 GlusterFS 最常见的报错。
可能原因与处理:
# 原因 1:brick 进程挂了 / 节点宕机
gluster volume status gv0 # 在服务端查 Online 列
gluster volume start gv0 force
# 原因 2:客户端进程(glusterfs 挂载进程)被 OOM kill 掉了
ps -ef | grep '[g]lusterfs.*gv0' # 挂载进程不存在
dmesg | grep -i 'killed process'
# 处理:卸载后重新挂载
umount -l /mnt/gv0
mount -a
# 原因 3:网络中断超过 ping-timeout(42 秒)
ping node1.com
ip -s link # 看丢包错误
# 恢复网络后通常会自动重连;不恢复则重新挂载
# 原因 4:brick 端口未放行(能拿 volfile 但连不上 brick)
for p in $(seq 49152 49160); do nc -zv node1.com $p; done
# 原因 5:卷被 stop / delete 了
gluster volume info gv0
# 通用恢复流程
umount -l /mnt/gv0 # 懒卸载僵死挂载点
umount -f /mnt/gv0 # 强制卸载
fuser -mk /mnt/gv0 # 杀掉占用进程(谨慎)
mount -a
df -hT
预防:挂载时使用 backup-volfile-servers,卷使用有冗余的类型(复制卷/EC 卷),网络做 bond。
2.22.6 其它常见故障处理
(1)rebalance 卡住 / 长时间不动
gluster volume rebalance gv0 status
# 观察 scanned 是否在增长;若长时间不变即为卡住
# 排查
tail -100 /var/log/glusterfs/gv0-rebalance.log
gluster volume status gv0 # 是否有 brick 掉线(rebalance 需所有 brick 在线)
df -h # 目标 brick 是否已满
gluster volume statedump gv0 # 看是否有锁挂起
# 处理
gluster volume rebalance gv0 stop # 安全中断,已迁移的不回滚
# 修复根因(补空间、拉起掉线 brick)后重新开始
gluster volume rebalance gv0 start
# 只做布局修复,代价小
gluster volume rebalance gv0 fix-layout start
常见卡住原因:某个 brick 掉线、目标 brick 空间不足(受 cluster.min-free-disk 限制)、大量文件被客户端持续写入而被跳过(skipped 增长)、单个超大文件迁移耗时长(属正常)。
(2)节点宕机后的恢复步骤
# 场景 A:节点只是重启/临时故障,数据盘完好
# 1. 开机后确认数据盘已挂载
df -hT | grep /data
mount -a
# 2. 启动服务
systemctl start glusterd
# 3. 检查加入集群与 brick 上线
gluster peer status
gluster volume status
# 4. 触发自愈补齐宕机期间的数据
gluster volume heal gv0
gluster volume heal gv0 info # 等到 Number of entries: 0
# 场景 B:节点彻底损坏,需用新机器替换(保留原主机名与 IP)
# 1. 新机器:同版本 GlusterFS、同主机名、同 IP、同 hosts、同数据盘路径
# 2. 从健康节点获取故障节点原来的 UUID
cat /var/lib/glusterd/peers/* # 找到故障节点的 uuid=xxx
# 3. 在新机器上写入相同的 UUID
systemctl stop glusterd
echo "UUID=<原来的UUID>" > /var/lib/glusterd/glusterd.info
systemctl start glusterd
# 4. 从健康节点 probe 一次,同步配置
gluster peer probe <新节点主机名> # 在健康节点执行
systemctl restart glusterd # 在新节点执行,拉取卷配置
# 5. 为每个 brick 目录恢复 volume-id 扩展属性(见 2.18.5),或用 replace-brick
gluster volume replace-brick gv0 <旧brick> <新brick> commit force
# 6. 全量自愈
gluster volume heal gv0 full
gluster volume heal gv0 info
(3)扩容后新 brick 上没有数据
这是正常现象,不是故障。 弹性哈希算法在扩容后老文件仍在原 brick,必须 rebalance 才会重新分布:
gluster volume rebalance gv0 fix-layout start # 新文件开始写入新 brick
gluster volume rebalance gv0 start # 老文件也搬过去
gluster volume rebalance gv0 status
(4)误删 brick 目录的后果与恢复
后果:
- 该 brick 的所有数据丢失(含
.glusterfs索引)。 - brick 进程会因缺少
trusted.glusterfs.volume-id而拒绝启动,volume status中 Online 为 N。 - 有冗余的卷(复制/EC):数据可从其它副本恢复,业务不中断。
- 纯分布卷/条带卷:该 brick 上的数据永久丢失,只能从备份恢复。
恢复(以复制卷为例):
# 方法 1:恢复 volume-id 后自愈(brick 路径不变)
# 1. 重建目录
mkdir -p /opt/gluster/exp5
# 2. 从健康的同组 brick 获取 volume-id
getfattr -n trusted.glusterfs.volume-id -e hex /opt/gluster/exp4 # 在健康节点执行
# 输出:trusted.glusterfs.volume-id=0x37c5200f75f74f53aca30a733a192708
# 3. 在故障节点设置相同的 volume-id
setfattr -n trusted.glusterfs.volume-id -v 0x37c5200f75f74f53aca30a733a192708 /opt/gluster/exp5
# 4. 重启并拉起 brick
systemctl restart glusterd
gluster volume start repl-volume force
gluster volume status repl-volume # Online 应变 Y
# 5. 全量自愈重建数据
gluster volume heal repl-volume full
gluster volume heal repl-volume info
# 方法 2:直接换成新 brick(更干净)
mkdir -p /data/newbrick/gv1
gluster volume replace-brick repl-volume node2.com:/opt/gluster/exp5 node2.com:/data/newbrick/gv1 commit force
gluster volume heal repl-volume full
(5)版本升级与 op-version
cluster.op-version 表示集群当前启用的操作版本,决定可用的特性集合。升级软件包不会自动提升 op-version,必须在所有节点都升级完成后手工提升,否则新特性不可用。
# 查看当前与最大可支持的 op-version
gluster volume get all cluster.op-version
gluster volume get all cluster.max-op-version
# 滚动升级流程(有冗余的卷可在线升级)
# 对每个节点依次执行:
gluster volume heal <卷名> info # 确认无待修复文件再动手!
systemctl stop glusterd
killall glusterfsd glusterfs # 或 pkill -f gluster
yum update glusterfs-* # 升级软件包
systemctl start glusterd
gluster volume status # 确认 brick 全部上线
gluster volume heal <卷名> # 触发自愈
gluster volume heal <卷名> info # 等到 0 entries 再升级下一个节点
# 所有节点升级完毕后,最后一步提升 op-version
gluster volume set all cluster.op-version <max-op-version 的值>
注意事项:
- 升级顺序:先服务端,后客户端(服务端版本要 >= 客户端)。
- 提升 op-version 后无法回退,老版本客户端可能无法挂载。
- 跨多个大版本升级要逐版本过渡(如 3.12 → 5 → 7 → 9),不要一步跳。
- 升级前后都要确认
heal info为 0,绝不能在有待修复文件时升级。 - stripe 卷在 GlusterFS 6+ 已被移除,升级前必须先把条带卷迁移到 dispersed 或 shard 方案,否则升级后卷不可用。
- 升级前备份
/var/lib/glusterd/。
2.23 取消存储共享(清理实验环境)
按依赖顺序拆除:
# 1. 客户端卸载所有挂载点
umount /mnt/a1 /mnt/a2 /mnt/a3 /mnt/dreplica /mnt/dstripe-volume /mnt/rstripe-volume /mnt/rdstripe
# 删除 /etc/fstab 中的对应行
# 2. 停止所有卷
for v in $(gluster volume list); do gluster volume stop $v --mode=script; done
# 3. 删除所有卷
for v in $(gluster volume list); do gluster volume delete $v --mode=script; done
# 4. 解散信任存储池(在 node1 上)
gluster peer detach node2.com
gluster peer detach node3.com
gluster peer status
# 5. 清理 brick 目录(数据会永久丢失!)
rm -rf /opt/gluster/*
# 6. 停止服务
systemctl stop glusterd # CentOS 7
systemctl disable glusterd
service glusterd stop # CentOS 6
chkconfig glusterd off
--mode=script 用于跳过 (y/n) 交互确认,适合脚本化操作。
练习
练习一 搭建各类型 GlusterFS 卷并验证数据分布
题目:使用 4 台 CentOS 7 主机(1 台客户端 + 3 台存储节点,每台存储节点 3 块 2GB 数据盘),完成:
- 组建可信存储池;
- 分别创建分布式卷、复制卷、分布式复制卷、纠删码卷;
- 客户端挂载每个卷,各写入 10 个文件;
- 到各节点的 brick 目录下查看文件的实际分布,说明每种卷的分布规律。
参考答案:
# ===== 步骤 1:环境准备(3 个存储节点都做)=====
# 主机名与 hosts
cat >> /etc/hosts << 'EOF'
192.168.224.10 server.com
192.168.224.11 node1.com
192.168.224.12 node2.com
192.168.224.13 node3.com
EOF
# 关闭防火墙与 SELinux(实验环境)
systemctl stop firewalld && systemctl disable firewalld
setenforce 0
sed -i 's/^SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
# 时间同步
systemctl start chronyd && systemctl enable chronyd
# 数据盘准备(以 sdb 为例,sdc/sdd 同理)
mkfs.xfs -i size=512 /dev/sdb
mkdir -p /data/brick1
echo '/dev/sdb /data/brick1 xfs defaults 0 0' >> /etc/fstab
mount -a
# 安装
yum install -y epel-release centos-release-gluster
yum install -y glusterfs glusterfs-server glusterfs-fuse glusterfs-rdma
systemctl start glusterd && systemctl enable glusterd
# ===== 步骤 2:组建存储池(在 node1 上执行)=====
gluster peer probe node2.com
gluster peer probe node3.com
gluster peer status # 应看到 2 个 Peer,State: Peer in Cluster (Connected)
gluster pool list
# ===== 步骤 3:创建四种卷 =====
# 3.1 分布式卷(3 brick,容量相加,无冗余)
gluster volume create dist-vol node1.com:/data/brick1/exp1 \
node2.com:/data/brick1/exp2 node3.com:/data/brick1/exp3 force
gluster volume start dist-vol
# 3.2 复制卷(replica 3,容量 = 单 brick,3 副本)
gluster volume create repl-vol replica 3 node1.com:/data/brick2/exp4 \
node2.com:/data/brick2/exp5 node3.com:/data/brick2/exp6 force
gluster volume start repl-vol
# 3.3 分布式复制卷(6 brick,replica 2 -> 3 个副本组)
gluster volume create dist-repl-vol replica 2 \
node1.com:/data/brick3/exp7 node2.com:/data/brick3/exp8 \
node2.com:/data/brick3/exp9 node3.com:/data/brick3/exp10 \
node3.com:/data/brick3/exp11 node1.com:/data/brick3/exp12 force
gluster volume start dist-repl-vol
# 3.4 纠删码卷(disperse 3 redundancy 1,利用率 2/3)
gluster volume create ec-vol disperse 3 redundancy 1 \
node1.com:/data/brick1/ec1 node2.com:/data/brick1/ec2 node3.com:/data/brick1/ec3 force
gluster volume start ec-vol
gluster volume info # 检查四个卷的 Type、Number of Bricks
gluster volume status # 检查所有 brick 的 Online 列均为 Y
# ===== 步骤 4:客户端挂载并写入 =====
# 客户端(server.com)
yum install -y glusterfs glusterfs-fuse
mkdir -p /mnt/{dist,repl,dist-repl,ec}
mount -t glusterfs node1.com:/dist-vol /mnt/dist
mount -t glusterfs node1.com:/repl-vol /mnt/repl
mount -t glusterfs node1.com:/dist-repl-vol /mnt/dist-repl
mount -t glusterfs node1.com:/ec-vol /mnt/ec
df -hT | grep fuse.glusterfs
for d in dist repl dist-repl ec; do
for i in $(seq 1 10); do
echo "hello gluster $i" > /mnt/$d/$i.txt
done
done
# ===== 步骤 5:到各节点查看实际分布 =====
# 在 node1/node2/node3 上分别执行
tree /data/brick1 /data/brick2 /data/brick3
find /data -name '*.txt' | sort
du -sh /data/brick*/*
预期结果与分布规律:
| 卷类型 | 观察到的现象 | 规律 |
|---|---|---|
分布式卷 dist-vol |
10 个文件分散在 3 个 brick 上(如 4+3+3),每个文件只出现一次,内容完整 | 按文件名哈希选一个 brick,整文件存放;容量相加,任一 brick 坏则该 brick 上的文件全丢 |
复制卷 repl-vol |
每个 brick 都有全部 10 个文件,内容完全相同 | 同步写所有副本;容量 = 单 brick;可容忍 N-1 个 brick 故障 |
分布式复制卷 dist-repl-vol |
10 个文件分散到 3 个副本组,每组内的 2 个 brick 内容相同 | 先哈希选组,再在组内做复制;容量 = 总容量/2 |
纠删码卷 ec-vol |
每个 brick 上都有同名文件,但内容是编码碎片(cat 出来是乱码,大小约为原文件的 1/2) |
数据被切成 2 个数据块 + 1 个校验块;可容忍 1 个 brick 故障;利用率 2/3 |
关键结论:绝不要直接修改或删除 brick 目录中的文件,所有操作必须通过挂载点进行,否则会造成元数据(扩展属性)与数据不一致,引发脑裂或数据丢失。
练习二 完成一次安全的扩容与缩容
题目:在练习一的分布式卷 dist-vol 上:
- 写入 20 个文件;
- 增加一个 brick 并执行 rebalance,观察数据是否迁移;
- 安全地移除刚加入的 brick,保证数据不丢失;
- 说明"直接
commit而不先start"会导致什么后果。
参考答案:
# ===== 1. 写入数据 =====
touch /mnt/dist/{10..29}.txt
for i in $(seq 10 29); do dd if=/dev/zero of=/mnt/dist/$i.txt bs=1M count=5 2>/dev/null; done
ls -l /mnt/dist/ | head
# 到三个节点查看初始分布
tree /data/brick1/exp1 # node1
tree /data/brick1/exp2 # node2
tree /data/brick1/exp3 # node3
# ===== 2. 扩容 =====
mkdir -p /data/brick1/exp13 # 在 node1 上新建 brick 目录
gluster volume add-brick dist-vol node1.com:/data/brick1/exp13 force
gluster volume info dist-vol # Number of Bricks 由 3 变 4
# 此时新 brick 是空的 —— 这是【正常现象】,不是故障!
tree /data/brick1/exp13 # 0 files
# 执行 rebalance(两阶段)
gluster volume rebalance dist-vol fix-layout start # 阶段一:只改哈希布局,新文件才会落到新 brick
gluster volume rebalance dist-vol status
gluster volume rebalance dist-vol start # 阶段二:fix-layout + migrate-data,迁移存量文件
gluster volume rebalance dist-vol status
# 关注字段:status 由 in progress 变为 completed;rebalanced-files / failures / skipped
# 迁移完成后再看,部分文件已搬到 exp13
tree /data/brick1/exp13
du -sh /data/brick1/exp*
# ===== 3. 安全缩容(三段式,一步都不能省)=====
# 第一步:start —— 启动数据迁移,把 exp13 上的数据搬到其它 brick
gluster volume remove-brick dist-vol node1.com:/data/brick1/exp13 start
# 交互提示(复制卷/条带卷会出现):
# It is recommended that remove-brick be run with cluster.force-migration
# option disabled to prevent possible data corruption...
# Do you want to continue with your current cluster.force-migration settings? (y/n) y
# 第二步:status —— 必须等到 status 为 completed
gluster volume remove-brick dist-vol node1.com:/data/brick1/exp13 status
# 关注 status 列必须是 "completed",failures 必须为 0
# 第三步:commit —— 确认无误后才真正从卷中摘除
gluster volume remove-brick dist-vol node1.com:/data/brick1/exp13 commit
gluster volume info dist-vol # Number of Bricks 回到 3
ls /mnt/dist/ | wc -l # 文件总数不变,数据未丢失!
# 摘除后 brick 目录中的残留数据可手工清理
rm -rf /data/brick1/exp13
"直接 commit"的严重后果:
# 【危险!绝对不要这样做】
gluster volume remove-brick dist-vol node1.com:/data/brick1/exp13 commit force
commit force(或不先start就commit)会立即把 brick 从卷中摘除,不做任何数据迁移;- 该 brick 上的所有文件在挂载点上瞬间消失(
ls看不到、读取报No such file or directory); - 数据本身还在原 brick 的磁盘目录里,但卷已经不认它了,只能手工从 brick 目录
cp回挂载点来抢救,且文件多时极其痛苦; - 对分布式卷是直接丢数据(无副本可依);对复制卷若摘掉整个副本组同样丢数据。
正确纪律:remove-brick 必须 start → 反复 status 到 completed 且 failures=0 → commit,三段式一步都不能省。误操作后可用 remove-brick ... stop 中止迁移(仅在 commit 之前有效)。
练习三 制造并修复一次脑裂
题目:在两副本卷上人为制造脑裂,然后用三种策略分别修复。
参考答案:
# ===== 1. 创建一个两副本卷(便于制造脑裂)=====
gluster volume create sb-vol replica 2 \
node1.com:/data/brick2/sb1 node2.com:/data/brick2/sb2 force
gluster volume start sb-vol
# 关闭 quorum 以便复现(生产环境千万不要这样配!)
gluster volume set sb-vol cluster.quorum-type none
gluster volume set sb-vol cluster.server-quorum-type none
mount -t glusterfs node1.com:/sb-vol /mnt/sb
echo "original content" > /mnt/sb/split.txt
# ===== 2. 制造脑裂 =====
# 2.1 隔离 node2(模拟网络分区)
# 在 node2 上:
iptables -I INPUT -s 192.168.224.11 -j DROP
iptables -I OUTPUT -d 192.168.224.11 -j DROP
# 2.2 客户端连 node1 写一次
mount -t glusterfs node1.com:/sb-vol /mnt/sb1
echo "written via node1 - AAAA" > /mnt/sb1/split.txt
# 2.3 另一个客户端连 node2 写一次(不同内容)
mount -t glusterfs node2.com:/sb-vol /mnt/sb2
echo "written via node2 - BBBBBBBB" > /mnt/sb2/split.txt
# 2.4 恢复网络
iptables -D INPUT -s 192.168.224.11 -j DROP
iptables -D OUTPUT -d 192.168.224.11 -j DROP
# ===== 3. 发现脑裂 =====
gluster volume heal sb-vol info
gluster volume heal sb-vol info split-brain
# 输出中会列出 /split.txt,标记 "Is in split-brain"
# 客户端访问会报错:
cat /mnt/sb/split.txt
# cat: /mnt/sb/split.txt: Input/output error <-- 脑裂的典型症状
# 查看两个 brick 上的 AFR 扩展属性,确认互相指责
getfattr -d -m . -e hex /data/brick2/sb1/split.txt # node1 上
getfattr -d -m . -e hex /data/brick2/sb2/split.txt # node2 上
# trusted.afr.sb-vol-client-0 / client-1 的计数都非零 => 互相认为对方有问题
# ===== 4. 三种修复策略(任选其一)=====
# 策略 A:保留【较大】的文件
gluster volume heal sb-vol split-brain bigger-file /split.txt
# 策略 B:保留【修改时间最新】的文件
gluster volume heal sb-vol split-brain latest-mtime /split.txt
# 策略 C:手工指定【以哪个 brick 为源】(最可控,推荐)
gluster volume heal sb-vol split-brain source-brick node1.com:/data/brick2/sb1 /split.txt
# 对整个卷的所有脑裂文件都以某个 brick 为源(慎用)
gluster volume heal sb-vol split-brain source-brick node1.com:/data/brick2/sb1
# ===== 5. 验证修复 =====
gluster volume heal sb-vol info split-brain # 应显示 Number of entries: 0
gluster volume heal sb-vol full # 触发一次全量修复
cat /mnt/sb/split.txt # 可正常读取
getfattr -d -m . -e hex /data/brick2/sb1/split.txt # afr 计数已清零
预防脑裂的正确配置:
# 方案一:三副本 + quorum(最可靠)
gluster volume create safe-vol replica 3 node1:/b1 node2:/b2 node3:/b3 force
gluster volume set safe-vol cluster.quorum-type auto # 需 >50% 副本在线才允许写
gluster volume set safe-vol cluster.server-quorum-type server
gluster volume set safe-vol cluster.server-quorum-ratio 51%
# 方案二:两副本 + 仲裁盘 arbiter(省一份空间)
gluster volume create arb-vol replica 3 arbiter 1 node1:/b1 node2:/b2 node3:/b3 force
# 第三个 brick 只存元数据不存数据,占用空间极小,但能提供投票防脑裂
关键认知:两副本 + 无 quorum = 必然脑裂。生产环境要么三副本,要么两副本 + arbiter,并且必须开启 quorum。
本章小结
GlusterFS 命令速查表
| 类别 | 命令 | 说明 |
|---|---|---|
| 服务 | systemctl start/enable glusterd |
启动/开机自启(CentOS 7) |
| 服务 | service glusterd start / chkconfig glusterd on |
CentOS 6 |
| 节点 | gluster peer probe <节点> |
添加节点到可信存储池 |
| 节点 | gluster peer detach <节点> [force] |
移除节点 |
| 节点 | gluster peer status |
查看节点状态(Peer in Cluster / Connected) |
| 节点 | gluster pool list |
列出存储池所有节点 |
| 卷 | gluster volume create <卷名> [replica N] [disperse N redundancy M] <brick...> force |
创建卷 |
| 卷 | gluster volume start/stop <卷名> |
启动/停止卷 |
| 卷 | gluster volume delete <卷名> |
删除卷(需先 stop) |
| 卷 | gluster volume list |
列出所有卷 |
| 卷 | gluster volume info [卷名] |
查看卷配置(Type/Bricks/Options) |
| 卷 | gluster volume status [卷名] [detail] |
查看运行状态(看 Online 列) |
| 参数 | gluster volume set <卷名> <参数> <值> |
设置调优参数 |
| 参数 | gluster volume get <卷名> all |
查看所有参数当前值 |
| 参数 | gluster volume reset <卷名> [参数] |
恢复默认值 |
| 扩容 | gluster volume add-brick <卷名> [replica N] <brick...> force |
增加 brick |
| 扩容 | gluster volume rebalance <卷名> fix-layout start |
阶段一:只改哈希布局 |
| 扩容 | gluster volume rebalance <卷名> start |
阶段二:fix-layout + 迁移存量数据 |
| 扩容 | gluster volume rebalance <卷名> status/stop |
查看/中止 rebalance |
| 缩容 | gluster volume remove-brick <卷名> <brick> start |
第一步:启动数据迁移 |
| 缩容 | gluster volume remove-brick <卷名> <brick> status |
第二步:等 completed 且 failures=0 |
| 缩容 | gluster volume remove-brick <卷名> <brick> commit |
第三步:正式摘除 |
| 缩容 | gluster volume remove-brick <卷名> <brick> stop |
中止迁移(commit 前有效) |
| 换盘 | gluster volume replace-brick <卷名> <旧> <新> commit force |
替换故障 brick |
| 修复 | gluster volume heal <卷名> |
触发一次修复 |
| 修复 | gluster volume heal <卷名> full |
全量修复 |
| 修复 | gluster volume heal <卷名> info |
查看待修复文件 |
| 修复 | gluster volume heal <卷名> info split-brain |
查看脑裂文件 |
| 修复 | gluster volume heal <卷名> split-brain bigger-file <路径> |
脑裂修复:保留大文件 |
| 修复 | gluster volume heal <卷名> split-brain latest-mtime <路径> |
脑裂修复:保留最新 |
| 修复 | gluster volume heal <卷名> split-brain source-brick <brick> [路径] |
脑裂修复:指定源(推荐) |
| 配额 | gluster volume quota <卷名> enable/disable |
开启/关闭配额 |
| 配额 | gluster volume quota <卷名> limit-usage <目录> <大小> |
设置目录配额 |
| 配额 | gluster volume quota <卷名> list |
查看配额使用 |
| 快照 | gluster snapshot create <快照名> <卷名> |
创建快照(需 LVM 精简卷) |
| 快照 | gluster snapshot list/info/activate/restore/delete |
快照管理 |
| 客户端 | mount -t glusterfs node1:/vol /mnt |
FUSE 挂载 |
| 客户端 | mount -t glusterfs -o backup-volfile-servers=node2:node3 node1:/vol /mnt |
带备用 volfile 服务器 |
| 客户端 | mount -t nfs -o vers=3 node1:/vol /mnt |
NFS 挂载 |
| 排错 | getfattr -d -m . -e hex <brick中文件> |
查看 AFR 扩展属性 |
| 排错 | gluster volume statedump <卷名> |
生成状态转储 |
| 排错 | gluster volume set all cluster.op-version <版本> |
升级后提升 op-version |
FastDFS 命令与配置速查表
| 类别 | 命令/配置 | 说明 |
|---|---|---|
| 服务 | systemctl start/enable fdfs_trackerd |
启动 Tracker(先启动) |
| 服务 | systemctl start/enable fdfs_storaged |
启动 Storage(后启动) |
| 服务 | service fdfs_trackerd restart |
CentOS 6 |
| 服务 | fdfs_trackerd /etc/fdfs/tracker.conf [restart\|stop] |
直接调用二进制 |
| 监控 | fdfs_monitor /etc/fdfs/storage.conf |
查看集群状态(最常用) |
| 监控 | fdfs_monitor /etc/fdfs/client.conf delete <group> <IP> |
从集群删除节点 |
| 上传 | fdfs_upload_file /etc/fdfs/client.conf <文件> |
上传,返回 file_id |
| 上传 | fdfs_upload_appender /etc/fdfs/client.conf <文件> |
上传可追加文件 |
| 追加 | fdfs_append_file /etc/fdfs/client.conf <file_id> <文件> |
追加内容 |
| 下载 | fdfs_download_file /etc/fdfs/client.conf <file_id> [本地路径] |
下载 |
| 删除 | fdfs_delete_file /etc/fdfs/client.conf <file_id> |
删除 |
| 信息 | fdfs_file_info /etc/fdfs/client.conf <file_id> |
查看源 IP/时间/大小/CRC32 |
| 测试 | fdfs_test /etc/fdfs/client.conf upload <文件> |
综合测试(输出全部 URL) |
| 校验 | fdfs_crc32 <文件> |
计算 CRC32 |
| 配置 | tracker.conf: port=22122 |
Tracker 端口 |
| 配置 | tracker.conf: base_path |
日志与数据目录(必须手工创建) |
| 配置 | tracker.conf: store_lookup |
0 轮询 / 1 指定组 / 2 选空间最大 |
| 配置 | tracker.conf: store_group |
store_lookup=1 时指定的组名 |
| 配置 | tracker.conf: store_server |
0 轮询 / 1 IP 最小 / 2 优先级最高 |
| 配置 | tracker.conf: store_path |
0 轮询 / 2 选空间最大(选哪块盘) |
| 配置 | tracker.conf: download_server |
0 轮询 / 1 从源节点下载 |
| 配置 | tracker.conf: reserved_storage_space |
磁盘预留(建议 10%~20%) |
| 配置 | tracker.conf: allow_hosts |
允许连接的客户端 IP(默认 *) |
| 配置 | storage.conf: group_name |
组名(同组必须一致,上线后不可改) |
| 配置 | storage.conf: port=23000 |
Storage 端口 |
| 配置 | storage.conf: tracker_server |
可配多个,一行一个 |
| 配置 | storage.conf: store_path_count / store_pathN |
数据盘数量与路径(对应 M00/M01) |
| 配置 | storage.conf: subdir_count_per_path=256 |
两级目录,共 65536 个(不可改) |
| 配置 | storage.conf: sync_start_time / sync_end_time |
允许同步的时间段 |
| 配置 | storage.conf: sync_interval |
同步间隔毫秒(0 = 最快) |
| 配置 | storage.conf: http.server_port=8888 |
内置 HTTP 端口(实际由 Nginx 提供) |
| 配置 | client.conf: tracker_server / base_path |
客户端配置 |
| 配置 | mod_fastdfs.conf: url_have_group_name |
URL 含 group 名则必须 true |
| 配置 | mod_fastdfs.conf: response_mode=proxy |
同步未完成时回源代理(避免 404) |
| 日志 | $base_path/logs/trackerd.log |
Tracker 日志 |
| 日志 | $base_path/logs/storaged.log |
Storage 日志(含同步过程) |
| 目录 | $base_path/data/sync/binlog.000 |
同步日志 |
| 目录 | $base_path/data/sync/<IP>_23000.mark |
同步进度标记 |
卷类型速查表
| 卷类型 | 创建关键字 | Brick 要求 | 容量 | 冗余 | 性能 | 适用场景 |
|---|---|---|---|---|---|---|
| 分布式卷 Distributed | 默认(无关键字) | ≥1 | 各 brick 相加 | 无 | 读写一般,可并发 | 大量文件、不要求冗余、追求容量 |
| 复制卷 Replicated | replica N |
= N | 单 brick 容量 | 可容忍 N-1 故障 | 读快(可从多副本读),写慢 | 高可靠性要求、数据量不大 |
| 条带卷 Striped | stripe N |
= N | 各 brick 相加 | 无 | 大文件读写快 | 已废弃(GlusterFS 6+ 移除),改用 shard |
| 分布式复制卷 Distributed-Replicated | replica N |
N 的倍数 | 总容量/N | 每组容忍 N-1 | 兼顾容量与可靠 | 生产最常用 |
| 分布式条带卷 Distributed-Striped | stripe N |
N 的倍数 | 各 brick 相加 | 无 | 大文件高吞吐 | 已废弃 |
| 条带复制卷 Striped-Replicated | stripe N replica M |
N×M | 总容量/M | 容忍 M-1 | 大文件 + 冗余 | 已废弃 |
| 分布式条带复制卷 | replica M stripe N |
N×M 的倍数 | 总容量/M | 容忍 M-1 | 综合 | 已废弃 |
| 纠删码卷 Dispersed | disperse K redundancy M |
= K(K=数据+校验) | (K-M)/K | 容忍 M 个故障 | CPU 开销大,大文件顺序读写好 | 空间敏感的归档/备份 |
| 分布式纠删码卷 Distributed-Dispersed | disperse K redundancy M |
K 的倍数 | (K-M)/K | 每组容忍 M | 同上 | 大规模归档 |
| 仲裁卷 Arbiter | replica 3 arbiter 1 |
3(第三个只存元数据) | 单 brick 容量 | 容忍 1 个数据 brick 故障 | 接近两副本 | 两副本防脑裂的经济方案 |
记忆要点:replica 管冗余,disperse 管省空间,brick 数量超过 replica/disperse 的倍数就自动带上"分布式";条带(stripe)系列已被废弃,大文件请用 features.shard。
端口清单
| 服务 | 端口 | 协议 | 说明 |
|---|---|---|---|
| GlusterFS | |||
| glusterd 管理 | 24007 | TCP | 集群管理与 volfile 获取(必开) |
| RDMA | 24008 | TCP | 使用 InfiniBand/RDMA 时需要 |
| brick 数据 | 49152 起,每个 brick 一个 | TCP | 3.4 之前是 24009 起。N 个 brick 需开 49152~49152+N-1 |
| gluster-NFS | 38465-38467 | TCP | 内置 NFS 导出 |
| rpcbind/portmap | 111 | TCP/UDP | NFS 依赖 |
| NFS-Ganesha | 2049 | TCP | 用 Ganesha 时 |
| Samba | 139, 445 | TCP | CIFS 导出时 |
| 自愈/其它 | 49152-49251(预留区间) | TCP | 建议整段放行便于扩容 |
| FastDFS | |||
| Tracker | 22122 | TCP | 客户端与 Storage 都要连 |
| Storage | 23000 | TCP | 客户端连;Storage 之间同步也走这个端口 |
| Nginx HTTP | 8888(Storage 侧)/ 80(Tracker 侧统一入口) | TCP | HTTP 下载 |
| 公共 | |||
| SSH | 22 | TCP | 运维 |
| NTP/chrony | 123 | UDP | 时间同步(FastDFS 同步依赖时间准确) |
# CentOS 7 firewalld 批量放行
firewall-cmd --permanent --add-port={24007-24008,49152-49251}/tcp # GlusterFS
firewall-cmd --permanent --add-port={22122,23000,8888}/tcp # FastDFS
firewall-cmd --reload && firewall-cmd --list-ports
# CentOS 6 iptables
iptables -I INPUT -p tcp -m multiport --dports 24007,24008 -j ACCEPT
iptables -I INPUT -p tcp --dport 49152:49251 -j ACCEPT
iptables -I INPUT -p tcp -m multiport --dports 22122,23000,8888 -j ACCEPT
service iptables save && service iptables restart
详见 07-03-防火墙与SELinux.md
选型决策树
需要存储服务
|
+-- 能上公有云?
| +-- 是 --> 【云对象存储 OSS/S3/COS】(默认答案,全托管、自带 CDN 与生命周期)
| | +-- 但需要 POSIX 挂载 --> 【云 NAS/EFS】 或 【JuiceFS + 对象存储】
| +-- 否(必须私有化)--> 往下
|
+-- 应用需要"像本地目录一样挂载"读写?(不想改代码 / 需随机读写 / 需目录与文件名语义)
| +-- 是 --> 主要存大文件?
| | +-- 是(视频/镜像/备份/数据集)--> 【GlusterFS 分布式复制卷】
| | | 或 【CephFS】(社区更活跃)
| | +-- 否(海量小文件)--> GlusterFS 小文件性能差
| | --> 【CephFS】或改造为对象存储访问
| +-- 否(可以改造成 API/HTTP 访问)--> 往下
|
+-- 需要 S3 兼容接口 / 版本控制 / 权限管理 / 纠删码省空间?
| +-- 是 --> 【MinIO】(首选)或 【Ceph RGW】
| +-- 否 --> 往下
|
+-- 海量中小文件、一次写多次读、前端挂 CDN、已有 FastDFS 经验?
| +-- 是 --> 【FastDFS】(存量系统维护可用;新项目不推荐)
| +-- 否 --> 往下
|
+-- 需要块存储给虚拟机/数据库?--> 【Ceph RBD】
+-- 大数据分析场景?--> 【HDFS】(> 详见 16-03-HDFS分布式文件系统.md)
+-- 只有两三台机器、需求简单?--> 【NFS + Keepalived】(> 详见 09-02-NFS网络文件系统.md)
+-- K8s 环境需要持久卷?--> 【Rook-Ceph / Longhorn / OpenEBS / CSI 对接对象存储】
成本与利用率速算:
| 方案 | 存 100TB 有效数据需要的原始容量 |
|---|---|
| GlusterFS 分布式卷(无冗余,不可用于生产) | 100TB |
| GlusterFS 两副本 / FastDFS 两节点组 | 200TB |
| GlusterFS 三副本 / FastDFS 三节点组 | 300TB |
| GlusterFS 两副本 + arbiter | 约 200TB + 少量元数据空间 |
| GlusterFS 纠删码 4+2 | 150TB(利用率 67%) |
| GlusterFS 纠删码 8+3 | 约 137TB(利用率 73%) |
| MinIO 默认纠删码(EC:4,8 盘) | 约 133TB(利用率 75%) |
排错口诀
GlusterFS 排错四步口诀:看服务、看 peer、看 Online、看日志
1. 看服务:systemctl status glusterd —— 进程活着吗?
2. 看 peer:gluster peer status —— 节点互认吗?(Connected?)
3. 看 Online:gluster volume status —— brick 起来了吗?(Online 列 Y?)
4. 看日志:/var/log/glusterfs/ —— glusterd.log / bricks/*.log / 挂载点.log
| 症状 | 第一反应 |
|---|---|
peer probe 失败 |
主机名双向可解析 + 24007 端口 + glusterd 是否启动 |
| peer 状态 Disconnected | 网络、防火墙、glusterd 进程、时间偏差 |
| Peer Rejected | 两边卷配置不一致 → 清理 /var/lib/glusterd/vols 后重启 glusterd |
volume create 报 already part of a volume |
brick 目录残留 xattr → setfattr -x trusted.glusterfs.volume-id + 删 .glusterfs 目录,或加 force |
| 卷启动失败 / brick Online = N | 看 /var/log/glusterfs/bricks/*.log;常见:挂载点未挂、目录不存在、端口被占、xattr 冲突 |
| 客户端挂载失败 | FUSE 模块(modprobe fuse)、glusterfs-fuse 包、24007 与 49152+、主机名解析 |
Transport endpoint is not connected |
brick 进程挂了或网络断了 → volume start <卷> force 重启 brick |
Input/output error 读文件 |
脑裂 → heal info split-brain |
| rebalance 卡住 | 看 rebalance 日志与 status 的 failures;可 stop 后重跑 |
| 扩容后新 brick 是空的 | 正常现象,需执行 rebalance start |
| 缩容后数据丢了 | 用了 commit force 跳过迁移 → 从 brick 目录手工抢救 |
| 空间显示不对 | 复制卷容量 = 单 brick;df 看到的是卷视图 |
| 小文件很慢 | 开 quick-read/stat-prefetch/parallel-readdir,或换 libgfapi,或换方案 |
FastDFS 排错四步口诀:看状态、看端口、看路径、看日志
1. 看状态:fdfs_monitor /etc/fdfs/storage.conf —— 是不是 ACTIVE?active count 对吗?
2. 看端口:netstat -tnlp | grep -E '22122|23000' + telnet 对端 —— 通不通?bind_addr 对吗?
3. 看路径:base_path / store_path 存在吗?权限对吗?M00 软链接在吗?
4. 看日志:$base_path/logs/{trackerd,storaged}.log + Nginx error.log
| 症状 | 第一反应 |
|---|---|
| Storage 不是 ACTIVE | 进程、23000 端口、group_name 是否一致、组内是否有 ACTIVE 的源 |
| 卡在 WAIT_SYNC | 组内唯一同伴不是 ACTIVE,或 group_name 写错导致变成两个组 |
| 连不上 Tracker | bind_addr 是不是写成了 127.0.0.1、22122 防火墙、allow_hosts |
上传 no available storage server |
无 ACTIVE 节点 / 磁盘低于 reserved_storage_space / store_group 指了不存在的组 |
上传 connect timed out |
Tracker 返回的 Storage IP 客户端访问不到(23000 未放行) |
| 启动即退出 | base_path/store_path 目录不存在(FastDFS 不自动创建) |
| 同组不同步 | 时间不同步 / sync_start_time~sync_end_time 限制 / Storage 间 23000 不通 / group_name 不一致 |
| Nginx 404 | url_have_group_name 设错 / store_path 与 storage.conf 不一致 / M00 软链接缺失 / 模块没编进去 |
| 刚上传的文件偶发 404 | 同步延迟 → 确认 response_mode = proxy 且模块的 tracker_server 配置正确 |
| 改完配置数据全没了 | 是不是改了 group_name / store_path / subdir_count_per_path / port?改回去 |
| 磁盘还有空间但传不上去 | reserved_storage_space 阈值,或 inode 用尽(df -i) |
核心知识点回顾
- GlusterFS 最大的设计特色是没有元数据服务器,用弹性哈希(Davies-Meyer)根据路径名实时计算文件位置,因此不存在元数据单点与元数据瓶颈;代价是文件重命名/迁移成本高、目录遍历需要广播 LOOKUP、小文件性能一般。
- FastDFS 最大的设计特色是把位置信息编码进 file_id,客户端持有 file_id 即可直接算出"哪个组、哪块盘、哪个目录、哪个文件",Tracker 只做调度不存文件元数据,因此 Tracker 无状态、可任意水平扩展。
remove-brick必须走start→status(completed 且 failures=0)→commit三段式,直接commit force会跳过数据迁移导致数据"消失"。rebalance有两个阶段:fix-layout只修改哈希布局(新文件才用新 brick),start(migrate-data)才会迁移存量数据。扩容后新 brick 为空是正常的。- 两副本 + 不开 quorum = 必然脑裂。生产环境用三副本或"两副本 + arbiter 仲裁盘",并开启 client/server quorum。脑裂修复有
bigger-file、latest-mtime、source-brick三种策略,推荐用source-brick明确指定源。 - FastDFS 的 group 是容量扩展的唯一单位:同组内数据完全相同(副本),容量取组内最小节点;扩容只能加新 group,没有 rebalance,老数据不迁移。因此规划阶段就要想好 group 划分。
- FastDFS 的同步是异步的(binlog + mark 文件),存在秒级延迟窗口,靠
fastdfs-nginx-module的response_mode = proxy解析 file_id 中的源 IP 回源代理来消除 404。 base_path与store_path必须手工创建,FastDFS 不会自动建目录;group_name、port、store_path、subdir_count_per_path上线后视为不可变。- 同组多副本不等于备份:误删操作会通过 binlog 同步到组内所有节点;GlusterFS 的复制卷同理。必须有独立的备份方案,且数据库中的 file_id 映射表和文件本体同等重要。
- CentOS 6 与 7 的差异要牢记:
service/chkconfigvssystemctl、iptablesvsfirewalld、ntpdvschronyd、/etc/sysconfig/networkvshostnamectl。 - 两者都在被替代:新项目优先选云对象存储;私有化选 MinIO(对象)或 CephFS(文件)。但它们的架构思想(一致性哈希、位置编码、最终一致、副本 vs 纠删码)是理解现代分布式存储的必经之路。
- 端口一定要记住:GlusterFS = 24007 + 49152 起每 brick 一个;FastDFS = Tracker 22122 + Storage 23000(Storage 之间同步也走 23000)+ Nginx 8888/80。
此处评论已关闭