Ceph df分析
1. 檢視叢集使用容量
ceph df
GLOBAL:
SIZE AVAIL RAW USED %RAW USED
61284G 26383G 34901G 56.95
POOLS:
NAME ID USED %USED MAX AVAIL OBJECTS
rbd 0 256k 0 6931G 4
cephfs_metadata 1 43928k 0 6931G 1043729
cephfs_data 2 11603G 62.60 6931G 7083637
說明:
ceph對容量的計算分為兩個維度:
- GLOBAL維度中有SIZE,AVAIL,RAW USED,%RAW USED
- POOLS的維度中有 USED,%USED,MAX AVAIL,OBJECTS
GLOBAL中的RAW USED :34901G, AVAIL:26383G
POOLS 中USED:11603G3 + (43928k/1024/1024)3 = 34809.123G MAX AVAIL:20793G
發現問題沒,pools使用的跟global有偏差,少了一部分資料。
2. 分析mon原始碼
分析/src/mon/Monitor.cc程式碼,跟蹤df邏輯如下:
從上面的程式碼可以知道,df命令的輸出兩個維度程式碼邏輯:
- GLOBAL維度pgmon()->dump_fs_stats
- POOLS這個維度pgmon()->dump_pool_stats
2.1 GLOBAL維度
void PGMapDigest::dump_fs_stats(stringstream *ss, Formatter *f, bool verbose) const
{
if (f) {
f->open_object_section("stats");
f->dump_int("total_bytes", osd_sum.kb * 1024ull);
f->dump_int("total_used_bytes", osd_sum.kb_used * 1024ull);
f->dump_int("total_avail_bytes", osd_sum.kb_avail * 1024ull);
if (verbose) {
f->dump_int("total_objects", pg_sum.stats.sum.num_objects);
}
f->close_section();
} else {
assert(ss != nullptr);
TextTable tbl;
tbl.define_column("SIZE", TextTable::LEFT, TextTable::RIGHT);
tbl.define_column("AVAIL", TextTable::LEFT, TextTable::RIGHT);
tbl.define_column("RAW USED", TextTable::LEFT, TextTable::RIGHT);
tbl.define_column("%RAW USED", TextTable::LEFT, TextTable::RIGHT);
if (verbose) {
tbl.define_column("OBJECTS", TextTable::LEFT, TextTable::RIGHT);
}
tbl << stringify(byte_u_t(osd_sum.kb*1024))
<< stringify(byte_u_t(osd_sum.kb_avail*1024))
<< stringify(byte_u_t(osd_sum.kb_used*1024));
float used = 0.0;
if (osd_sum.kb > 0) {
used = ((float)osd_sum.kb_used / osd_sum.kb);
}
tbl << percentify(used*100);
if (verbose) {
tbl << stringify(si_u_t(pg_sum.stats.sum.num_objects));
}
tbl << TextTable::endrow;
*ss << "GLOBAL:\n";
tbl.set_indent(4);
*ss << tbl;
}
}
void PGMap::calc_stats()
{
num_pg = 0;
num_pg_active = 0;
num_pg_unknown = 0;
num_osd = 0;
pg_pool_sum.clear();
num_pg_by_pool.clear();
pg_by_osd.clear();
pg_sum = pool_stat_t();
osd_sum = osd_stat_t();
num_pg_by_state.clear();
num_pg_by_osd.clear();
for (auto p = pg_stat.begin();
p != pg_stat.end();
++p) {
stat_pg_add(p->first, p->second);
}
for (auto p = osd_stat.begin();
p != osd_stat.end();
++p)
stat_osd_add(p->first, p->second);
}
說明:
計算的數值輸出主要依賴pg_map.osd_sum的值,而osd_sum就是osd_stat_t。
2.1.1 osd_stat_t分析
void OSDService::update_osd_stat(vector<int>& hb_peers)
{
// load osd stats first
struct store_statfs_t stbuf;
int r = osd->store->statfs(&stbuf);
if (r < 0) {
derr << "statfs() failed: " << cpp_strerror(r) << dendl;
return;
}
auto new_stat = set_osd_stat(stbuf, hb_peers, osd->num_pgs);
dout(20) << "update_osd_stat " << new_stat << dendl;
assert(new_stat.kb);
float ratio = ((float)new_stat.kb_used) / ((float)new_stat.kb);
check_full_status(ratio);
}
說明:
從上面我們可以看到update_osd_stat 主要是通過osd->store->statfs(&stbuf),來更新osd_stat的
因為這裡使用的是Filestore,所以需要進入FileStore看其是如何statfs的。
2.1.2 FileStore statfs分析
int KStore::statfs(struct store_statfs_t* buf0)
{
struct statfs buf;
buf0->reset();
if (::statfs(basedir.c_str(), &buf) < 0) {
int r = -errno;
assert(r != -ENOENT);
return r;
}
buf0->total = buf.f_blocks * buf.f_bsize;
buf0->available = buf.f_bavail * buf.f_bsize;
return 0;
}
說明:
可以看到上面FileStore主要是通過::statfs()這個系統呼叫來獲取資訊的,basedir.c_str()就是data目錄。
所以osd_sum計算的就是將所有osd 資料目錄的磁碟使用量加起來。會把該磁碟上的其它目錄也算到Raw Used中。
這個統計和我們傳統意義上的磁碟空間使用是一致的,比較準確地反應出了所有OSD的檔案系統的 總體使用量和總體剩餘空間。
2.2 POOLS維度
2.2.1 分析dump_pool_stats
void PGMapDigest::dump_pool_stats_full(
const OSDMap &osd_map,
stringstream *ss,
Formatter *f,
bool verbose) const
{
TextTable tbl;
if (f) {
f->open_array_section("pools");
} else {
tbl.define_column("NAME", TextTable::LEFT, TextTable::LEFT);
tbl.define_column("ID", TextTable::LEFT, TextTable::LEFT);
if (verbose) {
tbl.define_column("QUOTA OBJECTS", TextTable::LEFT, TextTable::LEFT);
tbl.define_column("QUOTA BYTES", TextTable::LEFT, TextTable::LEFT);
}
tbl.define_column("USED", TextTable::LEFT, TextTable::RIGHT);
tbl.define_column("%USED", TextTable::LEFT, TextTable::RIGHT);
tbl.define_column("MAX AVAIL", TextTable::LEFT, TextTable::RIGHT);
tbl.define_column("OBJECTS", TextTable::LEFT, TextTable::RIGHT);
if (verbose) {
tbl.define_column("DIRTY", TextTable::LEFT, TextTable::RIGHT);
tbl.define_column("READ", TextTable::LEFT, TextTable::RIGHT);
tbl.define_column("WRITE", TextTable::LEFT, TextTable::RIGHT);
tbl.define_column("RAW USED", TextTable::LEFT, TextTable::RIGHT);
}
}
map<int,uint64_t> avail_by_rule;
for (auto p = osd_map.get_pools().begin();
p != osd_map.get_pools().end(); ++p) {
int64_t pool_id = p->first;
if ((pool_id < 0) || (pg_pool_sum.count(pool_id) == 0))
continue;
const string& pool_name = osd_map.get_pool_name(pool_id);
const pool_stat_t &stat = pg_pool_sum.at(pool_id);
const pg_pool_t *pool = osd_map.get_pg_pool(pool_id);
int ruleno = osd_map.crush->find_rule(pool->get_crush_rule(),
pool->get_type(),
pool->get_size());
int64_t avail;
float raw_used_rate;
if (avail_by_rule.count(ruleno) == 0) {
// FIXME: we don't guarantee avail_space_by_rule is up-to-date before this function is invoked
avail = get_rule_avail(ruleno);
if (avail < 0)
avail = 0;
avail_by_rule[ruleno] = avail;
} else {
avail = avail_by_rule[ruleno];
}
raw_used_rate = ::pool_raw_used_rate(osd_map, pool_id);
if (f) {
f->open_object_section("pool");
f->dump_string("name", pool_name);
f->dump_int("id", pool_id);
f->open_object_section("stats");
} else {
tbl << pool_name
<< pool_id;
if (verbose) {
if (pool->quota_max_objects == 0)
tbl << "N/A";
else
tbl << si_t(pool->quota_max_objects);
if (pool->quota_max_bytes == 0)
tbl << "N/A";
else
tbl << si_t(pool->quota_max_bytes);
}
}
dump_object_stat_sum(tbl, f, stat.stats.sum, avail, raw_used_rate, verbose, pool);
if (f)
f->close_section(); // stats
else
tbl << TextTable::endrow;
if (f)
f->close_section(); // pool
}
if (f)
f->close_section();
else {
assert(ss != nullptr);
*ss << "POOLS:\n";
tbl.set_indent(4);
*ss << tbl;
}
}
其中中間輸出資料部分是dump_object_stat_sum .
2.2.2 dump_object_stat_sum
void PGMapDigest::dump_object_stat_sum(
TextTable &tbl, Formatter *f,
const object_stat_sum_t &sum, uint64_t avail,
float raw_used_rate, bool verbose,
const pg_pool_t *pool)
{
float curr_object_copies_rate = 0.0;
if (sum.num_object_copies > 0)
curr_object_copies_rate = (float)(sum.num_object_copies - sum.num_objects_degraded) / sum.num_object_copies;
float used = 0.0;
// note avail passed in is raw_avail, calc raw_used here.
if (avail) {
used = sum.num_bytes * raw_used_rate * curr_object_copies_rate;
used /= used + avail;
} else if (sum.num_bytes) {
used = 1.0;
}
if (f) {
f->dump_int("kb_used", SHIFT_ROUND_UP(sum.num_bytes, 10));
f->dump_int("bytes_used", sum.num_bytes);
f->dump_format_unquoted("percent_used", "%.2f", (used*100));
f->dump_unsigned("max_avail", avail / raw_used_rate);
f->dump_int("objects", sum.num_objects);
if (verbose) {
f->dump_int("quota_objects", pool->quota_max_objects);
f->dump_int("quota_bytes", pool->quota_max_bytes);
f->dump_int("dirty", sum.num_objects_dirty);
f->dump_int("rd", sum.num_rd);
f->dump_int("rd_bytes", sum.num_rd_kb * 1024ull);
f->dump_int("wr", sum.num_wr);
f->dump_int("wr_bytes", sum.num_wr_kb * 1024ull);
f->dump_int("raw_bytes_used", sum.num_bytes * raw_used_rate * curr_object_copies_rate);
}
} else {
tbl << stringify(si_t(sum.num_bytes));
tbl << percentify(used*100);
tbl << si_t(avail / raw_used_rate);
tbl << sum.num_objects;
if (verbose) {
tbl << stringify(si_t(sum.num_objects_dirty))
<< stringify(si_t(sum.num_rd))
<< stringify(si_t(sum.num_wr))
<< stringify(si_t(sum.num_bytes * raw_used_rate * curr_object_copies_rate));
}
}
}
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()。
3. 總結
- GLOBAL維度:osd的所在磁碟的statfs來計算所以還是比較準確的。
- POOLS維度: 由於需要考慮到POOL的副本策略,CRUSH RULE,OSD WEIGHT,計算起來還是比較複雜的。
容量的管理主要是在OSD端,且OSD會把資訊傳遞給MON,讓MON來維護.
說明:
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()
相關文章
- Ceph配置引數分析
- ceph-pg雜湊分析
- ceph的配額功能分析
- Linux基礎命令---dfLinux
- Linux基礎命令—dfLinux
- pandas 聚合 df.groupby.agg
- linux 命令之du與dfLinux
- 比df更好用的命令!
- Ceph核心元件元件
- ceph叢集
- Ceph介紹
- CEPH-4:ceph RadowGW物件儲存功能詳解物件
- CEPH-5:ceph叢集基本概念與管理
- ceph:忘記 甚至 從ceph裡刪除了 ceph.client.admin密碼,怎麼處理?client密碼
- linux df -h卡頓問題(卡住)Linux
- ceph安裝配置
- Ceph入門教程
- How OpenStack integrates with Ceph?
- Ceph心跳機制
- glance對接ceph
- ceph之pg inactive
- Ceph 管理和使用
- 《最後生還者 第二部》DF畫面分析:效果驚人表現良好
- du 及 df 命令的使用(附帶示例)
- CEPH 13.2.4 Mimic釋出
- Ceph 架構以及部署架構
- ceph-immmutable-object-cacheObject
- ceph-RBD塊操作
- 深入理解 ceph mgr
- Ceph提供nfs服務NFS
- Ceph儲存池管理
- Ceph的正確玩法之Ceph糾刪碼理論與實踐
- ceph-deploy離線部署ceph叢集及報錯解決FAQ
- Linux 基礎教程 40-df和du命令Linux
- 【北亞資料恢復】什麼是Ceph?Ceph資料恢復研究資料恢復
- ceph-mon IP更改方法
- Ceph 和防火牆的故事防火牆
- CentOS 7/8上部署CephCentOS