再次部署Flask app

Author Avatar
patrickcty 3月 02, 2017

再次部署Flask app

之前成功用Nginx+uWSGI部署了Flask app,但是之后想升级内容然而uWSGI出现奇怪的错误一直没办法解决。拖了好久之后还是决定再次重装系统重头搞一遍,顺便再加深一下相关的知识。

配置MySQL

MySQL在安装后一般是没有密码的,我用的这台主机已经装好了MySQL,但是密码是一个随机生成的,所以也需要再次配置。

参考文章

默认的账户在/etc/mysql/debian.cnf中,登录就用里面的账号和密码来登录。因为里面账号和密码都是明文的,所以要用root来查看。

接下来就是修改root的密码了

use mysql;
set password for 'root'@'localhost' = password('yourpasswprd');

搞定之后就创建相应的数据库

create database jnugeek default character set utf8;

后面的半句是为了避免中文乱码

顺便再把MySQL的Python驱动装了吧,当初这里也是一个大坑

sudo apt-get install libmysqlclient-dev
sudo pip3 install mysqlclient

OK,这样数据库的内容就搞定了。然后大型的项目的话MySQL还是比较靠谱的选择,SQLite的话可能有点问题,于是就选择MySQL了。

把代码上传到服务器上

还是用git比较方便。

有的服务器没装git,那就先装一个git

sudo apt-get install git

然后用git clone把代码拷贝到本地,注意服务器端没有公钥私钥,所以拷贝的时候用https,直接输入账号和密码。

配置好代码运行的环境

virtualenv

Python的虚拟环境,可以隔离开各个项目,使得同一台服务器同时运行多个版本的Python应用成为可能

sudo apt-get install python3-pip
sudo pip3 install virtualenv

安装好了之后初始化,进入虚拟环境
在项目根目录中运行

virtualenv jnugeek
source jnugeek/bin/activate

这样就建立并进入了一个叫jnugeek的虚拟环境

需要注意的就是如果激活了虚拟环境,那么下载模块的时候就不需要root权限了

安装依赖

pip install -r requirements.txt

项目中一般都有requirements.txt这个用来标志所需模块的文件,当然在本地创建这个文件也是很方便的事

pip freeze > requirements.txt

初始化应用

先把那几个环境变量设置了
在~/.bashrc中

export MAIL_ACCOUNT = youraccount
...

然后再把数据库的表建了

./entry.py db init
./entry.py db migrate
./entry.py db upgrade  // 不要忘...不然就连表都没建立...

到这里没有什么问题的话就可以跑起来了,当然虽然flask自带有web服务器,但是Flask毕竟是一个Web框架,自带的服务器只是为了方便调试,放在生产环境的话就要用更高效的选择了。而用C写的uWSGI无疑是一个很好的选择。

uWSGI配置

WSGI的话我还不是很理解,不过这里的uWSGI是一个高性能http服务器,用来和Python程序交换。

安装

pip install uwsgi

配置
在项目根目录创建config.ini的uWSGI的配置文件

[uwsgi]
master = true
home = venv
wsgi-file = entry.py
callable = app  
socket = :5000
processes = 4
threads = 2
buffer-size = 32768

其中callable就是Flask的实例,wsgi-file就是启动的脚本文件

配置好了之后使用uwsgi config.ini来运行

下面是一个成功的例子:

*** Starting uWSGI 2.0.13.1 (64bit) on [Sun Oct  2 15:44:11 2016] ***
compiled with version: 4.8.4 on 01 October 2016 16:15:02
os: Linux-4.4.0-38-generic #57~14.04.1-Ubuntu SMP Tue Sep 6 17:20:43 UTC 2016
nodename: ubuntu-512mb-sgp1-01
machine: x86_64
clock source: unix
detected number of CPU cores: 1
current working directory: /home/david/myapp
detected binary path: /home/david/myappenv/bin/uwsgi
!!! no internal routing support, rebuild with pcre support !!!
*** WARNING: you are running uWSGI without its master process manager ***
your processes number limit is 1824
your memory page size is 4096 bytes
detected max file descriptor number: 1024
lock engine: pthread robust mutexes
thunder lock: disabled (you can enable it with --thunder-lock)
uwsgi socket 0 bound to TCP address 128.199.97.37:8080 fd 3
Python version: 2.7.6 (default, Jun 22 2015, 18:01:27)  [GCC 4.8.2]
*** Python threads support is disabled. You can enable it with --enable-threads ***
Python main interpreter initialized at 0x16e99b0
your server socket listen backlog is limited to 100 connections
your mercy for graceful operations on workers is 60 seconds
mapped 72760 bytes (71 KB) for 1 cores
*** Operational MODE: single process ***
WSGI app 0 (mountpoint='') ready in 0 seconds on interpreter 0x16e99b0 pid: 11596 (default app)
*** uWSGI is running in multiple interpreter mode ***
spawned uWSGI worker 1 (and the only) (pid: 11596, cores: 1)

如果没有报错的话就是可以的,但是会有一些比较坑爹的错误

