- 存储
- 分布式文件系统
- 对象存储
- 云原生
【免费下载链接】cubefs
cloud-native distributed storage
导读
Clustermgr(集群管理器)是 CubeFS Blobstore 体系中负责元数据与集群状态管理的核心服务,它基于 Raft 保证集群元数据的强一致,并对外提供一套 HTTP 管理 API,覆盖集群状态查看、Raft 节点增删与领导权切换、磁盘状态管理、卷信息查询以及后台任务开关等运维场景。本文以官方管理 API 文档(docs/source/dev-guide/admin-api/blobstore/cm.md)为主线,结合仓库源码逐接口讲解请求格式、参数含义与底层实现原理,帮助读者掌握用 curl 与 blobstore-cli 完成 Clustermgr 日常运维的核心技能。
一、Clustermgr 管理 API 概览
Clustermgr 的所有管理接口都通过 HTTP 暴露,默认监听在127.0.0.1:9998。从路由注册源码 blobstore/clustermgr/handler.go 可以看到,这些管理接口集中在文件末尾的manage分区中:
POST /member/add:向 Raft 集群添加成员POST /member/remove:从 Raft 集群移除成员POST /leadership/transfer:切换 Raft 领导者GET /stat:获取集群整体状态POST /config/set、GET /config/get、POST /config/delete:配置键值读写- 以及磁盘、卷相关的
GET /disk/info、POST /disk/set、POST /disk/drop、POST /disk/access、GET /volume/get等
这些接口在 blobstore/api/clustermgr/client.go 中都有对应的 Go 客户端封装,本文统一使用 curl 演示,二者等价。
二、服务状态查询:GET /stat
/stat返回 Clustermgr 的集群整体状态,包括 Raft 状态、空间使用、卷统计等信息,是运维排障的第一入口。
curl "http://127.0.0.1:9998/stat"响应示例
{ "leader_host": "127.0.0.1:9998", "raft_status": { "applied": 291968826, "commit": 291968826, "leader": 3, "nodeId": 2, "peers": null, "raftApplied": 291968826, "raftState": "StateFollower", "term": 24, "transferee": 0, "vote": 3 }, "read_only": false, "space_stat": { "disk_stat_infos": [ { "available": 107, "broken": 0, "dropped": 10, "dropping": 0, "expired": 0, "idc": "z0", "readonly": 22, "repaired": 17, "repairing": 0, "total": 134, "total_chunk": 55619, "total_free_chunk": 37979 }, { "available": 93, "broken": 0, "dropped": 10, "dropping": 0, "expired": 0, "idc": "z1", "readonly": 22, "repaired": 19, "repairing": 0, "total": 122, "total_chunk": 51523, "total_free_chunk": 33425 }, { "available": 96, "broken": 0, "dropped": 53, "dropping": 1, "expired": 0, "idc": "z2", "readonly": 46, "repaired": 3, "repairing": 0, "total": 152, "total_chunk": 58123, "total_free_chunk": 40173 } ], "free_space": 1774492930453504, "total_blob_node": 17, "total_disk": 408, "total_space": 2155017090891776, "used_space": 380524160438272, "writable_space": 923847465369600 }, "volume_stat": { "active_volume": 345, "can_alloc_volume": 1651, "idle_volume": 1651, "lock_volume": 0, "total_volume": 1996, "unlocking_volume": 0 } }响应字段可分成三组来理解:
raft_status:Raft 协议运行细节,包括当前nodeId、term、leader、vote、applied/commit日志水位以及raftState(StateFollower/StateLeader)。leader_host则直接给出当前领导者地址,便于运维判断请求应发往哪个节点。space_stat:按 IDC 聚合的物理空间统计(disk_stat_infos数组)与全局总量。disk_stat_infos中total是磁盘总数,available/readonly/broken/repairing/repaired/dropping/dropped/expired分别对应各种磁盘状态的数量,total_chunk与total_free_chunk是 chunk(容量单元)维度统计;全局字段中total_space为物理总空间,free_space为可写物理空间,writable_space为可写逻辑空间,total_blob_node为 BlobNode 节点数,total_disk为磁盘总数。volume_stat:卷状态统计,total_volume为卷总数,active_volume为活跃(可服务读写)卷数,idle_volume为空闲卷数,can_alloc_volume为可分配卷数,lock_volume/unlocking_volume为锁定与解锁中的卷数。注意响应中的space_stat对应 blobstore 侧常说的 BlobNode(HDD)空间,而volume_stat中can_alloc_volume等字段来自卷分配器的实时统计。
从实现上看,blobstore/clustermgr/manage.go#L124-L137 中的Service.Stat依次聚合了 Raft 状态(s.raftNode.Status())、Leader 地址、BlobNode 空间统计、ShardNode 空间统计、卷统计与集群只读标志;其中卷统计由 blobstore/clustermgr/volumemgr/volumemgr.go#L545-L559 的VolumeMgr.Stat计算,直接取自各卷状态的运行时计数。响应体结构的字段定义可对照 blobstore/api/clustermgr/disk.go 中的DiskStatInfo、SpaceStatInfo类型。
三、节点管理
节点管理面向 Raft 集群本身的成员变更,包括添加节点、移除节点与切换领导者,是 Clustermgr 扩缩容的核心操作。
3.1 添加节点:POST /member/add
通过指定节点类型、地址与 ID 向 Raft 集群添加新成员:
curl -X POST --header 'Content-Type: application/json' -d '{"peer_id": 1, "host": "127.0.0.1:10110","node_host": "127.0.0.1:9998", "member_type": 2}' "http://127.0.0.1:9998/member/add"参数列表
| 参数 | 类型 | 说明 |
|---|---|---|
| peer_id | uint64 | Raft 节点 ID,必须唯一 |
| host | string | Raft 地址(节点间通信地址) |
| node_host | string | 服务地址(对外提供服务的地址) |
| member_type | uint8 | 节点类型,1 表示 leaner,2 表示 normal |
其中member_type的取值在 blobstore/api/clustermgr/client.go 中定义为MemberType枚举:MemberTypeLearner(1,学习者节点,只同步日志不参与投票,适合先追平数据再提升为正式成员)与MemberTypeNormal(2,正式投票成员)。请求体 JSON 字段与AddMemberArgs结构体一一对应:
type AddMemberArgs struct { PeerID uint64 `json:"peer_id"` Host string `json:"host"` MemberType MemberType `json:"member_type"` NodeHost string `json:"node_host"` }服务端 blobstore/clustermgr/manage.go#L40-L78 的MemberAdd处理逻辑值得注意:
- 首先校验
member_type是否落在合法区间(必须为 1 或 2),否则返回参数非法; - 遍历当前 Raft
status.Peers,若peer_id或host与已有成员重复,直接返回ErrDuplicatedMemberInfo,保证 ID 与地址的唯一性; node_host会被序列化为MemberContext随成员信息一起持久化,供后续路由使用;- 最终调用
s.raftNode.AddMember,按类型分别以Learner: true/false加入。
该接口没有独立参数表之外的可选字段,一次请求完成一个节点的加入。
3.2 移除节点:POST /member/remove
按 ID 从 Raft 集群中移除节点:
curl -X POST --header 'Content-Type: application/json' -d '{"peer_id": 1}' "http://127.0.0.1:9998/member/remove"参数列表
| 参数 | 类型 | 说明 |
|---|---|---|
| peer_id | uint64 | Raft 节点 ID,必须唯一 |
实现上(blobstore/clustermgr/manage.go#L80-L104)有两个关键约束:
- 先通过
checkPeerIDExist校验节点确实存在,不存在则返回参数非法; - 不允许直接移除当前 Leader,若
peer_id等于当前raftNode.Status().Leader,接口返回ErrRequestNotAllow。正确的扩缩容流程是先用/leadership/transfer把领导权转移给其他节点,再执行移除。
3.3 切换领导者:POST /leadership/transfer
按 ID 将 Raft 领导权转移到指定节点:
curl -X POST --header 'Content-Type: application/json' -d '{"peer_id": 1}' "http://127.0.0.1:9998/leadership/transfer"参数列表
| 参数 | 类型 | 说明 |
|---|---|---|
| peer_id | uint64 | Raft 节点 ID,必须唯一 |
对应实现 blobstore/clustermgr/manage.go#L106-L122 同样先校验节点存在,再调用s.raftNode.TransferLeadership(ctx, s.raftNode.Status().Id, args.PeerID)完成领导权转移。注意该接口的peer_id是目标节点(接收领导权的一方),常用于 Leader 节点需要下线维护或触发重新选举的场景。
四、磁盘管理
磁盘管理围绕 BlobNode 上报的物理磁盘元数据展开,支持查询、状态变更、读写属性切换与离线迁移。
4.1 查询磁盘信息:GET /disk/info
curl "http://127.0.0.1:9998/disk/info?disk_id=1"参数列表
| 参数 | 类型 | 说明 |
|---|---|---|
| disk_id | uint32 | 磁盘 ID |
响应示例
{ "cluster_id": 10001, "create_time": "2022-05-07T15:22:01.627271402+08:00", "disk_id": 1, "free": 1910475022336, "free_chunk_cnt": 106, "host": "http://127.0.0.1:8889", "idc": "bjht", "last_update_time": "2022-05-07T15:22:01.627271402+08:00", "max_chunk_cnt": 1037, "path": "/home/service/var/data21", "rack": "HT02-B11-F4-402-0203", "readonly": false, "size": 17828005326848, "status": 1, "used": 15917530304512, "used_chunk_cnt": 931 }响应字段覆盖磁盘的归属信息(cluster_id、idc、rack、host、path)、容量信息(size总容量、used/free已用与可用、max_chunk_cnt/used_chunk_cnt/free_chunk_cnt的 chunk 维度计数)以及运行状态(status磁盘状态、readonly是否只读、create_time/last_update_time)。这些字段对应 blobstore/api/clustermgr/disk.go 中的BlobNodeDiskInfo结构体,其中status的取值枚举定义在 blobstore/common/proto/const.go#L46-L51。
4.2 设置磁盘状态:POST /disk/set
curl -X POST --header 'Content-Type: application/json' -d '{"disk_id":2,"status":2}' "http://127.0.0.1:9998/disk/set"| 参数 | 类型 | 说明 |
|---|---|---|
| disk_id | uint32 | 磁盘 ID |
| status | uint8 | 磁盘状态只能递增,数值含义见下表 |
| 磁盘状态值 | 说明 |
|---|---|
| 1 | normal |
| 2 | broken |
| 3 | repairing |
| 4 | repaired |
| 5 | dropped |
状态枚举在 blobstore/common/proto/const.go#L46-L51 中定义:
DiskStatusNormal = DiskStatus(iota + 1) // 1 DiskStatusBroken // 2 DiskStatusRepairing // 3 DiskStatusRepaired // 4 DiskStatusDropped // 5 DiskStatusMax // 6服务端实现 blobstore/clustermgr/blobnode_disk.go#L152-L189 有几个细节需要运维人员注意:
- 该接口不允许设置 dropped(5)状态:代码限定
args.Status必须满足normal <= status < dropped,即只能设置 1~4,磁盘离线(dropped)必须走专门的/disk/drop接口,以保证数据迁移流程可控; - 状态相同则直接返回,不做重复写入;
- 当状态被设置为
broken时,会联动调用s.VolumeMgr.DiskWritableChange调整受影响卷的健康度,让上层及时感知容量变化; - 文档强调"磁盘状态只能递增",即运维应遵循 normal → broken → repairing → repaired 的推进顺序,避免状态回退造成管理混乱。
4.3 设置磁盘读写:POST /disk/access
将磁盘切换为只读或读写:
curl -X POST --header 'Content-Type: application/json' -d '{"disk_id":2,"readonly":false}' "http://127.0.0.1:9998/disk/access"参数列表
| 参数 | 类型 | 说明 |
|---|---|---|
| disk_id | uint32 | 磁盘 ID |
| readonly | bool | 是否只读,true 表示只读,false 表示读写 |
该接口与/disk/set互补:/disk/set管理生命周期状态,/disk/access管理读写权限。将磁盘置为只读常用于容量保护、异常排查或准备下线前的过渡,只读状态下新数据不再写入该盘,但存量数据仍可读取。
4.4 设置磁盘下线:POST /disk/drop
针对机器或磁盘过保等场景,可对磁盘执行下线以触发数据迁移。迁移过程中会先尝试直接读取离线磁盘上的数据,若读取失败则走"修复后读取"流程,保证数据尽量完整搬迁:
curl -X POST --header 'Content-Type: application/json' -d '{"disk_id":2}' "http://127.0.0.1:9998/disk/drop"服务端 blobstore/clustermgr/blobnode_disk.go#L191-L206 的DiskDrop仅接收disk_id,随后交给s.BlobNodeMgr.DropDisk执行下线逻辑——它会将磁盘标记为 dropping,由后台调度器接管数据迁移任务(迁移期间可通过GET /disk/droppinglist查询进行中的下线列表,相关路由见 blobstore/clustermgr/handler.go)。注意/disk/drop是异步触发的:接口返回只代表下线任务已受理,实际迁移进度由后台任务系统持续推进。
五、卷管理:GET /volume/get
查询单个卷的详细信息,包括卷的编码模式、容量使用与构成该卷的所有存储单元:
curl "http://127.0.0.1:9998/volume/get?vid=1"参数列表
| 参数 | 类型 | 说明 |
|---|---|---|
| vid | uint32 | 卷 ID |
响应示例
{ "code_mode": 12, "create_by_node_id": 1, "free": 1061027840, "health_score": 0, "status": 1, "total": 171798691840, "units": [ { "disk_id": 112, "host": "http://127.0.0.1:8889", "vuid": 4294967654 }, ... { "disk_id": 401, "host": "http://127.0.0.1:8889", "vuid": 4513071462 } ], "used": 170737664000, "vid": 1 }字段含义:
code_mode:卷的纠删码编码模式编号(如 12 对应某种 EC 冗余策略,编码模式定义见 blobstore/common/codemode 目录),它决定了units中存储单元的数量与冗余配比;vid/status:卷 ID 与卷状态;total/used/free:卷的逻辑容量、已用与可用空间;units:构成该卷的底层存储单元列表,每个单元由vuid(Volume Unit ID)、disk_id和所在 BlobNode 的host唯一标识,这也是卷路由(Volume Route)的基础数据;health_score:卷健康分,反映底层单元异常情况,数值越大通常意味着可用性越差。
对应的请求与响应结构定义在 blobstore/api/clustermgr/volume.go:GetVolumeArgs只含vid,响应VolumeInfo由Units []Unit与VolumeInfoBase组合而成,其中Unit结构体为{Vuid, DiskID, Host}三元组。
六、后台任务开关管理
Clustermgr 会把磁盘修复、数据均衡、磁盘离线、数据删除、数据修复、数据巡检等后台任务的启停状态保存在自身的 KV 配置中,运维可随时查询与切换。支持的六类任务如下:
| 任务类型(type) | 任务名称(key) | 开关(value) |
|---|---|---|
| Disk Repair | disk_repair | true/false |
| Data Balancing | balance | true/false |
| Disk Offline | disk_drop | true/false |
| Data Deletion | blob_delete | true/false |
| Data Repair | shard_repair | true/false |
| Data Inspection | volume_inspect | true/false |
这些任务名与 blobstore/common/proto/scheduler.go#L40-L46 中定义的TaskType常量一一对应(disk_repair、balance、disk_drop、volume_inspect、shard_repair、blob_delete),调度器与 CLI 工具均以这些字符串作为开关的 key。
6.1 查看任务状态
curl http://127.0.0.1:9998/config/get?key=balance # 或使用 blobstore-cli blobstore-cli cm background status balance6.2 开启任务
curl -X POST http://127.0.0.1:9998/config/set -d '{"key":"balance","value":"true"}' --header 'Content-Type: application/json' # 或使用 blobstore-cli blobstore-cli cm background enable balance6.3 关闭任务
curl -X POST http://127.0.0.1:9998/config/set -d '{"key":"balance","value":"false"}' --header 'Content-Type: application/json' # 或使用 blobstore-cli blobstore-cli cm background disable balance背后的实现机制在 blobstore/clustermgr/config.go 中非常清晰:
ConfigGet(L32-L59)属于线性一致读:先调用s.raftNode.ReadIndex(ctx)确认当前节点状态已追平 Leader 的提交水位,再从ConfigMgr读取配置,保证读到的一定是最新值;ConfigSet(L61-L90)走Raft Propose 持久化:将{key, value}序列化后封装为OperTypeSetConfig提案提交给 Raft,经多数派确认后才生效,因此任何节点发起的配置变更都会在集群内保持一致;同时,若 key 属于proto.IsUnmodifiableSysConfigKey保护的系统配置,接口会直接拒绝,防止误改核心配置。
6.4 blobstore-cli 用法补充
blobstore-cli cm background子命令定义在 blobstore/cli/clustermgr/background.go,支持status、enable、disable三个动作,交互式确认后执行,适合人工操作;其底层仍是通过上述config/get与config/set接口完成读写,并在enable/disable时校验任务名合法性。除后台任务外,blobstore-cli cm还提供磁盘管理、卷管理等更多子命令(见 blobstore/cli/clustermgr 目录),例如blobstore-cli cm disk set、blobstore-cli cm volume get等,可作为 curl 之外的另一条运维路径。
七、运维要点小结
结合文档与源码,使用 Clustermgr 管理 API 时有几个值得牢记的实践要点:
- 写操作务必发往 Leader:
/member/add、/disk/set、/config/set等写接口依赖 Raft Propose,若请求落在 Follower 上会因无法提交而失败。日常可通过GET /stat的leader_host字段定位当前 Leader,将管理请求定向发送。 - 移除节点前先转移领导权:
/member/remove明确禁止移除 Leader,标准流程是/leadership/transfer转移后再执行移除,避免集群在无主状态下操作。 - 磁盘状态单向推进:
/disk/set只能设置 normal→broken→repairing→repaired 序列,且不能直接置为 dropped;真正的下线操作必须走/disk/drop,由后台调度器完成数据迁移。 - 后台任务开关全局生效且持久化:任务开关以
true/false字符串存储在 Raft 共识的配置中,任意节点写入、全集群一致,适合在维护窗口统一关闭均衡、巡检等任务以减少干扰。
以上接口的路由、参数解析与实现均可对照仓库源码进一步学习:路由全表见 blobstore/clustermgr/handler.go,节点管理实现在 blobstore/clustermgr/manage.go,磁盘管理实现在 blobstore/clustermgr/blobnode_disk.go,配置管理实现在 blobstore/clustermgr/config.go,Go 客户端封装见 blobstore/api/clustermgr/client.go 及同目录下的disk.go、volume.go、config.go。
- 存储
- 分布式文件系统
- 对象存储
- 云原生
【免费下载链接】cubefs
cloud-native distributed storage
相关推荐
CubeFS BlobStore Clustermgr 管理 API 实战指南:节点、磁盘、卷与后台任务运维
CubeFS BlobStore Clustermgr 管理 API 实战指南:节点、磁盘、卷与后台任务运维 Clustermgr 是 CubeFS BlobS
存储分布式文件系统对象存储云原生本地 RAG 实战指南:Genkit 加 Ollama,5 分钟搭起宝可梦问答应用
本地 RAG 实战指南:Genkit 加 Ollama,5 分钟搭起宝可梦问答应用 想让数据不出本机也能做知识库问答?Genkit 的 js/testapps/
存储分布式文件系统对象存储云原生CubeFS Blobstore Scheduler 后台任务管理实战指南:状态查询、手动迁移与任务详情排查
CubeFS Blobstore Scheduler 后台任务管理实战指南:状态查询、手动迁移与任务详情排查 CubeFS 的 Blobstore 存储子系统通
存储分布式文件系统对象存储云原生
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考