Django部署到线上

前言

《Django开发简单Blog系统》系列中,我们已经完成了一个迷你Web项目。那么,怎么把这个项目发布到线上呢?怎样给它一个域名呢?

思路:nginx + uwsgi

环境准备

服务器

阿里云服务器,centos7系统。

python

升级python到3.6.1,统一线上和本地python环境。

1、下载python3.6.1源码
wget https://www.python.org/ftp/python/3.6.1/Python-3.6.1.tar.xz

2、解压源码

xz -d Python-3.6.1.tar.xz
tar -xvf Python-3.6.1.tar

3、编译源码

mkdir /usr/local/python3
cd Python-3.6.1
./configure --prefix=/usr/local/python3 --enable-optimizations
make && make install

如果编译失败,需要先更新编译环境:

gcc -v 
g++ -v

yum install gcc
yum install gcc-c++

注:我的环境版本为 gcc version 4.8.5 20150623 (Red Hat 4.8.5-11) (GCC) 。

4、替换python

cd /usr/bin
mv python python.bak
ln -s /usr/local/python3/bin/python3.6 /usr/bin/python
ll python*
python -V

5、解决遗留问题
所有python相关的应用,如果使用/usr/bin/python开头的脚本,替换为/usr/bin/python2.7。比如:

vim /usr/bin/yum
vim /usr/libexec/urlgrabber-ext-down

uwsgi

pip install uwsgi

编写测试:

# test.py
def application(env, start_response):
    start_response('200 OK', [('Content-Type','text/html')])
    return [b"Hello World"]

启动测试:
uwsgi --http :8001 --wsgi-file test.py

报错:uwsgi: command not found,看来我们需要把python3/bin加入到path。
vim /etc/profile,在文件最底部找到PATH,添加:

:/usr/local/python3/bin

使配置生效:source /etc/profile

访问 http://ip:8001 ,即可看到Hello World 。

nginx和mysql

参考《在CentOS7上配置PHP运行环境》,安装好了nginx和mysql。

项目部署

代码准备

1、克隆项目到服务器
git clone https://github.com/voidking/djsite.git

2、安装django
pip install django

3、安装pymysql
pip install pymysql

数据库准备

1、创建数据库

# mysql -uroot -p
mysql> create database `djsite` default character set utf8 collate utf8_general_ci; 
mysql> exit;

2、修改djsite/djsite/settings.py中的数据库配置
vim djsite/djsite/settings.py

3、创建表结构

python manage.py makemigrations
python manage.py migrate

报错:

django.db.utils.InternalError: (1665, 'Cannot execute statement: impossible to write to binary log since BINLOG_FORMAT = STATEMENT and at least one table uses a storage engine limited to row-based logging. InnoDB is limited to row-logging when transaction isolation level is READ COMMITTED or READ UNCOMMITTED.')

修改mysql的binlog格式为混合模式:

# mysql -uroot -p
mysql> set global binlog_format=mixed;

删除数据库djsite中的所有表,然后再次执行:

python manage.py migrate

启动项目

数据库问题

cd djsite
python manage.py runserver

报错:

File "/usr/local/python3/lib/python3.6/site-packages/django/db/backends/mysql/base.py", line 36, in <module>
    raise ImproperlyConfigured("mysqlclient 1.3.3 or newer is required; you have %s" % Database.__version__)
django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.3 or newer is required; you have 0.7.11.None

解决办法:

vim /usr/local/python3/lib/python3.6/site-packages/django/db/backends/mysql/base.py

进入vim命令模式,输入/version,按N查找下一个,找到:

if version < (1, 3, 3):
    raise ImproperlyConfigured("mysqlclient 1.3.3 or newer is required; you have %s" % Database.__version__)

注释掉它,问题解决。

url问题

cd djsite
python manage.py runserver

再次报错:

File "/root/djsite/djsite/urls.py", line 21, in <module>
    url(r'^blog/', include('blog.urls', namespace='blog')),
  File "/usr/local/python3/lib/python3.6/site-packages/django/urls/conf.py", line 39, in include
    'Specifying a namespace in include() without providing an app_name '
django.core.exceptions.ImproperlyConfigured: Specifying a namespace in include() without providing an app_name is not supported. Set the app_name attribute in the included module, or pass a 2-tuple containing the list of patterns and app_name instead.

解决办法:

vim /usr/local/python3/lib/python3.6/site-packages/django/urls/conf.py

找到:

if namespace and not app_name:
    raise ImproperlyConfigured(
        'Specifying a namespace in include() without providing an app_name '
        'is not supported. Set the app_name attribute in the included '
        'module, or pass a 2-tuple containing the list of patterns and '
        'app_name instead.',
    )

注释掉它,问题解决。

查看效果

cd djsite
python manage.py runserver

启动成功,在服务器上测试访问:
curl localhost:8000/blog/index

使用浏览器查看 http://ip:8000/blog/index ,却无法访问。这是因为在settings.py中,ALLOWED_HOSTS的配置为:

ALLOWED_HOSTS = []

官方文档说:

