Ceph动态更新参数机制浅析
Posted ygtff
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Ceph动态更新参数机制浅析相关的知识,希望对你有一定的参考价值。
说明:本篇中所有代码分析,都是基于Jewel 10.2.9版本。本篇都是个人理解,其中有些理解或者解释有不合理的,还请指正。
在Ceph的使用中,运行时调整参数值是个高频的操作,使用起来简单方便,最重要的是不用重启服务即可生效。
如何使用
Ceph动态调整参数有两种方式:
第一种:
|
第二种:
|
第二种还有两个比较好用的地方:
-
单条命令可以改变所有的实例的某个参数值:
ceph tell <mon
/osd/mds
>.* injectargs
'--<参数名> <参数值>'
设置所有OSD的heartbeat超时时间:
ceph tell osd.* injectargs
'--osd_heartbeat_grace 60'
-
单条命令可以改变多个参数:
ceph tell <mon
/osd/mds
>.1 injectargs
'--<参数名> <参数值> --<参数名> <参数值>'
设置OSD 1的heartbeat超时时间,及发起heartbeat的时间间隔
ceph tell osd.1 injectargs
'--osd_heartbeat_grace 60 --osd_heartbeat_interval 10'
-
当然,上面两个可以结合使用:
ceph tell <mon
/osd/mds
>.* injectargs
'--<参数名> <参数值> --<参数名> <参数值>'
设置所有OSD的heartbeat超时时间,及发起heartbeat的时间间隔
ceph tell osd.* injectargs
'--osd_heartbeat_grace 60 --osd_heartbeat_interval 10'
源码分析
那么Ceph内部是如何实现上面提到的动态更新呢?我们来深入代码中一探究竟。
tell方式的实现
我们先看tell...injectargs方式的实现:
ceph命令行的输入,源码入口都是ceph.in文件,是python文件,也就是/usr/bin/下的可执行文件ceph。
ceph.in中有关tell的代码:
|
从上面的代码,可知,tell命令,并没有走daemon的admin socket来进行通信,而是走正常的client->server的通信,而且,'*'也没有那么高效,就是拿到所有的id,然后逐个发送消息。
然后再经过librados、osdc等模块,将消息发送给具体的Monitor、OSD、MDS。
我们接下来直接看OSD端收到injectargs命令后,如何处理。
在OSD.cc中,do_command函数就是专门处理各种命令的,我们直接看injectargs分支:
|
拿到client想要更新的所有参数,然后调用了injectargs,每个OSD都有一个CephContext类变量cct,md_config_t类型的_conf变量也是CephContext类的成员。
|
上面代码完成了参数的更新,主要有两点:
- debug_类的参数(日志级别参数)单独走的一套,和其他参数不是同一种处理。
- 其他参数,先更新内存中的参数值;然后更重要的是:需要使其生效,即能够让系统真实的使用这些参数了。
后续的apply_changes是如何实现的?我们先暂放一下,先来看看daemon方式修改参数,因为这两种最终都会通过_apply_changes来使参数生效。
daemon方式的实现
我们继续看看daemon...config set方式的实现:
ceph.in中有关daemon的代码:
|
Admin socket是什么呢?其实就是UNIX Domain Socket,是在socket架构上发展出的一种IPC机制,虽然网络socket也可用于同一台主机的进程间通讯(通过loopback地址127.0.0.1),但是UNIX Domain Socket用于IPC更有效率:不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程。这是因为,IPC机制本质上是可靠的通讯,而网络协议是为不可靠的通讯设计的。UNIX Domain Socket也提供面向流和面向数据包两种API接口,类似于TCP和UDP,但是面向消息的UNIX Domain Socket也是可靠的,消息既不会丢失也不会顺序错乱。
UNIX Domain Socket与网络socket编程最明显的不同在于地址格式不同,用结构体sockaddr_un表示,网络编程的socket地址是IP地址加端口号,而UNIX Domain Socket的地址是一个socket类型的文件在文件系统中的路径,这个socket文件由bind()调用创建,如果调用bind()时该文件已存在,则bind()错误返回。
从上面的代码可以看到,我们现在是一个client,尝试进行socket连接、通信,那server端在哪里?
每个OSD实例在启动的时候,都有一个CephContext类变量cct变量,这个变量中会new AdminSocket对象,在ceph_osd.cc的main函数中会有cct的初始化操作:common_init_finish(g_ceph_context)。
我们先来看看common_init_finish函数:
|
可以看到:UNIX Domain Socket server已经准备就绪,开始监听网络。
在CephContext类构造函数中,创建了一个AdminSocket类变量_admin_socket,注册了一些_admin_socket可以处理的command:
|
从上面的代码可以看到,配置参数相关的这几个都已经注册在admin_socket中,而且这些命令相关的hook是CephContextHook类。
同时,在OSD启动时,admin_socket初始化成功后,调用了final_init,也注册了一些admin_socket可以识别的command。
我们再看看OSD的inal_init函数:
|
在这里继续注册了一些可以通过admin socket来执行的命令,这里的hook是OSDSocketHook类。
AdminSocket类继承的Thread类,在线程入口函数entry中,会通过poll方式等待event,然后有connection的时候,会进行do_accept,然后进行正常的网络stream读写。
当client通过admin socket向server端发送了命令后,admin socket server会接收消息,在do_accept函数中,会判断这个command是否注册,如果注册了,调用相应的hook->call处理,最后将结果回复给client。
在CephContext类中,hook是CephContextHook类变量:
|
在OSD类中,hook是一个OSDSocketHook类变量:
|
在这里,我们先只看config set相关的处理,在CephContext的do_command函数中:
|
可以看到,和tell方式一样,也是先改变参数值,然后再调用_apply_changes来生效之。
参数如何应用到运行中的系统
现在,我们来看看_apply_changes的实现:
|
上面代码中注释挺详细的了,我们在这里说一下observers是什么?就是具体的MON、OSD、MDS实例,这里用的是观察者模式,观察者关注自己关心的参数,这些参数一旦有更新,观察者会通过handle_conf_change使其生效。
那么,这些observer是什么时候开始watch的?我们这里只说OSD,在OSD::pre_init中,调用了cct->_conf->add_observer(this); pre_init也是在OSD启动时调用的。
|
我们先看看OSD都关注了哪些config options:(其实很少的,除了一些log的,就10几个)
|
看了上面的参数列表,是不是很惊讶,就这些?难道我经常设置的参数,貌似生效了(参数值确实改变了),其实根本没有真正的生效。
我们再来看OSD的handle_conf_changes:
|
这个函数就会根据不同的参数,调用具体的函数,将新的参数值应用到运行的系统中。
到这里,我们也基本搞清了动态更新参数的实现过程,当然,还有很多细节并没有研究。我们还是先搞清整个流程、原理,然后遇到问题,再深入细节分析。
总结
- 通过daemon admin socket修改参数走的是UNIX Domain Socket模式。所以,每次只能设置一个实例的参数;并且,只能在本地设置本地实例的参数。
- 通过tell修改参数走的是正常的命令发送流程。可以通过'*'的方式设置所有的实例的参数。
- 非log级别debug_类参数的动态更新,都经历了两步:设置进程内存中的参数值;调用相应的observer生效之。
- log级别的debug_类参数,是直接设置参数并生效的(当然,依赖于ceph中log的实现)。
- Ceph使用过程中,很多动态更新的参数,其实并没有真正的生效,修改参数时,返回的结果中有"unchangeable",或“not observed, change may require restart”这些字串的都没生效。 ----------------------------------------------------更正:感谢各位指正,“参数设置返回‘unchangeable’的,都没生效”确实有点问题,只能说可能没生效。如果在MON、OSD、MDS运行中使用某个参数时,直接取的cct->_conf->参数名,这种着实生效了;如果是在进程启动时,就已经加载的,运行时直接使用,并没有实时取_conf中参数的,是不生效的。所以,具体的参数还要具体分析,尤其是在调优或debug问题的时候,一定到代码里看看参数的使用,如果并没真正生效,可能会误以为该参数没有调优效果。
以上是关于Ceph动态更新参数机制浅析的主要内容,如果未能解决你的问题,请参考以下文章