ceph参数配置
ceph中的可配置参数(日志级别,缓存设置等等)都定义在legacy_config_opts.h中,可以通过ceph daemon命令来查看或者修改这些参数配置。参数配置方式有两种:永久和临时。永久方式,就是在配置文件(如ceph.conf)中添加该参数的配置,重启进程后,参数就生效了。临时方式,就是通过ceph daemon命令设置内存中的参数。
# xxx为 admin_socket = $rundir/$cluster-$id.asok (/run/ceph/ceph-osd.0.asok)
ceph daemon xxx config show
例如:"log_file"参数可以配置日志文件的路径
Option("log_file", Option::TYPE_STR, Option::LEVEL_BASIC)
.set_default("")
.set_daemon_default("/var/log/ceph/$cluster-$name.log")
.set_description("path to log file")
.add_see_also({"log_to_stderr", "err_to_stderr", "log_to_syslog", "err_to_syslog"}),
/*
所有选项的头文件包含在src/common/options/legacy_config_opts.h中
xx.cc xx.h文件由对应的xx.yaml配置文件使用src/common/options/y2c.py脚本自动生成
*/
#include "global_legacy_options.h"
#include "cephfs-mirror_legacy_options.h"
#include "mds_legacy_options.h"
#include "mds-client_legacy_options.h"
#include "mgr_legacy_options.h"
#include "mon_legacy_options.h"
#include "osd_legacy_options.h"
#include "rbd_legacy_options.h"
#include "rbd-mirror_legacy_options.h"
#include "immutable-object-cache_legacy_options.h"
#include "rgw_legacy_options.h"
.h文件 | .cc文件 | .yaml |
---|---|---|
global_legacy_options.h | global_options.cc | global.yaml |
cephfs-mirror_legacy_options.h | cephfs-mirror_options.cc | cephfs-mirror.yaml |
mds_legacy_options.h | mds_options.cc | mds.yaml |
mds-client_legacy_options.h | mds-client_options.cc | mds-client.yaml |
mgr_legacy_options.h | mgr_options.cc | mgr.yaml |
mon_legacy_options.h | mon_options.cc | mon.yaml |
osd_legacy_options.h | osd_options.cc | osd.yaml |
rbd_legacy_options.h | rbd_options.cc | rbd.yaml |
rbd-mirror_legacy_options.h.h | rbd-mirror_options.cc | rbd-mirror.yaml |
immutable-object-cache_legacy_options.h | immutable-object-cache_options.cc | immutable-object-cache.yaml |
rgw_legacy_options.h | rgw_options.cc | rgw.yaml |
参数是以Option结构体存在(src/common/options.h),Option结构体的定义如下
struct Option {
enum type_t { // 参数值的类型
TYPE_UINT = 0,
TYPE_INT = 1,
TYPE_STR = 2,
TYPE_FLOAT = 3,
TYPE_BOOL = 4,
TYPE_ADDR = 5,
TYPE_ADDRVEC = 6,
TYPE_UUID = 7,
TYPE_SIZE = 8,
TYPE_SECS = 9,
TYPE_MILLISECS = 10,
};
enum level_t {
LEVEL_BASIC = 0,
LEVEL_ADVANCED = 1,
LEVEL_DEV = 2,
LEVEL_UNKNOWN = 3,
};
using value_t = std::variant<
std::monostate,
std::string,
uint64_t,
int64_t,
double,
bool,
entity_addr_t,
entity_addrvec_t,
std::chrono::seconds,
std::chrono::milliseconds,
size_t,
uuid_d>;
const std::string name; // 参数名
const type_t type; // 参数类型
const level_t level; // 参数级别
std::string desc; // 该参数的含义
std::string long_desc;
value_t value; // 参数值
value_t daemon_value; // 有daemon的参数值
std::vector<const char*> services; // services即服务类型,比如"mds"
std::vector<const char*> see_also; // 该参数对应的类似的参数
std::vector<const char*> enum_allowed;
...
}
在src/common/options/build_options.cc中,定义的参数分为几类
std::vector<Option> get_global_options();
std::vector<Option> get_mgr_options();
std::vector<Option> get_mon_options();
std::vector<Option> get_crimson_options();
std::vector<Option> get_osd_options();
std::vector<Option> get_rgw_options();
std::vector<Option> get_rbd_options();
std::vector<Option> get_rbd_mirror_options();
std::vector<Option> get_immutable_object_cache_options();
std::vector<Option> get_mds_options();
std::vector<Option> get_mds_client_options();
std::vector<Option> get_cephfs_mirror_options();
std::vector<Option> get_ceph_exporter_options();
从函数名可以看出来:get_global_options函数中的参数是global类型的,即基本所有的模块都可以使用这些参数。get_rgw_options函数中的参数是给rgw模块使用。get_rbd_options函数和get_rbd_mirror_options函数中的参数是给rbd相关模块使用。get_mds_options函数中的参数是给mds模块使用。get_mds_client_options函数中的参数是给mds client模块使用。
全局变量ceph_options
ceph_options包含了所有的参数,ceph_options = { Option{“log_file”}, Option(“***”)}
const std::vector<Option> ceph_options = build_options();
在build_options中将所有的Option类放入ceph_options这个vector里面。
std::vector<Option> build_options()
{
std::vector<Option> result = get_global_options();
auto ingest = [&result](std::vector<Option>&& options, const char* svc) {
for (auto &o : options) {
if (std::none_of(o.services.begin(), o.services.end(),
[svc](const char* known_svc) {
return std::strcmp(known_svc, svc) == 0;
})) {
o.add_service(svc);
}
result.push_back(std::move(o));
}
};
ingest(get_crimson_options(), "osd");
ingest(get_mgr_options(), "mgr");
ingest(get_mon_options(), "mon");
ingest(get_osd_options(), "osd");
ingest(get_rgw_options(), "rgw");
ingest(get_rbd_options(), "rbd");
ingest(get_rbd_mirror_options(), "rbd-mirror");
ingest(get_immutable_object_cache_options(), "immutable-object-cache");
ingest(get_mds_options(), "mds");
ingest(get_mds_client_options(), "mds_client");
ingest(get_cephfs_mirror_options(), "cephfs-mirror");
ingest(get_ceph_exporter_options(), "ceph-exporter");
return result;
}
md_config_t 和 ConfigValues
md_config_t结构体表示当前的Ceph配置,ceph_options中的参数都会存在这里。在ConfigValues中有特殊的成员变量定义。
class ConfigValues {
using values_t = std::map<std::string_view, std::map<int32_t,Option::value_t>>;
values_t values;
// for populating md_config_impl::legacy_values in ctor
friend struct md_config_t;
public:
EntityName name;
/// cluster name
std::string cluster;
//subsys用来存日志项
ceph::logging::SubsystemMap subsys;
bool no_mon_config = false;
// Set of configuration options that have changed since the last
// apply_changes
using changed_set_t = std::set<std::string>;
changed_set_t changed;
// This macro block defines C members of the md_config_t struct
// corresponding to the definitions in legacy_config_opts.h.
// These C members are consumed by code that was written before
// the new options.cc infrastructure: all newer code should
// be consume options via explicit get() rather than C members.
#define OPTION_OPT_INT(name) int64_t name;
#define OPTION_OPT_LONGLONG(name) int64_t name;
#define OPTION_OPT_STR(name) std::string name;
#define OPTION_OPT_DOUBLE(name) double name;
#define OPTION_OPT_FLOAT(name) double name;
#define OPTION_OPT_BOOL(name) bool name;
#define OPTION_OPT_ADDR(name) entity_addr_t name;
#define OPTION_OPT_ADDRVEC(name) entity_addrvec_t name;
#define OPTION_OPT_U32(name) uint64_t name;
#define OPTION_OPT_U64(name) uint64_t name;
#define OPTION_OPT_UUID(name) uuid_d name;
#define OPTION_OPT_SIZE(name) uint64_t name;
#define OPTION(name, ty) \
public: \
OPTION_##ty(name)
#define SAFE_OPTION(name, ty) \
protected: \
OPTION_##ty(name)
#include "common/options/legacy_config_opts.h"
#undef OPTION_OPT_INT
#undef OPTION_OPT_LONGLONG
#undef OPTION_OPT_STR
#undef OPTION_OPT_DOUBLE
#undef OPTION_OPT_FLOAT
#undef OPTION_OPT_BOOL
#undef OPTION_OPT_ADDR
#undef OPTION_OPT_ADDRVEC
#undef OPTION_OPT_U32
#undef OPTION_OPT_U64
#undef OPTION_OPT_UUID
#undef OPTION
#undef SAFE_OPTION
}
最终md_config_t结构体定义如下
struct md_config_t {
public:
typedef std::variant<int64_t ConfigValues::*,
uint64_t ConfigValues::*,
std::string ConfigValues::*,
double ConfigValues::*,
bool ConfigValues::*,
entity_addr_t ConfigValues::*,
entity_addrvec_t ConfigValues::*,
uuid_d ConfigValues::*> member_ptr_t;
// For use when intercepting configuration updates
typedef std::function<bool(
const std::string &k, const std::string &v)> config_callback;
/// true if we are a daemon (as per CephContext::code_env)
const bool is_daemon;
/*
* Mapping from legacy config option names to class members
* 将ceph_options中的Option的name与对应的md_config_t中的成员指针作为key-value保存
*/
std::map<std::string_view, member_ptr_t> legacy_values;
/**
* The configuration schema, in the form of Option objects describing
* possible settings.
* schema保存ceph_options中的所有Option参数
*/
std::map<std::string_view, const Option&> schema;
...
}
// Populate list of legacy_values according to the OPTION() definitions
// Note that this is just setting up our map of name->member ptr. The
// default values etc will get loaded in along with new-style data,
// as all loads write to both the values map, and the legacy
// members if present.
legacy_values = {
#define OPTION(name, type) \
{STRINGIFY(name), &ConfigValues::name},
#define SAFE_OPTION(name, type) OPTION(name, type)
#include "options/legacy_config_opts.h"
#undef OPTION
#undef SAFE_OPTION
};
其中schema保存ceph_options中的所有Option参数,即
schema = { <“host”, Options(“host”)>, …, <“log_file”, Option(“log_file”)>, …}
legacy_values将ceph_options中的Option的name与对应的md_config_t中的成员指针作为key-value保存,即
legacy_values = {<“host”, &md_config_t::host>, … , <“log_file”, &md_config_t::log_file>, …}
subsys用来存日志项,即
subsys = ceph::logging::SubsystemMap( m_subsys[ceph_subsys_ == 0] = Subsystem<name = “none”, log_level = 0, gather_level = 5>, … , m_subsys[ceph_subsys_crush == 3] = Subsystem<name = “crush”, log_level = 1, gather_level = 1>, m_subsys[ceph_subsys_mds == 4] = Subsystem<name = “mds”, log_level = 1, gather_level = 5>, … }。
g_ceph_context和g_conf全局变量
一个模块的进程会有多个线程,比如ceph-mds,进程中有些内容需要整个进程中的所有线程都可以访问,比如参数配置和以及上下文内容,所以就有两个全局变量g_conf和g_ceph_context,在src/global/global_context.cc中定义如下
CephContext *g_ceph_context = NULL;
ConfigProxy& g_conf() {
#if defined(WITH_SEASTAR) && !defined(WITH_ALIEN)
return crimson::common::local_conf();
#else
return g_ceph_context->_conf;
#endif
}
md_config_t 实例对象作为ConfigProxy的成员,
配置的代理类ConfigProxy,实际是封装了:
a) ConfigValues values;
b) ObserverMgr<md_config_obs_t> obs_mgr;
c) md_config_t config;
1、如果是set类接口:先通过md_config_t获取option,通过option获取name、类型、默认值、校验等信息,解析value,最后设置到ConfigValues中。
2、如果是get类接口:
2.1、流程也是从md_config_t到ConfigValues, 如果从ConfigValues不能获取到,则获取Option的默认值
2.2、md_config_t::expand_meta 解析${var}到实际的值
class CephContext {
...
public:
ConfigProxy _conf;
ceph::logging::Log *_log;
...
}
class ConfigProxy {
/**
* The current values of all settings described by the schema
*/
ConfigValues values;
using md_config_obs_t = ceph::md_config_obs_impl<ConfigProxy>;
ObserverMgr<md_config_obs_t> obs_mgr;
// 配置对象
md_config_t config;
/** A lock that protects the md_config_t internals. It is
* recursive, for simplicity.
* It is best if this lock comes first in the lock hierarchy. We will
* hold this lock when calling configuration observers. */
mutable ceph::recursive_mutex lock =
ceph::make_recursive_mutex("ConfigProxy::lock");
}
每个模块启动时,都需要实例化CephContext,就不需要单独去实例化md_config_t。
在md_config_t的构造函数的入参为is_daemon,它是用来判断该模块是单独起一个守护线程,或者由别的线程去调用对外接口,不单独起线程。构造函数如下
CephContext::CephContext(uint32_t module_type_,
enum code_environment_t code_env,
int init_flags_)
: nref(1),
_conf{code_env == CODE_ENVIRONMENT_DAEMON}
{...}
md_config_t::md_config_t(ConfigValues& values,
const ConfigTracker& tracker,
bool is_daemon){ ... }
即判断code_env == CODE_ENVIRONMENT_DAEMON,在各个模块的main函数中有code_env的入参。即ceph_mds.cc, ceph_fuse.cc和ceph_mon.cc等中的md_config_t中is_daemon都是true,libcephfs的md_config_t中is_daemon是false。
参数配置其它源码分析
global_init分析
直接看代码,从ceph_mon.cc中开始看
auto cct = global_init(&defaults, args,
CEPH_ENTITY_TYPE_MON, CODE_ENVIRONMENT_DAEMON,
flags);
boost::intrusive_ptr<CephContext>
global_init(const std::map<std::string,std::string> *defaults,
std::vector < const char* >& args,
uint32_t module_type, code_environment_t code_env,
int flags, bool run_pre_init)
{ // module_type = CEPH_ENTITY_TYPE_MON, code_env = CODE_ENVIRONMENT_DAEMON, flags = 0
// run_pre_init = true
// Ensure we're not calling the global init functions multiple times.
static bool first_run = true;
if (run_pre_init) {
// We will run pre_init from here (default).
ceph_assert(!g_ceph_context && first_run);
global_pre_init(defaults, args, module_type, code_env, flags);
} else {
// Caller should have invoked pre_init manually.
ceph_assert(g_ceph_context && first_run);
}
first_run = false;
...
}
global_pre_init是关键函数:
void global_pre_init(
const std::map<std::string,std::string> *defaults,
std::vector < const char* >& args,
uint32_t module_type, code_environment_t code_env,
int flags)
{ // module_type = CEPH_ENTITY_TYPE_MON, code_env = CODE_ENVIRONMENT_DAEMON, flags = 0
std::string conf_file_list;
std::string cluster = "";
// ensure environment arguments are included in early processing
// 环境变里CEPH_ARGS中的选项加入到args中
env_to_vec(args);
/*
解析options来获取对应的值, 预解释命令行参数,对于只是显示版本号的命令,只把
版本号打出来程序就直接可以exit(0)了;对于其他的参数,解释好后放到
CephInitParameters里供后面使用。
*/
CephInitParameters iparams = ceph_argparse_early_args(
args, module_type,
&cluster, &conf_file_list);
// 主要是实例化new CephContext(iparams.module_type, flags);
CephContext *cct = common_preinit(iparams, code_env, flags);
// cct->_conf->cluster = "ceph"
cct->_conf->cluster = cluster;
global_init_set_globals(cct);
auto& conf = cct->_conf;
if (flags & (CINIT_FLAG_NO_DEFAULT_CONFIG_FILE|
CINIT_FLAG_NO_MON_CONFIG)) {
conf->no_mon_config = true;
}
// alternate defaults
if (defaults) {
for (auto& i : *defaults) {
conf.set_val_default(i.first, i.second);
}
}
if (conf.get_val<bool>("no_config_file")) {
flags |= CINIT_FLAG_NO_DEFAULT_CONFIG_FILE;
}
//读取配置文件内容,命令里不指明的话,默认使用/etc/ceph/ceph.conf。
int ret = conf.parse_config_files(c_str_or_null(conf_file_list),
&cerr, flags);
...
}
env_to_vec分析
split_dashdash把args根据"–“拆分成两段(注意:–不同于–param),前段为类型为vector的options, 后段为类型为vector的arguments。
get_str_vec。把环境变量值取出来:默认的环境变量是"CEPH_ARGS”,可以通过参数来指定名字。以空格“ ” 为间隔,把环境变量拆分为一个个字符串到g_str_vec中。
g_str_vec腾挪到env 的vector<const char*>中,同args一样以“–”进行拆分。
分别聚合env和args的options和arguments到args中, 还是以“–”来间隔,无论是options还是arguments,env的参数都在前。
void env_to_vec(std::vector<const char*>& args, const char *name)
{
if (!name)
name = "CEPH_ARGS";
//把args根据"--"拆分成两段,前段为options,后段为arguments
auto [options, arguments] = split_dashdash(args);
/*
* We can only populate str_vec once. Other threads could hold pointers into
* it, so clearing it out and replacing it is not currently safe.
*/
g_str_vec_lock.lock();
if (g_str_vec.empty()) {
char *p = getenv(name);
if (!p) {
g_str_vec_lock.unlock();
return;
}
//以空格“ ” 为间隔,把环境变量拆分为一个个字符串到g_str_vec中
get_str_vec(p, " ", g_str_vec);
}
g_str_vec_lock.unlock();
std::vector<const char*> env;
for (const auto& s : g_str_vec) {
env.push_back(s.c_str());
}
//同args一样以“--”进行拆分
auto [env_options, env_arguments] = split_dashdash(env);
//聚合env和args的options和arguments到args中, 还是以“--”来间隔,无论是options还是arguments,env的参数都在前。
args.clear();
args.insert(args.end(), env_options.begin(), env_options.end());
args.insert(args.end(), options.begin(), options.end());
if (arguments.empty() && env_arguments.empty()) {
return;
}
args.push_back("--");
args.insert(args.end(), env_arguments.begin(), env_arguments.end());
args.insert(args.end(), arguments.begin(), arguments.end());
}
调用ceph_argparse_early_args来解析进程启动时设置的参数,比如ceph-mon启动时设置的参数如下
/usr/bin/ceph-osd -f --cluster ceph --id 0 --setuser ceph --setgroup ceph
common_preinit
CephContext *common_preinit(const CephInitParameters &iparams,
enum code_environment_t code_env, int flags)
{
// set code environment
ANNOTATE_BENIGN_RACE_SIZED(&g_code_env, sizeof(g_code_env), "g_code_env");
g_code_env = code_env;
// Create a configuration object
/*
CephContext初始化时最重要的就是实例化md_config_t,md_config_t保存了osd的所有
默认配置信息,主要是使用OPTION_OPT_INT,OPTION_OPT_LONGLONG等一系列的宏加上
common/config_opts.h灵活实现配置字段的定义。同时CephContext实例化时还初始化
了adminsocket,log等模块。
*/
CephContext *cct = new CephContext(iparams.module_type, code_env, flags);
auto& conf = cct->_conf;
// add config observers here
// Set up our entity name.
conf->name = iparams.name;
...
return cct;
}
最重要的就是parse_config_files
int md_config_t::parse_config_files(ConfigValues& values,
const ConfigTracker& tracker,
const char *conf_files_str,
std::ostream *warnings,
int flags)
{
if (safe_to_start_threads)
return -ENOSYS;
if (values.cluster.empty() && !conf_files_str) {
values.cluster = get_cluster_name(nullptr);
}
// open new conf
for (auto& fn : get_conffile_paths(values, conf_files_str, warnings, flags)) {
bufferlist bl;
std::string error;
// 将ceph.conf(如/etc/ceph/ceph.conf)中的内容读上来保存在bl中
if (bl.read_file(fn.c_str(), &error)) {
parse_error = error;
continue;
}
ostringstream oss;
//解释配置文件
int ret = parse_buffer(values, tracker, bl.c_str(), bl.length(), &oss);
if (ret == 0) {
parse_error.clear();
conf_path = fn;
break;
}
parse_error = oss.str();
if (ret != -ENOENT) {
return ret;
}
}
// it must have been all ENOENTs, that's the only way we got here
if (conf_path.empty()) {
return -ENOENT;
}
if (values.cluster.empty()) {
values.cluster = get_cluster_name(conf_path.c_str());
}
update_legacy_vals(values);
return 0;
}
md_config_t::_expand_meta函数用来替换"$"后面的字符串,比如admin_socket = r u n d i r / rundir/ rundir/cluster-$id.asok,调用expand_meta函数后admin_socket = /var/run/ceph/ceph-mon.node1.asok