cephfs根据存储池显示df容量

前言

如果用cephfs比较多,应该都知道,在cephfs的客户端进行mount以后,看到的容量显示的是集群的总的容量,也就是你的总的磁盘空间是多少这个地方显示的就是多少

这个一直都是这样显示的,我们之前在hammer版本的时候,阿茂和大黄一起在公司内部实现了这个功能,社区会慢慢的集成一些类似的面向面向商业用户的需求

社区已经开发了一个版本,接口都做的差不多了,那么稍微改改,就能实现想要的需求的

本篇内的改动是基于内核客户端代码的改动,改动很小,应该能够看的懂

改动过程

首先找到这个补丁

Improve accuracy of statfs reporting for Ceph filesystems comprising exactly one data pool. In this case, the Ceph monitor can now report the space usage for the single data pool instead of the global data for the entire Ceph cluster. Include support for this message in mon_client and leverage it in ceph/super.

地址:https://www.spinics.net/lists/ceph-devel/msg37937.html

这个说的是改善了statfs的显示,这个statfs就是在linux下面的mount的输出的显示的,说是改善了在单存储池下的显示效果,也就是在单存储池下能够显示存储池的容量空间,而不是全局的空间

这里就有个疑问了,单存储池?那么多存储池呢?我们测试下看看

这里这个补丁已经打到了centos7.5的默认内核里面去了,也就是内核版本

Linux lab103 3.10.0-862.el7.x86_64

对应的rpm包的版本是

1
2
3
[root@lab103 ceph]# rpm -qa|grep  3.10.0-862
kernel-devel-3.10.0-862.el7.x86_64
kernel-3.10.0-862.el7.x86_64

下载的地址为:

1
http://mirrors.163.com/centos/7.5.1804/os/x86_64/Packages/kernel-3.10.0-862.el7.x86_64.rpm

或者直接安装centos7.5也行,这里只要求是这个内核就可以了

我们看下默认情况下是怎样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@lab102 ~]# ceph -s
data:
pools: 3 pools, 72 pgs
objects: 22 objects, 36179 bytes
usage: 5209 MB used, 11645 GB / 11650 GB avail
pgs: 72 active+clean

[root@lab102 ~]# ceph fs ls
name: ceph, metadata pool: metadata, data pools: [data ]
[root@lab102 ~]# ceph df
GLOBAL:
SIZE AVAIL bash USED %bash USED
11650G 11645G 5209M 0.04
POOLS:
NAME ID USED %USED MAX AVAIL OBJECTS
data 9 0 0 3671G 0
metadata 10 36179 0 11014G 22
newdata 11 0 0 5507G 0
[root@lab102 ~]# ceph osd dump|grep pool
pool 9 'data' replicated size 3 min_size 1 crush_rule 0 object_hash rjenkins pg_num 32 pgp_num 32 last_change 136 flags hashpspool stripe_width 0 application cephfs
pool 10 'metadata' replicated size 1 min_size 1 crush_rule 0 object_hash rjenkins pg_num 32 pgp_num 32 last_change 112 flags hashpspool stripe_width 0 application cephfs
pool 11 'newdata' replicated size 2 min_size 1 crush_rule 0 object_hash rjenkins pg_num 8 pgp_num 8 last_change 134 flags hashpspool stripe_width 0 application cephfs

从上面可以看到我的硬盘裸空间为12T左右,data存储池副本3那么可用空间为4T左右,文件系统里面只有一个data存储池,看下挂载的情况

1
2
3
4
[root@lab101 ~]# uname -a
Linux lab101 3.10.0-862.el7.x86_64 #1 SMP Fri Apr 20 16:44:24 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
[root@lab101 ~]# df -Th|grep mnt
192.168.19.102:/ ceph 3.6T 0 3.6T 0% /mnt

可以看到显示的容量就是存储池的可用容量为总空间的,现在我们加入一个数据池

1
2
[root@lab102 ~]# ceph mds add_data_pool newdata
added data pool 11 to fsmap

再次查看df的显示

1
2
[root@lab101 ~]# df -Th|grep mnt
192.168.19.102:/ ceph 12T 5.1G 12T 1% /mnt

容量回到了原始的显示的方式,这个跟上面的补丁的预期是一样的,我们看下代码这里怎么控制的

