学习 Flask,写完一个 Flask 应用须要部署的时候,就想着折腾本身的服务器。根据搜索的教程照作,对于原理只知其一;不知其二,磕磕碰碰,只要运行起来了,谢天谢地而后再也不折腾了,到下一次还须要部署时,这样的过程就会重复一次。不知道多少人的膝盖中箭了呢?我也这样干过,这么作确实很蠢,因此我决定写一篇 Flask+uwsgi+Nginx+Ubuntu 的部署教程,解答一些我本身在这个过程当中的疑问,从原理到方案,以一个小白的角度,总结一下部署、运维这件事,应该对初学 Flask 须要部署的同窗有些帮助。html
我使用的 Ubuntu 系统版本是 14.04,用过几个 Linux 发行版,如今挑选系统的第一选择基本就是 Ubuntu 了,由于 Ubuntu 有商业公司 Canonical 作开发维护;使用的人多,有庞大的社区支持;遇到问题容易解决。我折腾过很长时间的 Linux 系统,我对新手的建议是,不要把时间浪费在这上面,应该以解决实际问题为导向,踏实点提升编程能力。装系统、优化系统、记各类酷炫的命令对于提升编程能力并无实际帮助。因此你问我资瓷不资瓷 Ubuntu,我固然是资瓷的啦,用 Ubuntu 固然也会遇到坑,但相比于其余系统会少一些,也会容易解决一点。事实上,Ubuntu 已经成为了服务器的首选,AWS 上被选择最多的 Linux 发行版就是 Ubuntu。Quora 用的 Linux 发行版也是 Ubuntu,创始人 Adam D'Angelo 在这个回答里解释了缘由。总的来讲,没有特别的理由的话,Ubuntu 理应是首选,经验多一些以后,若是对某个发行版感兴趣,或者想要作一些特别的尝试,跳出温馨区,试试其余系统也无妨。python
咱们知道 Flask 中自带了 web server,经过 Werkzeug,咱们能够搭建 WSGI 服务,运行咱们的网站,但 Flask 是 Web 框架,并非 Web 服务器,尽管 Werkzeug 很强大,但只能用于开发,不能用于生产,对于 Web 服务器,咱们有更专业的选择,那就是 uWSGI, uWSGI 是一个全站式的托管服务,它实现了应用服务器(支持多种编程语言)、代理、进程管理器、监视器。取名为 uWSGI 是由于它最先实现的是 Python 语言的 WSGI。nginx
uWSGI 包括四个部分:git
uWSGI 是 C 语言写的,性能比较高。github
推荐阅读web
当咱们部署完一个应用程序,浏览网页时具体的过程是怎样的呢?首先咱们得有一个 Web 服务器来处理 HTTP 协议的内容,Web 服务器得到客户端的请求,交给应用程序,应用程序处理完,返回给 Web 服务器,这时 Web 服务器再返回给客户端。Web 服务器与应用程序之间显然要进行交互,这时就出现了不少 Web 服务器与应用程序之间交互的规范,最先出现的是 CGI,后来又出现了改进 CGI 性能的FasgCGI,Java 专用的 Servlet 规范,Python 专用的 WSGI 规范等等。有了统一标准,程序的可移植性就大大提升了。这里咱们只介绍 WSGI。sql
WSGI 全称是 Web Server Gateway Interface,也就是 Web 服务器网关接口,它是 Python 语言定义出来的 Web 服务器和 Web 应用程序之间的简单而通用的接口,基于现存的 CGI 标准设计,后来在不少其余语言中也出现了相似的接口。 总的来讲,WSGI 能够分为服务器和应用程序两个部分,实际上能够将 WSGI 理解为服务器与应用程序之间的一座桥,桥的一边是服务器,另外一边是应用程序。shell
按照 web 组件分类,WSGI 内部能够分为三类,web 应用程序,web 服务器,web 中间件。应用程序端的部分经过Python 语言的各类 Web 框架实现,好比 Flask,Django这些,有了框架,开发者就不须要处理 WSGI,框架会帮忙解决这些,开发者只需处理 HTTP 请求和响应,web 服务器的部分就要复杂一点,能够经过 uWSGI 实现,也能够用最多见的 Web 服务器,好比 Apache、Nginx,但这些 Web 服务器没有内置 WSGI 的实现,是经过扩展完成的。如 Apache,经过扩展模块 mod_wsgi 来支持WSGI,Nginx能够经过代理的方式,将请求封装好,交给应用服务器,好比 uWSGI。uWSGI 能够完成 WSGI 的服务端,进程管理以及对应用的调用。WSGI 中间件的部分能够这样理解:咱们把 WSGI 看作桥,这个桥有两个桥墩,一个是应用程序端,另外一个是服务器端,那么桥面就是 WSGI 中间件,中间件同时具有服务器、应用程序端两个角色,固然也须要同时遵照 WSGI 服务器和 WSGI 应用程序两边的限制和须要。更详细的内容能够看PEP-333 中间件的描述编程
Flask 依赖的 Werkzeug 就是一个 WSGI 工具包,官方文档的定义是 Werkzeug 是为 Python 设计的 HTTP和 WSGI 实用程序库。咱们须要注意的是,Flask 自带的 Werkzeug 是用来开发的,并不能用于生产环境,Flask 是 Web 框架,而 Werkzeug 不是 Web框架,不是 Web 服务器,它只是一个 WSGI 工具包,它在 Flask 的做用是做为 Web 框架的底层库,它方便了咱们的开发。flask
咱们将 uwsgi 和 uWSGI 放在一块儿讲解。uWSGI 是一个 Web 服务器程序,WSGI,上面已经谈到,是一种协议,uwsgi 也是一种协议,uWSGI 实现了 uwsgi、WSGI、http 等协议。 uwsgi 的介绍能够看这里,uwsgi 是 uWSGI 使用的一个自有的协议,它用4个字节来定义传输数据类型描述。尽管都是协议,uwsgi 和 WSGI 并无联系,咱们须要区分这两个词。
Nginx 是高效的 Web 服务器和反向代理服务器,能够用做负载均衡(当有 n 个用户访问服务器时,能够实现分流,分担服务器的压力),与 Apache 相比,Nginx 支持高并发,能够支持百万级的 TCP 链接,十万级别的并发链接,部署简单,内存消耗少,成本低,但 Nginx 的模块没有 Apache 丰富。Nginx 支持 uWSGI 的 uwsgi 协议,所以咱们能够将 Nginx 与 uWSGI 结合起来,Nginx 经过 uwsgi_pass
将动态内容交给 uWSGI 处理。
官方文档在这
最好的 Nginx 教程在这
从上面的讲解中,咱们知道,uWSGI 能够起到 Web 服务器的做用,那么为何有了 uWSGI 还须要 Nginx 呢?
最广泛的说法是 Nginx 对于处理静态文件更有优点,性能更好。其实若是是小网站,没有静态文件须要处理,只用 uWSGI 也是能够的,但加上 Nginx 这一层,优点能够很具体:
对于运维来讲比较方便,若是服务器被某个 IP 攻击,在 Nginx 配置文件黑名单中添加这个 IP 便可,若是只用 uWSGI,那么就须要在代码中修改了。另外一方面,Nginx 是身经百战的 Web 服务器了,在表现上 uWSGI 显得更专业,好比说 uWSGI 在早期版本里是不支持 https 的,能够说 Nginx 更安全。
Nginx 的特色是可以作负载均衡和 HTTP 缓存,若是不止一台服务器,Nginx 基本就是必选项了,经过 Nginx,将资源能够分配给不一样的服务器节点,只有一台服务器,也能很好地提升性能,由于 Nginx 能够经过 headers 的Expires or E-Tag,gzip 压缩等方式很好地处理静态资源,毕竟是 C 语言写的,调用的是 native 的函数,针对 I/O作了优化,对于动态资源来讲,Nginx 还能够实现缓存的功能,配合 CDN 优化(这是 uWSGI 作不到的)。Nginx 支持epoll/kqueue 等高效网络库,可以很好地处理高并发短链接请求,性能比 uWSGI 不知道高到哪里去了。
若是服务器主机上运行了PHP,Python 等语言写的多个应用,都须要监听80端口,这时候 Nginx 就是必选项了。由于咱们须要一个转发的服务。
因此说,Nginx 基本也是必选项。
这里我假设咱们拿到的是一台全新的服务器。 通常来讲,Linux 系统都会预装 Python 的,但不必定装了 easy_install 工具,咱们能够经过 apt-get install python-setuptools
来安装 easy_install,再经过 easy_install 安装 pip。
$ sudo apt-get install python-setuptools $ sudo easy_install pip
咱们也能够直接装 pip:
$ sudo apt-get install python-pip
这样,咱们就能够经过 pip 安装 virtualenv,为 flask 项目构建虚拟环境。
$ sudo pip install virtualenv
$ sudo apt-get install nginx
启动 nginx 的方法:
$ sudo /etc/init.d/nginx start
这时候在浏览器地址栏输入服务器的 ip 地址,看到下面的页面就代表 Nginx 已经启动了:
在安装 uWSGI 前,须要解决 uWSGI 的依赖问题,由于 uWSGI 是一个 C 语言写的应用,因此咱们须要 C 编译器,以及 python 开发相关组件:
$ sudo apt-get install build-essential python-dev $ sudo pip install uwsgi
到这,咱们就安装好了 uWSGI,
首先,咱们把应用程序上传到服务器中,我在用 git 管理项目,因此只须要 git clone 一下就能够了:
$ git clone http://url/of/you/git/repo
若是你须要从本地上传项目文件,能够用 scp 命令,这里就不啰嗦用法了。总之咱们将项目文件放到服务器,而后就能够用 virtualenv 管理 Python 环境:
$ virtualenv ENV
$ source ENV/bin/activate # 激活虚拟环境 $ pip install -r requirement.txt # 解决依赖问题 $ deactivate # 退出依赖环境
这里就用 Flask 的7行代码作示例吧,我新建了一个文件夹,名为 helloflask,将下面的内容:
from flask import Flask app = Flask(__name__) @app.route("/") def hello(): return "Hello World!" if __name__ == "__main__": app.run(host='0.0.0.0', port=5001)
保存为hello.py,运行试试,在浏览器输入服务器公网 ip 地址,加端口号5001就能够看到结果。
好了,如今咱们用 Nginx 来承担 Web 服务。
删掉 Nginx 的默认配置文件:
$ sudo rm /etc/nginx/sites-enabled/default
有心的话,其实能够从 Nginx 默认配置中了解一些配置参数,固然最靠谱的途径仍是看 Nginx 的文档。这里只简单尝试 Nginx,下面给出一个简单的配置:
server { listen 80; server_name your.website.url charset utf-8; client_max_body_size 75M; location / { try_files $uri @yourapplication; } location @yourapplication { include uwsgi_params; uwsgi_pass unix:/home/frank/Documents/helloflask/helloflask_uwsgi.sock; } }
咱们能够将上述内容保存为 helloflask_nginx.conf,稍做解释:server_name 能够是域名,也能够写 ip 地址,uwsgi_pass 是代表 Nginx 与 uwsgi 的交流方式,我这里用的是 sock 文件,固然你也能够用指定端口号的形式,具体能够看这里。将 Nginx 配置文件用软连接连接到 Nginx 配置文件夹中:
sudo ln -s /home/frank/Documents/helloflask/helloflask_nginx.conf /etc/nginx/conf.d/
重启 Nginx:
sudo /etc/init.d/nginx restart
这时刷新一下以前打开的服务器公网 ip(或绑定的域名),这时看到的就不是「Welcome to Nginx」,而是「502 Bad Way」,由于咱们尚未启动 uWSGI,如今咱们将下面的内容保存为 helloflask_uwsgi.ini(用 xml 的格式也是能够的,具体能够看文档):
#application's base folder base = /home/frank/Documents/helloflask #python module to import app = hello module = %(app) home = %(base)/ENV pythonpath = %(base) #socket file's location socket = /home/frank/Documents/helloflask/%n.sock #permissions for the socket file chmod-socket = 666 #the variable that holds a flask application inside the module imported at line #6 callable = app #location of log files logto = /home/frank/Documents/helloflask/%n.log
稍稍解释一下,socket 指定的是与 nginx 进行通讯的端口文件。其余的参数,如线程数,处理器数等,能够查看文档后进行配置。上面的内容都是能够经过 uwsgi 命令的参数指定的,在命令行中敲入一行命令就能够了,为了「可持续发展」,固然是用文件保存下来比较好。
经过 uwsgi 命令,--ini 参数:
$ uwsgi --ini helloflask_uwsgi.ini &
指定配置文件,后台运行 uwsgi, 这时再刷新一下以前打开的页面,就能够看到应用正常运行了。
我尝试了在一台服务器上运行多个应用,其实只须要改一下文件名,分别处理 uWSGI 和 Nginx 的配置文件便可(Nginx 的配置,能够写在同一个文件中,写两个 server 就好了)
启动命令:
$ sudo nginx
或
$ sudo /usr/sbin/nginx
中止 nginx
$ sudo nginx -s stop
平滑启动 nginx
sudo nginx -s reload
所谓平滑启动就是在不中止 nginx 的状况下,重启 nginx,从新加载配置文件,用新的工做进程代替旧的工做进程。
曾经玩过 PHP,相比于 PHP 的几乎一键式部署,Python 的部署确实要繁琐不少,但 Python 的强大之处在于语言简洁优雅,毕竟人生苦短,有得便有失,不过我相信这个繁琐是暂时的。
最后给出的一个简单的示例,实际上是不够规范的,好比应用文件应该放在 /var/www/ 下,log 文件应该放到系统的 log 文件夹下等等,这个只是简单示例,更多配置内容,咱们应该经过 uWSGI、Nginx 的文档学习。