從零開始自動部署Django項目(三):使用uWSGI emperor管理進程

引言

在上一篇從零開始自動部署Django項目(二):使用Python編寫Git Hooks,筆者直接通過Python模擬正常的人肉linux命令來確定python debug server是否在指定端口運行,如果正在運行則先殺掉該進程,在更新了Git倉庫之後再人肉啓動python debug server。

咦,好像有哪裏不對,爲什麼不直接刪掉文件,然後進程不就自動結束了嗎?這樣子就不用檢查端口是否有進程在運行了。

相信大家都試過,在linux下如果先開啓python debug server進程,然後把進程的相關文件都刪掉,而進程還能正常運行。這是因爲linux下的刪除(rm)其實是減少對文件的link數目,雖然刪除了文件,但是打開的進程還保持着對文件的一個link,因此在執行了rm之後文件並不會被立即刪除,只有link數爲0的時候纔會被刪除。

因此,筆者希望在服務器上運行一個進程管理工具監控Git倉庫,每當倉庫更新的時候,進程管理工具就自動重啓python debug server,而Git Hooks只要負責倉庫的更新就可以了。

在Python的進程管理工具中,supervisor應該是比較廣爲人知,而uWSGI emperor的曝光度似乎並不太高,然而基於nginx+uwsgi+django的標配,筆者還是對uWSGI emperor的折騰比較期待的。
uWSGI文檔傳送門:Quickstart for Python/WSGI applications

準備

  • uWSGI與uwsgi是同一樣東西嗎?它們有什麼區別?

    先給出結論:uWSGI是一個生產用服務器,而uwsgi是一種網絡通信協議(在uWSGI文檔中也承認了這個名字的選擇真的是wrong name choice)。
    Django爲開發者提供了一個開發調試用的服務器,只要通過python manage.py runserver就可以在默認的運行端口8000提供服務,但是這個服務器不能在生產環境中使用,需要更爲穩定的服務器提供服務,而uWSGI也是Django官方文檔中推薦使用的生產服務器。
    至於uwsgi協議則多用於與反向代理服務器的內部信息通信,nginx支持對uwsgi協議的解析,而爲什麼使用uwsgi協議而不是在內部繼續使用http協議進行通信,uWSGI的文檔給出以下解釋:

    Why not simply use HTTP as the protocol?
    A good question with a simple answer: HTTP parsing is slow, really slow. Why should we do a complex task twice? The web server has already parsed the request! The uwsgi protocol is very simple to parse for a machine, while HTTP is very easy to parse for a human. As soon as humans are being used as servers, we will abandon the uwsgi protocol in favor of the HTTP protocol. All this said, you can use uWSGI via Native HTTP support, FastCGI, ZeroMQ and other protocols as well.

    uWSGI文檔傳送門:Frequently Asked Questions (FAQ)

  • 折騰環境選擇
    筆者開發使用的mac系統:

    uname -a
    Darwin bogon 15.6.0 Darwin Kernel Version 15.6.0: Thu Jun 23 18:25:34 PDT 2016; root:xnu-3248.60.10~1/RELEASE_X86_64 x86_64

    要部署到的centos服務器系統:

    Linux localhost.localdomain 3.10.0-327.28.3.el7.x86_64 #1 SMP Thu Aug 18 19:05:49 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux

配置文件uwsgi.ini

首先新建一個名爲uwsgi_test的Django項目:

uwsgi_test
    ├── manage.py
    ├── uwsgi_test
    │   ├── __init__.py
    │   ├── __init__.pyc
    │   ├── settings.py
    │   ├── settings.pyc
    │   ├── urls.py
    │   ├── urls.pyc
    │   ├── wsgi.py
    │   └── wsgi.pyc
    └── uwsgi_test.ini

uwsgi_test.ini配置如下:

[uwsgi]
chdir = /path/to/uwsgi_test
module = uwsgi_test.wsgi
master = true
processes = 4
http = :8080
vaccum = true

當在Mac系統上運行如下的uwsgi的啓動命令之後就能訪問瀏覽器的8080端口並看到Django的默認歡迎界面了。

uwsgi  --ini uwsgi_test.ini

在以上的配置中生成了一個主進程和四個進程,以及一個Http路由(Http Router)進程,其中主進程負責當4個進程中有進程die的時候重新生成一個進程,而Http Router進程則負責將請求轉發給WSGI Application。

*** Operational MODE: preforking ***
WSGI app 0 (mountpoint='') ready in 0 seconds on interpreter 0x7f9c5ae000d0 pid: 8514 (default app)
*** uWSGI is running in multiple interpreter mode ***
spawned uWSGI master process (pid: 8514)
spawned uWSGI worker 1 (pid: 8515, cores: 1)
spawned uWSGI worker 2 (pid: 8516, cores: 1)
spawned uWSGI worker 3 (pid: 8517, cores: 1)
spawned uWSGI worker 4 (pid: 8518, cores: 1)
spawned uWSGI http 1 (pid: 8519)

