編程派微信號:codingpy
Python部署是一件讓人頭疼的事。你有沒有碰到過 PyPI 服務器掛掉的情況?本文提供了 Git + PyPI 部署之外的方法,能夠避免部署 Python 應用時可能會遇到的坑。本文分享自伯樂在線。
在Nylas,我們喜歡使用Python進行開發。它的語法簡單并富有表現力,擁有大量可用的開源模塊和框架,而且這個社區既受歡迎又有多樣性。我們的后臺是純用 Python 寫的,團隊也經常在 PyCon 和 meetups 上演講。你可以認為我們是 Python 的超級粉。
然而,Python 的一個大缺陷是沒有一個明確的工具來部署 Python 服務端應用。工作的情況就像是“執行 git 的 pull 命令后剩下的就只有祈禱了”,但這并不是一個好的方式,尤其當用戶依賴于我們的應用。當你的應用引用了很多仍在變化的依賴時,這會讓 Python 的部署工作變得更加復雜。下面 HN 上的評論概括了 Python 部署的糟糕情況。
為什么這么多年了,仍沒有一個有效的辦法幫我將 Python 編寫的軟件轉換成 deb 格式?—— 來自一個受挫的 HN 用戶
在 Nylas,我們開發了一種更好的方法可以將 Python 代碼連同其依賴一起部署,使得我們能夠輕松地對輕量級包進行安裝、升級或者刪除。該方式的實現并不需要將我們的整個棧遷移到像 Docker、 CoreOS 或者AMIs 這樣的系統上。
新手才用 GIT 和 PIP 進行 Python 部署
Python 提供了豐富的模塊。不管你搭建的是一個web 服務器還是機器學習分類器,總會有一個合適的模塊幫你啟動項目。現在獲取這些模塊的標準方法是通過 pip 從 Python 包索引 (亦稱 PyPI)中下載和安裝。這就跟 apt,yum,rubgem 等命令操作是一樣。
大多數人搭建開發環境的第一步就是用 git 克隆份代碼,然后通過 pip 安裝其依賴。這也是大多數人第一次嘗試部署代碼的做法。部署腳本大致如下:
git clone https://github.com/company/somerepo.git
cd /opt/myproject
pip install -r requirements.txt
python start_server.py
但是當部署大量生產服務時,這種策略有下面幾個原因可能導致失敗:
PIP 并沒有提供“部署回滾”策略
pip unistall 并不是每次都能正常工作,也沒有辦法“回滾”到上一個狀態。雖然可以用Virtualenv 實現,但它并不是用來管理歷史環境的。
用 pip 安裝依賴會讓部署變得極其慢
使用pip install 來安裝一個由C語言編寫的模塊經常需要從源碼進行編譯,對于一個新建立的virtualenv環境,這將花費幾分鐘的時間。但部署應用應該是一個以秒計算的快速而輕量的過程。
在每臺主機上分別構建代碼會有一致性問題
當你使用 pip 部署時,無法保證不同服務器上運行的應用版本是一致的。構建過程或現有依賴中的錯誤導致的不一致,這是很難去調試的。
如果 PyPI 或者你的 git 服務器掛掉會導致部署失敗
pip install 和 git pull 通常依賴于外部服務器。你可以選擇使用第三方平臺 (如 Github,PyPI)或者自己搭建服務器,重要的是確保部署過程滿足正常運行時間和規模的預期。當擴展自己的基礎設施,尤其是在大型部署時,外部服務往往是第一個掛掉的。
如果你在運營一個人們依賴的應用系統,而且它又是部署在多個服務器上,那么采用 git + pip 部署策略只會讓你更加頭痛。我們需要的部署策略應該是快速的、一致而且可靠的。更具體地說:
能夠構建代碼成單一、有版本控制的工件
受版本控制的工件可以進行單元測試和系統測試
一種簡單的機制可以從遠程主機完全的安裝或卸載工件
有了這三樣東西可以讓我們將更多的時間花在功能的構建上,以更少的時間進行代碼一致性遷移。
“干脆用DOCKER吧”
初看下,這似乎最適合用 Docker 了,Docker 是當前盛行的容器管理工具。在一個 Dockerfile里,只要簡單地添加代碼倉庫的引用,安裝必要的庫和依賴。那么我們就建好 Docker 鏡像,將它作為受版本控制的工件發往到遠程主機。
然而,當我們嘗試這樣操作時遇到了幾個問題:
我們的內核版本 (3.2)并不完美支持 Docker,如果為了更快的遷移代碼而升級內核,我們覺得這有點像在用牛刀殺雞。
在專用網絡里分發 Docker 鏡像要有一個獨立服務,這樣我們又需要對服務進行配置,測試和維護。
將 ansible 自動化安裝轉換成 Dockerfile 的過程是痛苦的,需要對日志配置、用戶權限和秘鑰管理等進行大量繁瑣的編輯。
即便我們順利的解決了這些問題,為了調試生產上的問題,我們的工程師團隊又不得不學習如何跟 Docker 連接交互。我們認為更快地遷移代碼并不應該重新執行整個基礎架構自動化和編排層。所以我們繼續調查其他方案。
PEX
PEX 是 Twitter 開發的一個智能工具,它允許 Python 代碼以可執行壓縮文件進行傳送。這是一個很酷的想法,關于這個主題我們建議去看Brian Wickman 在推特大學的演講。
設置 PEX 比 Docker 還簡單,因為他只需要運行生成好的可執行壓縮文件,但是構建 PEX 文件卻是一項巨大的工程。我們在構建第三方庫需求時遇到問題,特別是當中包含了靜態文件。我們也遭遇到 PEX 源代碼產生的混亂的堆棧跟蹤,使得調試工作變得更加困難。這是個異份子,因為我們主要目標是改善工程效率,讓事情更加易懂。
使用 Docker 會增加運行時的復雜度。而 PEX 會增加構建時的復雜度。我們需要一個方案可以最小化整體復雜度,同時提供可靠的部署,所以我們繼續調查其他方案。
包:原始的“容器”
幾年前,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,我們可以最小化部署腳本的復雜度。部署腳本大致如下:
temp=$(mktemp /tmp/deploy.deb.XXXXX)
curl “https://artifacts.nylas.net/sync-engine-3k48dls.deb” -o $temp
dpkg -i $temp
sv reload sync-engine
要回滾的話,我們只需要部署上一版本的工件。dpkg 工具可以免費幫你清除舊代碼。
這個策略最重要的一方面是它實現了一致性和可靠性,同時匹配了我們的開發環境。我們的工程師已經在用virtualenv,而dh-virtualenv 只是一個將其遷往遠程主機的方式。如果我們選擇 Docker 或者 PEX,明顯需要改變我們本地開發的方式又增加了復雜度。我們同樣不希望給使用我們開源代碼的開發者帶來復雜度負擔。
現在,我們用 Debian 包來遷移我們所有的 Python 代碼。完整構建我們代碼庫(包含數十個依賴)只要不到2分鐘時間,部署更是數秒便完成。
DH-VIRTUALENV 入門
如果你受夠了 Python 部署的折磨,那就試試 dh-virtualenv吧。它會是你不錯的選擇!
配置 Debian 包對于初學者而言是棘手的,所以我們構建了 make-deb 工具來幫你入手。它會基于你 Python 項目里的 setup.py 文件生成 Debian 配置。
首先安裝make-deb工具,然后在你項目的根目錄下運行它:
cd /my/project
pip install make-deb
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創建好的配置規則里了。當這條命令執行好后,你就構建好一個可部署的漂亮的工件。
一段簡單的部署腳本如下:
scp my-package.deb remote-host.example.org:
ssh remote-host.example.org
# Run the next commands on remote-host.example.org
dpkg -i my-package.deb
/usr/share/python/myproject/bin/python
>>> import myproject # it works!
部署時,你需要將這個工件上傳到生產服務器上。運行dpkg -i my-package.deb命令來安裝。virtualenv 會被放在/usr/share/python/目錄下,所有預設在 setup.py 文件里的腳本文件則會在bin目錄里。就是它了!你這就走上了簡易部署的光明大道。
總結
在構建大型系統時,項目難點往往是在尋求合適的工具,而非從頭開始重構一個新的系統。我們認為使用 Debian 基于包的部署是部署 Python 應用的一個極佳方案,最重要的是它可以幫我們平穩而快速的遷移代碼。
本文由 伯樂在線 - 范二少年 翻譯,艾凌風 校稿。
英文出處:Rob McQueen。
歡迎轉發至朋友圈。如無特殊注明,本公號所發文章均為原創或編譯,如需轉載,請聯系「編程派」獲得授權。
【近期優秀教程推薦】
掃碼關注編程派
獲取最新教程及資源推送
↓↓↓ 點擊閱讀原文,查看更多Python教程