When DEBUG is True and ALLOWED_HOSTS is empty, the host is validated against ['localhost', '127.0.0.1', '[::1]'].

修改ALLOWED_HOSTS的配置为:

ALLOWED_HOSTS = ['*']

然后启动命令改为:python manage.py runserver 0.0.0.0:8000,此时即可在浏览器看到部署好的项目。

如果还是不能访问,尝试先关闭防火墙:systemctl stop firewalld

nginx配置

1、首先,在万网上配置域名解析,添加A记录,解析到阿里云服务器IP。假设解析好的域名为django.voidking.com。

2、在nginx的vhost中,添加django.voidking.com.conf,内容为:

server {
    listen 80;
    server_name django.voidking.com;
    charset utf-8;
    location /{
        proxy_set_header   Host             $host;
        proxy_set_header   X-Real-IP        $remote_addr;
        proxy_set_header  X-Forwarded-For  $proxy_add_x_forwarded_for;
        client_max_body_size       1024m;
        client_body_buffer_size    128k;
        client_body_temp_path      data/client_body_temp;
        proxy_connect_timeout      90;
        proxy_send_timeout         90;
        proxy_read_timeout         90;
        proxy_buffer_size          4k;
        proxy_buffers              4 32k;
        proxy_busy_buffers_size    64k;
        proxy_temp_file_write_size 64k;
        proxy_temp_path            data/proxy_temp;

        proxy_pass http://127.0.0.1:8000;
    }
}

3、重启nginx,./nginx -s reload

4、测试访问
服务器:curl django.voidking.com/blog/index
本地浏览器:http://django.voidking.com/blog/index

至此,django项目已经部署成功,没有用到uwsgi。如果给django添加守护进程,那么我们的部署就接近完美了。那么,uwsgi又能干什么呢,我们继续研究。

uwsgi

一般启动

1、编写wsgi.py文件
编写django_wsgi.py文件,将其放在与文件manage.py同一个目录下。

#!/usr/bin/env python
# coding: utf-8

import os,django
from django.core.handlers.wsgi import WSGIHandler

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djsite.settings")
django.setup()
application = WSGIHandler()

2、启动项目
uwsgi --http :8000 --chdir ~/djsite/ --module django_wsgi

3、查看启动结果
lsof -i :8000ps aux | grep uwsgi

4、测试访问
http://ip:8000/blog/index
此时,页面是没有样式的,也就是说静态资源加载失败。

5、配置静态资源
uwsgi --http :8000 --chdir ~/djsite/ --module django_wsgi --static-map=/static=static
此时,页面样式就正常了。

高级启动

1、新建uwsgi.ini,与manage.py在同一级目录。

[uwsgi]
http = :8000
chdir = /root/djsite/
wsgi-file = django_wsgi.py
static-map = '/static=static'

2、启动uwsgi
uwsgi uwsgi.ini

3、测试访问
http://ip:8000/blog/index

supervisor

关闭shell后,uwsgi服务就很快关闭了。为了让它后台运行,需要让它变成守护进程。

安装supervisor

pip install supervisor

报错,因为supervisor不支持python3:
Supervisor requires Python 2.4 or later but does not work on any version of Python 3. You are using version 3.6.1 (default, Dec 6 2017, 12:03:59)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-11)]. Please install using a supported version.
Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-y9wv4fmm/supervisor/

安装pyenv

为了使用supervisor,我们需要python2.7的环境。而多版本python的管理,推荐使用pyenv。

1、安装pyenv套装
curl -L https://raw.githubusercontent.com/pyenv/pyenv-installer/master/bin/pyenv-installer | bash
内容除了包含 pyenv 以外,还包含如下插件:

  • pyenv-doctor
  • pyenv-installer
  • pyenv-update
  • pyenv-virtualenv
  • pyenv-which-ext

2、路径添加
vim ~/.bash_profile,添加:

export PATH="/root/.pyenv/bin:$PATH"
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"

3、使配置立即生效
source ~/.bash_profile

4、查看安装情况
pyenv -v

5、常用命令

  • 查看可安装的python版本列表:pyenv install -l
  • 安装指定版本的python:pyenv install 2.7.13
  • 查看已安装的python:pyenv versions
  • 查看当前设为默认的python版本:pyenv version

安装python2.7环境

1、配置pyenv下载源为本地目录(可选操作,不做的话下载速度会很慢)

mkdir /root/python/ && cd /root/python/

# 设置变量
export PYTHON_BUILD_CACHE_PATH=/root/python

# 设置变量
export PYTHON_BUILD_MIRROR_URL=/root/python

# 查看变量设置
env | grep PYTHON_BUILD_MIRROR_URL

wget https://www.python.org/ftp/python/2.7.13/Python-2.7.13.tar.xz

2、安装python2.7.13
pyenv install 2.7.13

3、改变全局版本
pyenv global 2.7.13python -V

附:改变回原版本
pyenv global systempython -V

4、刷新数据库
python rehash

安装虚拟环境

1、新建supervisor虚拟环境
pyenv virtualenv 2.7.13 supervisor

