python環境部署(一) —— pip依賴遷移

前言

工程實際應用時,我們需要考慮如何在各種情況下順利地將工程的運行環境部署起來。
就Python工程來說,最主要的就是將程序運行所需的各種依賴模塊安裝起來。目前Python最常用包管理工具是conda和pip,其中conda還具有虛擬環境管理功能,而且conda環境下可能還有很多包是通過pip安裝的。
避免混亂,我將分兩篇文章分別介紹純粹使用pip以及使用conda虛擬環境時的部署方法。這篇文章,從簡單的pip開始。

教條的方法

pip提供了導出依賴的方法,在測試機器上執行以下命令導出依賴文件requirements.txt:

pip freeze >  requirements.txt

文件格式如下:

<package>==<version>

隨後,在待部署機器上,使用以下命令安裝就可以恢復依賴環境:

pip install -r requirements.txt

網上能搜到的大部分教程,大都是這麼寫的。所以我稱之爲教條的方法。如果真的有這麼暢通無阻,那就真的太好了,畢竟誰都不願意折騰。但是事實上,在實際部署中,存在這樣那樣的各種問題,想不折騰的話,請大家看下去。

問題一、找不到匹配的包

一個最常遇到的問題是:找不到匹配的包(No matching distribution found)。在這個提示信息之前,還會有Could not find a version that satisfies the requirement … 。很多情況下確實只是找不到適配的版本,而有的時候是真的找不到包。

pip的鏡像源處於不斷更新的狀態,而且更新異常活躍,甚至有人發佈了包又將其撤回、或者刪去之前的版本,都可能造成找不到對應版本的包的情況。

例如:
在這裏插入圖片描述
而如果一些包來源於外部鏈接,在pip倉庫中並沒有的話,得到的提示仍然是No matching distribution found,這時pip是真找不到這個包了。例如:

在這裏插入圖片描述

屬於第一種情況的,有以下幾個可能的解決方法:

(1)調整requirements.txt,把“==”變成“>=”。這樣即使源更新了,還是能找到絕大多數包的。但是,這方法其實只是取巧。調整後的整體依賴環境是否能夠正常、穩定地工作,並沒有得到驗證。

(2)更新測試機器上的包,在測試機器上驗證是否能夠正常、穩定地工作後,再導出requirements.txt。

批量更新pip的包可以使用:

pip list --outdated --format=freeze | grep -v '^\-e' | cut -d = -f 1  | xargs -n1 pip install -U
(如果不支持的話可能是pip版本太舊,先升級下pip)

這個方法也存在一些問題。
首先,升級新版本是冒險的,由其對於開發週期較長的程序來說,開發過程中,可能所依賴的包的版本已經有了較大的更新,程序中用到的一些方法可能在新版本中都被取消了,所以如果要升級,升級後一定要確保程序可以正常且穩定地運行。這個可能就比較耗時了。
其次,如果測試機器上升級完並導出後,並不具備快速進行部署的條件,升級也是枉然的,間隔幾天後進行部署,依然面臨一些包不匹配的問題。這個問題的根源就是pip源的不斷更新,那麼,要徹底解決這個問題,就得建立自己的源或者在安裝時指定本地文件了。這一點,後面在離線部署時一併介紹。

屬於第二種情況的,可以用以下方法解決:

(1)在requirements.txt中,相應的包之前添加–find-links,例如:

--find-links https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-2.1.0/en_core_web_sm-2.1.0.tar.gz
en_core_web_sm==2.1.0

問題二、不支持離線部署

源的不斷更新可以令用戶更快用上來自上游的更新,但是對部署來說卻未必是一件好事。
爲了實現離線部署,可以將安裝所需的文件從源上下載下來,然後從本地安裝。
首先,還是需要導出requirements.txt文件,然後在測試機器上使用以下命令下載所有的安裝包:

pip download -d ~/my_env_pkgs/ -r requirements.txt

在下載文件的過程中,我們依然會遇到找不到適配的包的問題。這時要做的是,仔細對照這些包,如果並不是十分重要的包,可以在本地更新修改requirements.txt文件,下載更新的版本;如果非常關鍵的包(比如做的深度學習的項目,用到的深度學習框架),應該去網上搜索,找到舊版本的包,放到~/my_env_pkgs/ 目錄下,requirements.txt中可以暫時刪除這一項;通過外部鏈接安裝的包,就將包先下載到~/my_env_pkgs/ 目錄下,同樣地,requirements.txt中可以暫時刪除這一項。

如此一來,requirements.txt文件(記得補上剛剛暫時刪去的項)中的依賴與所提供的文件及版本就是完全一致的了,在部署時就不會遇到上述的問題。

複製到待部署機器上後,可以使用以下命令安裝:

pip install -r requirements.txt --no-index --find-links=file:///home/user_name/my_env_pkgs

