Python 无间断部署
admin
2023-07-31 00:37:36
0

当你开始着手部署应用时,最简单的方式莫过于使用管理员身份重启my_app或者所有服务,使产品升级至当前版本。开始的时候一切都很好,但是最终你会发现一旦应用启动以后,在重启期间去尝试连接会得到众多HTTP 503 错误。

最后你可能发现Gunicorn和uWSGI可以在不关闭套接字的情况下重新加载你的应用,这样在你的应用启动时,网络请求仅仅是被延时了一点点。只要你的应用不会花费很长时间在启动上,它就会工作的很好。不幸的是,现有的许多应用可能会花费1分钟的时间在启动上,对于等待在套接字上的链接来说,这太长了。

Gunicorn使用kill -HUP $PID,通过关闭所有工作进程,然后再启动它们来重新加载。但是工作进程缓慢的初始化过程往往会导致问题的产生。uWSGI使用链式重载,它每次只会启动一个工作进程。我需要对Tornado的支持,它当前并不十分适合uWSGI。

使用负载均衡器

一种常见的技术是从负载均衡器中移除单个服务器,升级/重启应用,然后再把它加载回来。我们正在使用负载均衡器,但是为了调度整个过程,在配置节点的时候需要协调使用HAProxy来管理套接字。我们当前的部署方案是同时部署到所有节点,而不是一个接一个的来,一个相当大的变化。在等待LBs(译注:负载均衡器)将节点移出池期间,可以使用404’ing状态页来欺骗healthcheck。这比我想要的时间要多一点,对于每个服务器来说,两次healthcheck失败间隔5秒钟,这包括了升级完成后web进程恢复的时间。

Gunicorn 重载 ++

Gunicorn会自动重启失败的web进程,所以它可能会杀掉每个进程,在其间休眠,直到所有的子进程执行完毕。这很有效,不过如果应用启动的次数变动显著的话,我们要么会为重启等待过长时间,要么会等待不长的时间并承担一些故障宕机的风险。

因为Gunicorn包含了指向应用的Python钩子,所以完全可能写出一小段代码,在工作进程准备就绪的时候通知重启进程。Gunicorn并不包含需要的钩子,但做出改变非常简单。在新版本发布前它需要一些修改。

现在重启进程发挥了这样的事实优势,就是说单个的soket具有接受连接的多个进程。重启只会极微弱的减少服务能力(1/N),但我们因此可以继续处理流量而无需让连接等待过长时间。

这种进程一般是这样的

123   for child_pid of gunicornmaster:    kill child_pid    wait for app startup

我的第一个版本使用shell和nc来监听应用启动的UDP数据包。尽管将我们的进程管理器集成到shell环境比我预想的要麻烦一点,但它工作的很好。

重启脚本被调用的时候应该带上Gunicorn的PID,就是masterrestart.sh的 $PID

12345678910111213 echo \’Killing children of \’ $1; children=$(pgrep P $1)for child in $childrendo    echo \’Killing\’ $child    kill $child    response=$(timeout 60 nc w 0 ul 4012)    if [ \”$response\” != \’200 OK\’ ]; then        echo \’BROKEN\’        exit 1;    fidone

在串联上post_worker_init脚本,以便app运行的时候通知重启脚本。

1234567891011 import socketimport timedef post_worker_init(worker):    _send_udp(\’200 OK\\n\’) def _send_udp(message):    udp_ip = \”127.0.0.1\”    udp_port = 4012    sock = socket.socket(socket.AF_INET, # Internet                         socket.SOCK_DGRAM) # UDP    sock.sendto(message, (udp_ip, udp_port))

如果我们有这样一个WSGI(
Python Web Server Gateway Interface)应用:

12345678910 from werkzeug.wrappers import Request, Response @Request.applicationdef application(request):    resp = Response(\’Hello World!\’)    if request.path == \’/_status\’:        resp.status = \’200 OK\’    else:        resp.status =\’404 Not Found\’    return resp

我们甚至可以去做检查/_status页面之类的事情,以此来验证应用是否已运行。

123456789 def post_worker_init(worker):    env = {        \’REQUEST_METHOD\’: \’GET\’,        \’PATH_INFO\’: \’/_status\’,    }    def start_response(*args, **kwargs):        _send_udp(args[0])     worker.wsgi(env, start_response)

注意不要试图在这个健康检测中运行太多的应用,如果不管什么原因你的post_worker_init产生了一个错误,那么工作进程将会退出,并阻止应用的启动。在你检查可能失效的DB链接的时候这会是一个问题,即使你的应用可以工作,它也无法再次启动。

现在通过一分钟的应用启动,我们实现了滚动重启,而无需停止应用或者丢弃任何链接!


相关内容

热门资讯

500 行 Python 代码... 语法分析器描述了一个句子的语法结构,用来帮助其他的应用进行推理。自然语言引入了很多意外的歧义,以我们...
定时清理删除C:\Progra... C:\Program Files (x86)下面很多scoped_dir开头的文件夹 写个批处理 定...
65536是2的几次方 计算2... 65536是2的16次方:65536=2⁶ 65536是256的2次方:65536=256 6553...
Mobi、epub格式电子书如... 在wps里全局设置里有一个文件关联,打开,勾选电子书文件选项就可以了。
scoped_dir32_70... 一台虚拟机C盘总是莫名奇妙的空间用完,导致很多软件没法再运行。经过仔细检查发现是C:\Program...
小程序支付时提示:appid和... [Q]小程序支付时提示:appid和mch_id不匹配 [A]小程序和微信支付没有进行关联,访问“小...
pycparser 是一个用... `pycparser` 是一个用 Python 编写的 C 语言解析器。它可以用来解析 C 代码并构...
微信小程序使用slider实现... 众所周知哈,微信小程序里面的音频播放是没有进度条的,但最近有个项目呢,客户要求音频要有进度条控制,所...
python查找阿姆斯特朗数 题目解释 如果一个n位正整数等于其各位数字的n次方之和,则称该数为阿姆斯特朗数。 例如1^3 + 5...
Apache Doris 2.... 亲爱的社区小伙伴们,我们很高兴地向大家宣布,Apache Doris 2.0.0 版本已于...