作爲一個合格的開發者,在本地環境下完成開發還遠遠不夠,我們需要把Web App部署到遠程服務器上,這樣,廣大用戶才能訪問到網站。
很多做開發的同學把部署這件事情看成是運維同學的工作,這種看法是完全錯誤的。首先,最近流行DevOps理念,就是說,開發和運維要變成一個整體。其次,運維的難度,其實跟開發質量有很大的關係。代碼寫得垃圾,運維再好也架不住天天掛掉。最後,DevOps理念需要把運維、監控等功能融入到開發中。你想服務器升級時不中斷用戶服務?那就得在開發時考慮到這一點。
下面,我們就來把awesome-python-webapp部署到Linux服務器。
搭建Linux服務器
要部署到Linux,首先得有一臺Linux服務器。要在公網上體驗的同學,可以在Amazon的AWS申請一臺EC2虛擬機(免費使用1年),或者使用國內的一些雲服務器,一般都提供Ubuntu Server的鏡像。想在本地部署的同學,請安裝虛擬機,推薦使用VirtualBox。
我們選擇的Linux服務器版本是Ubuntu Server 12.04 LTS,原因是apt太簡單了。如果你準備使用其他Linux版本,也沒有問題。
Linux安裝完成後,請確保ssh服務正在運行,否則,需要通過apt安裝:
1
|
$ sudo
apt-get install
openssh-server |
有了ssh服務,就可以從本地連接到服務器上。建議把公鑰複製到服務器端用戶的.ssh/authorized_keys中,這樣,就可以通過證書實現無密碼連接。
部署方式
在本地開發時,我們可以用Python自帶的WSGI服務器,但是,在服務器上,顯然不能用自帶的這個開發版服務器。可以選擇的WSGI服務器很多,我們選gunicorn:它用類似Nginx的Master-Worker模式,同時可以提供gevent的支持,不用修改代碼,就能獲得極高的性能。
此外,我們還需要一個高性能Web服務器,這裏選擇Nginx,它可以處理靜態資源,同時作爲反向代理把動態請求交給gunicorn處理。gunicorn負責調用我們的Python代碼,這個模型如下:
Nginx負責分發請求:
在服務器端,我們需要定義好部署的目錄結構:
+- srv/
+- awesome/ <-- Web App根目錄
+- www/ <-- 存放Python源碼
| +- static/ <-- 存放靜態資源文件
+- log/ <-- 存放log
在服務器上部署,要考慮到新版本如果運行不正常,需要回退到舊版本時怎麼辦。每次用新的代碼覆蓋掉舊的文件是不行的,需要一個類似版本控制的機制。由於Linux系統提供了軟鏈接功能,所以,我們把www作爲一個軟鏈接,它指向哪個目錄,哪個目錄就是當前運行的版本:
而Nginx和gunicorn的配置文件只需要指向www目錄即可。
Nginx可以作爲服務進程直接啓動,但gunicorn還不行,所以,Supervisor登場!Supervisor是一個管理進程的工具,可以隨系統啓動而啓動服務,它還時刻監控服務進程,如果服務進程意外退出,Supervisor可以自動重啓服務。
總結一下我們需要用到的服務有:
- Nginx:高性能Web服務器+負責反向代理;
- gunicorn:高性能WSGI服務器;
- gevent:把Python同步代碼變成異步協程的庫;
- Supervisor:監控服務進程的工具;
- MySQL:數據庫服務。
在Linux服務器上用apt可以直接安裝上述服務:
1
|
$ sudo
apt-get install
nginx gunicorn python-gevent supervisor mysql-server |
然後,再把我們自己的Web App用到的Python庫安裝了:
1
|
$ sudo
apt-get install
python-jinja2 python-mysql.connector |
在服務器上創建目錄/srv/awesome/以及相應的子目錄。
在服務器上初始化MySQL數據庫,把數據庫初始化腳本schema.sql複製到服務器上執行:
1
|
$ mysql -u root -p < schema.sql |
服務器端準備就緒。
部署
用FTP還是SCP還是rsync複製文件?如果你需要手動複製,用一次兩次還行,一天如果部署50次不但慢、效率低,而且容易出錯。
正確的部署方式是使用工具配合腳本完成自動化部署。Fabric就是一個自動化部署工具。由於Fabric是用Python開發的,所以,部署腳本也是用Python來編寫,非常方便!
要用Fabric部署,需要在本機(是開發機器,不是Linux服務器)安裝Fabric:
1
|
$ easy_install fabric |
Linux服務器上不需要安裝Fabric,Fabric使用SSH直接登錄服務器並執行部署命令。
下一步是編寫部署腳本。Fabric的部署腳本叫fabfile.py,我們把它放到awesome-python-webapp的目錄下,與www目錄平級:
+- fabfile.py
+- www/
+- ...
Fabric的腳本編寫很簡單,首先導入Fabric的API,設置部署時的變量:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
# fabfile.py import
os, re from
datetime import datetime # 導入Fabric API: from
fabric.api import
* # 服務器登錄用戶名: env.user =
'michael' # sudo用戶爲root: env.sudo_user
= 'root' # 服務器地址,可以有多個,依次部署: env.hosts =
[ '192.168.0.3' ] # 服務器MySQL用戶名和口令: db_user =
'www-data' db_password
= 'www-data' |
然後,每個Python函數都是一個任務。我們先編寫一個打包的任務:
1
2
3
4
5
6
7
8
9
10
11
|
_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)可以把當前命令的目錄設定爲lcd()指定的目錄,注意Fabric只能運行命令行命令,Windows下可能需要Cgywin環境。
在awesome-python-webapp目錄下運行:
1
|
$ fab build |
看看是否在dist目錄下創建了dist-awesome.tar.gz的文件。
打包後,我們就可以繼續編寫deploy任務,把打包文件上傳至服務器,解壓,重置www軟鏈接,重啓相關服務:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
_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) # 創建新目錄: 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服務和nginx服務器: with settings(warn_only = True ): sudo( 'supervisorctl stop awesome' ) sudo( 'supervisorctl start awesome' ) sudo( '/etc/init.d/nginx reload' ) |
注意run()函數執行的命令是在服務器上運行,with cd(path)和with lcd(path)類似,把當前目錄在服務器端設置爲cd()指定的目錄。如果一個命令需要sudo權限,就不能用run(),而是用sudo()來執行。
配置Supervisor
上面讓Supervisor重啓gunicorn的命令會失敗,因爲我們還沒有配置Supervisor呢。
編寫一個Supervisor的配置文件awesome.conf,存放到/etc/supervisor/conf.d/目錄下:
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]指定服務名爲awesome,command指定啓動gunicorn的命令行,設定gunicorn的啓動端口爲9000,WSGI處理函數入口爲wsgiapp:application。
然後重啓Supervisor後,就可以隨時啓動和停止Supervisor管理的服務了:
1
2
3
4
|
$ sudo
supervisorctl reload $ sudo
supervisorctl start awesome $ sudo
supervisorctl status awesome RUNNING pid 1401, uptime 5:01:34 |
配置Nginx
Supervisor只負責運行gunicorn,我們還需要配置Nginx。把配置文件awesome放到/etc/nginx/sites-available/目錄下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
server { listen
80 ; # 監聽80端口 root
/ srv / awesome / www; access_log
/ srv / awesome / log / access_log; error_log
/ srv / awesome / log / error_log; # server_name awesome.liaoxuefeng.com; # 配置域名 # 處理靜態文件/favicon.ico: location
/ favicon.ico { root
/ srv / awesome / www; } # 處理靜態資源: location ~ ^\ / static\ / . * $
{ root
/ srv / awesome / www; } # 動態請求轉發到9000端口(gunicorn): location
/ { proxy_pass http: / / 127.0 . 0.1 : 9000 ; 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/目錄下創建軟鏈接:
1
2
3
|
$ pwd /etc/nginx/sites-enabled $ sudo
ln -s /etc/nginx/sites-available/awesome
. |
讓Nginx重新加載配置文件,不出意外,我們的awesome-python-webapp應該正常運行:
1
|
$ sudo
/etc/init .d /nginx
reload |
如果有任何錯誤,都可以在/srv/awesome/log下查找Nginx和App本身的log。如果Supervisor啓動時報錯,可以在/var/log/supervisor下查看Supervisor的log。
如果一切順利,你可以在瀏覽器中訪問Linux服務器上的awesome-python-webapp了:
如果在開發環境更新了代碼,只需要在命令行執行:
1
2
|
$ fab build $ fab deploy |
自動部署完成!刷新瀏覽器就可以看到服務器代碼更新後的效果。
友情鏈接
嫌國外網速慢的童鞋請移步網易和搜狐的鏡像站點:
http://mirrors.163.com/
http://mirrors.sohu.com/