其中 --no-index代表不去搜索源,file://是必須的,此後再接上絕對路徑(一定要使用絕對路徑,所以有三個/)。

當然,你也可以構建一個本地的pypi源:

# 安裝pip2pi模塊
pip install pip2pi
# 建立索引(my_env_pkgs目錄下會多出一個simple文件夾)
dir2pi ~/my_env_pkgs/
# 進入目標文件夾(就是對外發布的文件夾,因爲開啓HTTP Server是將當前文件夾發佈)
cd ~/my_env_pkgs/simple/
# 開啓HTTP Server - Python3
python -m http.server
# 開啓HTTP Server - Python2
python -m SimpleHTTPServer

然後使用以下命令安裝:

pip install -i http://127.0.0.1:8000/ -r requirements.txt

二者無甚區別。

問題三、跨平臺

當需要跨平臺部署環境時,最妥當的方法應該是在不同的平臺上做好測試,然後再按以上方法分別部署。我並不建議大家用我下面說的方法直接部署,而是希望大家將其當做減少在新測試機器上部署測試環境的工作量的方法。
一般來說,在發佈一個模塊時,開發者會同時發佈支持多個平臺的獨立的binary distribution(擴展名爲whl)和一個source distribution(簡稱sdist,擴展名爲tar.gz),但這並不是強制要求,所以存在例外。
在前面的方法中,我們稍作修改:

pip download --no-binary=:all: -d ~/my_env_pkgs/ -r requirements.txt

這是要求pip只下載sdist,而不下載binary distribution。
在新測試機器上,仍然使用以下命令安裝,不需更改:

pip install -r requirements.txt --no-index --find-links=file:///home/user_name/my_env_pkgs

此時,在新測試機器上,pip的setuptools將會根據平臺環境和源碼去構建合適的whl包,繼而安裝。
對於那些不提供sdist的包,則需要在新測試機器上獨立安裝了。

其他的小問題

(1)依賴安裝順序問題
pip導出的requirements.txt文件中各項是按字母表順序排序的,因此其中存在一些屬於前面需要安裝的包的依賴包的包被放到了後面。正常情況下,安裝這些包時,也會進行依賴的檢查,並不會出什麼問題,但是,由於pip的處理邏輯是whl下載下來後不會先安裝,但是sdist下載下來後會先編譯成whl,這時,就可能用到這些依賴,這應該是個小bug吧。如果遇到這個問題,就先安裝下這個依賴吧。
在這裏插入圖片描述
(2)python版本與pip版本
最起碼應保證測試機器與待部署機器上的python版本與pip版本的一致,避免語法上的錯誤或者其他不可預見的錯誤。

關於其他工具的說明

(1)pip-bundle
pip-bundle可以在導出的requirements.txt的基礎上一鍵創建一個bundle文件,實際上就是把所有的需要的安裝的包融合到一起。具體如下:

# 安裝(測試機器和待部署機器上都需要)
pip install pip-bundle
# 測試機器上下載包
pip-bundle create my_env.pip-bundle
# 待部署機器上安裝這些包
pip-bundle install my_env.pip-bundle

需要注意的是,pip-bundle這個工具也依賴於requirements.txt,所以還是存在“找不到匹配的包”的問題,所以,該更新還是得更新,該修改requirements.txt文件,還是得修改,參照上面的方法。對於那類從外部源安裝的包,則需要用–find-links在requirements.txt中做好標註了。

遺憾的是,這個工具還在打包時編譯成whl,也就使其失去了跨平臺使用的特性。

(2)pipreqs
pipreqs可以通過對項目目錄下文件的掃描,確定文件運行需要哪些依賴,在pip環境不純淨時可以用於替代pip freeze。
具體用法如下:

# 安裝
pip install pipreqs
# 進入項目目錄
cd ~/my_project/
# 導出requirements.txt
pipreqs ./ --encoding=utf8

(3)pyinstaller
如有打包成exe文件的需求,可以看看pyinstaller這個模塊。

(4)查找資料時發現一個將文件打包成zip的方法,具體是否還奏效我沒有去測試,感興趣的自己看看吧,鏈接如下:
https://www.zhihu.com/question/21639330
https://blogs.gnome.org/jamesh/2012/05/21/python-zip-files/

參考文獻

https://pip.readthedocs.io/en/1.1/requirements.html
https://pip.pypa.io/en/stable/reference/pip_wheel/
https://blog.csdn.net/iodjSVf8U1J7KYc/article/details/90585771
https://stackoverflow.com/questions/18883430/trouble-installing-private-github-repository-using-pip
https://pypi.org/project/pip-bundle/
https://www.zhihu.com/question/21639330
https://blogs.gnome.org/jamesh/2012/05/21/python-zip-files/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章