2012-06-13 173 views
15

我們的Django項目變得越來越龐大。我們有數百個應用程序,並使用大量的第三方Python包,其中很多需要C編譯。當我們需要爲主要版本創建新的虛擬環境時,我們的部署需要很長時間。有了這個說法,我希望能夠加快速度,從Pip開始。有誰知道Pip的一個分支將並行安裝軟件包嗎?並行Pip安裝

步驟我已經採取了迄今:

  • 我尋找那些只是做這個收效甚微的一個項目。我確實發現了這個Github Gist:https://gist.github.com/1971720,但結果幾乎與我們單線程的朋友完全一樣。

  • 然後我在Github上找到了Pip項目,並開始查看fork的網絡以查看是否可以找到任何提到正在執行的操作。那裏很混亂。如果必須的話,我會分叉它並嘗試自己並行化,我只是想避免花時間這樣做。

  • 我在ep.io上看到了一個關於ep.io的演講,解釋了他們的部署內容,他們提到並行化pip,發佈.so文件,而不是編譯C和鏡像Pypi,但他們沒有涉及他們如何做或他們用了什麼。

+0

使用虛擬機作爲部署的單元,讓一切都變成OS(Debian的)包是我們做什麼。然後,您可以運行自己的存儲庫,並順利進行增量升級並完成安裝。預先構建OS包是確保您具有可重複安裝的好方法,您可以使它們依賴於非python的東西,如apache或nginx。 –

+0

@ NickCraig-Wood雖然這是一個好主意,但我們人手不足,沒有時間將所有的python包轉換成我們用於.debs的版本。我們已經在KVM上運行了所有的東西。我們只需要儘快部署。 – Kyle

+0

這是一個古老的問題,但現在你可以建立一個pip控制室緩存,大大減少了軟件包的安裝時間。 –

回答

7

您是否分析了部署過程以查看時間真的到了哪裏?令我感到驚訝的是,運行多個並行點子進程並沒有加速太多。

如果時間去查詢PyPI並找到軟件包(特別是當您從Github和其他源下載時),那麼設置您自己的PyPI可能會有所幫助。您可以自己主持的PyPI並添加以下到您的requirements.txt文件(docs):

--extra-index-url YOUR_URL_HERE 

或以下,如果你想完全取代了官方的PyPI:

--index-url YOUR_URL_HERE 

這可能會加快下載時間因爲現在所有的軟件包都在附近的機器上。

大量的時間也進入編譯與C代碼,如PIL包。如果這成爲瓶頸,那麼值得考慮在多個進程中編譯代碼。你甚至可以在你的機器之間共享已編譯的二進制文件(但許多東西需要類似,比如操作系統,CPU字長等)

+0

我的第一步是使用z3c.pypimirror鏡像Pypi,這有幫助。我認爲下一步是使用二進制文件來編譯或者並行化。我盡我所能瞭解了Gist代碼中發生的事情。我相信它正在調度子進程,並在稍後運行。我不知道如何確保所有的子進程使用gevent同時運行。 – Kyle

+0

如果你想加快編譯速度,我以前在ccache上運氣很好。 –

3

如果你有你的構建系統,它會有幫助嗎(例如Jenkins )構建並將所有內容安裝到特定於構建的虛擬環境目錄中?當構建成功時,您可以使虛擬環境可重定位,將其壓縮並將生成的選項卡推送到「發佈的tarball」存儲。在部署時,您需要獲取最新的tarball並將其解包到目標主機上,然後應該準備好執行。因此,如果需要2秒鐘下載tarball,0.5秒鐘將其解包到目標主機上,則您的部署需要2.5秒。

這種方法的優點是所有軟件包的安裝都在構建時發生,而不是在部署時發生。

注意事項:構建/編譯/安裝東西到虛擬環境中的構建系統工作者必須使用與目標硬件相同的架構。另外,您的生產箱配置系統需要處理一些Python包可能具有的各種C庫依賴關係(例如,PIL需要在編譯JPEG相關代碼之前安裝libjpeg,如果未在目標上安裝libjpeg也會中斷盒)

它適用於我們。

製作一個虛擬的env重定位:

virtualenv --relocatable /build/output/dir/build-1123423

在這個例子中build-1123423是特定生成的虛擬ENV目錄。

6

並行PIP安裝

