代理子进程状态

https://blueprints.launchpad.net/neutron/+spec/agent-child-processes-status

Neutron 代理会生成外部分离的进程,这些进程未被监控。如果这些进程发生任何问题,Neutron 不会采取任何措施,从而无法可靠地提供这些服务。

我们建议监控这些进程,并采取可配置的操作,从而提高 Neutron 对外部故障的弹性。

问题描述

当 l3-agent 中的 ns-metadata-proxy 崩溃时 [4],由该 ns-metadata-proxy 服务的所有子网在路由器发生任何更改之前将没有元数据,这将重新检查元数据代理的存活状态。

dhcp-agent 同样会发生这种情况 [3],lbaas 和 vpnaas 代理也是如此。

这是一个长期存在的 bug,通常由 dnsmasq 或 ns-metadata-proxy 中的 bug 触发,并且在大型云和 HA 环境中尤其关键。

提议的变更

我建议使用 neutron.agent.linux.external_process.ProcessMonitor 类来监控生成的进程,该类依赖于 ProcessManager 定期检查存活状态。

如果应该处于活动状态的进程未运行,则会记录下来,并且我们可以采取以下管理员配置的操作,按照配置中指定的顺序。

  • 重新启动进程:失败的外部进程将被重新启动。

  • 退出代理:用于 HA 服务管理器负责代理并将在不同主机上重新启动它时使用。在退出操作期间,所有其他外部进程将继续运行,就像任何其他代理停止一样。因此,在 HA 解决方案接管故障代理之前,受影响的租户网络不会有停机时间。在故障转移的情况下,清理责任(进程和端口)在于 neutron-netns-cleanup 和 neutron-ovs-cleanup。

在未来的后续更新中,我们计划在 oslo 中实现相应的代码后,向进程管理器发送通知操作 [6]

配置示例可以是

  • 禁用:不轮询外部进程的存活状态

check_child_processes_period  = 0
  • 记录(隐式)并重新启动

check_child_processes_action = respawn
check_child_processes_period = 60
  • 记录(隐式)并通知

check_child_processes_action = notify
check_child_processes_period = 60
  • 记录(隐式),并退出

check_child_processes_action = exit
check_child_processes_period = 60

此功能默认启用(60 秒),默认操作将是“重新启动”。

数据模型影响

REST API 影响

安全影响

通知影响

其他最终用户影响

性能影响

通过检查底层子进程,将增加一些额外的周期性负载。通过为检查子进程启动一个绿色线程池,将减少对其他绿色线程的锁定。引入一个信号量以避免同时启动多个检查周期。

由于担心轮询 /proc/$pid/cmdline,我实现了一个简单的基准测试

i=10000000
while  i>0:
  f = open ('/proc/8125/cmdline','r')
  f.readlines()
  i = i - 1

请注意,cmdline 文件由内核函数 [9] 在内存中寻址,不依赖于通过块设备进行的任何 I/O,这意味着没有缓存可以加速此文件的读取,从而使此基准测试失效。

root@ns316109:~# time python test.py
real  0m59.836s
user  0m23.681s
sys 0m35.679s

这意味着,使用 1 个核心 / 100% CPU 在 7400 bogomips 机器上读取 170,000 次/秒。

如果我们需要检查 1000 个子进程,则需要 1000/170000 = 0.0059 秒,再加上中间方法调用的开销和生成绿色线程的开销。

我认为检查 1000 个子进程的 ~6ms CPU 使用率是可以接受的,即使检查间隔可调,并且默认禁用以让部署者平衡性能影响与故障检测延迟。

轮询不是理想的,但替代方案也不理想,我们需要一个解决方案来解决这个问题,尤其是在 HA 环境中。

IPv6 影响

预计此处对 IPv6 没有影响。

其他部署者影响

实施自己子进程外部监控的人员可能需要迁移到新的解决方案,利用退出方法,或者在可用时使用后续的通知方法。

开发人员影响

生成外部进程的开发人员可以开始使用 ProcessMonitor,而不是直接使用 ProcessManager。

社区影响

此更改已在邮件列表、IRC 上讨论了多次,并已为 Juno 接受,但未能在截止日期前完成。这是社区期望的,因为它使 Neutron 代理对外部故障更具弹性。

备选方案

  • 使用 popen 在前台启动服务并等待 SIGCHLD,而不是轮询。在退出或重新启动代理后,将无法重新附加,因为父进程将与子进程分离,并且在代理重新启动时无法重新附加(除非使用 ptrace,这听起来太 hackish)。这是一个 POSIX 限制。在我们的设计中,当代理退出时,所有底层子进程都保持活动状态,与父进程分离并继续运行,以确保在升级期间没有服务中断。当代理再次启动时,它将在 /var/neutron/{$resource}/ 中检查为每个资源服务的子进程的 pid 及其配置,并确保它正在运行(否则重新启动)。这是我们无法重新附加或等待 [7] 特定非子进程 PID [8] 的地方。

  • 将代理的重新启动机制更改为从代理内部通过信号捕获进行 execve。execve 系统调用保留原始 PID 和子进程 PID 关系,因此我们可以等待子进程 pid。但这会阻止代理的停止/启动功能,这在维护和开发期间可能很有用。如果将来决定更改此设置,ProcessMonitor 实现可以轻松地修改为非轮询等待 pid,而无需更改其任何 API。

  • 使用中间守护进程启动长期运行的进程并通过 SIGCHLD 监控它们,作为第一种替代方案中的问题的解决方法。这与即将发布的 oslo rootwrap 守护进程中的功能非常相似,但 rootwrap 守护进程尚未支持长期运行的进程,即使此替代方案的问题是中间进程管理器崩溃或被杀死时的情况。在这种情况下,我们失去了对生成子进程的控制(我们将通过 SIGCHLD 监控这些子进程)。

  • 与其定期检查所有子进程,不如将负载分散到一段时间内的几个批次中。这将是一个更复杂实现,可能可以在第一轮或作为最后的任务项中解决,如果初始实现对于大量资源(路由器、dhcp 服务、lbaas 等)的性能不佳。

  • 最初,通知部分计划在 Neutron 本身中实现,但设计已在 oslo 中模块化,并提供不同类型(systemd、init.d、upstart 等)的驱动程序。

实现

负责人

添加 brian-haley,因为我借鉴了他的一些想法,并部分重用了他在 [5] 上的工作。

工作项

  • ProcessMonitor 和功能测试:完成

  • 在 dhcp-agent 中实现,重构与 neutron.agent.linux.external_process 的代码重复 [1]

  • 在 l3-agent 中实现 [2]

  • 在 lbaas-agent 中实现

  • 在 vpnaas-agent 中实现

说明:计划了通知操作,但它取决于新的 oslo 功能,一旦 oslo 功能被接受和实现,可以通过 bug 流程稍后添加此操作。

依赖项

通知操作取决于 [6] 的实现,但所有其他功能/操作无需该功能即可完成。

测试

Tempest 测试

Tempest 测试无法在网络节点中执行任意命令执行(例如,杀死进程)。因此,我们无法在没有在 tempest 中实现某种故障注入的情况下检查此问题。

功能测试

功能测试用于验证负责此规范的核心功能的 ProcessMonitor 类。

API 测试

文档影响

用户文档

新的配置选项需要为每个代理记录。

以下是建议的默认值

check_child_processes_action = respawn
check_child_processes_period  = 0

开发人员文档

参考资料