如果你喜欢干货,想要直接了解一些关于python项目部署的操作性内容,请直接看后半部分
所有编程人员都是乐观主义者
年轻人总是有些乐观注意——无论什么样的程序,结果是毋庸置疑的:“这次它肯定会运行。”或者“我刚刚找出了最后一个错误。”———摘自《人月神话》
没有银弹
在所有恐怖民间传说的妖怪中,最可怕的是人狼,因为它们可以完全出乎意料地从熟悉的面孔编程可怕的妖怪。为了对付人狼,我们在寻找可以消灭它们的银弹。 ———摘自《人月神话》
对于DEV和OPS
既然大家都是乐观主义者大部分情况下一时不到bug的存在,而且有没有可以一劳永逸彻底消灭bug的银弹
三点问题
首先,在开发中我们必然是会犯错误,有些bug甚至一直存在而我们却束手无策。
其次,在生产环境中尤其是基于网络的程序,我们会遇到各种各样在开发时完全没有预测到的问题
最后,DEV和OPS完全是两回事,开发环境和生产环境截然不同的。
如何解决
要确保一个产品的稳定及良好的用户体验,单就开发层面来说:
- 需要开发人员丰富的经验,事实证明踩过得坑和产品的稳定性成正比
早在2009年的时候,我就遇到一个程序员习惯性的把用户姓名字段在数据中设置的很大,后来才知道,这里面有一个段子:他曾经给某机场做过项目,后来出现了一个让人啼笑皆非的事情。是有一个新疆同胞因为中文名字太长(超过32个字节)以至于系统不能出票。
- 需要项目负责人对项目良好的规划和对情况的预判
比如有一个web项目起初只是传统的web应用,后来要做移动端app。然而在开发初期并没有考虑设计API,以至于项目在已经有海量数据和用户的情况下被大规模的重构,甚至开发人员直接在生产服务器上修改代码。这很危险,真的!
- 需要开发团队有良好的技术规范
天知道代码跑起来以后会不会失控,在不同的平台,不同的环境下。唯一好的办法是保持良好的代码规范,如果是python的话,我希望大家可以用几乎变态的PEP8来培养自己良好的编码规范(折磨自己)。
同时再给一点忠告:一般情况下:开发的时候永远不要想当然,比如“我的代码只需要在Ubuntu系统下运行良好就行了”
那么,从运维层面来说:
- 需要谨慎的选择最合适项目的稳定平台
从服务器提供商到节点选择操作系统版本选择,服务器配置选择,软件版本选择等等等 每一点都不应被忽视
- 需要提高自己系统的容错能力
虽然大部分的错误都会在开发测试的迭代中被解决,但总会有那么一些漏网之鱼,或者以写目前确实无法很好解决的问题,这个时候就需要OPS去构建一套良好的容错系统
比如,某些开源软件总会有一些issue一直拖着没有解决,开发中又没有好的解决办法,此时恰好又没有替代品,或者重构成本太大。这个时候只能暂时寄希望于OPS了。稍后会在一个python项目中详细阐述这一点。
- 必要的时候,定制一套完善的监控系统
监控系统并不能解决产品运行的实际问题,但是他确实有效。比如你可以对某一个核心进程进行监控,对某一个进程所占用的内存进行监控,这都是十分有效的。当然,对于一般系统,服务器提供商一般都会自带一套监控系统(告警系统),也就足够了!
使用一个Python项目来描述上述问题
项目简介和技术构架
这个项目是一个提供高质量数据的API,他包含一个后台服务(负责爬取数据)和一个flask程序(负责提供数据):
有关项目的详情,可以参考我之前的文章从python开始一个全栈项目 程序员&设计师福利篇
我们要讨论的第一个问题是,环境的分离
virtualenv可以很方便的分离python项目,但是它如何运行呢?
解决的办法是使用supervisor去运行项目,在项目程序推出的时候还可以自动重启:
那么,用我的项目来说,首先我需要flask运行,通过uwsgi启动设置代理,通过nginx来做最外层服务器。
首先,安装uwsgi(虚拟环境下)并作配置
uwsgi通过一个配置文件启动,配置文件大致如下:
[uwsgi]
# uwsgi 启动时所使用的地址与端口
socket = 127.0.0.1:8001
# 指向flask程序的目录
chdir = /home/www/design
# python 启动程序文件
wsgi-file = rest.py
# python 程序内用以启动的 application 变量名
callable = app
# 处理器数
processes = 1
# 线程数
threads = 1
#状态检测地址
stats = 127.0.0.1:9191
然后,用supervisor来执行uwsgi
superviso 安装好以后是一个系统服务,在ubuntu下使用如下命令控制,其他系统类似:
service superviso start|stop|status
它需要从配置文件读取信息,从而启动某一个程序:
ubuntu下配置文件的地址 /etc/superviso/conf.d/
你可以在conf.d目录下新建自己的配置文件已对应需要使用superviso来管理的应用
flask.conf
# 指向网站目录
chdir = /home/www/design
# python 启动程序文件
wsgi-file = rest.py
# python 程序内用以启动的 application 变量名
callable = app
# 处理器数
processes = 1
# 线程数
threads = 1
#状态检测地址
stats = 127.0.0.1:9191
root@iZ62zfrrhbsZ:~# cat /etc/supervisor/conf.d/d
dataservice.conf design_supervisor.conf
root@iZ62zfrrhbsZ:~# cat /etc/supervisor/conf.d/design_supervisor.conf
[program:design]
# 启动命令入口(使用虚拟环境下的uwsgi启动)
command=/home/design/python-design/bin/uwsgi /home/www/design/config.ini
# 命令程序所在目录(虚拟环境下的命令程序目录)
directory=/home/design/python-design
#运行命令的用户名
user=root
autostart=true
autorestart=true
#日志地址
stdout_logfile=/home/www/design/logs/uwsgi_supervisor.log
最后,启动nginx
service nginx start
这样的话,flask能够运行起来了。
后台服务的技术构架
- redis负责存储数据并作持久化(使用快照)。
- selenium + phantomJS + pyquery 负责进行数据的爬取。
开发过程中,我可以保证程序工作的状态是可维护的:
- 数据不能丢失(这个交给redis快照,不需要操太多心)
- 进度的保存。(当我爬取了某一个页面程序突然退出的话,下一次程序依旧能够从改进读继续执行)
- 保证程序能7×24小时不间断运行。(事实上phantomJS经常会假死,从而导致程序假死,开发层面很难解决)
那么,这几个问题,我在生产环境是如何解决的呢?
后台服务运行的干将莫邪
关于数据和进度保存,根据项目对数据的要求,我很放心的把数据的持久话交给redis的快照做,即使断电了,我的程序依旧可以在下一次重启之后依靠快照和快照内的进度继续进行。
那么,问题来了:phantomJS老是假死,怎么办呢?
解决办法是这样的,我可以每一个小时让程序自行断掉,然后再自己运行起来。
但是,我可以轻松的关掉进程,但是如何让重新启动呀?我的程序可是在虚拟环境下的呀!
答案一就是supervisor呀!停掉以后,它可以自己启动呢!
在配置文件目录(上面提到过的conf.d,不同系统应该不同)添加一个后台服务启动的命令配置文件就好了:
dataservice.conf
command=/home/design/python-design/bin/python /home/design/service/mining.py
directory=/home/design/service
user=root
这样的话,进程就能在推出时被supervisor启动了,那么如何定时结束这个进程呢?
答案是Linux定时任务Crontab
首先编写一个定时关闭mining.py这个进程的脚本:
stopMin.sh:
ps -ef|grep mining.py -v grep|cut -c 9-15|xargs kill
然后添加这个脚本每一个小时自动执行:
编辑/etc/crontab文件,添加一行:
00 1 * * * /path/to/stopMin.sh
关于contab可以惨开这篇文章[Linux定时任务Crontab详解 ]
TIPS
你可以使用 tail命令打开日志的最后50行,并且实时更新哦:
tail -n -50 -f /path/to/xxx.log