此示例使用xargs大約4倍並行構建過程。您可以使用下面的max-procs來增加並行化因子(保持大約等於核心數)。

如果您嘗試加速您正在反覆進行的成像過程,它可能會更容易,更低的帶寬消耗,直接在結果上成像,而不是每次都做,或使用pip -tvirtualenv構建圖像。

下載和並行安裝軟件包,四在一個時間:

xargs --max-args=1 --max-procs=4 sudo pip install < requires.txt 

注:xargs的具有不同Linux發行版本不同的參數名。檢查您的發行版的手冊頁以瞭解具體情況。有一種遙遠的可能性,這種方法的速度可能會混淆軟件包清單(取決於你的發行版)如果多個PIP的安裝嘗試:用here-doc:

cat << EOF | xargs --max-args=1 --max-procs=4 sudo pip install 
awscli 
bottle 
paste 
boto                   
wheel 
twine                   
markdown 
python-slugify 
python-bcrypt 
arrow 
redis 
psutil 
requests 
requests-aws 
EOF 

警告

同樣的事情,內聯在同一時間同樣的依賴關係,但是如果你一次只做4個,這是不太可能的。它可以很容易地通過pip install --uninstall depname修復。

+1

很酷的黑客,但我討厭不得不調試一個依賴關係的問題:)你是怎麼來的順序? –

+0

謝謝@ItamarHaber ..我同意 - 這不會很有趣:)它是我通常使用的包文件的一個片段(它在一點上是按字母順序排列的)。 Spooky與Redis一起工作尤其酷(因爲它似乎並不在此列表中)。 –

-1

Jamieson Becker's answer的啓發,我修改了一個安裝腳本來執行並行點安裝,它看起來像和改進。我的bash腳本現在包含這樣的代碼片段:

requirements=''\ 
'numpy '\ 
'scipy '\ 
'Pillow '\ 
'feedgenerator '\ 
'jinja2 '\ 
'docutils '\ 
'argparse '\ 
'pygments '\ 
'Typogrify '\ 
'Markdown '\ 
'jsonschema '\ 
'pyzmq '\ 
'terminado '\ 
'pandas '\ 
'spyder '\ 
'matplotlib '\ 
'statlab '\ 
'ipython[all]>=3 '\ 
'ipdb '\ 
''tornado>=4' '\ 
'simplepam '\ 
'sqlalchemy '\ 
'requests '\ 
'Flask '\ 
'autopep8 '\ 
'python-dateutil '\ 
'pylibmc '\ 
'newrelic '\ 
'markdown '\ 
'elasticsearch '\ 
"'"'docker-py==1.1.0'"'"' '\ 
"'"'pycurl==7.19.5'"'"' '\ 
"'"'futures==2.2.0'"'"' '\ 
"'"'pytz==2014.7'"'"' ' 

echo requirements=${requirements} 
for i in ${requirements}; do (pip install $i > /tmp/$i.out 2>&1 &); done 

我至少可以手動查找問題。

+0

您應該使用需求文件(例如'pip install -r requirements.txt')。不太重要的是,Bash允許在字符串中換行,所以你不需要關閉字符串並跳過換行符 – Leons

8

基於Jamieson Becker的回答,下面的代碼會並行Pip下載,然後快速安裝軟件包。

首先,我們將軟件包並行下載到發行版(「dist」)目錄中。這很容易並行且沒有衝突。下載之前打印出每個包裝名稱,這有助於調試。要獲得額外幫助,請將-P9更改爲-P1,以便按順序下載。

下載後,下一個命令會告訴Pip安裝/更新軟件包。文件不下載,它們從快速本地目錄中獲取。

它與當前版本的PIP 1.7兼容,也與PIP 1.5兼容。

要僅安裝一個軟件包的子集,請將「cat requirements.txt」語句替換爲您的自定義命令,例如, 「egrep的-v github上requirement.txt」

cat requirements.txt | xargs -t -n1 -P9 pip install -q --download ./dist 

pip install --no-index --find-links=./dist -r ./requirements.txt 
+2

我喜歡它!這會慢一些(如果你正在編譯C擴展,它不會並行實際安裝),但是這很好地防止了任何併發寫入問題。很酷的想法。 –

+0

不幸的是,這很容易弄亂依賴關係的版本。但是,如果所有庫版本都受到限制(使用-c開關),它應該可以正常工作。 – allprog