這篇文章主要介紹了Python部署web app的詳細(xì)教程,示例代碼基于Python2.x版本,需要的朋友可以參考下
作為一個合格的開發(fā)者,在本地環(huán)境下完成開發(fā)還遠(yuǎn)遠(yuǎn)不夠,我們需要把Web App部署到遠(yuǎn)程服務(wù)器上,這樣,廣大用戶才能訪問到網(wǎng)站。
很多做開發(fā)的同學(xué)把部署這件事情看成是運維同學(xué)的工作,這種看法是完全錯誤的。首先,最近流行DevOps理念,就是說,開發(fā)和運維要變成一個整體。其次,運維的難度,其實跟開發(fā)質(zhì)量有很大的關(guān)系。代碼寫得垃圾,運維再好也架不住天天掛掉。最后,DevOps理念需要把運維、監(jiān)控等功能融入到開發(fā)中。你想服務(wù)器升級時不中斷用戶服務(wù)?那就得在開發(fā)時考慮到這一點。
下面,我們就來把awesome-python-webapp部署到Linux服務(wù)器。
搭建Linux服務(wù)器
要部署到Linux,首先得有一臺Linux服務(wù)器。要在公網(wǎng)上體驗的同學(xué),可以在Amazon的AWS申請一臺EC2虛擬機(免費使用1年),或者使用國內(nèi)的一些云服務(wù)器,一般都提供Ubuntu Server的鏡像。想在本地部署的同學(xué),請安裝虛擬機,推薦使用VirtualBox。
我們選擇的Linux服務(wù)器版本是Ubuntu Server 12.04 LTS,原因是apt太簡單了。如果你準(zhǔn)備使用其他Linux版本,也沒有問題。
Linux安裝完成后,請確保ssh服務(wù)正在運行,否則,需要通過apt安裝:
$ sudo apt-get install openssh-server
有了ssh服務(wù),就可以從本地連接到服務(wù)器上。建議把公鑰復(fù)制到服務(wù)器端用戶的.ssh/authorized_keys中,這樣,就可以通過證書實現(xiàn)無密碼連接。
部署方式
在本地開發(fā)時,我們可以用Python自帶的WSGI服務(wù)器,但是,在服務(wù)器上,顯然不能用自帶的這個開發(fā)版服務(wù)器。可以選擇的WSGI服務(wù)器很多,我們選gunicorn:它用類似Nginx的Master-Worker模式,同時可以提供gevent的支持,不用修改代碼,就能獲得極高的性能。
此外,我們還需要一個高性能Web服務(wù)器,這里選擇Nginx,它可以處理靜態(tài)資源,同時作為反向代理把動態(tài)請求交給gunicorn處理。gunicorn負(fù)責(zé)調(diào)用我們的Python代碼,這個模型如下:
2015430121016418.png (487×128)
Nginx負(fù)責(zé)分發(fā)請求:
2015430121039437.png (457×265)
在服務(wù)器端,我們需要定義好部署的目錄結(jié)構(gòu):
代碼如下:
/
+- srv/
+- awesome/ <-- Web App根目錄
+- www/ <-- 存放Python源碼
| +- static/ <-- 存放靜態(tài)資源文件
+- log/ <-- 存放log
在服務(wù)器上部署,要考慮到新版本如果運行不正常,需要回退到舊版本時怎么辦。每次用新的代碼覆蓋掉舊的文件是不行的,需要一個類似版本控制的機制。由于Linux系統(tǒng)提供了軟鏈接功能,所以,我們把www作為一個軟鏈接,它指向哪個目錄,哪個目錄就是當(dāng)前運行的版本:
2015430121102755.png (640×522)
而Nginx和gunicorn的配置文件只需要指向www目錄即可。
Nginx可以作為服務(wù)進(jìn)程直接啟動,但gunicorn還不行,所以,Supervisor登場!Supervisor是一個管理進(jìn)程的工具,可以隨系統(tǒng)啟動而啟動服務(wù),它還時刻監(jiān)控服務(wù)進(jìn)程,如果服務(wù)進(jìn)程意外退出,Supervisor可以自動重啟服務(wù)。
總結(jié)一下我們需要用到的服務(wù)有:
Nginx:高性能Web服務(wù)器+負(fù)責(zé)反向代理;
gunicorn:高性能WSGI服務(wù)器;
gevent:把Python同步代碼變成異步協(xié)程的庫;
Supervisor:監(jiān)控服務(wù)進(jìn)程的工具;
MySQL:數(shù)據(jù)庫服務(wù)。
在Linux服務(wù)器上用apt可以直接安裝上述服務(wù):
$ sudo apt-get install nginx gunicorn python-gevent supervisor mysql-server
然后,再把我們自己的Web App用到的Python庫安裝了:
$ sudo apt-get install python-jinja2 python-mysql.connector
在服務(wù)器上創(chuàng)建目錄/srv/awesome/以及相應(yīng)的子目錄。
在服務(wù)器上初始化MySQL數(shù)據(jù)庫,把數(shù)據(jù)庫初始化腳本schema.sql復(fù)制到服務(wù)器上執(zhí)行:
$ mysql -u root -p < schema.sql
服務(wù)器端準(zhǔn)備就緒。
部署
用FTP還是SCP還是rsync復(fù)制文件?如果你需要手動復(fù)制,用一次兩次還行,一天如果部署50次不但慢、效率低,而且容易出錯。
正確的部署方式是使用工具配合腳本完成自動化部署。Fabric就是一個自動化部署工具。由于Fabric是用Python開發(fā)的,所以,部署腳本也是用Python來編寫,非常方便!
要用Fabric部署,需要在本機(是開發(fā)機器,不是Linux服務(wù)器)安裝Fabric:
$ easy_install fabric
Linux服務(wù)器上不需要安裝Fabric,F(xiàn)abric使用SSH直接登錄服務(wù)器并執(zhí)行部署命令。
下一步是編寫部署腳本。Fabric的部署腳本叫fabfile.py,我們把它放到awesome-python-webapp的目錄下,與www目錄平級:
代碼如下:
awesome-python-webapp/
+- fabfile.py
+- www/
+- ...
Fabric的腳本編寫很簡單,首先導(dǎo)入Fabric的API,設(shè)置部署時的變量:
# fabfile.py
import os, re
from datetime import datetime
# 導(dǎo)入Fabric API:
from fabric.api import *
# 服務(wù)器登錄用戶名:
env.user = 'michael'
# sudo用戶為root:
env.sudo_user = 'root'
# 服務(wù)器地址,可以有多個,依次部署:
env.hosts = ['192.168.0.3']
# 服務(wù)器MySQL用戶名和口令:
db_user = 'www-data'
db_password = 'www-data'
然后,每個Python函數(shù)都是一個任務(wù)。我們先編寫一個打包的任務(wù):
_TAR_FILE = 'dist-awesome.tar.gz'
def build():
includes = ['static', 'templates', 'transwarp', 'favicon.ico', '*.py']
excludes = ['test', '.*', '*.pyc', '*.pyo']
local('rm -f dist/%s' % _TAR_FILE)
with lcd(os.path.join(os.path.abspath('.'), 'www')):
cmd = ['tar', '--dereference', '-czvf', '../dist/%s' % _TAR_FILE]
cmd.extend(['--exclude='%s'' % ex for ex in excludes])
cmd.extend(includes)
local(' '.join(cmd))
Fabric提供local('...')來運行本地命令,with lcd(path)可以把當(dāng)前命令的目錄設(shè)定為lcd()指定的目錄,注意Fabric只能運行命令行命令,Windows下可能需要Cgywin環(huán)境。
在awesome-python-webapp目錄下運行:
$ fab build
看看是否在dist目錄下創(chuàng)建了dist-awesome.tar.gz的文件。
打包后,我們就可以繼續(xù)編寫deploy任務(wù),把打包文件上傳至服務(wù)器,解壓,重置www軟鏈接,重啟相關(guān)服務(wù):
_REMOTE_TMP_TAR = '/tmp/%s' % _TAR_FILE
_REMOTE_BASE_DIR = '/srv/awesome'
def deploy():
newdir = 'www-%s' % datetime.now().strftime('%y-%m-%d_%H.%M.%S')
# 刪除已有的tar文件:
run('rm -f %s' % _REMOTE_TMP_TAR)
# 上傳新的tar文件:
put('dist/%s' % _TAR_FILE, _REMOTE_TMP_TAR)
# 創(chuàng)建新目錄:
with cd(_REMOTE_BASE_DIR):
sudo('mkdir %s' % newdir)
# 解壓到新目錄:
with cd('%s/%s' % (_REMOTE_BASE_DIR, newdir)):
sudo('tar -xzvf %s' % _REMOTE_TMP_TAR)
# 重置軟鏈接:
with cd(_REMOTE_BASE_DIR):
sudo('rm -f www')
sudo('ln -s %s www' % newdir)
sudo('chown www-data:www-data www')
sudo('chown -R www-data:www-data %s' % newdir)
# 重啟Python服務(wù)和nginx服務(wù)器:
with settings(warn_only=True):
sudo('supervisorctl stop awesome')
sudo('supervisorctl start awesome')
sudo('/etc/init.d/nginx reload')
注意run()函數(shù)執(zhí)行的命令是在服務(wù)器上運行,with cd(path)和with lcd(path)類似,把當(dāng)前目錄在服務(wù)器端設(shè)置為cd()指定的目錄。如果一個命令需要sudo權(quán)限,就不能用run(),而是用sudo()來執(zhí)行。
配置Supervisor
上面讓Supervisor重啟gunicorn的命令會失敗,因為我們還沒有配置Supervisor呢。
編寫一個Supervisor的配置文件awesome.conf,存放到/etc/supervisor/conf.d/目錄下:
代碼如下:
[program:awesome]
command = /usr/bin/gunicorn --bind 127.0.0.1:9000 --workers 1 --worker-class gevent wsgiapp:application
directory = /srv/awesome/www
user = www-data
startsecs = 3
redirect_stderr = true
stdout_logfile_maxbytes = 50MB
stdout_logfile_backups = 10
stdout_logfile = /srv/awesome/log/app.log
配置文件通過[program:awesome]指定服務(wù)名為awesome,command指定啟動gunicorn的命令行,設(shè)定gunicorn的啟動端口為9000,WSGI處理函數(shù)入口為wsgiapp:application。
然后重啟Supervisor后,就可以隨時啟動和停止Supervisor管理的服務(wù)了:
$ sudo supervisorctl reload
$ sudo supervisorctl start awesome
$ sudo supervisorctl status
awesome RUNNING pid 1401, uptime 5:01:34
配置Nginx
Supervisor只負(fù)責(zé)運行g(shù)unicorn,我們還需要配置Nginx。把配置文件awesome放到/etc/nginx/sites-available/目錄下:
server {
listen 80; # 監(jiān)聽80端口
root /srv/awesome/www;
access_log /srv/awesome/log/access_log;
error_log /srv/awesome/log/error_log;
# server_name awesome.liaoxuefeng.com; # 配置域名
# 處理靜態(tài)文件/favicon.ico:
location /favicon.ico {
root /srv/awesome/www;
}
# 處理靜態(tài)資源:
location ~ ^/static/.*$ {
root /srv/awesome/www;
}
# 動態(tài)請求轉(zhuǎn)發(fā)到9000端口(gunicorn):
location / {
proxy_pass ;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
然后在/etc/nginx/sites-enabled/目錄下創(chuàng)建軟鏈接:
$ pwd
/etc/nginx/sites-enabled
$ sudo ln -s /etc/nginx/sites-available/awesome .
讓Nginx重新加載配置文件,不出意外,我們的awesome-python-webapp應(yīng)該正常運行:
$ sudo /etc/init.d/nginx reload
如果有任何錯誤,都可以在/srv/awesome/log下查找Nginx和App本身的log。如果Supervisor啟動時報錯,可以在/var/log/supervisor下查看Supervisor的log。
如果一切順利,你可以在瀏覽器中訪問Linux服務(wù)器上的awesome-python-webapp了:
2015430121102755.png (640×522)
如果在開發(fā)環(huán)境更新了代碼,只需要在命令行執(zhí)行:
$ fab build
$ fab deploy
自動部署完成!刷新瀏覽器就可以看到服務(wù)器代碼更新后的效果。
更多信息請查看IT技術(shù)專欄