Python 無間斷部署

發表於2016-01-17

當你開始著手部署應用時,最簡單的方式莫過於使用管理員身份重啟my_app或者所有服務,使產品升級至當前版本。開始的時候一切都很好,但是最終你會發現一旦應用啟動以後,在重啟期間去嘗試連線會得到眾多HTTP 503 錯誤。

最後你可能發現Gunicorn和uWSGI可以在不關閉套接字的情況下重新載入你的應用,這樣在你的應用啟動時,網路請求僅僅是被延時了一點點。只要你的應用不會花費很長時間在啟動上,它就會工作的很好。不幸的是,現有的許多應用可能會花費1分鐘的時間在啟動上,對於等待在套接字上的連結來說,這太長了。

Gunicorn使用kill -HUP $PID,通過關閉所有工作程式,然後再啟動它們來重新載入。但是工作程式緩慢的初始化過程往往會導致問題的產生。uWSGI使用鏈式過載,它每次只會啟動一個工作程式。我需要對Tornado的支援,它當前並不十分適合uWSGI。

使用負載均衡器

一種常見的技術是從負載均衡器中移除單個伺服器,升級/重啟應用,然後再把它載入回來。我們正在使用負載均衡器,但是為了排程整個過程,在配置節點的時候需要協調使用HAProxy來管理套接字。我們當前的部署方案是同時部署到所有節點,而不是一個接一個的來,一個相當大的變化。在等待LBs(譯註:負載均衡器)將節點移出池期間,可以使用404’ing狀態頁來欺騙healthcheck。這比我想要的時間要多一點,對於每個伺服器來說,兩次healthcheck失敗間隔5秒鐘,這包括了升級完成後web程式恢復的時間。

Gunicorn 過載 ++

Gunicorn會自動重啟失敗的web程式,所以它可能會殺掉每個程式,在其間休眠,直到所有的子程式執行完畢。這很有效,不過如果應用啟動的次數變動顯著的話,我們要麼會為重啟等待過長時間,要麼會等待不長的時間並承擔一些故障當機的風險。

因為Gunicorn包含了指向應用的Python鉤子,所以完全可能寫出一小段程式碼,在工作程式準備就緒的時候通知重啟程式。Gunicorn並不包含需要的鉤子,但做出改變非常簡單。在新版本釋出前它需要一些修改。

現在重啟程式發揮了這樣的事實優勢,就是說單個的soket具有接受連線的多個程式。重啟只會極微弱的減少服務能力(1/N),但我們因此可以繼續處理流量而無需讓連線等待過長時間。

這種程式一般是這樣的

我的第一個版本使用shell和nc來監聽應用啟動的UDP資料包。儘管將我們的程式管理器整合到shell環境比我預想的要麻煩一點,但它工作的很好。

重啟指令碼被呼叫的時候應該帶上Gunicorn的PID,就是masterrestart.sh的 $PID

在串聯上post_worker_init指令碼,以便app執行的時候通知重啟指令碼。

如果我們有這樣一個WSGI(
Python Web Server Gateway Interface)應用:

我們甚至可以去做檢查/_status頁面之類的事情,以此來驗證應用是否已執行。

注意不要試圖在這個健康檢測中執行太多的應用,如果不管什麼原因你的post_worker_init產生了一個錯誤,那麼工作程式將會退出,並阻止應用的啟動。在你檢查可能失效的DB連結的時候這會是一個問題,即使你的應用可以工作,它也無法再次啟動。

現在通過一分鐘的應用啟動,我們實現了滾動重啟,而無需停止應用或者丟棄任何連結!

相關文章