启用项目以支持可变配置¶
从 OpenStack Newton 开始,配置选项可以标记为“mutable”(可变的)。这意味着它们可以在运行时重新加载(通常通过 SIGHUP 信号),而无需重启服务。但是,每个项目必须先启用此功能才能生效,并且在将某个选项标记为可变之前,需要仔细考虑该选项的使用方式。
调用 mutate_config_files¶
通过调用 ConfigOpts#mutate_config_files 来触发配置变更。使用 oslo.service 启动的服务会在 SIGHUP 信号上获取信号处理程序,但默认情况下,该处理程序会调用较旧的 ConfigOpts#reload_config_files 方法。要获得新的行为,我们必须传递 restart_method='mutate'。例如
service.ProcessLauncher(CONF, restart_method='mutate')
一个示例补丁在这里:https://review.openstack.org/#/c/280851
有些项目可能会直接调用 reload_config_files,在这种情况下,只需将该调用更改为 mutate_config_files。如果没有信号处理程序,或者您想通过其他方法(例如通过 Web UI 或监视文件)触发重新加载,只需确保您的触发器调用 mutate_config_files。
使选项支持可变配置¶
当选项发生变更时,它们会在 ConfigOpts 对象中发生变化,但这不一定会立即影响您的服务。有三种主要情况需要处理
每次都会检查该选项
该选项缓存在堆栈上
该选项影响状态
每次都会检查该选项¶
这种模式已经安全。示例代码
while True:
progress_timeout = CONF.libvirt.live_migration_progress_timeout
completion_timeout = int(
CONF.libvirt.live_migration_completion_timeout * data_gb)
if libvirt_migrate.should_abort(instance, now, progress_time,
progress_timeout, completion_timeout):
guest.abort_job()
该选项缓存在堆栈上¶
只需将选项值放入局部变量就足以对其进行缓存。这很容易在循环中进行操作。示例代码
progress_timeout = CONF.libvirt.live_migration_progress_timeout
completion_timeout = int(
CONF.libvirt.live_migration_completion_timeout * data_gb)
while True:
if libvirt_migrate.should_abort(instance, now, progress_time,
progress_timeout, completion_timeout):
guest.abort_job()
目标是每次选项可能产生影响时,都检查它一次。通常,这就像每次都检查它一样,例如将局部变量移入循环。示例补丁:https://review.openstack.org/#/c/319203
有时需要使用选项值执行多个计算,并且重要的是结果保持一致。在这种情况下,需要将选项值缓存在局部变量中。示例补丁:https://review.openstack.org/#/c/319254
该选项影响状态¶
选项值也可以通过状态(系统或外部)进行缓存。例如,oslo.log 的“debug”选项用于在启动时设置默认日志级别。通常不会再次检查该选项,因此如果发生变更,系统状态将不会反映选项的新值。在这种情况下,我们必须使用一个 *mutate hook*(变更钩子)
def _mutate_hook(conf, fresh):
if (None, 'debug') in fresh:
if conf.debug:
log_root.setLevel(logging.DEBUG)
def register_options(conf):
... snip ...
conf.register_mutate_hook(_mutate_hook)
变更钩子函数将传递两个位置参数,“conf”和“fresh”。“conf”是对更新后的 ConfigOpts 对象的引用。“fresh”看起来像
{ (group, option_name): (old_value, new_value), ... }
例如
{ (None, 'debug'): (False, True),
('libvirt', 'live_migration_progress_timeout'): (50, 75) }
钩子可以按任何顺序调用。
每个项目应该注册一个钩子,该钩子执行所有必要的操作以应用所有新的选项值。这个钩子函数可能会变得非常大。为了保持良好的风格,使用辅助函数模块化钩子,而不是积累一个庞大的单体或注册多个钩子。