Ceph MDS 状态机与 Monitor 中的状态流转分析

基于 Ceph 14.2.18源码(mds/MDSMap.h, mds/MDSMap.cc, mds/FSMap.h, mds/FSMap.cc, mon/MDSMonitor.cc)分析


一、MDS 状态定义

所有 MDS 状态定义在 mds/MDSMap.h 的枚举 DaemonState 中(L62-L100):

cpp 复制代码
// mds/MDSMap.h: L62-L100
typedef enum {
    // ---- 不持有 rank 的 daemon 状态 ----
    STATE_NULL             // 空值
    STATE_BOOT             // 进程已启动,向 Mon 宣告自己存在
    STATE_STANDBY          // 空闲,等待 Monitor 分配
    STATE_STANDBY_REPLAY   // 正在 replay 某个 active rank,随时可接管

    // ---- MDS rank 自身的状态(可能没有 daemon 持有)----
    STATE_STOPPED          // rank 曾存在,已正常停止,日志为空
    STATE_CREATING         // daemon 正在为新 rank 创建日志/元数据
    STATE_STARTING         // daemon 正在启动一个 stopped rank
    STATE_REPLAY           // 恢复场景:daemon 正在扫描之前失败实例的日志
    STATE_RESOLVE          // 恢复场景:消歧义分布式操作(import/rename等)
    STATE_RECONNECT        // 恢复场景:重新连接客户端
    STATE_REJOIN           // 恢复场景:重新 join 分布式缓存
    STATE_CLIENTREPLAY     // 重放客户端请求
    STATE_ACTIVE           // 正常工作状态
    STATE_STOPPING         // 正在导出元数据,准备退出
    STATE_DNE              // rank 不存在

    // ---- daemon 向 Mon 上报的特殊状态 ----
    STATE_DAMAGED          // 磁盘/日志损坏,需要离线修复
} DaemonState;

重要区分:rank 的隐式状态

MDSMap.h 注释(L91-L99)明确说明:rank 还有三个隐式状态 ,不通过 DaemonState 枚举表示,而是通过 MDSMap 中的集合来判断:

隐式状态 判断方法
FAILED rank ∈ mds_map.failed 且没有 daemon 持有该 rank
STOPPED rank ∈ mds_map.stopped 且没有 daemon 持有该 rank
DNE rank 既不在 failed 也不在 stopped

MDSMap 的核心数据结构(MDSMap.h L196-L201):

cpp 复制代码
std::set<mds_rank_t> in;              // 集群中已定义的 rank 集合
std::set<mds_rank_t> failed;          // 失败的 rank(等待新 standby 接管)
std::set<mds_rank_t> stopped, damaged;
std::map<mds_rank_t, mds_gid_t> up;   // rank → 持有该 rank 的 daemon gid
std::map<mds_gid_t, mds_info_t> mds_info; // gid → daemon 详细信息

ceph fs status 显示 failed,本质上是显示 mds_map.failed 集合中的 rank,而非某个 daemon 的 DaemonState


二、Monitor 中状态流转的完整路径

