千家信息网

Ceph中容量计算与管理的示例分析

发表于:2026-11-02 作者:千家信息网编辑
千家信息网最后更新 2026年11月02日,这篇文章主要介绍Ceph中容量计算与管理的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!Ceph中的容量计算与管理在部署完Ceph集群之后,一般地我们可以通过Ceph
千家信息网最后更新 2026年11月02日Ceph中容量计算与管理的示例分析

这篇文章主要介绍Ceph中容量计算与管理的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!

Ceph中的容量计算与管理

在部署完Ceph集群之后,一般地我们可以通过Ceph df这个命令来查看集群的容量状态,但是Ceph是如何计算和管理的呢?相信大家都比较好奇。因为用过 ceph df这个命令的人都会有这个疑问,它的输出到底是怎么计算的呢?为什么所有pool的可用空间有时候等于GLOBAL中的可用空间,有时候不等呢? 带着这些疑问我们可以通过分析ceph df的实现,来看看Ceph是如何计算容量和管理容量的。

一般情况下ceph df的输出如下所示:

ceph-df

[root@study-1 ~]# ceph dfGLOBAL:    SIZE     AVAIL      RAW USED     %RAW USED     196G     99350M       91706M         45.55 POOLS:    NAME           ID     USED       %USED     MAX AVAIL     OBJECTS     rbd            1      20480k      0.02        49675M          11     x              2         522         0        49675M          11

GLOBAL 维度中有SIZE,AVAIL,RAW USED,%RAW USED。从上面的输出可以看到,ceph对容量的计算其实是分为两个维度的。一个是GLOBAL维度,一个是POOLS的维度。

POOLS 维度中有 USED,%USED,MAX AVAIL,OBJECTS。

我们这里先把注意力放在RAW USED,和AVAIL上。这个两个分析清楚之后,其它的也就迎刃而解了。

这里我们粗略算一下GLOBAL中的RAW USED 为91706M,明显大于下面pool 中USED 20480k*3 + 522bytes*3啊。而且各个pool的MAX AVAIL 相加并不等于GLOBAL中的AVAIL。我们需要深入代码分析一下为什么。

分析

Ceph 命令基本上都是首先到Montior这里,如何Monitor能处理请求,就直接处理,不能就转发。

我们看看Monitor是如何处理ceph df这个命令的。Monitor处理命令主要是在Monitor::hanlde_command函数里。

handle_command

else if (prefix == "df") {      bool verbose = (detail == "detail");      if (f)        f->open_object_section("stats");      pgmon()->dump_fs_stats(ds, f.get(), verbose);      if (!f)        ds << '\n';      pgmon()->dump_pool_stats(ds, f.get(), verbose);      if (f) {        f->close_section();        f->flush(ds);        ds << '\n';      }    }

dump_fs_stats 对应GLOBAL这个维度。dump_pool_stats对应POOLS这个维度。从上面的代码可以知道,主要是两个函数完成了df命令的输出。一个是pgmon()->dump_fs_stats,另一个是pgmon()->dump_pool_stats。

  • GLOBAL维度

从PGMonitor::dump_fs_stats开始:

dump_fs_stats

