启用项目以支持可变配置

从 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) }

钩子可以按任何顺序调用。

每个项目应该注册一个钩子,该钩子执行所有必要的操作以应用所有新的选项值。这个钩子函数可能会变得非常大。为了保持良好的风格,使用辅助函数模块化钩子,而不是积累一个庞大的单体或注册多个钩子。

示例补丁:https://review.openstack.org/#/c/254821/