Monitor 通过两种途径修改 MDS 状态:

  1. 被动响应 :处理 MDS daemon 主动上报的 beacon 消息(prepare_beacon()
  2. 主动检测 :定时 tick 检测 beacon 超时(tick()maybe_replace_gid()

2.1 MDS 主动驱动的状态变更:prepare_beacon()

mon/MDSMonitor.cc L536-L796,prepare_beacon() 是 Monitor 接收 MDS 心跳并处理状态变更请求的入口。

2.1.1 BOOT 流程(进程启动 → STANDBY)
cpp 复制代码
// MDSMonitor.cc L587-L634
if (state == MDSMap::STATE_BOOT) {
    // 1. 如果同名 MDS 已存在,先 fail 掉旧实例
    if (g_conf()->mds_enforce_unique_name) {
        while (mds_gid_t existing = pending.find_mds_gid_by_name(m->get_name())) {
            fail_mds_gid(pending, existing);  // 清除旧 gid
        }
    }
    // 2. 创建新的 mds_info,初始状态为 STATE_STANDBY
    MDSMap::mds_info_t new_info;
    new_info.state = MDSMap::STATE_STANDBY;
    pending.insert(new_info);
    // 3. 初始化该 gid 的 beacon 时间戳
    last_beacon[gid].stamp = mono_clock::now();
}

STATE_BOOT 是一个临时过渡状态,只出现在 MDS 向 Mon 发送的消息中,Monitor 处理后立即将其注册为 STATE_STANDBY,不会持久化 BOOT 状态。

2.1.2 MDS 上报状态变更(STANDBY → REPLAY → ... → ACTIVE)
cpp 复制代码
// MDSMonitor.cc L756-L778
} else if (info.state != MDSMap::STATE_STANDBY && state != info.state &&
           !MDSMap::state_transition_valid(info.state, state)) {
    // 非法状态转换,拒绝
    derr << "daemon reported invalid state transition" << dendl;
    return true;
} else {
    // 合法转换,直接记录 daemon 上报的新状态
    pending.modify_daemon(gid, [state, seq](auto& info) {
        info.state = state;
        info.state_seq = seq;
    });
}

关键约束 :状态转换合法性由 MDSMap::state_transition_valid() 验证(mds/MDSMap.cc L885-L909):

cpp 复制代码
bool MDSMap::state_transition_valid(DaemonState prev, DaemonState next)
{
    if (prev == MDSMap::STATE_REPLAY) {
        // REPLAY 只能转到 RESOLVE 或 RECONNECT
        if (next != MDSMap::STATE_RESOLVE && next != MDSMap::STATE_RECONNECT)
            state_valid = false;
    } else if (prev == MDSMap::STATE_REJOIN) {
        // REJOIN 只能转到 ACTIVE/CLIENTREPLAY/STOPPED
        if (next != STATE_ACTIVE && next != STATE_CLIENTREPLAY && next != STATE_STOPPED)
            state_valid = false;
    } else if (prev >= MDSMap::STATE_RESOLVE && prev < MDSMap::STATE_ACTIVE) {
        // RESOLVE/RECONNECT/REJOIN/CLIENTREPLAY:只能按序前进一步
        if (next != prev + 1)
            state_valid = false;
    }
    return state_valid;
}

这意味着:RESOLVE → RECONNECT → REJOIN → CLIENTREPLAY → ACTIVE 必须严格按顺序,每步只能前进一个状态。Monitor 不会主动驱动 MDS 从 REPLAY 走到 RESOLVE 等后续状态,这些状态变更完全由 MDS daemon 自己通过发送 beacon 上报触发

2.1.3 特殊状态处理
上报状态 Monitor 处理逻辑 来源
STATE_STOPPED 调用 fsmap.stop(gid):从 up 移除,加入 stopped,清理 standby-replay L686-L704
STATE_DAMAGED 调用 fsmap.damaged(gid, epoch):加入 damaged,blacklist IP L707-L732
STATE_DNE 调用 fail_mds_gid():走 erase() 逻辑,加入 failed L733-L749
2.1.4 laggy 标记清除
cpp 复制代码
// MDSMonitor.cc L673-L680
if (info.laggy()) {
    dout(1) << "prepare_beacon clearing laggy flag on " << addrs << dendl;
    pending.modify_daemon(info.global_id, [](auto& info) {
        info.clear_laggy();
    });
}

只要 MDS daemon 还能发来 beacon,无论状态如何,Monitor 都会清除 laggy 标记。


2.2 Monitor 主动检测:tick()maybe_replace_gid()

tick() 是 Monitor 的定时任务(MDSMonitor.cc L2001-L2089),每个 tick_interval 调用一次。

2.2.1 tick() 的执行流程
cpp 复制代码
void MDSMonitor::tick()
{
    // 1. 仅 active leader 执行
    if (!is_active() || !is_leader()) return;

    // 2. 检测 Mon 自身的延迟(选举等),重置 beacon 时间戳
    if (since_last > (mds_beacon_grace - mds_beacon_interval)) {
        for (auto &p : last_beacon) {
            p.second.stamp = now;   // 防止 Mon 自身慢导致误判
        }
    }
    last_tick = now;

    // 3. 确保 last_beacon 对所有已知 gid 都有初始值(首次 tick 时用当前时间初始化)
    for (auto &p : pending.mds_roles) {
        last_beacon.emplace(gid, {mono_clock::now(), 0});
    }

    // 4. 遍历所有 gid,检测 beacon 超时
    for (auto it = last_beacon.begin(); it != last_beacon.end(); ) {
        auto since_last = now - beacon_info.stamp;

        if (!pending.gid_exists(gid)) {
            it = last_beacon.erase(it);  // gid 不存在则清理
            continue;
        }

        if (since_last >= mds_beacon_grace) {
            // 超过 grace 时间没收到 beacon → 触发处理
            if (osdmap_writeable) {
                maybe_replace_gid(pending, gid, info, ...);
            }
        }
        ++it;
    }

    // 5. 尝试用 standby 补充 failed 的 rank
    for (auto &p : pending.filesystems) {
        maybe_promote_standby(pending, *p.second);
    }
}

注意第 3 步last_beacon.emplace 使用的是默认参数,只有当 last_beacon没有该 gid 的记录 时才插入(emplace 语义)。因此只有新出现的 gid 才会被以当前时间初始化,已有记录不受影响。

2.2.2 maybe_replace_gid() 详细逻辑

这是整个状态流转的核心函数(MDSMonitor.cc L1870-L1946):

cpp 复制代码
void MDSMonitor::maybe_replace_gid(FSMap &fsmap, mds_gid_t gid,
    const MDSMap::mds_info_t& info, bool *mds_propose, bool *osd_propose)
{
    // ── 步骤1:计算 may_replace 守卫 ──────────────────────────────────
    // 找到 last_beacon 中所有 MDS 里最新的一条 beacon 时间
    mono_time latest_beacon = mono_clock::zero();
    for (const auto &p : last_beacon) {
        latest_beacon = std::max(p.second.stamp, latest_beacon);
    }
    chrono::duration<double> since = now - latest_beacon;

    // may_replace = true 的条件:
    //   "集群中至少有某个 MDS 最近发来过 beacon"
    //   (距离最新一次任意 beacon 的时间 < max(beacon_interval, beacon_grace * 0.5))
    const bool may_replace = since.count() <
        std::max(g_conf()->mds_beacon_interval, g_conf()->mds_beacon_grace * 0.5);

    const bool frozen = info.is_frozen();

    // ── 步骤2:三路分支 ──────────────────────────────────────────────

    // 分支A:有 rank 的 daemon(REPLAY/RESOLVE/RECONNECT/REJOIN/ACTIVE/STOPPING 等)
    //        且 may_replace=true 且有可用 standby
    if (info.rank >= 0 &&
        info.state != STATE_STANDBY &&
        info.state != STATE_STANDBY_REPLAY &&
        may_replace &&
        !frozen &&
        !fsmap.test_flag(CEPH_MDSMAP_NOT_JOINABLE) &&
        (sgid = fsmap.find_replacement_for(...)) != MDS_GID_NONE)
    {
        // ✅ 触发 fail:移除旧 daemon,提升 standby
        fail_mds_gid(fsmap, gid);
        fsmap.promote(sgid, *fs, info.rank);
    }
    // 分支B:STANDBY 或 STANDBY_REPLAY 且 may_replace=true
    else if ((STATE_STANDBY_REPLAY || STATE_STANDBY) && may_replace && !frozen) {
        // ✅ 触发 fail:直接移除,不需要替代者
        fail_mds_gid(fsmap, gid);
    }
    // 分支C:以上均不满足(包括 may_replace=false 的情况)
    else if (!info.laggy()) {
        // ⚠️ 仅标记 laggy,不做任何 fail 操作
        fsmap.modify_daemon(info.global_id, [](auto& info) {
            info.laggy_since = ceph_clock_now();
        });
    }
    // 如果已经是 laggy,则什么都不做(静默)
}
2.2.3 fail_mds_gid()FSMap::erase()failed.insert()

只有调用了 fail_mds_gid() 才能将 rank 放入 failed 集合(MDSMonitor.cc L1140-L1162):

cpp 复制代码
bool MDSMonitor::fail_mds_gid(FSMap &fsmap, mds_gid_t gid)
{
    // 1. 对持有 rank 的非 STANDBY_REPLAY daemon,blacklist 其 IP
    if (info.rank >= 0 && info.state != STATE_STANDBY_REPLAY) {
        blacklist_epoch = mon->osdmon()->blacklist(info.addrs, until);
    }
    // 2. 调用 FSMap::erase()
    fsmap.erase(gid, blacklist_epoch);
    last_beacon.erase(gid);
    ...
}

FSMap::erase() 中的关键逻辑(mds/FSMap.cc L879-L907):

cpp 复制代码
void FSMap::erase(mds_gid_t who, epoch_t blacklist_epoch)
{
    auto &fs = filesystems.at(mds_roles.at(who));
    const auto &info = fs->mds_map.mds_info.at(who);

    if (info.state != MDSMap::STATE_STANDBY_REPLAY) {
        if (info.state == MDSMap::STATE_CREATING) {
            // CREATING 阶段失败:从 in 中移除(rank 从未真正存在过)
            fs->mds_map.in.erase(info.rank);
        } else {
            // 其他所有有 rank 的状态(包括 REPLAY/RESOLVE/RECONNECT/ACTIVE 等):
            // 将 rank 加入 failed 集合 ← 这就是 ceph fs status 显示 "failed" 的来源
            fs->mds_map.failed.insert(info.rank);
        }
        fs->mds_map.up.erase(info.rank);     // rank 不再被 daemon 持有
    }
    fs->mds_map.mds_info.erase(who);          // 删除 gid 记录
    fs->mds_map.last_failure_osd_epoch = blacklist_epoch;
    mds_roles.erase(who);
}

结论:STATE_RESOLVE 的 daemon 如果被 erase(),会走 else 分支,rank 进入 failed 集合,ceph fs status 显示 failed。但前提是 fail_mds_gid() 被调用。

2.2.4 maybe_promote_standby():处理 failed 集合
cpp 复制代码
// MDSMonitor.cc L1948-L1999
bool MDSMonitor::maybe_promote_standby(FSMap &fsmap, Filesystem& fs)
{
    // 遍历所有 failed rank,尝试找到 standby 接管
    set<mds_rank_t> failed;
    fs.mds_map.get_failed_mds_set(failed);
    for (const auto& rank : failed) {
        auto&& sgid = fsmap.find_replacement_for({fs.fscid, rank}, {});
        if (sgid) {
            // 找到 standby,promote → state = STATE_REPLAY,rank 从 failed 移出
            fsmap.promote(sgid, fs, rank);
        }
    }
}

三、完整状态流转图

复制代码
MDS 进程启动
    │
    ▼ (发送 BOOT beacon)
[STATE_BOOT]  ──────────────────────────────────────────────────────────────
    │ Monitor: prepare_beacon() 将 BOOT 处理为 STANDBY,插入 standby_daemons
    ▼
[STATE_STANDBY] ←─── 每次 tick last_beacon 刷新
    │ Monitor: maybe_resize_cluster() 或 maybe_promote_standby() 调用 promote()
    ▼ (rank 已有 failed 状态时)
[STATE_REPLAY] ──── daemon 自己通过 beacon 上报推进 ────────────────────────
    │ MDS daemon 完成日志扫描后,自行上报 STATE_RESOLVE
    ▼
[STATE_RESOLVE] ──── daemon 自己通过 beacon 上报推进 ───────────────────────
    │ MDS daemon 完成分布式操作消歧后,自行上报 STATE_RECONNECT
    ▼
[STATE_RECONNECT]
    │ MDS daemon 完成客户端重连后,自行上报 STATE_REJOIN
    ▼
[STATE_REJOIN]
    │ MDS daemon 完成缓存 rejoin 后,自行上报 STATE_ACTIVE/CLIENTREPLAY
    ▼
[STATE_ACTIVE] ←────────────────────────────────────────────────────────────
    │ Monitor: maybe_resize_cluster() 收缩时设置 STATE_STOPPING
    ▼
[STATE_STOPPING]
    │ MDS daemon 导出完元数据后,自行上报 STATE_STOPPED
    ▼
rank 进入 stopped 集合(no daemon holds it)

────────────── 故障路径 ───────────────────────────────────────────────────
[任意有 rank 的状态] + beacon 超时 + may_replace=true + 有 standby
    │ Monitor: maybe_replace_gid() → fail_mds_gid() → erase()
    ▼
rank 进入 [failed] 集合(ceph fs status 显示 "failed")
    │ Monitor: maybe_promote_standby() 找到 standby
    ▼
rank 重新进入 [STATE_REPLAY](由新 standby daemon 持有)

四、MDS 心跳(Beacon)对状态变化的影响

4.1 心跳的记录机制

cpp 复制代码
// MDSMonitor.cc L308-L317
void MDSMonitor::_note_beacon(MMDSBeacon *m)
{
    auto &beacon = last_beacon[gid];
    beacon.stamp = mono_clock::now();  // 记录收到 beacon 的时间
    beacon.seq = seq;
}

last_beacon 是一个 map<mds_gid_t, beacon_info_t> 的内存数据结构,记录每个 gid 最后一次收到 beacon 的时间。这个数据结构是 Monitor leader 独有的内存状态,不持久化到 Paxos。

_note_beacon() 在以下情况被调用:

  • preprocess_beacon() 中:beacon 需要更新 map(有状态变更/健康变更/laggy 清除)时
  • preprocess_beacon()reply: 标签处:无变更但需要回复时

4.2 心跳超时对状态的影响(核心)

maybe_replace_gid() 中的 may_replace 守卫是心跳影响状态流转的关键:

cpp 复制代码
// MDSMonitor.cc L1881-L1889
mono_time latest_beacon = mono_clock::zero();
for (const auto &p : last_beacon) {
    latest_beacon = std::max(p.second.stamp, latest_beacon);
    //              ↑ 取所有 MDS 中最新一次 beacon 的时间(不只是当前 gid)
}
mono_time now = mono_clock::now();
chrono::duration<double> since = now - latest_beacon;

const bool may_replace = since.count() <
    std::max(g_conf()->mds_beacon_interval, g_conf()->mds_beacon_grace * 0.5);

这段逻辑的含义是:may_replace 反映的不是"当前 gid 的心跳是否正常",而是"集群中是否至少有一个 MDS 最近发来过心跳"。

设计意图(代码注释 L1878-L1880):

"We will only take decisive action (replacing/removing a daemon) if we have some indication that some other daemon(s) are successfully getting beacons through recently."

如果所有 MDS 的 beacon 都超时了,可能是 Monitor 自身的网络/性能问题,不应草率地 fail 掉所有 MDS。

4.3 心跳对各状态的具体影响矩阵

MDS 当前状态 心跳正常 心跳超时 + may_replace=true 心跳超时 + may_replace=false
STATE_STANDBY 正常维护在 standby_daemons fail_mds_gid() → 从 standby 移除 仅标记 laggy
STATE_STANDBY_REPLAY 正常维护 fail_mds_gid() → 从 mds_info 移除 仅标记 laggy
STATE_REPLAY 正常,等 daemon 推进状态 fail_mds_gid() + 提升 standby → rank 进入 failed 再立即由新 standby 接管 仅标记 laggy
STATE_RESOLVE 同上 同上 ⚠️ 仅标记 laggy,rank 停留在 RESOLVE(laggy or crashed)
STATE_RECONNECT 同上 同上 ⚠️ 同上
STATE_ACTIVE 同上 同上 ⚠️ 同上
STATE_STOPPING 正常,等 daemon 推进到 STOPPED 同上 ⚠️ 同上

4.4 laggy 标记的作用与局限

maybe_replace_gid() 进入分支 C 时,只是设置 laggy_since

cpp 复制代码
fsmap.modify_daemon(info.global_id, [](auto& info) {
    info.laggy_since = ceph_clock_now();
});

laggy 标记的影响:

  1. ceph fs status 输出变化 :状态显示从 resolve 变为 resolve(laggy or crashed)FSMap.cc L186-L188)
  2. find_replacement_for() 中跳过 laggy standbyget_available_standby() 会跳过 laggy() 为 true 的 standby(FSMap.cc L719)
  3. 不影响 rank 的归属 :rank 仍然在 up 中,failed 集合中没有它

