一種部署 Python 程式碼的新方法

範二少年發表於2016-05-12

在Nylas,我們喜歡使用Python進行開發。它的語法簡單並富有表現力,擁有大量可用的開源模組和框架,而且這個社群既受歡迎又有多樣性。我們的後臺是純用 Python 寫的,團隊也經常在 PyCon 和 meetups演講。你可以認為我們是 Python 的超級粉。

一種部署 Python 程式碼的新方法

然而,Python 的一個大缺陷是沒有一個明確的工具來部署 Python 服務端應用。工作的情況就像是“執行 git 的 pull 命令後剩下的就只有祈禱了”,但這並不是一個好的方式,尤其當使用者依賴於我們的應用。當你的應用引用了很多仍在變化的依賴時,這會讓 Python 的部署工作變得更加複雜。下面 HN 上的評論概括了 Python 部署的糟糕情況。

 

為什麼這麼多年了,仍沒有一個有效的辦法幫我將 Python 編寫的軟體轉換成 deb 格式?

—— 來自一個受挫的 HN 使用者

在 Nylas,我們開發了一種更好的方法可以將 Python 程式碼連同其依賴一起部署,使得我們能夠輕鬆地對輕量級包進行安裝、升級或者刪除。該方式的實現並不需要將我們的整個棧遷移到像 Docker、 CoreOS 或者AMIs 這樣的系統上。

一種部署 Python 程式碼的新方法

新手才用 GIT 和 PIP 進行 Python 部署

Python 提供了豐富的模組。不管你搭建的是一個web 伺服器還是機器學習分類器,總會有一個合適的模組幫你啟動專案。現在獲取這些模組的標準方法是通過 pip 從 Python 包索引 (亦稱 PyPI)中下載和安裝。這就跟 apt,yum,rubgem 等命令操作是一樣。

大多數人搭建開發環境的第一步就是用 git 克隆份程式碼,然後通過 pip 安裝其依賴。這也是大多數人第一次嘗試部署程式碼的做法。部署指令碼大致如下:

但是當部署大量生產服務時,這種策略有下面幾個原因可能導致失敗:

PIP 並沒有提供“部署回滾”策略

pip unistall 並不是每次都能正常工作,也沒有辦法“回滾”到上一個狀態。雖然可以用Virtualenv 實現,但它並不是用來管理歷史環境的。

用 pip 安裝依賴會讓部署變得極其慢

使用pip install 來安裝一個由C語言編寫的模組經常需要從原始碼進行編譯,對於一個新建立的virtualenv環境,這將花費幾分鐘的時間。但部署應用應該是一個以秒計算的快速而輕量的過程。

在每臺主機上分別構建程式碼會有一致性問題

當你使用 pip 部署時,無法保證不同伺服器上執行的應用版本是一致的。構建過程或現有依賴中的錯誤導致的不一致,這是很難去除錯的。

如果 PyPI 或者你的 git 伺服器掛掉會導致部署失敗

pip installgit pull 通常依賴於外部伺服器。你可以選擇使用第三方平臺 (如 Github,PyPI)或者自己搭建伺服器,重要的是確保部署過程滿足正常執行時間和規模的預期。當擴充套件自己的基礎設施,尤其是在大型部署時,外部服務往往是第一個掛掉的。

如果你在運營一個人們依賴的應用系統,而且它又是部署在多個伺服器上,那麼採用 git + pip 部署策略只會讓你更加頭痛。我們需要的部署策略應該是快速的、一致而且可靠的。更具體地說:

  1. 能夠構建程式碼成單一、有版本控制的工件
  2. 受版本控制的工件可以進行單元測試和系統測試
  3. 一種簡單的機制可以從遠端主機完全的安裝或解除安裝工件

有了這三樣東西可以讓我們將更多的時間花在功能的構建上,以更少的時間進行程式碼一致性遷移。

“乾脆用DOCKER吧”

初看下,這似乎最適合用 Docker 了,Docker 是當前盛行的容器管理工具。在一個 Dockerfile裡,只要簡單地新增程式碼倉庫的引用,安裝必要的庫和依賴。那麼我們就建好 Docker 映象,將它作為受版本控制的工件發往到遠端主機。

然而,當我們嘗試這樣操作時遇到了幾個問題:

  • 我們的核心版本 (3.2)並不完美支援 Docker,如果為了更快的遷移程式碼而升級核心,我們覺得這有點像在用牛刀殺雞。
  • 在專用網路裡分發 Docker 映象要有一個獨立服務,這樣我們又需要對服務進行配置,測試和維護。
  • ansible 自動化安裝轉換成 Dockerfile 的過程是痛苦的,需要對日誌配置、使用者許可權和祕鑰管理等進行大量繁瑣的編輯。

一種部署 Python 程式碼的新方法

即便我們順利的解決了這些問題,為了除錯生產上的問題,我們的工程師團隊又不得不學習如何跟 Docker 連線互動。我們認為更快地遷移程式碼並不應該重新執行整個基礎架構自動化和編排層。所以我們繼續調查其他方案。

PEX

