在服务器端实现无间断部署Python应用的教程


Posted in Python onApril 16, 2015

 当你开始着手部署应用时,最简单的方式莫过于使用管理员身份重启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),但我们因此可以继续处理流量而无需让连接等待过长时间。

这种进程一般是这样的
 

for child_pid of gunicorn-master:
 kill child_pid
 wait for app startup

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

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

echo 'Killing children of ' $1;
 
children=$(pgrep -P $1)
for child in $children
do
 echo 'Killing' $child
 kill $child
 response=$(timeout 60 nc -w 0 -ul 4012)
 if [ "$response" != '200 OK' ]; then
  echo 'BROKEN'
  exit 1;
 fi
done

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

import socket
import time
 
def 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)应用:
 
from werkzeug.wrappers import Request, Response
 
@Request.application
def application(request):
 resp = Response('Hello World!')
 if request.path == '/_status':
  resp.status = '200 OK'
 else:
  resp.status ='404 Not Found'
 return resp

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

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链接的时候这会是一个问题,即使你的应用可以工作,它也无法再次启动。

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

Python 相关文章推荐
Python学习笔记(二)基础语法
Jun 06 Python
python实现的DES加密算法和3DES加密算法实例
Jun 03 Python
python Django模板的使用方法
Jan 14 Python
使用Python生成XML的方法实例
Mar 21 Python
替换python字典中的key值方法
Jul 06 Python
Python项目 基于Scapy实现SYN泛洪攻击的方法
Jul 23 Python
face++与python实现人脸识别签到(考勤)功能
Aug 28 Python
python3常用的数据清洗方法(小结)
Oct 31 Python
django-orm F对象的使用 按照两个字段的和,乘积排序实例
May 18 Python
利用Vscode进行Python开发环境配置的步骤
Jun 22 Python
教你用Python写一个植物大战僵尸小游戏
Apr 25 Python
Python虚拟环境virtualenv是如何使用的
Jun 20 Python
使用Protocol Buffers的C语言拓展提速Python程序的示例
Apr 16 #Python
使用Python编写一个模仿CPU工作的程序
Apr 16 #Python
利用Python中的mock库对Python代码进行模拟测试
Apr 16 #Python
使用Python脚本来控制Windows Azure的简单教程
Apr 16 #Python
在Python下利用OpenCV来旋转图像的教程
Apr 16 #Python
在Python中使用Neo4j数据库的教程
Apr 16 #Python
使用Python的Zato发送AMQP消息的教程
Apr 16 #Python
You might like
中国的第一台收音机
2021/03/01 无线电
解决了Ajax、MySQL 和 Zend Framework 的乱码问题
2009/03/03 PHP
PHP 数组基础知识小结
2010/08/20 PHP
php中adodbzip类实例
2014/12/08 PHP
php 中htmlentities导致中文无法查询问题
2018/09/10 PHP
Laravel框架基于ajax和layer.js实现无刷新删除功能示例
2019/01/17 PHP
JavaScript语言中的Literal Syntax特性分析
2007/03/08 Javascript
defer属性导致引用JQuery的页面报“浏览器无法打开网站xxx,操作被中止”错误的解决方法
2010/04/27 Javascript
js对table的td进行相同内容合并示例详解
2013/12/27 Javascript
jQuery 2.0.3 源码分析之core(一)整体架构
2014/05/27 Javascript
JavaScript显示当前文档最后修改日期的方法
2015/03/19 Javascript
在HTML中插入JavaScript代码的示例
2015/06/03 Javascript
js获取字符串字节数方法小结
2015/06/09 Javascript
详解jQuery的Cookie插件
2016/11/23 Javascript
NPM 安装cordova时警告:npm WARN deprecated minimatch@2.0.10: Please update to minimatch 3.0.2 or higher to
2016/12/20 Javascript
javascript使用btoa和atob来进行Base64转码和解码
2017/03/20 Javascript
JavaScript数据结构之广义表的定义与表示方法详解
2017/04/12 Javascript
Webpack中雪碧图插件使用详解
2018/05/25 Javascript
微信小程序实现自定义picker选择器弹窗内容
2020/05/26 Javascript
使用layui定义一个模块并使用的例子
2019/09/14 Javascript
Layui数据表格 前后端json数据接收的方法
2019/09/19 Javascript
Vue循环遍历选项赋值到对应控件的实现方法
2020/06/22 Javascript
[01:08:33]OG vs VGJ.T 2018国际邀请赛小组赛BO2 第一场 8.18
2018/08/19 DOTA
python实现从字符串中找出字符1的位置以及个数的方法
2014/08/25 Python
跟老齐学Python之用while来循环
2014/10/02 Python
从零开始学Python第八周:详解网络编程基础(socket)
2016/12/14 Python
python字符串过滤性能比较5种方法
2017/06/22 Python
Python高级特性 切片 迭代解析
2019/08/23 Python
pycharm修改file type方式
2019/11/19 Python
Anaconda使用IDLE的实现示例
2020/09/23 Python
澳大利亚最受欢迎的女士度假服装:Kabana Shop
2020/10/10 全球购物
建筑工程实习自我鉴定
2013/09/19 职场文书
超市实习总结自我鉴定
2013/09/19 职场文书
金融专业个人求职信
2013/09/22 职场文书
小孩不笨观后感
2015/06/03 职场文书
中学后勤工作总结2015
2015/07/22 职场文书