關於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是否設置正確:
——遊響雲停