2、激活虚拟环境
source /root/.pyenv/versions/2.7.13/envs/supervisor/bin/activate supervisor
或者source activate supervisor

3、安装supervisor
yum install supervisor
pip install supervisor

4、生成配置文件

mkdir -p /etc/supervisor/
echo_supervisord_conf > /etc/supervisord.conf

5、修改配置文件
vim /etc/supervisord.conf,添加:

[include]
files = /etc/supervisor/*.conf

6、运行
/root/.pyenv/versions/2.7.13/envs/supervisor/bin/supervisord -c /etc/supervisord.conf

7、编辑supervisord.service
vi /usr/lib/systemd/system/supervisord.service,修改为:

[Unit]
Description=Process Monitoring and Control Daemon
After=rc-local.service nss-user-lookup.target

[Service]
Type=forking
ExecStart=/root/.pyenv/versions/2.7.13/envs/supervisor/bin/supervisord -c /etc/supervisord.conf  
ExecReload=/root/.pyenv/versions/2.7.13/envs/supervisor/bin/supervisorctl reload       
ExecStop=/root/.pyenv/versions/2.7.13/envs/supervisor/bin/supervisorctl shutdown      

[Install]
WantedBy=multi-user.target

8、重启supervisor

ps aux | grep supervisord
systemctl stop supervisord
systemctl start supervisord

9、开机启动
systemctl enable supervisord

守护uwsgi

1、在/etc/supervisor中新建djsite.conf文件:

[program:djsite]
command=/usr/local/python3/bin/uwsgi --http :8000 --chdir /root/djsite/ --module django_wsgi --static-map=/static=static
directory=/root/djsite/
startsecs=0
stopwaitsecs=0
autostart=true
autorestart=true

2、重启supervisor

systemctl stop supervisord
systemctl start supervisord

附:重启djsite命令

supervisorctl -c /etc/supervisord.conf restart djsite

3、测试访问
http://ip:8000/blog/index
页面显示正常,至此守护进程配置成功。

4、退出supervisor环境
source deactivate,守护进程并没有受到影响。

nginx+uwsgi

以上,我们的djsite项目已经通过uwsgi方式启动起来,并且可以保持后台运行。nginx配置不改变的情况下,我们可以正常访问 http://django.voidking.com/blog/index 。此时,nginx作为反向代理,和uwsgi间通过http交互。

接下来,就配置下nginx和uwsgi通过socket结合的方式。原理:用户发送http请求到nginx,nginx通过socket把请求交给uwsgi,uwsgi拿到django的处理结果,通过socket返还给nginx,nginx通过http返回结果给用户。

1、因为nginx和uwsgi通过socket方式交互,我们需要修改uwsgi.ini的配置为:

[uwsgi]
socket = :8000
chdir = /root/djsite/
wsgi-file = django_wsgi.py
static-map = '/static=static'
master = true
processes = 2
enable-threads = true
# daemonize = /root/djsite/uwsgi.log

2、/etc/supervisor/djsite.conf,修改为

[program:djsite]command=/usr/local/python3/bin/uwsgi uwsgi.ini
directory=/root/djsite/
startsecs=0
stopwaitsecs=0
autostart=true
autorestart=true

3、重启supervisor
systemctl stop supervisord
systemctl start supervisord

4、修改nginx配置djsite.voidking.com.conf:

server {
    listen      80;
    server_name djsite.voidking.com;
    charset     utf-8;

    location / {
        uwsgi_pass     127.0.0.1:8000;
        include        uwsgi_params;
    }

    location /static {
        alias /root/djsite/static;
    }
}

5、重启nginx
./nginx -s reload

6、测试访问
此时,访问 http://ip:8000/blog/index 失败,访问 http://django.voidking.com/blog/index 正常。因为8000端口不再提供http服务,而是一个和nginx连接的socket。

7、static
请问,此时的静态资源,是通过uwsgi获取的?还是通过nginx直接获取的?做一个测试即可,修改uwsgi为:

[uwsgi]
socket = :8000
chdir = /root/djsite/
wsgi-file = django_wsgi.py
# static-map = '/static=static'
master = true
processes = 2
enable-threads = true
# daemonize = /root/djsite/uwsgi.log

此时,uwsgi不再提供静态资源。重启supervisor,页面样式正常,可见,静态资源是通过nginx获取的。之所以可以获取到,是因为我们之前在djsite/settings.py中配置了:

STATICFILES_DIRS = (
    os.path.join(BASE_DIR, "static"),
)

小结

至此,django部署完毕,我们实现了三种部署方法:

  • nginx + django(http方式)
  • nginx + uwsgi(http方式)
  • nginx + uwsgi(socket方式)

在此过程中,解决了一些奇怪的bug,学习了升级python的方法,学习了使用pyenv安装多版本python的方法(类似的还有anaconda),学习了给django或者uwsgi添加守护进程的方法,收获颇丰。

书签

Python Web部署方式总结

Python网络框架——Web服务器

Django在生产环境中的部署

Django 部署(Nginx)

使用Supervisor管理SpiderKeeper和Scrapyd

使用uWSGI提供静态文件 (更新至1.9)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容