关键局限laggy 标记一旦设置,且 may_replace 持续为 false,则:

  • 下次 tick 检测时,因为 info.laggy() 已为 true,进入分支 C 的 else if (!info.laggy()) 判断为 false
  • 整个 maybe_replace_gid() 什么都不做,彻底静默
cpp 复制代码
// 后续 tick 中,如果 laggy 已设置且 may_replace 仍为 false:
// 分支A: may_replace=false → 不进入
// 分支B: 不是 STANDBY/STANDBY_REPLAY → 不进入
// 分支C: info.laggy() == true → !info.laggy() = false → 不进入
// 结果:完全静默,状态永远不变

五、针对现网问题的根因分析

问题场景还原

复制代码
1. 主 MDS 机器挂掉
2. 备 MDS 顶上,调用 FSMap::promote(),state = STATE_REPLAY
3. daemon 通过 beacon 推进:REPLAY → RESOLVE
   Monitor 收到 beacon,记录 state = STATE_RESOLVE
4. 此时 systemctl 重启失败,19/28号 MDS 进程已停止
5. MDS 停止发送 beacon
6. tick() 检测到 beacon 超时,调用 maybe_replace_gid()

关键问题:may_replace 的计算

cpp 复制代码
mono_time latest_beacon = mono_clock::zero();
for (const auto &p : last_beacon) {
    latest_beacon = std::max(p.second.stamp, latest_beacon);
}