PEX 是 Twitter 開發的一個智慧工具,它允許 Python 程式碼以可執行壓縮檔案進行傳送。這是一個很酷的想法,關於這個主題我們建議去看Brian Wickman 在推特大學的演講

設定 PEX 比 Docker 還簡單,因為他只需要執行生成好的可執行壓縮檔案,但是構建 PEX 檔案卻是一項巨大的工程。我們在構建第三方庫需求時遇到問題,特別是當中包含了靜態檔案。我們也遭遇到 PEX 原始碼產生的混亂的堆疊跟蹤,使得除錯工作變得更加困難。這是個異份子,因為我們主要目標是改善工程效率,讓事情更加易懂。

使用 Docker 會增加執行時的複雜度。而 PEX 會增加構建時的複雜度。我們需要一個方案可以最小化整體複雜度,同時提供可靠的部署,所以我們繼續調查其他方案。

包:原始的“容器”

一種部署 Python 程式碼的新方法

幾年前,Spotify 悄悄釋出了一個工具叫 dh-virtualenv,你可以用來構建內含 virtualenv 的 debian 包。我們覺得這很有意思,也已經有了很多 Debian 相關經驗並在生產環境上執行。(Christine,我們的聯合創始人之一,就是一個 Debian 的開發者。)

dh-virtualenv可以很簡單的建立一個 debian 包,它包含了 virtualenv 和羅列在requirement.txt 檔案裡的所有依賴。當在主機上安裝這個 debian 包時, 它會將 virtualenv 放置在/usr/share/pyton/路徑下。就是它了。

這就是我們在 Nylas 上部署程式碼的關鍵。我們的持續整合伺服器 (Jenkins) 執行 dh-virtualenv 來構建包, 用 Python 的wheel 快取來避免對依賴重新構建。這就建立一個捆綁式工件(debian 包),然後對其進行大量的單元測試和系統測試。如果通過測試,則可認為生產上是安全的,可以上傳到 s3.

這個過程的關鍵部分是通過均衡 Debian 的內建包管理器 dpkg,我們可以最小化部署指令碼的複雜度。部署指令碼大致如下:

要回滾的話,我們只需要部署上一版本的工件。dpkg 工具可以免費幫你清除舊程式碼。

這個策略最重要的一方面是它實現了一致性和可靠性,同時匹配了我們的開發環境。我們的工程師已經在用virtualenv,而dh-virtualenv 只是一個將其遷往遠端主機的方式。如果我們選擇 Docker 或者 PEX,明顯需要改變我們本地開發的方式又增加了複雜度。我們同樣不希望給使用我們開原始碼的開發者帶來複雜度負擔。

現在,我們用 Debian 包來遷移我們所有的 Python 程式碼。完整構建我們程式碼庫(包含數十個依賴)只要不到2分鐘時間,部署更是數秒便完成。

DH-VIRTUALENV 入門

如果你受夠了 Python 部署的折磨,那就試試 dh-virtualenv吧。它會是你不錯的選擇!

配置 Debian 包對於初學者而言是棘手的,所以我們構建了 make-deb 工具來幫你入手。它會基於你 Python 專案裡的 setup.py 檔案生成 Debian 配置。

首先安裝make-deb工具,然後在你專案的根目錄下執行它:

如果你的 setup.py 檔案有缺失資訊的話,make-deb會要求你新增。一旦它收齊所需的資訊,make-deb會在你專案的根目錄下建立一個 debian 目錄,包含了 dh-virtualenv 需要的所有配置。

構建 Debian 包需要你在裝有 dh-virtualenv 的 Debian 系統上進行。如果你沒有 Debian 環境,我們建議你在 Mac 或者 Windows 上用 Vagrant 和 Virtualbox 安裝一個 Debian 的虛擬機器。你也可以參考我們放在 Git 倉庫下 sync-engine 專案裡的Vagrantfile 來作配置。

最後,執行dpkg-builpackage -us -uc來建立 Debian 包。你不需要直接呼叫 dh-virtualenv,因為它已經在之前make-deb建立好的配置規則裡了。當這條命令執行好後,你就構建好一個可部署的漂亮的工件。

一段簡單的部署指令碼如下:

部署時,你需要將這個工件上傳到生產伺服器上。執行dpkg -i my-package.deb命令來安裝。virtualenv 會被放在/usr/share/python/目錄下,所有預設在 setup.py 檔案裡的指令碼檔案則會在bin目錄裡。就是它了!你這就走上了簡易部署的光明大道。

總結

在構建大型系統時,專案難點往往是在尋求合適的工具,而非從頭開始重構一個新的系統。我們認為使用 Debian 基於包的部署是部署 Python 應用的一個極佳方案,最重要的是它可以幫我們平穩而快速的遷移程式碼。

聯絡我們,如果你有任何的意見或建議,又或者是覺得這篇文章很有意思。感謝你閱讀這篇文章!

打賞支援我翻譯更多好文章,謝謝!

打賞譯者

打賞支援我翻譯更多好文章,謝謝!

任選一種支付方式

一種部署 Python 程式碼的新方法 一種部署 Python 程式碼的新方法

相關文章