关于Django应用部署
Django是一个高效、多功能和动态地进化的Web应用开发框架。目前比较流行的部署、运行Django应用方式是基于Apache的mod_wsgi模块,但更加高效、弹性,同时又更加复杂的方式是使用以下工具来部署实施:Nginx、Gunicorn、virtualenv、supervisord、Postgresql。以下详细介绍如何结合这些工具来部署Django应用到Linux上。
准备工作
需要有一台拥有root权限的Linux服务器,这是部署应用的平台。本文采用CentOS,域名直接使用localhost,ip地址为192.168.56.1。
使用yum在线升级机制来更新系统:
yum update -y
PostgreSQL
使用PostgreSQL来作为Django应用的后台数据库,在CentOS上安装:
yum install postgresql-contrib postgresql postgresql-server postgresql-devel
创建应用的数据库用户django和数据库hello_django:
su - postgres
createuser -P
Enter name of role to add: django
Enter password for new role:
Enter it again:
Shall the new role be a superuser? (y/n) n
Shall the new role be allowed to create databases? (y/n) n
Shall the new role be allowed to create more new roles (y/n) n
createdb --owner django hello_django
logout
为应用添加系统用户
为Django应用创建专门的系统用户,并加入django_webapps群组:
groupadd django_webapps
useradd -g django_webapps -d /django_webapps/hello_django django
创建应用的Python虚拟环境
创建django_webapps目录来保存web apps,创建hello_django目录存储应用,并修改应用目录的拥有者:
mkdir -p /django_webapps/hello_django/
chown django /django_webapps/hello_django/
在应用目录创建虚拟环境,以django用户来创建:
su - django
cd /django_webapps/hello_django/
virtualenv .
激活虚拟环境:
source bin /activate
安装django:
pip install django
创建一个空的django项目,项目名为hello:
django-admin.py startproject hello
运行django自带的服务器来测试:
cd hello
python manage.py runserver localhost:8000
Validating models...
0 errors found
June 14, 2014 - 03:28:01
Django version 1.6.5, using settings 'hello.settings'
Starting development server at http://localhost:8000/
Quit the server with CONTROL-C.
登录http://localhost:8000 ,可查看到django的欢迎页面。
允许其他用户对应用目录写使能
应用是以django用户来运行的,该用户具有应用目录的所有权限。如果想让其他的常规用户也能够修改应用文件,那么可以设置群组users为该目录的群拥有者,并赋予users写权限:
chown -R django:users /django_webapps/hello_django
chmod -R g+w /django_webapps/hello_django
将django加入users群组中,并检查:
usermod -a -G users `whoami`
id
uid=507(django) gid=510(django_webapps) groups=510(django_webapps),100(users)
配置PostgreSQL
为了在Django中使用PostgrelSQL,需要安装psycopg2。在安装psycopg2之前需要安装python-devel,以保证能构建python模块:
yum install python-devel
在虚拟环境中使用pip安装psycopg2数据库适应器:
pip install psycopg2
修改settings.py文件来配置数据库:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'hello_django',
'USER': 'django',
'PASSWORD': '131415', #此处最好使用密钥,可通过md5来加密
'HOST': 'localhost',
'PORT': '', #set to empty string for default
}
}
最后构建Django的初始化数据库:
python manage.py syncdb
注解:构建过程中会出现error:IDENT authentication failed for user “django”
出现这个问题是因为PostgreSQL的Client Authentication 使用了ident authentication。
通过修改pg_hba.conf可以解决,编辑/var/lib/pgsql/data/pg_hba.conf,修改为:
# TYPE DATABASE USER CIDR-ADDRESS METHOD
# "local" is for Unix domain socket connections only
local all all trust
# IPv4 local connections:
host all all 127.0.0.1/32 trust
# IPv6 local connections:
host all all ::1/128 trust
Gunicorn
在生产环境中,我们将不会使用Django自带的单线程的开发服务器,而是使用专门的Python WSGI 应用服务器――gunicorn,在本博的另一篇文章中有对gunicorn的详细介绍:Gunicorn
首先在你的应用的虚拟环境中安装gunicorn:
pip install gunicorn
安装成功后可以测试下gunicorn是否可以运行Django 应用,在项目hello目录下运行以下命令:
(hello_django)[django@rango hello]$ gunicorn hello.wsgi:application --bind localhost:8001
通过访问http://localhost:8001 ,可以访问到你的Gunicorn服务器。此处特意将端口从8000改为8001是为了迫使浏览器建立一个新的连接。
Gunicorn现在已经准备好运行你的app,现在可以配置一些选项来使得Gunicorn更加有用。此处通过编写一个shell脚本来设置一些参数,脚本保存为/django_webapps/hello_django/bin/gunicorn_start:
#!/bin/bash
NAME="hello_app" # Name of the application
DJANGODIR=/django_webapps/hello_django/hello #Django project directory
SOCKFILE=/django_webapps/hello_django/run/gunicorn.sock #unix socket fro communication
USER=django # the user to run as
GROUP=django_webapps # the group to run as
NUM_WORKERS=5 # how many worker processes should Gunicorn spawn
DJANGO_SETTINGS_MODULE=hello.settings # which settings file should Django use
DJANGO_WSGI_MODULE=hello.wsgi # WSGI module name
echo "Starting $NAME as `whoami`"
# Activate the virtual environment
cd $DJANGODIR
source ../bin/activate
export DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE
export PYTHONPATH=$DJANGODIR:$PYTHONPATH
# Create the run directory if it doesn't exist
RUNDIR=$(dirname $SOCKFILE)
test -d $RUNDIR || mkdir -p $RUNDIR
# Start your Django Unicorn
# Programs meant to be run under supervisor should not daemonize themselves (do not use --daemon)
exec ../bin/gunicorn ${DJANGO_WSGI_MODULE}:application \
--name $NAME \
--workers $NUM_WORKERS \
--user=$USER --group=$GROUP \
--log-level=debug \
--bind=unix:$SOCKFILE
为该脚本增加可执行权限:
chmod u+x bin/gunicorn_start
以用户django的身份来运行这个脚本:
su - django
bin/gunicorn_start
Starting hello_app as django
2014-06-15 15:17:20 [18037] [INFO] Starting gunicorn 18.0
2014-06-15 15:17:20 [18037] [DEBUG] Arbiter booted
2014-06-15 15:17:20 [18037] [INFO] Listening at: unix:/django_webapps/hello_django/run/gunicorn.sock (18037)
2014-06-15 15:17:20 [18037] [INFO] Using worker: sync
2014-06-15 15:17:20 [18048] [INFO] Booting worker with pid: 18048
2014-06-15 15:17:20 [18049] [INFO] Booting worker with pid: 18049
2014-06-15 15:17:20 [18050] [INFO] Booting worker with pid: 18050
2014-06-15 15:17:20 [18051] [INFO] Booting worker with pid: 18051
2014-06-15 15:17:20 [18052] [INFO] Booting worker with pid: 18052
^C
2014-06-15 15:17:51 [18052] [INFO] Worker exiting (pid: 18052)
2014-06-15 15:17:51 [18051] [INFO] Worker exiting (pid: 18051)
2014-06-15 15:17:51 [18037] [INFO] Handling signal: int
2014-06-15 15:17:51 [18048] [INFO] Worker exiting (pid: 18048)
2014-06-15 15:17:51 [18049] [INFO] Worker exiting (pid: 18049)
2014-06-15 15:17:51 [18050] [INFO] Worker exiting (pid: 18050)
2014-06-15 15:17:51 [18037] [INFO] Shutting down: Master
注解:需要按照你自己的设置来修改脚本的路径和文件名。
--workers的设置一般是按照2*CPUS+1,我的cpu为双核,所以设置为5.
--name(NAME)为你的应用在某些程序(top,ps)中的标识,默认为gunicorn,此处设置为hello_app以便与其他gunicorn应用区别开来。
为了能够设置--name,需要安装一个名为setproctitle的Python模块:pip install setproctitle
通过ps可查看到哪个gunicorn属于哪个应用:
ps aux | grep hello_app
django 15226 0.0 0.2 15184 8068 ? S Jun13 0:25 gunicorn: master [hello_app]
django 15237 0.0 0.4 27636 16556 ? S Jun13 0:01 gunicorn: worker [hello_app]
django 15238 0.0 0.4 27640 16592 ? S Jun13 0:02 gunicorn: worker [hello_app]
django 15239 0.0 0.4 27644 16600 ? S Jun13 0:01 gunicorn: worker [hello_app]
django 15240 0.0 0.4 27656 16624 ? S Jun13 0:01 gunicorn: worker [hello_app]
django 15242 0.0 0.2 19276 10820 ? S Jun13 0:01 gunicorn: worker [hello_app]
Supervisord
通过上述操作,gunicorn_start脚本已经可以工作了。为了确保脚本能够自动跟随系统启动和不期望的原因退出时能够重启,我们将会使用supervisord来完成这些工作。
安装supervisord
yum install supervisord
配置hello应用:vim /etc/supervisord.conf
[program:hello]
command=/django_webapps/hello_django/bin/gunicorn_start ;command to start app
user=django ;user to run as
stdout_logfile=/django_webapps/hello_django/logs/gunicorn_supervisor.log ;path to write log messages
redirect_stderr=true ;save stderr in the same log[program:hello]
command=/django_webapps/hello_django/bin/gunicorn_start ;command to start app
user=django ;user to run as
stdout_logfile=/django_webapps/hello_django/logs/gunicorn_supervisor.log ;path to write log messages
redirect_stderr=true ;save stderr in the same log
创建相应的文件来存储应用的日志信息:
mkdir -p /django_webapps/hello_django/logs/
touch /django_webapps/hello_django/logs/gunicorn_supervisord.log
保存好配置文件后让supervisord重载和更新,从而加入刚刚注册的应用hello:
supervisorctl reread
hello: available
supervisorctr update
hello:added process group
检查hello应用的status以及启动、停止或重启它:
supervisorctl status hello
hello RUNNING pid 15226, uptime 1 day, 23:26:29
supervisorctl stop hello
hello: stopped
supervisorctl start hello
hello: started
supervisorctl restart hello
hello: stopped
hello: started
通过上述设置,你的应用将会跟随系统自动重启以及因某些原因崩溃后的自动重启。
Nginx
接下来设置Nginx作为我们的应用的反向代理服务器。以下是关于Nginx正向代理和反向代理的一些知识补充:
正向代理,也就是传说中的代理,他的工作原理就像一个跳板,简单的说,我是一个用户,我访问不了某网站,但是我能访问一个代理服务器,这个代理服务器呢,他 能访问那个我不能访问的网站,于是我先连上代理服务器,告诉他我需要那个无法访问网站的内容,代理服务器去取回来,然后返回给我。从网站的角度,只在代理 服务器来取内容的时候有一次记录,有时候并不知道是用户的请求,也隐藏了用户的资料,这取决于代理告不告诉网站。
结论就是,正向代理 是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内 容返回给客户端。客户端必须要进行一些特别的设置才能使用正向代理。
反向代理,举例说明:用户访问 http://www.test.com/readme,但www.test.com上并不存在readme页面,他是偷偷从另外一台服务器上取回来,然后作为自己的内容返回用户,但用户并不知情。这里所提到的 www.test.com 这个域名对应的服务器就设置了反向代理功能。
结论就是,反向代理正好相反,对于客户端而言它就像是原始服务器,并且客户端不需要进行任何特别的设置。客户端向反向代理的命名空间(name- space)中的内容发送普通请求,接着反向代理将判断向何处(原始服务器)转交请求,并将获得的内容返回给客户端,就像这些内容原本就是它自己的一样。
两者区别:正向代理的典型用途是为在防火墙内的局域网客户端提供访问Internet的途径。正向代理还可以使用缓冲特性减少网络使用率。反向代理的典型用途是将防火 墙后面的服务器提供给Internet用户访问。反向代理还可以为后端的多台服务器提供负载平衡,或为后端较慢的服务器提供缓冲服务。另外,反向代理还可 以启用高级URL策略和管理技术,从而使处于不同web服务器系统的web页面同时存在于同一个URL空间下。
安装nginx:
yum install nginx
启动nginx:
service nginx start
通过登录http://localhost可以访问到nginx的欢迎页面
ps:如果系统中也安装了并启动了监听在80端口的apache,需要事先关闭apache服务,不然nginx无法启动。
创建Django的Nginx虚拟服务器:
在Nginx的主配置文件中创建一个新的nginx虚拟服务器配置:cat /etc/nginx/nginx.conf
user root;
worker_processes 1;
error_log /var/log/nginx/error.log;
#error_log /var/log/nginx/error.log notice;
#error_log /var/log/nginx/error.log info;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
# Nginx virtual server configuration for Django
upstream hello_app_server {
# fail_timeout=0 means we always retry an upstream even if it failed
# to return a good HTTP response (in case the Unicorn master nukes a
# single worker for timing out).
server unix:/django_webapps/hello_django/run/gunicorn.sock fail_timeout=0;
}
server {
listen 80;
server_name localhost;
client_max_body_size 4G;
access_log /django_webapps/hello_django/logs/nginx-access.log;
error_log /django_webapps/hello_django/logs/nginx-error.log;
location /static/ {
alias /django_webapps/hello_django/static/;
}
location /media/ {
alias /django_webapps/hello_django/media/;
}
location / {
# an HTTP header important enough to have its own Wikipedia entry:
# http://en.wikipedia.org/wiki/X-Forwarded-For
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# enable this if and only if you use HTTPS, this helps Rack
# set the proper protocol for doing redirects:
# proxy_set_header X-Forwarded-Proto https;
# pass the Host: header from the client right along so redirects
# can be set properly within the Rack application
proxy_set_header Host $http_host;
# we don't want nginx trying to do something clever with
# redirects, we set the Host: header above already.
proxy_redirect off;
# set "proxy_buffering off" *only* for Rainbows! when doing
# Comet/long-poll stuff. It's also safe to set if you're
# using only serving fast clients with Unicorn + nginx.
# Otherwise you _want_ nginx to buffer responses to slow
# clients, really.
# proxy_buffering off;
# Try to serve static files from nginx, no point in making an
# *application* server like Unicorn/Rainbows! serve static files.
if (!-f $request_filename) {
proxy_pass http://hello_app_server;
break;
}
}
# Error pages
error_page 500 502 503 504 /500.html;
location = /500.html {
root /django_webapps/hello_django/static/;
}
}
# Load config files from the /etc/nginx/conf.d directory
# The default server is in conf.d/default.conf
include /etc/nginx/conf.d/*.conf;
}
注解:
此处将user设置为超级用户root是因为nginx及其他用户没有对django项目所在目录的任何权限,如果不设置为root,登录http://localhost会显示403 forbidden错误。或者也可以添加nginx用户对应用目录的读权限。
upstream表示nginx代理的上游服务器其实是gunicorn。
重启nginx:
service nginx restart
登录http://localhost可以看到由Nginx和Gunicorn支持的Django欢迎页面。现在可以着手构建你自己的项目和应用,enjoy!!
卸载Django应用
通过以下步骤可以卸载掉不需要的应用:
首先修改nginx配置文件来去除虚拟服务器的设置,然后重启:
service nginx restart
使用supervisord来停止应用,并删除supervisord配置文件中对应的应用配置信息:
supervisorctl stop hello
如果再也不想使用这个应用,可以考虑删除掉整个应用文件所在目录:
rm -rf /django_webapps/hello_django/
运行多个Django应用
在同一个Nginx服务器上可以运行多个Django应用,通过不同的域名设置可以登录到不同的url来实现对多个django应用的访问。每一个应用将会设置自己的Python虚拟开发环境,创建专门的运行用户和对应的权限设置、数据库用户和数据库等。
依据前文构建hello项目的步骤可以构建一个jay项目,包括数据库的创建、系统用户的设置、supervisord的设置等等。现在最重要的是Nginx虚拟服务器的设置了:
与前文设置hello_app_server类似,可在nginx配置文件中增加jay_app_server虚拟服务器的设置:
upstream jay_app_server {
# fail_timeout=0 means we always retry an upstream even if it failed
# to return a good HTTP response (in case the Unicorn master nukes a
# single worker for timing out).
server unix:/django_webapps/jay_django/run/gunicorn.sock fail_timeout=0;
}
server {
listen 80;
server_name jaycn.sysu;
client_max_body_size 4G;
access_log /django_webapps/jay_django/logs/nginx-access.log;
error_log /django_webapps/jay_django/logs/nginx-error.log;
location /static/ {
alias /django_webapps/jay_django/static/;
}
location /media/ {
alias /django_webapps/jay_django/media/;
}
location / {
# an HTTP header important enough to have its own Wikipedia entry:
# http://en.wikipedia.org/wiki/X-Forwarded-For
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# enable this if and only if you use HTTPS, this helps Rack
# set the proper protocol for doing redirects:
# proxy_set_header X-Forwarded-Proto https;
# pass the Host: header from the client right along so redirects
# can be set properly within the Rack application
proxy_set_header Host $http_host;
# we don't want nginx trying to do something clever with
# redirects, we set the Host: header above already.
proxy_redirect off;
# set "proxy_buffering off" *only* for Rainbows! when doing
# Comet/long-poll stuff. It's also safe to set if you're
# using only serving fast clients with Unicorn + nginx.
# Otherwise you _want_ nginx to buffer responses to slow
# clients, really.
# proxy_buffering off;
# Try to serve static files from nginx, no point in making an
# *application* server like Unicorn/Rainbows! serve static files.
if (!-f $request_filename) {
proxy_pass http://jay_app_server;
break;
}
}
# Error pages
error_page 500 502 503 504 /500.html;
location = /500.html {
root /django_webapps/jay_django/static/;
}
}
可以修改hello_app_server服务器的server_name为hello.sysu
保存配置文件并重启nginx:
service nginx restart
最后设置好域名,以便DNS服务器能够解析刚才设置好的两个域名:vim /etc/hosts
192.168.56.1 hello.sysu jaycn.sysu
通过登录每个域名来测试服务器上的app是否设置正确:
http://hello.sysu
http://jaycn.sysu
――游响云停