此时遍历的是 last_beacon map 中所有 gid 的记录。如果整个集群的 MDS 都处于异常状态(其他 MDS 也可能故障),没有任何 MDS 在正常发 beacon,则:

  • latest_beacon = 很久之前的时间
  • since.count() = 很大的值
  • may_replace = false

进入分支 C:else if (!info.laggy()) → 只标记 laggy_since

之后的每次 tick:三个分支都不满足,完全静默

为什么不显示 failed

因为 fail_mds_gid() 从未被调用,所以 FSMap::erase() 从未执行,所以 rank 从未进入 mds_map.failed 集合。ceph fs status 读取的正是 failed 集合,所以显示的不是 failed

实际显示的是:resolve(laggy or crashed) ← state=RESOLVE + laggy=true

完整问题路径图

复制代码
MDS 19/28 进程停止
    │
    ▼ tick() beacon 超时检测
maybe_replace_gid()
    │
    ├── 计算 may_replace
    │   集群中所有 MDS 都不健康,latest_beacon 很旧
    │   → may_replace = FALSE
    │
    ├── 分支A:may_replace=false → ✗ 不进入
    ├── 分支B:state=RESOLVE,非 STANDBY → ✗ 不进入
    └── 分支C:!info.laggy() → ✓ 首次进入,设置 laggy_since
                                       后续 tick:info.laggy()=true → ✗ 不进入