获取当前内核版本的代码

首先要找到当前的内核的src.rpm包,这样可以拿到当前内核版本的源码

1
wget http://vault.centos.org/7.5.1804/os/Source/SPackages/kernel-3.10.0-862.el7.src.rpm

解压源码包

1
2
3
[root@lab103 origin]# rpm2cpio kernel-3.10.0-862.el7.src.rpm |cpio -div
[root@lab103 origin]# tar -xvf linux-3.10.0-862.el7.tar.xz
[root@lab103 origin]# cd linux-3.10.0-862.el7/fs/ceph/

上面的操作后我们已经进入了我们想要看的源码目录了
我们看下super.c这个文件,这个df的显示的控制是在这个文件里面的

1
[root@lab103 ceph]# cat super.c |less

看下这段代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static int ceph_statfs(struct dentry *dentry, struct kstatfs *buf)
{
struct ceph_fs_client *fsc = ceph_inode_to_client(dentry->d_inode);
struct ceph_monmap *monmap = fsc->client->monc.monmap;
struct ceph_statfs st;
u64 fsid;
int err;
u64 data_pool;

if (fsc->mdsc->mdsmap->m_num_data_pg_pools == 1) {
data_pool = fsc->mdsc->mdsmap->m_data_pg_pools[0];
} else {
data_pool = CEPH_NOPOOL;
}

dout("statfs\n");
err = ceph_monc_do_statfs(&fsc->client->monc, data_pool, &st);
if (err < 0)
return err;

其中的fsc->mdsc->mdsmap->m_num_data_pg_pools == 1和data_pool = fsc->mdsc->mdsmap->m_data_pg_pools[0];这个地方的意思是如果fs里面包含的存储池的存储池个数为1那么data_pool就取这个存储池的信息,所以上面的我们的实践过程中的就是单个存储池的时候显示存储池的容量,超过一个的时候就显示的全局的容量,这个是跟代码对应的上的

我们基于上面的已经做好的功能改变一下需求

需要可以根据自己的需要指定存储池的容量来显示,通过挂载内核客户端的时候传递一个参数进去来进行显示

代码改动

[root@lab103 ceph]# vim super.h
在super.h内定义一个默认值

1
2
3
4
5
6
7
8
9
10
#define ZP_POOL_DEFAULT      0  /* pool id */
#define CEPH_CAPS_WANTED_DELAY_MAX_DEFAULT 60 /* cap release delay */
struct ceph_mount_options {
int flags;
int sb_flags;

int wsize; /* max write size */
int rsize; /* max read size */
int zp_pool; /* pool id */
int rasize; /* max readahead */

这里增加了两个一个zp_pool和ZP_POOL_DEFAULT
这个文件的改动就只有这么多了

改动super.c的代码
在enum里面加上Opt_zp_pool

1
2
3
4
5
6
enum {
Opt_wsize,
Opt_rsize,
Opt_rasize,
Opt_caps_wanted_delay_min,
Opt_zp_pool,

在match_table_t fsopt_tokens里面添加Opt_zp_pool相关的判断,我们自己注意传的是pool在fs里面的id即可

1
2
3
4
5
6
static match_table_t fsopt_tokens = {
{Opt_wsize, "wsize=%d"},
{Opt_rsize, "rsize=%d"},
{Opt_rasize, "rasize=%d"},
{Opt_caps_wanted_delay_min, "caps_wanted_delay_min=%d"},
{Opt_zp_pool, "zp_pool=%d"},

在static int parse_fsopt_token中添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
case Opt_caps_wanted_delay_max:
if (intval < 1)
return -EINVAL;
fsopt->caps_wanted_delay_max = intval;
break;
case Opt_zp_pool:
if (intval < 0)
return -EINVAL;
fsopt->zp_pool = intval;
break;
case Opt_readdir_max_entries:
if (intval < 1)
return -EINVAL;
fsopt->max_readdir = intval;
break;

判断如果小于0就抛错,这个id从0开始上升的,所以也不允许小于0

在static int parse_mount_options中添加

1
2
3
fsopt->caps_wanted_delay_min = CEPH_CAPS_WANTED_DELAY_MIN_DEFAULT;
fsopt->zp_pool = ZP_POOL_DEFAULT;
fsopt->caps_wanted_delay_max = CEPH_CAPS_WANTED_DELAY_MAX_DEFAULT;

在static int ceph_show_options中添加

1
2
3
4
5
6
7
8
9
if (fsopt->caps_wanted_delay_min != CEPH_CAPS_WANTED_DELAY_MIN_DEFAULT)
seq_printf(m, ",caps_wanted_delay_min=%d",
fsopt->caps_wanted_delay_min);
if (fsopt->zp_pool)
seq_printf(m, ",zp_pool=%d",
fsopt->zp_pool);
if (fsopt->caps_wanted_delay_max != CEPH_CAPS_WANTED_DELAY_MAX_DEFAULT)
seq_printf(m, ",caps_wanted_delay_max=%d",
fsopt->caps_wanted_delay_max);

这个是用来在执行mount命令的时候显示选项的数值的
改动到这里我们检查下我们对super.c做过的的改动

1
2
3
4
5
6
7
8
9
[root@lab103 ceph]# cat super.c |grep zp_pool
Opt_zp_pool,
{Opt_zp_pool, "zp_pool=%d"},
case Opt_zp_pool:
fsopt->zp_pool = intval;
fsopt->zp_pool = ZP_POOL_DEFAULT;
if (fsopt->zp_pool)
seq_printf(m, ",zp_pool=%d",
fsopt->zp_pool);

做了以上的改动后我们就可以把参数给传进来了,现在我们需要把参数传递到需要用的地方
也就是static int ceph_statfs内需要调用这个参数

在static int ceph_statfs中添加上struct ceph_mount_options *fsopt = fsc->mount_options;

1
2
3
4
5
6
7
static int ceph_statfs(struct dentry *dentry, struct kstatfs *buf)
{
struct ceph_fs_client *fsc = ceph_inode_to_client(dentry->d_inode);
struct ceph_monmap *monmap = fsc->client->monc.monmap;
struct ceph_statfs st;
struct ceph_mount_options *fsopt = fsc->mount_options;
u64 fsid;

然后改掉这个fsc->mdsc->mdsmap->m_num_data_pg_pools == 1的判断,我们判断大于0即可

1
2
3
4
5
if (fsc->mdsc->mdsmap->m_num_data_pg_pools > 0) {
data_pool = fsc->mdsc->mdsmap->m_data_pg_pools[fsopt->zp_pool];
} else {
data_pool = CEPH_NOPOOL;
}

并且把写死的0改成我们的变量fsopt->zp_pool

到这里改动就完成了,这里还没有完,我们需要编译成我们的需要的模块

1
2
[root@lab103 ceph]# modinfo ceph
filename: /lib/modules/3.10.0-862.el7.x86_64/kernel/fs/ceph/ceph.ko.xz

可以看到内核在高版本的时候已经改成了xz压缩的模块了,这里等会需要多处理一步
我们只需要这一个模块就编译这一个ceph.ko模块就好
编译需要装好kernel-devel包kernel-devel-3.10.0-862.el7.x86_64

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
[root@lab103 ceph]# pwd
/home/origin/linux-3.10.0-862.el7/fs/ceph
[root@lab103 ceph]# make CONFIG_CEPH_FS=m -C /lib/modules/3.10.0-862.el7.x86_64/build/ M=`pwd` modules
make: Entering directory `/usr/src/kernels/3.10.0-862.el7.x86_64'
CC [M] /home/origin/linux-3.10.0-862.el7/fs/ceph/super.o
CC [M] /home/origin/linux-3.10.0-862.el7/fs/ceph/inode.o
CC [M] /home/origin/linux-3.10.0-862.el7/fs/ceph/dir.o
CC [M] /home/origin/linux-3.10.0-862.el7/fs/ceph/file.o
CC [M] /home/origin/linux-3.10.0-862.el7/fs/ceph/locks.o
CC [M] /home/origin/linux-3.10.0-862.el7/fs/ceph/addr.o
CC [M] /home/origin/linux-3.10.0-862.el7/fs/ceph/ioctl.o
CC [M] /home/origin/linux-3.10.0-862.el7/fs/ceph/export.o
CC [M] /home/origin/linux-3.10.0-862.el7/fs/ceph/caps.o
CC [M] /home/origin/linux-3.10.0-862.el7/fs/ceph/snap.o
CC [M] /home/origin/linux-3.10.0-862.el7/fs/ceph/xattr.o
CC [M] /home/origin/linux-3.10.0-862.el7/fs/ceph/mds_client.o
CC [M] /home/origin/linux-3.10.0-862.el7/fs/ceph/mdsmap.o
CC [M] /home/origin/linux-3.10.0-862.el7/fs/ceph/strings.o
CC [M] /home/origin/linux-3.10.0-862.el7/fs/ceph/ceph_frag.o
CC [M] /home/origin/linux-3.10.0-862.el7/fs/ceph/debugfs.o
CC [M] /home/origin/linux-3.10.0-862.el7/fs/ceph/acl.o
LD [M] /home/origin/linux-3.10.0-862.el7/fs/ceph/ceph.o
Building modules, stage 2.
MODPOST 1 modules
CC /home/origin/linux-3.10.0-862.el7/fs/ceph/ceph.mod.o
LD [M] /home/origin/linux-3.10.0-862.el7/fs/ceph/ceph.ko
make: Leaving directory `/usr/src/kernels/3.10.0-862.el7.x86_64'

正常应该就是上面的没有报错的输出了
压缩ko模块

1
2
3
4
5
6
7
8
9
[root@lab103 ceph]# find * -name '*.ko' | xargs -n 1 xz
[root@lab103 ceph]# rmmod ceph
[root@lab103 ceph]# rm -rf /lib/modules/3.10.0-862.el7.x86_64/kernel/fs/ceph/ceph.ko.xz
[root@lab103 ceph]# cp -ra ceph.ko.xz /lib/modules/3.10.0-862.el7.x86_64/kernel/fs/ceph/
[root@lab103 ceph]# lsmod |grep ceph
ceph 345111 0
libceph 301687 1 ceph
dns_resolver 13140 1 libceph
libcrc32c 12644 2 xfs,libceph

现在已经加载好模块了,我们试验下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@lab103 ceph]# ceph df
GLOBAL:
SIZE AVAIL bash USED %bash USED
11650G 11645G 5210M 0.04
POOLS:
NAME ID USED %USED MAX AVAIL OBJECTS
data 9 0 0 3671G 0
metadata 10 36391 0 11014G 22
newdata 11 0 0 5507G 0

[root@lab103 ceph]# mount -t ceph 192.168.19.102:/ /mnt
[root@lab103 ceph]# df -h|grep mnt
192.168.19.102:/ 3.6T 0 3.6T 0% /mnt
[root@lab103 ceph]# ceph fs ls
name: ceph, metadata pool: metadata, data pools: [data newdata ]

我们给了一个默认存储池的值为0的编号的,现在显示的是data的容量,没有问题,我们想显示newdata存储池的

1
2
3
[root@lab103 ceph]# mount -t ceph 192.168.19.102:/ /mnt -o zp_pool=1
[root@lab103 ceph]# df -h|grep mnt
192.168.19.102:/ 5.4T 0 5.4T 0% /mnt

这里我们显示的要么0,要么1的存储池的那么我如果想显示全局的怎么处理?那就是给个不存在的编号就行了

1
2
3
4
5
[root@lab103 ceph]# mount -t ceph 192.168.19.102:/ /mnt -o zp_pool=1000
[root@lab103 ceph]# mount|grep ceph|grep zp_pool
192.168.19.102:/ on /mnt type ceph (rw,relatime,acl,wsize=16777216,zp_pool=1000)
[root@lab103 ceph]# df -h|grep mnt
192.168.19.102:/ 12T 5.1G 12T 1% /mnt

也可以自己去改成读取all字段的时候取全局变量,这个是直接用一个不存在的编号去走到全局的容量的逻辑里面去了,这样比较简单

通过mount命令可以查询到挂载的选项

到这里就根据需求改完了

总结

本篇里面涉及的知识点包括了rpm包的源码的获取,解压,以及内核模块的单独编译,改动单个模块进行替换,cephfs客户端的内核参数的自定义传递等等,在本博客的第三篇文章就有一个单独编译一个ext4模块的

变更记录

Why Who When
创建 武汉-运维-磨渣 2018-08-20