本文主要講解在 Linux 平臺下,使用 Nginx + uWSGI 的方式來部署來 Django,這是目前比較主流的方式。當然你也可以使用 Gunicorn 代替 uWSGI,不過原理都是類似的,弄懂了其中一種,其它的方式理解起來問題也不會很大。
?
有很多人曾經在郵件中咨詢過問我如何部署,部署確實對開發者有著較高的要求,尤其是初學者來說,部署比較難,這很正常,千萬不要輕易就放棄了。
?
回想我2013年剛開始接觸也是一頭霧水,整整弄了四五天才完全搞懂,部署好了真是欣喜若狂。我自認為算不上聰明,我都能搞明白,你也一定能行。最關鍵和寶貴的品質就是堅持不懈和勇于試錯。下面就讓我們一起開始正式的部署教程吧。
?
基礎知識儲備
當我們發現用瀏覽器不能訪問的時候,我們需要一步步排查問題。
整個部署的鏈路是 Nginx -> uWSGI -> Python Web程序,通常還會提到supervisord工具。
uWSGI是一個軟件,部署服務的工具,了解整個過程,我們先了解一下WSGI規范,uwsgi協議等內容。
?
WSGI(Web Server Gateway Interface)規范,WSGI規定了Python Web應用和Python Web服務器之間的通訊方式。
目前主流的Python Web框架,比如Django,Flask,Tornado等都是基于這個規范實現的。
?
uwsgi協議是uWSGI工具獨有的協議,簡潔高效的uwsgi協議是選擇uWSGI作為部署工具的重要理由之一,詳細的 uwsgi協議 可以參考uWSGI的文檔。
uWSGI是 實現了uwsgi協議,WSGI規范和HTTP協議的 一個C語言實現的軟件。
?
Nginx是一個Web服務器,是一個反向代理工具,我們通常用它來部署靜態文件。主流的Python Web開發框架都遵循WSGI規范。
uWSGI通過WSGI規范和我們編寫的服務進程通訊,然后通過自帶的高效的 uwsgi 協議和 Nginx進行通訊,最終Nginx通過HTTP協議將服務對外透出。
當一個訪問進來的時候,首先到 Nginx,Nginx會把請求(HTTP協議)轉換uwsgi協議傳遞給uWSGI,uWSGI通過WSGI和web server進行通訊取到響應結果,再通過uwsgi協議發給Nginx,最終Nginx以HTTP協議發現響應給用戶。
有些同學可能會說,uWSGI不是支持HTTP協議么,也支持靜態文件部署,我不用Nginx行不行?
當然可以,這么做沒問題,但目前主流的做法是用Nginx,畢竟它久經考驗,更穩定,當然也更值得我們信賴。
?
supervisor?是一個進程管理工具。任何人都不能保證程序不異常退出,不別被人誤殺,所以一個典型的工程做法就是使用supervisor看守著你的進程,一旦異常退出它會立馬進程重新啟動起來。
如果服務部署后出現異常,不能訪問。我們需要分析每一步有沒有問題,這時候就不得不用到Linux中一些命令。
?
進程分析
進程是計算機分配資源的最小單位,我們的程序至少是運行在一個進程中。
1. 查看進程信息
通常我們使用 ps aux | grep python 來查看系統中運行的 python 進程,輸出結果如下:
tu@linux / $ ps uax | grep python
USER ? ? ? PID %CPU %MEM ? ?VSZ ? RSS TTY ? ? ?STAT START ? TIME COMMAND
root ? ? ?1780 ?0.0 ?0.0 ?58888 10720 ? ? ? ? ?Ss ? Jan15 ? 8:46 /usr/bin/python /usr/local/bin/supervisord -c /etc/supervisord.conf
tu ? ? ?4491 ?0.0 18.0 3489820 2960628 ? ? ? Sl ? Jan29 ? 0:19 python a.py
tu ? ? 12602 ?5.3 26.4 4910444 4343708 pts/1 Sl+ ?12:34 ? 4:07 python b.py
有些同學習慣使用 ps -ef | grep xxx 結果也是類似的,讀者可以自行嘗試。
?
輸出結果中 USER 后面的 PID 代表進程編號。
我們可以通過查看 /proc/PID/ 目錄的文件信息來得到這個進程的一些信息(Linux中一切皆文件,進程信息也在文件中),比如它是在哪個目錄啟動的,啟動命令是什么等信息。執行命令后輸入內容如下:
tu@linux /proc/4491 $?sudo ls -ahl
...
dr-xr-xr-x ? 2 tu tu 0 Feb 17 13:32 attr
-rw-r--r-- ? 1 tu tu 0 Feb 17 13:32 autogroup
-r-------- ? 1 tu tu 0 Feb 17 13:32 auxv
-r--r--r-- ? 1 tu tu 0 Feb 17 13:32 cgroup
--w------- ? 1 tu tu 0 Feb 17 13:32 clear_refs
-r--r--r-- ? 1 tu tu 0 Feb 17 12:49 cmdline ?這個文件中有啟動進程具體的命令
-rw-r--r-- ? 1 tu tu 0 Feb 17 13:32 comm
-rw-r--r-- ? 1 tu tu 0 Feb 17 13:32 coredump_filter
-r--r--r-- ? 1 tu tu 0 Feb 17 13:32 cpuset
lrwxrwxrwx ? 1 tu tu 0 Feb 17 13:32 cwd -> /home/tu ?啟動進程時的工作目錄
-r-------- ? 1 tu tu 0 Feb 17 13:32 environ ?進程的環境變量列表
lrwxrwxrwx ? 1 tu tu 0 Feb 17 12:00 exe -> /usr/bin/python2.7 鏈接到進程的執行命令文件
...省去了部分內容
?
2. 向進程發送信號
我們可以使用 kill PID 殺死一個進程,或者使用 kill -9 PID 強制殺死一個進程。
記得以前在研究生的時候師弟和師妹經常問我,kill -9 里面的 -9 是什么意思,我告訴他們,這是強制殺死進程的意思,讓這個進程“九死一生”。當然這是開玩笑,這里的 -9 是信號的一種,kill 命令會向進程發送一個信號,-9代表 SIGKILL 之意,用于強制終止某個進程,當然這是一種無情地,野蠻地方式干掉進程。
我們可以通過 kill -l 命令查看到所有的信號
HUP INT QUIT ILL TRAP ABRT BUS FPE KILL USR1 SEGV USR2 PIPE ALRM TERM STKFLT CHLD CONT STOP TSTP TTIN TTOU URG XCPU XFSZ VTALRM PROF WINCH POLL PWR SYS
?
上面的信號是有順序的,比如第1個是 HUP,第9個是 KILL,下面兩種方式是等價的:
kill -1 PID 和 kill -HUP PID
kill -9 PID 和 kill -KILL PID
信號HUP通常程序用這個信號進行優雅重載配置文件,重新啟動并且不影響正在運行的服務。比如
pkill -1 uwsgi 優雅重啟uwsgi 進程,對服務器沒有影響
kill -1 NGINX_PID 優雅重啟nginx進程,對服務器沒有影響
除了知道可以這么使用之外,感興趣的讀者還可以自行學習,深入了解下uwsgi和nginx無損reload的機制。
我們常用CTRL+C中斷一個命令的執行,其實就是發送了一個信號到該進程
CTRL-C 發送 SIGINT 信號給前臺進程組中的所有進程,常用于終止正在運行的程序。
CTRL-Z 發送 SIGTSTP 信號給前臺進程組中的所有進程,常用于掛起一個進程。
每個程序可能對部分信號的功能定義不一致,其它信號的含義大家可以自行學習。
?
3. 查看進程打開了哪些文件
sudo lsof -p PID
如果是分析一個你不太了解的進程,這個命令比較有用。
可以使用 lsof -p PID | grep TCP 查看進程中的 TCP 連接信息。
?
4. 查看文件被哪個進程使用
使用這個命令查看一個文件被哪些進程正在使用 sudo lsof /path/to/file,示例如下:
> sudo lsof /home/tu/.virtualenvs/mic/bin/uwsgi
COMMAND ? PID USER ?FD ? TYPE DEVICE SIZE/OFF ? ? NODE NAME
uwsgi ? ?2071 tu txt ? ?REG 253,17 ?1270899 13240576 /home/tu/.virtualenvs/mic/bin/uwsgi
uwsgi ? 13286 tu txt ? ?REG 253,17 ?1270899 13240576 /home/tu/.virtualenvs/mic/bin/uwsgi
uwsgi ? 13287 tu txt ? ?REG 253,17 ?1270899 13240576 /home/tu/.virtualenvs/mic/bin/uwsgi
uwsgi ? 13288 tu txt ? ?REG 253,17 ?1270899 13240576 /home/tu/.virtualenvs/mic/bin/uwsgi
?
5. 查看進程當前狀態
當我們發現一個進程啟動了,端口也是正常的,但好像這個進程就是不“干活”。比如我們執行的是數據更新進程,這個進程不更新數據了,但還是在跑著。可能數據源有問題,可能我們寫的程序有BUG,也可能是更新時要寫入到的數據庫出問題了(數據庫連接不上了,寫數據死鎖了)。我們這里主要說下第二種,我們自己的程序如果有BUG,導致工作不正常,我們怎么知道它當前正在干什么呢,這時候就要用到Linux中的調試分析診斷strace,可以使用?sudo strace -p PID這個命令。
通過執行后輸出的一些信息,推測分析看是哪些出了問題。
?
這里我們講了一些進程分析的工具和方法,關于進程分析工具和方法還有許多,大家需要不斷練習,熟練運用這些工具去排查遇到的問題。
?
端口分析
比如我們在服務器上運行 Nginx,訪問的時候就是連接不上,我們可以使用 ps aux | grep nginx看下nginx進程是不是啟動了,也可以看下 80端口有沒有被占用。換句話說,如果沒有任何程序跑在這個端口上(或者說沒有任何程序使用這個端口),證明忘了啟動相關程序或者沒能啟動成功,或者說程序使用的端口被修改了,不是80了,那又怎么可能能訪問到呢?
1. 查看全部端口占用情況
Linux中我們可以使用 netstat 工具來進程網絡分析,netstat 命令有非常多選項,這里只列出了常用的一部分
-a或--all 顯示所有連接中的Socket,默認不顯示 LISTEN 相關的。
-c或--continuous 持續列出網絡狀態,不斷自動刷新輸出。
-l或--listening 顯示監聽中的服務器的Socket。
-n或--numeric 直接使用IP地址,而不是展示域名。
-p或--programs 顯示正在使用Socket的程序進程PID和名稱。
-t或--tcp 顯示TCP傳輸協議的連接。
-u或--udp 顯示UDP傳輸協議的連接。
比如我們可以查看服務器中監控了哪些端口,如果我們的nginx是使用80端口,uwsgi使用的是7001端口,我們就能知道通過下面的命令
> netstat -nltp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address ? ? ? ? ? Foreign Address ? ? ? ? State ? ? ? PID/Program name
tcp ? ? ? ?0 ? ? ?0 0.0.0.0:7001 ? ? ? ? ? ?0.0.0.0:* ? ? ? ? ? ? ? LISTEN ? ? ?2070/uwsgi ? ? ?
tcp ? ? ? ?0 ? ? ?0 127.0.0.1:6379 ? ? ? ? ?0.0.0.0:* ? ? ? ? ? ? ? LISTEN ? ? ?1575/redis-server 1
就能知道80端口的 nginx 是不是啟動成功了,7001端口的uwsgi是不是啟動成功了。
注意:如果PID和Program Name顯示不出來,證明是權限不夠,可以使用sudo運行
2. 查看具體端口占用情況
> sudo lsof -i :80 (注意端口80前面有個英文的冒號)
COMMAND ? ?PID ? ? USER ? FD ? TYPE DEVICE SIZE/OFF NODE NAME
nginx ? 4123 ? admin ? ?3u ?IPv4 ?13031 ? ? ?0t0 ?TCP *:http (LISTEN)
nginx ? 4124 ? admin ? ?3u ?IPv4 ?13031 ? ? ?0t0 ?TCP *:http (LISTEN)
我們可以通過這個方法查詢出占用端口的程序,如果遇到端口已經被占用,原來的進程沒有正確地終止,可以使用kill命令停掉原來的進程,這樣我們就又可以使用這個端口了。
?
除了上面講的一些命令,在部署過程中會經常用到下面的一些Linux命令,如果不清楚它們是做什么的,可以提前自行學習下這些Linux基礎命令:
ls, touch, mkdir, mv, cp, ps, chmod, chown
學習完了這些內容,我們應該就具備了部署Linux服務器的基礎知識了,在遇到問題后,應該也會有一些調查思路。
剩余部分大家再去?自強學堂上看?Django部署,應該會容易懂的多。