结果:fail_mds_gid() 从未调用
      → FSMap::erase() 从未调用
      → failed.insert() 从未调用
      → rank 不在 failed 集合中
      → ceph fs status 显示 resolve(laggy or crashed)
      → 永远不会自动恢复

这是一个真实的 BUG

may_replace 的保守策略在单个或少量 MDS 异常 时是合理的保护,但在整集群 MDS 都处于异常状态 时,会导致任何有 rank 的 daemon 都无法被 fail 掉。

尤其对于已经处于 RESOLVE 等恢复中间状态的 MDS,进程停止后:

  • 不能自动流转到 failedmaybe_replace_gidmay_replace=false 阻止)
  • 不会被新的 standby 接管(maybe_promote_standby 只处理 failed 集合中的 rank)
  • ceph fs status 永远显示 resolve(laggy or crashed),直到人工干预

根本修复方向 :对于 laggy_since 已超过某个阈值(例如 mds_beacon_grace 的若干倍)的 daemon,即使 may_replace=false,也应强制执行 fail_mds_gid()。或者区分"没有 standby 可替换"(需要保守)和"集群整体异常"(需要激进清理)两种场景。


六、相关配置参数

参数 默认值 说明
mds_beacon_interval 4s MDS 发送 beacon 的周期
mds_beacon_grace 15s beacon 超时阈值,超过后触发 maybe_replace_gid()
mon_mds_blacklist_interval 1天 fail 后 blacklist 持续时间

may_replace 的阈值 = max(mds_beacon_interval, mds_beacon_grace * 0.5) = max(4, 7.5) = 7.5 秒 。即:如果 7.5 秒内集群中没有任何 MDS 发来 beacon,may_replace 就为 false。

相关推荐
Virtual_human08065 天前
在VMware workstation上,部署3节点ceph测试,及加入openstack
ceph·云计算·openstack·osd·ceph集群
Brandon汐12 天前
从0开始搭建一主两节点k8s集群对接Ceph集群
ceph·容器·kubernetes
泡沫·15 天前
CEPH的基本认识
ceph
2301_7679026416 天前
ceph分布式存储(三)
分布式·ceph
2301_7679026417 天前
ceph分布式存储(一)
分布式·ceph
2301_7679026417 天前
ceph分布式存储(二)
分布式·ceph
FJW02081419 天前
cephadm部署ceph集群以及k8s对接
ceph·容器·kubernetes
韭菜张师傅20 天前
Ceph RBD 命令详解
ceph
韭菜张师傅21 天前
Ceph FS 命令详解
ceph