void PGMonitor::dump_fs_stats(stringstream &ss, Formatter *f, bool verbose) const{  if (f) {    f->open_object_section("stats");    f->dump_int("total_bytes", pg_map.osd_sum.kb * 1024ull);    f->dump_int("total_used_bytes", pg_map.osd_sum.kb_used * 1024ull);    f->dump_int("total_avail_bytes", pg_map.osd_sum.kb_avail * 1024ull);    if (verbose) {      f->dump_int("total_objects", pg_map.pg_sum.stats.sum.num_objects);    }    f->close_section();  }

stat_pg_update

void OSDService::update_osd_stat(vector& hb_peers){  Mutex::Locker lock(stat_lock);  osd_stat.hb_in.swap(hb_peers);  osd_stat.hb_out.clear();  osd->op_tracker.get_age_ms_histogram(&osd_stat.op_queue_age_hist);  // fill in osd stats too  struct statfs stbuf;  int r = osd->store->statfs(&stbuf);  if (r < 0) {    derr << "statfs() failed: " << cpp_strerror(r) << dendl;    return;  }  uint64_t bytes = stbuf.f_blocks * stbuf.f_bsize;  uint64_t used = (stbuf.f_blocks - stbuf.f_bfree) * stbuf.f_bsize;  uint64_t avail = stbuf.f_bavail * stbuf.f_bsize;  osd_stat.kb = bytes >> 10;  osd_stat.kb_used = used >> 10;  osd_stat.kb_avail = avail >> 10;  osd->logger->set(l_osd_stat_bytes, bytes);  osd->logger->set(l_osd_stat_bytes_used, used);  osd->logger->set(l_osd_stat_bytes_avail, avail);  check_nearfull_warning(osd_stat);  dout(20) << "update_osd_stat " << osd_stat << dendl;

可以看到相关字段数值的输出主要依赖pg_map.osd_sum的值,而osd_sum是各个osd_stat的总和。所以我们需要知道单个osd的osd_stat_t是如何计算的。


FIleStore::statfs从上面我们可以看到update_osd_stat 主要是通过osd->store->statfs(&stbuf),来更新osd_stat的。因为这里使用的是Filestore,所以需要进入FileStore看其是如何statfs的。

int FileStore::statfs(struct statfs *buf){  if (::statfs(basedir.c_str(), buf) < 0) {    int r = -errno;    assert(!m_filestore_fail_eio || r != -EIO);    assert(r != -ENOENT);    return r;  }  return 0;}

可以看到上面FileStore主要是通过::statfs()这个系统调用来获取信息的。这里的basedir.c_str()就是data目录。所以osd_sum计算的就是将所有osd 数据目录的磁盘使用量加起来。回到上面的输出,因为我使用的是一个磁盘上的目录,所以在statfs的时候,会把该磁盘上的其它目录也算到Raw Used中。回到上面的输出,因为使用两个OSD,且每个OSD都在同一个磁盘下,所以GLOBAL是这么算的

同上,就知道Ceph如何算Raw Used,AVAIL的。

  • POOLS维度

从PGMonitor::dump_pool_stats()来看,该函数以pool为粒度进行循环,通过 pg_map.pg_pool_sum来获取pool的信息。其中USED,%USED,OBJECTS是根据pg_pool_sum的信息算出来的。而MAX AVAIL 是单独算出来的。

这里有一张图,可以帮助同学们梳理整个的流程。中间仅取了一些关键节点。有一些省略,如想知道全貌,可以在PGMonitor::dump_pool_stats查阅。

通过分析代码我们知道,pool的使用空间(USED)是通过osd来更新的,因为有update(write,truncate,delete等)操作的的时候,会更新ctx->delta_stats,具体请见ReplicatedPG::do_osd_ops。举例的话,可以从处理WRITE的op为入手点,当处理CEPH_OSD_OP_WRITE类型的op的时候,会调用write_update_size_and_usage()。里面会更新ctx->delta_stats。当IO处理完,也就是applied和commited之后,会publish_stats_to_osd()。

这里会将变化的pg的stat_queue_item入队到pg_stat_queue中。然后设置osd_stat_updated为True。入队之后,由tick_timer在C_Tick_WithoutOSDLock这个ctx中通过send_pg_stats()将PG的状态发送给Monitor。这样Monitor就可以知道pg的的变化了。

可用空间,即MAX AVAIL的值,计算稍微有点复杂。Ceph是先计算Available的值,然后根据副本策略再计算MAX AVAIL的值。Available的值是在get_rule_avail()中计算的。在该函数中通过get_rule_weight_osd_map()算出来一个有weight的osd列表。

注意这里的weight一般是小于1的,因为它除以了sum。而sum就是pool中所有osd weight的总和。在拿到weight列表后,就会根据pg_map.osd_stat中kb_avail的值进行除以weight,选出其中最小的,作为Available的值。

这么描述有些抽象了,具体举一个例子。比如这里我们的pool中有三个osd,假设kb_avail都是400G

即,

{osd_0: 0.9, osd_1, 0.8, osd_2: 0.7}。计算出来的weight值是{osd_0: 0.9/2.4,osd_1: 0.8/2.4,osd_2: 0.7/2.4}

这样后面用osd的available 空间除以这里的weight值,这里的Available的值就是400G*0.7/2.4。这里附上一个公式,可能更直观一些。

然后根据你的POOL的副本策略不同,POOL的AVAL计算方式也不同。如果是REP模式,就是直接除以副本数。如果是EC模式,则POOL的AVAL是Available * k / (m + k)。

所以一般情况下,各个POOL的MAX AVAIL之和与GLOBAL的AVAIL是不相等的,但是可以很接近(相差在G级别可以忽略为接近)。

总结

分析到这里,我们知道CEPH中容量的计算是分维度的,如果是GLOBAL维度的话,因为使用的是osd的所在磁盘的statfs来计算所以还是比较准确的。而另一个维度POOLS

由于需要考虑到POOL的副本策略,CRUSH RULE,OSD WEIGHT,计算起来还是比较复杂的。容量的管理主要是在OSD端,且OSD会把信息传递给MON,让MON来维护。

计算osd weight值比较复杂,这里附上算weight的函数,添加了一些注释,有助于感兴趣的同学一起分析。

int CrushWrapper::get_rule_weight_osd_map(unsigned ruleno, map *pmap){  if (ruleno >= crush->max_rules)    return -ENOENT;  if (crush->rules[ruleno] == NULL)    return -ENOENT;  crush_rule *rule = crush->rules[ruleno];  // build a weight map for each TAKE in the rule, and then merge them  for (unsigned i=0; ilen; ++i) {    map m;    float sum = 0;    if (rule->steps[i].op == CRUSH_RULE_TAKE) {//如果是take的话,则进入      int n = rule->steps[i].arg1;      if (n >= 0) { // n如果大于等于0的话是osd,否则是buckets    m[n] = 1.0; // 如果是osd的话,因为这里是直接take osd,所有有没有权重已经不重要了    sum = 1.0;      } else { // 不是osd,是buckets的话    list q;    q.push_back(n); // buckets 的id 入队    //breadth first iterate the OSD tree    while (!q.empty()) {      int bno = q.front(); // 取出buckets的id      q.pop_front();  // 出队      crush_bucket *b = crush->buckets[-1-bno]; // 根据序号拿到buckets      assert(b); // 这个buckets必须是存在的      for (unsigned j=0; jsize; ++j) { // 从buckets的items数组中拿相应的bucket        int item_id = b->items[j];          if (item_id >= 0) { //it's an OSD          float w = crush_get_bucket_item_weight(b, j);  // 拿出该osd的weight          m[item_id] = w; // m 入队          sum += w; // weight加和        } else { //not an OSD, expand the child later          q.push_back(item_id);  // 如果不是osd,则添加其item_id,所以这里是一个树的深度遍历        }      }    }      }    }    for (map::iterator p = m.begin(); p != m.end(); ++p) {      map::iterator q = pmap->find(p->first);      // 因为我们这里传入的pmap是没有数据的      // 所以第一次必中,      if (q == pmap->end()) {    (*pmap)[p->first] = p->second / sum;      } else {    // 这里还需要考虑osd在不同的buckets里的情况    q->second += p->second / sum;      }    }  }  return 0;}

以上是"Ceph中容量计算与管理的示例分析"这篇文章的所有内容,感谢各位的阅读!希望分享的内容对大家有帮助,更多相关知识,欢迎关注行业资讯频道!

维度 容量 分析 输出 管理 命令 处理 函数 就是 磁盘 空间 两个 信息 副本 目录 面的 更新 不同 复杂 代码 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 广东万易互联网科技有限公司惠州分公司 互联网的科技 互联网是不是新科技 金融科技创新监管互联网金融 合肥互联网科技公司 嘟嘟瓜互联网科技 万易互联网科技有限公司 佩信高科技互联网 科技与互联网哪个更好 互联网大会黑科技排名 南山科技园互联网公司有哪些 重庆互联网医院沙龙医盟信息科技 互联网科技大奖都有哪些公司 深圳育鹏互联网科技有限公司招聘 国大互联网教育科技有限公司工作 工业互联网等科技实现智能化赋能 互联网营销推广黑科技 易企秀互联网科技是做什么的 绵阳真晨互联网科技有限公司 长沙易易互联网科技有限公司 中智农互联网科技 互联网科技垄断 亿策互联网科技 奔汉互联网科技 深圳期待互联网科技 盛创互联网科技有限公司 河南猎农互联网科技有限公司 上海钢联属于互联网科技股吗 中天科技中国工业互联网 互联网科技大佬云集
0