简单的、不针对特定服务的服务平滑重载工具
项目描述
简单的服务平滑重载工具。
通用用法基于我的另一个项目ianitor。 hupwatch是一个简单的shell命令,可以包装进程,可以简单地用于您现有的进程/服务监控工具,如supervisord、circus、runit等。
为什么?
优雅地重新加载Web应用程序代码是当今的必备功能。每个DevOps都应该告诉你这一点。许多Python Web服务器都允许这种场景,但处理方式不同。通常,你只有少数合理的选择
gunicorn允许通过向gunicorn进程发送Unix HUP信号来实现这一点。它会优雅地停止现有工作者并启动新版本代码的新工作者。另一种选择是发送USR2信号,这将产生带有更新工作者集的新主gunicorn进程。然后你可以
uWSGI提供了一些选项,其中最好的一个与向gunicorn发送USR2信号的效果非常相似。uWSGI提供了关于该功能的非常详尽的文档。有人可能会说“太啰嗦了”。祝你好运,理解它!
您可能依赖于更高层次架构堆栈中的负载均衡。尽管如此,您仍然需要能够在部署期间完成正在处理的当前请求。因此,您需要一个至少可以优雅地关闭其工作者的Web服务器。
gunicorn 和 uWSGI 提供的优雅重载实现都很整洁,但在尝试自动化或与任何流行的进程/服务监控工具一起使用时,在现实中都会失败。
如果你使用符号链接切换代码库,Gunicorn 的 HUP-reload 会失败。这在许多组织中是一个非常流行的技术。Gunicorn 还不会在 HUP 信号更新其自身的配置,而只会重启带有新代码的工作进程。如果你只是简单地替换现场代码而不需要调整配置选项,如工作进程类或并发设置,这种方法将工作得很好。但是,如果你使用 Django 并对你的最新版本进行了大量模板更新,那么祝你好运。旧的 gunicorn 工作进程将“优雅地”关闭,但如果你在原地替换了代码,它们也可能“优雅地”响应服务器错误。Gunicorn 接受一个 --chdir 参数,应该可以解决这个问题,但它再次与一些流行的部署解决方案(如 buildout)不太兼容。
第二种方法(生成新的主服务器进程)似乎是一种更好的方式,通常与符号链接配合得很好,但与许多进程监控工具不太兼容。在 Python 世界中最流行的一个——supervisord——必须自己启动一个子进程才能控制它。一旦 gunicorn(在 USR2 信号之后)或 uWSGI(在其所需之后)生成了新的主进程,它就不再是父进程的监督者。简而言之,它最终将成为 init 的子进程,而且无法通过监督器“领养”这样的进程。监督器将尝试再次启动它,最终导致工作进程数量是你预期的两倍。如果你没有注意到这一点,并以这种方式执行许多后续的重载,你最终可能会耗尽资源。
通常,通过负载均衡器帮助处理更新似乎是一个更安全的解决方案,因为一旦它们从你的堆栈中移除,你可以简单地重新启动 Web 服务器。遗憾的是,这要困难得多,并为你的整个操作增加了额外的复杂性层次。
当然,你可以通过为使用的任何进程监控工具提供一些自定义集成或进行一些疯狂的下/当前实例切换来解决这些问题,只为这个单一目的维护服务的双倍配置。这将使你的解决方案要么无法在其他工具中移植,要么使整个解决方案在可靠方式中自动化变得更加困难。
它是如何工作的?
hupwatch 提供了一个单一解决方案,可以轻松地与几乎所有监控工具集成,并允许通过任何 POSIX 系统上可用的单个命令重新加载整个 Web 服务器。它是 kill
# on terminal or inside supervisord config: $ hupwatch -- gunicorn myapp:application --bind unix:/tmp/myapp.sock => HUP watch [INFO ]: Starting HUP watch (92808) => HUP watch [INFO ]: Child process 92809 started => HUP watch [INFO ]: Pausing for signal [2016-01-27 17:24:13 +0100] [92809] [INFO] Starting gunicorn 19.4.5 [2016-01-27 17:24:13 +0100] [92809] [INFO] Listening at: unix:/tmp/myapp.sock (92809) [2016-01-27 17:24:13 +0100] [92809] [INFO] Using worker: sync [2016-01-27 17:24:13 +0100] [92812] [INFO] Booting worker with pid: 92812 # issued on any other terminal: kill -HUP <hupwatch_pid> # continued result in the hupwatch stdout: [...] => HUP watch [DEBUG ]: HUP: >>> => HUP watch [DEBUG ]: HUP: Waiting for process (92955) to warm up => HUP watch [DEBUG ]: HUP: Sending SIGTERM to old process (92809) => HUP watch [DEBUG ]: HUP: Waiting for process (92809) to quit... [2016-01-27 17:24:46 +0100] [92809] [INFO] Handling signal: term [2016-01-27 17:24:46 +0100] [92955] [INFO] Starting gunicorn 19.4.5 [2016-01-27 17:24:46 +0100] [92955] [INFO] Listening at: unix:/tmp/myapp.sock (92955) [2016-01-27 17:24:46 +0100] [92955] [INFO] Using worker: sync [2016-01-27 17:24:46 +0100] [92964] [INFO] Booting worker with pid: 92964 [2016-01-27 17:24:58 +0100] [92812] [INFO] Worker exiting (pid: 92812) [2016-01-27 17:24:58 +0100] [92809] [INFO] Shutting down: Master => HUP watch [DEBUG ]: CHLD: >>> => HUP watch [INFO ]: CHLD: Child process quit => HUP watch [DEBUG ]: CHLD: <<< => HUP watch [INFO ]: HUP: Old process quit with code: 0 => HUP watch [DEBUG ]: HUP: <<< => HUP watch [INFO ]: Pausing for signal
hupwatch 将在你提供的 -- 字符后面的任何内容作为其自己的子进程(使用 subprocess.Popen())启动,并监听传入的 Unix 信号。每当它接收到 HUP 信号时,它都会使用相同的参数启动一个新的进程,并向先前启动的进程发送 TERM 信号,以便它可以优雅地关闭。
这使得整个重新加载过程非常容易自动化。不需要执行多个命令并在它们之间保持任何状态。只需 HUP 即可!这对 fabric 爱好者来说是个好消息——你不需要担心在整个重载过程中丢失 shh 连接,因为这只涉及一步(至少如果你使用符号链接)。没有什么可以中断的!
回滚更新也毫不费力:只需更改项目符号链接,并对同一 hupwatch pid 发出另一个 HUP 信号。自动回滚也应该很容易实现,我们欢迎任何贡献!
如果你注意到了,你应该已经注意到,这只需要两样东西就可以使其作为优雅重载的解决方案工作
您需要使用 Unix 套接字而不是端口,这样新旧进程都可以绑定到相同的地址。
当您的 Web 服务器收到 TERM 信号时,需要进行优雅的关闭。Gunicorn 已经实现了这一点。uWSGI 目前还不支持。
还有其他问题吗?
使用 hupwatch --help 查看更多有关可能的配置选项的信息。
还有一些处理故障的重要细节以及当 hupwatch 收到其他信号(例如 KILL、TERM、INT)时应做什么。默认情况下,它假设您希望无论父进程(hupwatch)发生什么情况,您的进程都能正常工作。因此,在发生故障的情况下,它将保持原样 - 生成的进程将成为 init 的子进程。如果您遇到这种情况,可以手动清理混乱而不会中断处理 Web 请求的过程。可以通过 --kill-at-exit 标志来更改这种行为。
这个项目的状态如何?
这与其说是一个经过实战考验的工具,不如说是一个概念验证。无论如何,实际工作的代码只有几行。这个包中的大部分代码都是广泛的日志记录和参数解析。这个包的状态在不久的将来将会改变,因为它解决了我们组织中的一个真实问题。所以至少在您的预发布/测试环境中试一试。
欢迎贡献!
项目详情
下载文件
下载适用于您的平台的文件。如果您不确定选择哪个,请了解更多关于 安装包 的信息。
源代码分布
构建分布
hupwatch-0.0.3.tar.gz 的散列
算法 | 散列摘要 | |
---|---|---|
SHA256 | 3d411ad2ece401b598d544a2dec5aa2594df9b9eef19a83aa5951edf4e9da5c9 |
|
MD5 | 142fd81f70ac0040f8a49a02d0079037 |
|
BLAKE2b-256 | fef8863ce323017dd552b105fe4ff5f9d13bfc49ce60f3e04b5206f4e0783b10 |
hupwatch-0.0.3-py2-none-any.whl 的散列
算法 | 散列摘要 | |
---|---|---|
SHA256 | 79d5a825eae57e1264adca9882202257343d1dc495c61d34f7234a0cb276513d |
|
MD5 | 9c5c7df6e58b583b85ec7b1ef812bfed |
|
BLAKE2b-256 | 46b4ed09fc1a8f62cc89abac84edaea0d46c0dfca11e0fab97c29b38f7614f86 |