!!! no internal routing support, rebuild with pcre support !!!

这个是因为pcre没弄好,一般去掉缓存重装就可以了,-I的作用是重装

pip install uwsgi -I --no-cache-dir

callable not found

这个可能是因为没有导入callable

但是!!!只配置好了uWSGI是不行的,uWSGI只负责后端,还需要一个前端的http服务器,详情看这里

摘录一段

uwsgi 实际上也是一个 http 服务器,只不过它只面向 python 网络应用程序。虽然 uwsgi 也是 http 服务器,但是却不能直接使用它部署 python web 应用程序,否则会出错。

在本文中,uwsgi 所扮演的的角色是后端 http 服务器,nginx 扮演的角色是前端 http 服务器,hello.py 是客户端应用程序。 用户从网页浏览器中发出请求,nginx 服务器收到请求后,会通过它的 uwsgi 模块将用户的请求转发给 uwsgi 服务器,uwsgi 服务器处理完毕后将结果返回给 nginx,浏览器将最终的结果展现给用户。

uWSGI好是好,但是要是它万一断了,或者出问题了怎么办?要是有这样的一个程序可以自动监控运行uWSGI那岂不是美滋滋,而supervisor就是这样的程序。

supervisor配置

安装

sudo apt-get install supervisor

在/etc/supervisor/conf.d/下建立一个配置文件sp.conf

[program:jnugeek] 
# 启动命令入口 
command=/home/ubuntu/JNUGeek/jnugeek/bin/uwsgi /home/ubuntu/JNUGeek/config.ini            
# 命令程序所在目录 
directory=/ubuntu/JNUGeek
# 运行命令的用户名 
user=ubuntu
autostart=true
autorestart=true
# 日志地址 这个还是很有用的,可以查看出现的错误
stdout_logfile=/home/ubuntu/JNUGeek/logs/uwsgi_supervisor.log

启动/重启/查看状态命令

sudo service supervisor start/restart/stats

接下来就是Nginx了

Nginx配置

这部分配置坑也比较多,待会一一道来。

安装

sudo apt-get install nginx

直接修改配置文件/etc/nginx/sites-available/default

server { 
  listen 80; 
  listen [::]:80 ipv6only=on default_server;
  server_name X.X.X.X; # 公网地址 
  rewrite ^(.*)$  https://$host$1 permanent;  # 把http请求强制转换到https
}

# 配置https
server {
    # ssl参数
    listen              443;
    server_name         example.com;
    ssl on;
    # 证书文件,这个和下面的私钥要自己下载放在服务器里面
    ssl_certificate     example.com.crt;
    # 私钥文件
    ssl_certificate_key example.com.key;
    ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers         HIGH:!aNULL:!MD5;
    # 项目相关的内容
    location / { 
    include uwsgi_params;
    uwsgi_pass 127.0.0.1:5000; # 指向uwsgi 所应用的内部地址,所有请求将转发给uwsgi 处理 
    uwsgi_param UWSGI_PYHOME /home/ubuntu/JNUGeek/jnugeek; # 指向虚拟环境目录 
    uwsgi_param UWSGI_CHDIR /home/ubuntu/JNUGeek; # 指向网站根目录 
    uwsgi_param UWSGI_SCRIPT entry:app; # 指定启动程序
    uwsgi_read_timeout 100; 
    }  
}

上面的需要注意的是,http请求默认是到80端口的,然而既然已经有了https当然首选https,rewrite就是把http的请求导向了https。Nginx配置https参考文章,注意对于不同的端口,是可以写到一起的,也可以不写到一起,当然我这里是因为要把http重定向到https,如果写在一起就没办法实现了,贸然加入rewrite只会导致无限重定向(说起来都是眼泪)。强制转换的参考文章

必须要有的参数就是:

  • listen 监听的端口
  • server_name 主机名,域名

可选的参数有:
location 里面包含了项目的信息,这里是直接把uWSGI给包含进去了,让前后端的服务器进行了无缝对接。

最后,server可以有多个,也可以相互独立,必须要有某些参数,不然监听什么呢。

写完了当然要测试一下是不是正确的了

sudo nginx -t

在哪里查看错误日志呢?

/var/log/nginx/error.log

其中这几天遇到了

2015/12/29 12:33:37 [emerg] 24489#0: bind() to 0.0.0.0:80 failed (98: Address already in use)
2015/12/29 12:33:37 [emerg] 24489#0: still could not bind()

这里有一个坑就是nginx先监听了ipv4的80端口之后又监听了ipv6的80端口,于是就重复占用了。更加坑人的是你去看了端口占用它又把80端口释放了。来源在这里

解决方法就是
修改listen [::]:80 default_server;

listen [::]:80 ipv6only=on default_server;

然而这样我的Nginx还是fail,结果重启主机解决问题…

上面的都搞定了之后就可以愉快的启动服务来查看自己的app了

sudo service supervisor start
sudo service nginx restart

参考文章

最后

到此,部署成功,https也搞定了,然而不知道为什么CSS貌似没加载出来…没想到最后败在了CSS上…卒。