在服務器端實現(xiàn)無間斷部署Python應用的教程
當你開始著手部署應用時,最簡單的方式莫過于使用管理員身份重啟my_app或者所有服務,使產(chǎn)品升級至當前版本。開始的時候一切都很好,但是最終你會發(fā)現(xiàn)一旦應用啟動以后,在重啟期間去嘗試連接會得到眾多HTTP 503 錯誤。
最后你可能發(fā)現(xiàn)Gunicorn和uWSGI可以在不關(guān)閉套接字的情況下重新加載你的應用,這樣在你的應用啟動時,網(wǎng)絡請求僅僅是被延時了一點點。只要你的應用不會花費很長時間在啟動上,它就會工作的很好。不幸的是,現(xiàn)有的許多應用可能會花費1分鐘的時間在啟動上,對于等待在套接字上的鏈接來說,這太長了。
Gunicorn使用kill -HUP $PID,通過關(guān)閉所有工作進程,然后再啟動它們來重新加載。但是工作進程緩慢的初始化過程往往會導致問題的產(chǎn)生。uWSGI使用鏈式重載,它每次只會啟動一個工作進程。我需要對Tornado的支持,它當前并不十分適合uWSGI。
使用負載均衡器
一種常見的技術(shù)是從負載均衡器中移除單個服務器,升級/重啟應用,然后再把它加載回來。我們正在使用負載均衡器,但是為了調(diào)度整個過程,在配置節(jié)點的時候需要協(xié)調(diào)使用HAProxy來管理套接字。我們當前的部署方案是同時部署到所有節(jié)點,而不是一個接一個的來,一個相當大的變化。在等待LBs(譯注:負載均衡器)將節(jié)點移出池期間,可以使用404'ing狀態(tài)頁來欺騙healthcheck。這比我想要的時間要多一點,對于每個服務器來說,兩次healthcheck失敗間隔5秒鐘,這包括了升級完成后web進程恢復的時間。
Gunicorn 重載 ++
Gunicorn會自動重啟失敗的web進程,所以它可能會殺掉每個進程,在其間休眠,直到所有的子進程執(zhí)行完畢。這很有效,不過如果應用啟動的次數(shù)變動顯著的話,我們要么會為重啟等待過長時間,要么會等待不長的時間并承擔一些故障宕機的風險。
因為Gunicorn包含了指向應用的Python鉤子,所以完全可能寫出一小段代碼,在工作進程準備就緒的時候通知重啟進程。Gunicorn并不包含需要的鉤子,但做出改變非常簡單。在新版本發(fā)布前它需要一些修改。
現(xiàn)在重啟進程發(fā)揮了這樣的事實優(yōu)勢,就是說單個的soket具有接受連接的多個進程。重啟只會極微弱的減少服務能力(1/N),但我們因此可以繼續(xù)處理流量而無需讓連接等待過長時間。
這種進程一般是這樣的
for child_pid of gunicorn-master: kill child_pid wait for app startup
我的第一個版本使用shell和nc來監(jiān)聽應用啟動的UDP數(shù)據(jù)包。盡管將我們的進程管理器集成到shell環(huán)境比我預想的要麻煩一點,但它工作的很好。
重啟腳本被調(diào)用的時候應該帶上Gunicorn的PID,就是masterrestart.sh的 $PID
echo 'Killing children of ' $1; children=$(pgrep -P $1) for child in $children do echo 'Killing' $child kill $child response=$(timeout 60 nc -w 0 -ul 4012) if [ "$response" != '200 OK' ]; then echo 'BROKEN' exit 1; fi done
在串聯(lián)上post_worker_init腳本,以便app運行的時候通知重啟腳本。
import socket import time def post_worker_init(worker): _send_udp('200 OK\n') def _send_udp(message): udp_ip = "127.0.0.1" udp_port = 4012 sock = socket.socket(socket.AF_INET, # Internet socket.SOCK_DGRAM) # UDP sock.sendto(message, (udp_ip, udp_port)) 如果我們有這樣一個WSGI( Python Web Server Gateway Interface)應用: from werkzeug.wrappers import Request, Response @Request.application def application(request): resp = Response('Hello World!') if request.path == '/_status': resp.status = '200 OK' else: resp.status ='404 Not Found' return resp
我們甚至可以去做檢查/_status頁面之類的事情,以此來驗證應用是否已運行。
def post_worker_init(worker): env = { 'REQUEST_METHOD': 'GET', 'PATH_INFO': '/_status', } def start_response(*args, **kwargs): _send_udp(args[0]) worker.wsgi(env, start_response)
注意不要試圖在這個健康檢測中運行太多的應用,如果不管什么原因你的post_worker_init產(chǎn)生了一個錯誤,那么工作進程將會退出,并阻止應用的啟動。在你檢查可能失效的DB鏈接的時候這會是一個問題,即使你的應用可以工作,它也無法再次啟動。
現(xiàn)在通過一分鐘的應用啟動,我們實現(xiàn)了滾動重啟,而無需停止應用或者丟棄任何鏈接!
相關(guān)文章
使用Python腳本對GiteePages進行一鍵部署的使用說明
剛好之前有了解過python的自動化,就想著自動化腳本,百度一搜還真有類似的文章。今天就給大家分享下使用Python腳本對GiteePages進行一鍵部署的使用說明,感興趣的朋友一起看看吧2021-05-05詳解python函數(shù)的閉包問題(內(nèi)部函數(shù)與外部函數(shù)詳述)
這篇文章主要介紹了python函數(shù)的閉包問題,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-05-05Sublime?Text4?配置?Python3?環(huán)境、代碼提示、編譯報錯的解決方案
這篇文章主要介紹了Sublime?Text4?配置?Python3?環(huán)境、代碼提示、編譯報錯教程,通過圖文并茂的形式給大家介紹了配置自動代碼提示的方法,需要的朋友可以參考下2022-01-01matlab中二維插值函數(shù)interp2的使用詳解
這篇文章主要介紹了matlab中二維插值函數(shù)interp2的使用詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-04-04Python創(chuàng)建SQL數(shù)據(jù)庫流程逐步講解
會寫SQL很重要,能高效地查詢數(shù)據(jù)庫被認為是數(shù)據(jù)分析師/科學家最基本的技能之一。SQL不僅重要,而且非常常用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習吧2022-09-09