想做一些Python項目的自動上線部署工具,但是Python的包依賴,不能像Java那樣把具體的jar打包部署時直接解壓再改個設定檔就好,必須在部署之前要一個個安裝所依賴的模組,這樣一個是效率低不說,而且在安裝的過程中出錯的幾率也比較高,
想知道目前各互連網公司都是如何做的,有哪些成熟的方案 ?
回複內容:
> Python 的包依賴,不能像 Java 那樣把具體的 jar 打包部署時直接解壓
在一定程度上這是可以做到的。 類似 Java 的 jar 包,Python 長久以來存在一種 egg 包 (其實 wheel 包也可以,但我後面介紹的工具不支援), 只要放在 sys.path 中就可直接被 import。
比如我的這個項目 GitHub - youngking/buildout-package: My Custom buildout script
參考下 bin/buildout 的寫法, 代碼拖到哪裡都可以直接在項目下運行 bin/buildout。
使用 zc.buildout 這個工具能夠自動做我上面那個項目的封裝。這個工具可以把項目所有依賴的包 download 下來,並打成 egg 包。預設egg 包放在你的項目下的 eggs 目錄, 同時會產生一個 bin 目錄, 裡面放著根據當前項目的 setup.py 定義的 entrypoint 產生的各種命令, buildout 會修改命令指令碼的 sys.path, 把依賴的egg 都加進去。
假如你的項目之前是這樣的:
project/ setup.py src/ tests/ .....
- Docker 不是銀彈。開發偷懶了,營運、平台就要還債。如果想在生產環境使用它,你的團隊需要有成熟的營運,和有追蹤上遊 Bug 的能力的工程師,以及能從業務需求中抽身出來填技術坑的條件。所以,小公司小團隊請慎用。
- 複製 virtualenv 部署可行,但移植性不好。一來 virtualenv 不允許絕對路徑變動,二來 virtualenv 中一些已經裝上的包可能依賴系統的動態庫(如 OpenSSL),直接複製可能遇到 ABI 相容問題。
- 僅僅對於 Python 項目部署來說,利用 setup.py 打包是個不錯的選擇。一般 Web 應用程式中只有 requirements.txt 沒有 setup.py,那麼可以寫一個僅用於打包的 setup.py,其中 install_require 部分讀入 requirements.txt 的資料
用
setup.py 打包 Web 應用程式的話,需要一個 CI 伺服器,Travis CI、GitLab CI、Jenkins 等均可。
setup.py 預設不支援滾動發行,所以讀入
JOB_ID、
BUILD_ID 之類的 CI 環境變數作為版本號碼,或者用當前日期時間產生一個。CI 服務中,可以設定主倉庫
master 通過測試以後,自動向內部的 PyPI 發版本(如果用 Jenkins 也可以用 Jenkins 內建的靜態檔案服務)。在部署時再在內部 PyPI 中選擇一個版本,
pip install 到生產環境節點上。
基於
setup.py,可以通過
http://MANIFEST.in 的定義,將資源檔也打到發行包中,例如 gulp 編譯產生的前端靜態檔案。而且如果有其他項目(比如獨立的管理員後台)依賴當前應用,做服務又太麻煩時,可以直接向內部 PyPI 援引當前應用作為依賴。這樣可以諸如避免手動修改
sys.path 那樣的高度環境綁定且不可移植的 hack。
我在我現在就職的公司已經嘗試推行了這種方法,目前看來沒有遇到什麼坑的問題。而且打成
wheel 包之後,部署速度也變快了(省下了部署時編譯附加資源的時間)。這裡貼出我們在用的配置,供參考: setup.py - GitHub
其他的一些資訊:
- 如果需要搭建內網 PyPI,推薦 devpi
- setup.py 的 classifiers 一節請記得加入 Private :: Do Not Upload,公用 PyPI 會拒絕收錄有這個標記的包。這樣可以防止萌萌噠隊友手滑把公司代碼傳到公網 PyPI。
- 如果把項目的其他依賴也打成 wheel 發布到內網 PyPI,可以省下很多構建編譯的時間。但是要當心,wheel 格式目前無法做到 ABI 相容(這也是公用 PyPI 暫時只允許針對 Windows 和 OS X 發布 wheel 的原因),請盡量保持打包環境(即 CI 伺服器)所用的 Linux 發行版和生產環境一致
- 配置 CI 時不應該將內網 PyPI 的登入密碼提交到版本庫。絕大多數 CI 系統都支援私密環境變數
- 不管用不用 setup.py 來做打包,Python 項目的生產環境部署都應該使用 virtualenv,即使一台機器只部署一個應用。virtualenv 至少能將項目依賴和 Linux 發行版的依賴隔離開。
考慮下將依賴打成wheel包來離線安裝?瀏覽了以上所有人的答案,結合我平常在項目中的實際經驗,談談我們團隊的Python部署與發布流程。
目前很多公司還是用著
石器時代的部署方式,怎麼做呢?
1. 本地寫代碼,可能還沒有virtualenv環境,是的其實我的老東家就是這樣的。2. 寫一個指令碼,安裝需要的依賴到系統global環境,比如說 MySQLdb,可能還要用apt-get 或者 yum 安裝 python-dev 等等系統依賴,然後用pip 安裝Python依賴。3. 提交到svn/git,然後在測試機器上拉代碼下來,運行指令碼安裝完依賴後,如果是一個web項目,那麼可能會直接 python web.py 8080 測試一下會不會報錯,測試完幾個介面發現沒問題,關掉測試機器。4. 在生產環境把代碼拉下來,或者通過部署系統,這裡的部署系統一般是一個web頁面,能夠將svn/git 上的代碼打包後執行某一個指令碼,來完成相應的部署,也有可能是直接在機器上執行:nohup python /path/to/python/main.py 2&1 > /dev/null & 就啟動來這個進程,然後自己可能還有一些業務監控來定時的監控這個指令碼的存活狀態。5. 這裡可能nginx已經配置好,你發布的是一個Django應用,那麼開啟瀏覽器,查看網頁無誤。
對於互連網應用,依賴問題的最佳解決方案就是靜態聯編式的打包——我指的是思路而不是具體技術。
按照這個思路,要麼在語言層面上搞定它,比如說golang,如果是其它語言,用docker就對了@松鼠奧利奧的答案已經很完全了。貌似都在說docker,這裡我也說說docker的情況吧,畢竟我司也是重度docker依賴。
首先就回答一下 @alexsunmiu的那些疑問,顯然這位老兄用docker的姿勢不對:
1. Docker這個玩意,有容器和鏡像兩個主要的概念,擔負的職責不同。鏡像負責運行環境的封裝,容器則是運行時的封裝。至於提到的主流老作業系統,這個屬於具體的業務依賴,沒法使用Docker那也是沒法的事情,但是請不要擅自斷言那些老的系統是主流,每個廠都不一樣的。
2. 容器移植打包其實是很方便的,你看到的容量大,只是表面的東西。Docker底層設計是高度複用的。一個鏡像是很多個層疊在一起的,比如你有一個docker鏡像,裡面是ubuntu+python,另一個是ubuntu+java,如果兩者的ubuntu版本一樣,那麼這部分的layer就是複用的。如果你之前已經pull過這個對應的ubuntu的鏡像,那麼這部分是不會重複下載的,只會下載對應的python和java的部分。
3. Docker通過cgroup隔離,安全性雖然沒有核心層級隔離的vm那麼高,但是控制好許可權,也是沒那麼容易淪陷的。
4.你apt要那麼久,是因為你沒有替換原始ubuntu鏡像裡的sources.list,自然要被偉大的GFW制裁一番。事實上我國國情決定了無論使用啥外來工具,都得先改造一番。所以凡事用docker的廠子,都會先搞一個私人的registry和官方鏡像,然後再依據廠內的環境構建幾個基礎的docker鏡像。後面所有發布的app都是基於這些基礎鏡像來做。
5. Dockerfile的設計又不是給你做shell用的,這玩意是用來定義docker鏡像構建資訊的,你有複雜的構建邏輯,一樣可以封裝到專門的指令碼裡,然後ADD進去運行不就完事麼。
營運關注Docker的地方,主要在於穩定性,監控,日誌這幾個傳統方面。這些跟營運的KPI是掛鈎的。因為這東西還很新,而且特性還在慢慢增加,隱含的bug還是不少的,只不過只有在有足夠量,以及特定的一些情境(最常見的就是網路部分和日誌部分)才會出現。不是每個問題都能用自己熟悉的方式繞過去的。所以當碰到這些問題的時候,組裡有能追蹤bug的人才,甚至有能直接改代碼的人才會顯得很重要,因為這才能保證問題是可控的,否則哪天出問題了大家都搞不定,輕則年終獎完蛋,重則卷鋪子走人了。
=== update: 2016-01-15 ===
添加 @松鼠奧利奧 的評論:
除了營運問題之外,容器還有點像一個巨型“全靜態連結”,所以安全也是一個需要關注的問題…… 此前 coreos 開發了clair 檢查 quay.io 上的鏡像,一堆堆的都存在 CVE 已公布的安全問題……
安全性確實也是一個問題。沒人提到 zc.buildout 麼, 知乎正在用呀。AWS Codedeploy……只是作一下說明,其實依賴的包除了整體複製一份virtual environment這種主流做法外,還可以選擇帶著site package一起打包部署的……pyenv + 自建pypi源,沒有sei了。