配置選項:http,socket與http-socket

  • 當選擇http的選項的時候,uWSGI就會啓動一個Http服務器(或者叫Http Router)來負責把請求傳給WSGI的Application,因此,所有的瀏覽器都能直接訪問由uWSGI通過http選項啓動的端口(此時uWSGI使用的是HTTP協議進行通信)。此時,Http Router就相當於一個nginx負責反向代理,然後在內部通過uwsgi協議或其他協議傳給處理的進程。
    (uWSGI官方文檔對Http選項以及Http Router的一些講解傳送門:Native HTTP support
    (uWSGI官方文檔傳送門:Things to know (best practices and “issues”) READ IT !!!

  • 當選擇socket選項的時候,此時uWSGI就會通過默認的uwsgi協議與外界進行通信了,因此socket選項一般是與nginx反向服務器搭配進行,並且不會啓動Http Router進程,如果此時直接通過瀏覽器訪問端口(Http協議),uWSGI是不會進行響應的。其中,官方文檔更推薦通過.sock文件進行信息通信而非TCP端口。

  • 當選擇http-socket選項的時候,也不會產生Http Router進程,而uWSGI中所有負責處理的進程都會通過Http協議與外界通信。

在centos系統上運行配置文件

當筆者在centos系統上部署相同的項目和配置文件時,uWSGI卻報了一個奇怪的錯誤。

The -s/--socket option is missing and stdin is not a socket.

筆者在上面使用的明明是http選項,爲什麼會出現socket選項呢?
這是因爲不同的linux發行版的包安裝工具所安裝的uWSGI,有時候會以模塊的方式安裝,並且在運行的時候默認不會加載這些模塊,比如http選項就需要http的plugin。因此官方建議下載uWSGI的源碼下來自行build。
uWSGI官方文檔解釋如下:

Installing uWSGI with Python support
When you start learning uWSGI, try to build from official sources: using distribution-supplied packages may bring you plenty of headaches. When things are clear, you can use modular builds (like the ones available in your distribution).

Installing via your package distribution is not covered (would be impossible to make everyone happy), but all of the general rules apply.

One thing you may want to take into account when testing this quickstart with distro-supplied packages, is that very probably your distribution has built uWSGI in modular way (every feature is a different plugin that must be loaded). To complete this quickstart, you have to prepend –plugin python,http to the first series of examples, and –plugin python when the HTTP router is removed (if this doesn’t make sense to you, just continue reading).

因此,在ini配置文件中加上:

plugin = python, http

就能解決這個問題了。

uWSGI emperor

OK,在前面的介紹中,uWSGI終於能正常運行了。下面介紹正餐emperor模式吧。

emperor翻譯爲中文的意思其實就是皇帝的意思,那麼既然有皇帝,那就肯定有“臣子”(vassals),其中“臣子”就是指實際運行的一個app實例,在這裏就是uwsgi_test.ini配置文件了。

emperor模式開啓的命令是:

sudo uwsgi --emperor /path/to/vassals/ 

其中,vassals文件夾裏包含了各個app的配置文件,這個文件夾將會被emperor一直監視,只要有配置文件的修改或新建,app實例就會被重新加載或新建,可以通過軟鏈接將實際項目中的配置文件鏈接到vassal文件夾目錄下:

ln -s /path/to/uwsgi_test.ini /path/to/vassals/

uWSGI官方文檔展示了uWSGI emperor的功能:

  • Whenever an imperial monitor detects a new configuration file, a new uWSGI instance will be spawned with that configuration.
  • Whenever a configuration file is modified (its modification time changed, so touch –no-dereference may be your friend), the corresponding app will be reloaded.
  • Whenever a config file is removed, the corresponding app will be stopped.
  • If the emperor dies, all the vassals die.
  • If a vassal dies for any reason, the emperor will respawn it.

在文檔中,emperor監視的是配置文件目錄,但是問題來了,筆者希望每次Git倉庫更新一次就重新啓動實例,而如果這次更新並沒有對配置文件進行修改,實例自然就不會重新啓動了。
幸好配置文件提供了touch-reload選項,只要指定文件夾發生改動就重啓實例:

touch-reload = /path/to/uwsgi_test
# or touch-reload = .git/index

最後一個問題,在上一篇 從零開始自動部署Django項目(一):開發配置與生產配置中,筆者通過環境變量來確定Django配置的加載,因此在uWSGI的配置文件中還需要加上對環境變量的添加:

env = DJANGO_PRODUCTION_SETTINGS=TRUE

最後,使用nohup命令啓動uWSGI emperor即可實現進程管理(別忘了Git Hooks需修改爲只拉取倉庫更新)

完整的配置

[uwsgi]
plugin = python, http
env = DJANGO_PRODUCTION_SETTINGS=TRUE

chdir = /path/to/uwsgi_test
module = umefit.wsgi

master=True
processes = 4
http = :8080
vaccum=True

touch-reload = /path/to/uwsgi_test

最後祝大家國慶快樂,哈哈。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章