接觸了flask開發有一小段時間了,使用flask主要完成了我們產品的Android客戶端的后台服務(提供REST API),還有就是為運營提供數據統計及應用發布的web系統。之前都是通過傳統的筆記本來記錄開發中遇到的問題,漸漸發現很多問題存在着很多的共性,通過博客的方式開啟一個記錄和交流的新方法吧。文中如果有什么錯誤歡迎指出,對flask開發有興趣的朋友也可以互相交流,探討問題。
flask在他的官方文檔中是這樣描述的,flask是一個python的微框架,基於Werkzeuk和Jinjia2。目前對於python2和python3的支持都很好。入門也很簡單,強烈推薦官方文檔,例子簡單清楚。官方文檔.另外一個參考的就是《FlaskWeb開發:基於Python的Web應用開發實戰》在網上可以找到很多電子版的,通過官方的文檔可以了解flask是什么以及如何開始一個hello word,而通過上面提到第二個參考資料,可以實現一個簡單的博客系統,更好的了解flask的。在這里就不多介紹如何開始flask開發的,我感覺官方的文檔已經介紹的足夠簡單清楚,這些內容也不是本文的主要內容,本文的焦點主要放在如何實現flask的部署,以及分享我在部署過程中遇到的問題,以及經過這些折騰之后我的建議吧。
系統: centos 6.5 x86_64
python: 版本3.5.2
flask:用來實現REST API及相關的web功能, 版本0.11
celery : 一個很好的分布式管理隊列,在我的應用場景中主要使用它來做一些定時的任務,其實主要是定時向我們的運維人員發送一些日志及告警信息。
官方文檔.版本3.1.24
gunicorn : 作為flask應用的web server,自帶的web server是用來我們開發和測試過程中進行調試的,是一個單進程單線程的,無法做生產部署只用,
之前嘗試過uwsgi作為web server,但是從部署角度來說,gunicorn配置更加方便,另外,gunicorn 對於gevent的支持也是非常不錯的,
所以綜合以上兩個原因,才用的gunicorn作為web server部署。官方配置文檔, 版本19.6
gevent: 一個很好的基於協程的python網絡庫,可以很容易的提升系統的並發性,而且gunicorn對gevent的支持也很好,配置簡單。官方文檔, 版本1.2
nginx: 用作反向代理, 版本1.6.2
supervisord: 用以管理我的flask應用以及celery任務。gunicorn已經可以實現將自己作為一個守護進程,從一定程度上來說,即使不適用supervisord也沒有任何
問題,但是celery在centos 6.5上配置自啟動以及變成守護進程相對麻煩,另外從統一管理及維護簡單的角度來考慮,采用supervisord也是一個不錯
的選擇。不得不說下,在使用supervisord之前,都是手動殺gunicorn的進程的。同樣附上官方文檔.版本3.3.1
我再啰嗦一下,為什么每個都附上了官方的文檔,因為我在我的部署過程中遇到過不少的問題,搜索過到很多其他人分享的東西,發現很多別人分享的東西都有特定的版本限制,而通常過程中,作者並沒有寫清楚他當時使用的版本,而對於一個開始接觸這種部署方式的人來說,調錯是一件耗時耗力的事情,雖然過程中我們可以通過解決問題提升自己,但從我個人的感受來看,如果第一次配置部署中出現了難以解決的問題,很可能會迫使你轉用其他的方式,所以官方文檔給的配置說明是最清晰的。
一、一定使用Python虛擬環境(即virtualenv),為什么這么說呢,從部署的角度來說,方便,易維護,如果服務器上有多個版本的python或者當你使用Python3 來開發項目,然后使用跟我一樣的部署方式的時候你就會跟我有一樣的體會。
1、安裝virtualenv: yum install virtualenv
2、進入到你的工程目錄,個人建議把虛擬環境放到你的工程目錄當中,保證每一個工程有自己獨立的python環境,
在shell中執行: virtualenv -p python3 venv 來創建虛擬環境,其中 -p 參數制定python的路徑,執行完之后可以發現在工程目錄中產生一個
名為venv的文件夾,以后該虛擬環境所有的擴展包的安裝目錄。
3、安裝Python虛擬環境的所有依賴包:
在開發過程中可以通過執行指令 pip freeze > requirement.txt 來導出依賴包及其對應的版本。
在工程目錄中,執行: source venv/bin/activate 進入虛擬環境,這時,在你的shell中應該是如下類似的顯示: (venv) localhost:service allan$
接着通過執行 pip install -r requirement.txt 來安裝所有的擴展包,至此,虛擬環境配置完成。如果需要退出虛擬環境,
可以在shell中執行 deactivate
在工程目錄中使用flask自帶的web server 運行flask應用,應用功能一切正常。
二、nginx相關配置:
1 location / { 2 proxy_pass http://127.0.0.1:9000; 3 proxy_set_header Host $host; 4 proxy_set_header X-Real-IP $remote_addr; 5 proxy_set_header REMOTE-HOST $remote_addr; 6 proxy_set_header X-Forward-For $proxy_add_x_forwarded_for; 7 proxy_redirect off; 8 }
三、gunicorn已經在第一步虛擬環境的配置的部分的擴展包安裝中完成,其簡單配置如下:
1 import gevent.monkey 2 import multiprocessing 3 4 gevent.monkey.patch_all() 5 6 bind = '0.0.0.0:9000' 7 # restart workers when code change, only use in development 8 #reload = True 10 preload_app = True 12 # debug when development and error when production 13 loglevel = 'error' 14 logfile = 'log/debug.log' 15 accesslog = 'log/access.log' 16 access_log_format = '%(h)s %(t)s %(U)s %(q)s' 17 errorlog = 'log/error.log' 18 # process name 19 proc_name = 'vservice' 21 pidfile = 'log/gunicorn.pid' 22 # set process daemon, not use in default 23 #daemon = True 25 # number of processes 26 workers = multiprocessing.cpu_count() * 2 + 1 27 # number of threads of per process 28 threads = multiprocessing.cpu_count() * 2 29 worker_class = 'gevent'
其中23行的daemon參數的設置是要特殊注意的,如果只是使用gunicorn而不使用supervisord類似的進程管理工具,該參數設置為true,使進程成為守護進程,但一旦使用類似supervisord等進程管理工具時,該參數一定不要設置,gunicorn默認該參數為False,否則會出現問題。
當然,gunicorn還有很多的參數配置,具體可參考前面部分提供的官方配置文檔部分。
如果單獨使用gunicorn運行,通過指令 gunicorn -c config.py manager:app 來運行,其中manager對應工程目錄中的manager.py, app為flask應用的名稱。
至此,如果在flask應用中不使用celery也不使用supervisord做進程管理,在一定程度上已經實現了服務器端的部署。到了這里仍然缺少一個很重要的環節,那就是監控,我是通過在跟服務器不同網絡的一台測試機器上部署了一個模仿正常客戶端的Python腳本來實現對服務器是否正常工作的檢測,如果連續丟失幾個包會認為服務器程序異常會向運維人員發送報警的短信及郵件。
四、supervisord配置及安裝:
在虛擬環境的shell中執行 pip install supervisor 安裝supervisord, 我使用的Python版本為3.5.2, pip安裝之后supervisord的版本為3.3.1.
安裝完成之后,利用supervisord自帶的命令產生配置文件模板,強烈建議這么做,因為從網上其他人的分享中找過來的模板很可能版本不匹配,導致supervisord一直不能正常運行,而且supervisord報的錯誤也會讓你一開始沒有頭緒。
假定在工程目錄中放置你的supervisord配置文件: echo_supervisord_conf /data/vservice/supervisord.conf ,該指令會產生一個你安裝的版本的supervisord對應的模板配置文件。
下面是我在我的工程實踐中的supervisord的配置文件,在模板的基礎上,參考官方文檔各個參數的意義進行的修改:
1 [program:vservice] 2 command=/data/vservicer/venv/bin/gunicorn -c /data/vservice-server/config.py manager:app 3 process_name=%(program_name)s 4 numprocs=1 5 directory=/data/vservice/ 6 autostart=true 7 autorestart=unexpected 8 user=allan 9 stdout_logfile=/data/vservice/log/supervisor/stdout.log 10 stderr_logfile=/data/vservice/log/supervisor/stderr.log 11 12 [program:send_mail] 13 command=/data/vservice/venv/bin/celery worker --app=manager.celery --beat -l INFO 14 process_name=%(program_name)s 15 numprocs=1 16 directory=/data/vservice/ 17 autostart=true 18 autorestart=unexpected 19 stdout_logfile=/data/vservice/log/celery/stdout.log 20 stderr_logfile=/data/vservice/log/celery/stderr.log
首先我們要測試我們的配置是否正確,由於supervisord默認是開啟deamon的,所以在測試supervisord是否正確工作的情況下我們先加一個參數,讓supervisord現在前台工作:
supervisord -c /data/vserver/supervisord.conf -n
這個時候如果你的supervisord如果已經正常工作且沒有錯誤的話,應該會顯示vservice以及send_mail兩個程序進入running狀態,下面是我的輸出,僅供參考:
1 2016-11-04 16:18:31,125 INFO RPC interface 'supervisor' initialized 2 2016-11-04 16:18:31,125 CRIT Server 'unix_http_server' running without any HTTP authentication checking 3 2016-11-04 16:18:31,125 INFO supervisord started with pid 1812 4 2016-11-04 16:18:32,128 INFO spawned: 'send_mail' with pid 1817 5 2016-11-04 16:18:32,129 INFO spawned: 'vservice' with pid 1818 6 2016-11-04 16:18:33,130 INFO success: send_mail entered RUNNING state, process has stayed up for > than 1 seconds (startsecs) 7 2016-11-04 16:18:33,131 INFO success: vservice entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
這個時候你可以通過supervisorctl 指令查看狀態,啟動,停止或者重啟對應的程序
查看狀態: supervisorctl -c /data/vservice/supervisord.conf status
執行以上指令之后之后顯示的結果:
send_mail RUNNING pid 1911, uptime 0:00:11 vservice RUNNING pid 1912, uptime 0:00:11
停止某個應用: supervisorctl -c /data/vservice/supervisord.conf stop send_mail(或者all或者vservice)
執行以上指令之后之后顯示的結果:
[allan@TEST vservice]# supervisorctl -c /data/vservice/supervisord.conf stop send_mail send_mail: stopped [allan@TEST vservice]# supervisorctl -c /data/vservice/supervisord.conf status send_mail STOPPED Nov 04 04:28 PM vservice RUNNING pid 1912, uptime 0:06:16
啟動某個應用: supervisorctl -c /data/vservice/supervisord.conf start send_mail(或者all或者vservice)
重啟某個應用: supervisorctl -c /data/vservice/supervisord.conf restart send_mail(或者all或者vservice)
一切測試正常之后,現在可以去掉supervisord的 -n參數,這時候supervisord會變成一個守護進程進入后台,至此在外部測試你的flask應用是否正常工作即可,部署在這時候已經基本完成。
在使用supervisord的過程中我遇到過不少的問題。
1、最開始在網上看到有人說supervisord對Python3的支持目前只是demo階段,還不穩定,當時心涼了一截,后來仔細查各種文檔及別人分享的經驗,發現supervisord 本身運行需要的Python環境是2.x的版本,因此在這個時候Python虛擬環境的重要性再次體現出來:使用2.x的版本環境運行supervisord,但是supervisord本身作為一個進程管理軟件,它啟動Python3開發的應用用是完全沒有問題的。
2、如果首次使用supervisord 啟動程序出現錯誤,比如exit with code 1, not expected等時,你再次運行supervisord時會報地址端口被占用的錯誤(在supervisord的錯誤日志目錄中),這時候你一定記得通過linux的進程管理查看下你的flask 應用是不是已經啟動了,因為很可能出現的情況是,supervisord已經把你的程序啟動了,但是出現錯誤是在其他的步驟中,因此你再次啟動時,會出現以上的錯誤情況。
3、supervisord的用戶配置問題,如root賬戶的問題。
以上是我的部署過程以及部署過程中曾經遇到過的問題,當然過程中也還有其他的一些小問題,並沒有一一的在這列出,如果發現有什么錯誤或者有相關的問題要進行交流討論,可以通過郵件進行交流:yanhaicheng@gmail.com