函式計算安裝依賴庫方法小結
在通常的程式設計實踐中,專案,庫和系統環境需要協同安裝和配置。而函式計算的執行環境是預製的,捨棄一些靈活性以換取更好併發效率和系統安全性。當系統和程式碼在執行期變成只讀後,原本系統層面依賴庫的安裝操作,轉移到專案內部。而函式計算作為一種新興的平臺,安裝工具還沒來得及應對這些變化。本文的目的就是從已有的工具中找到一些適用的方法,以較少手工操作,解決安裝依賴庫到專案內的問題。
函式計算開發時常需要安裝的依賴包分為兩類,一類是通過 apt 包管理工具安裝的 deb 軟體包。另一類是具體語言環境包管理工具(如 maven, pip 等)安裝的包。下面我們先分析一下不同語言環境的包管理器。
包管理器的安裝目錄
目前函式計算支援的語言執行環境為:Java/Python/Nodejs。這三種語言對應的包管理工具分別對應為 maven/pip/npm。下面我們分別討論一下這些包管理器的安裝目錄。
maven
maven 是 Java 平臺的包管理工具。maven 工具會從中央庫或者私有庫將專案檔案 pom.xml 宣告的依賴下載到 $M2_HOME/repository
目錄內。M2_HOME
的預設值為 $HOME/.m2
。 一臺開發機上的所有 Java 專案都共享這個本地 repository 目錄下的 jar 包。由於 mvn package
階段, 所有依賴的 jar 包都會被打包進最後的交付物內。所以 Java 執行時並沒有依賴 $M2_HOME/repository
下的檔案。
pip
pip 是 python 平臺的包管理工具。pip 是當下最流行和推薦的 python 包管理方式。但是把安裝包安裝包本地目錄會涉及到 python 包管理的很多細節,為了更好的理解,先展開討論一下 python 包管理的發展歷程。
2004 年之前推薦的安裝方式是 setup.py, 下載一個模組以後,可以使用這個模組提供的 setup.py 檔案
python setup.py install
setup.py 是利用 distutils 的功能寫成的。distutils 是 python 標準庫的一部分,2000年釋出,用於 python 模組的構建和安裝。
所以使用 setup.py 也可以釋出一個 python 模組
python setup.py sdist
甚至可以打包成 rpm 或者 exe 安裝包
python setup.py bdist_rpm
python setup.py bdist_wininst
setup.py 類似於 Makefile,可使用者構建和安裝。但是沒有將構建和安裝分離,每個使用者在 install 的過程中都執行一次構建有些浪費。所以 Python 社群有了 setuptools。setuptools 釋出於 2004 年,它包含了 easy_install 工具。與之一起 python 也有了 egg 格式和 PyPi 線上倉庫,對標 java 社群的 jar 格式和 Maven 倉庫。
線上模組倉庫 PyPi 帶了兩個主要的優勢
- 只需要安裝預編譯打包好的 egg 包格式,效率更好
- 解決了包依賴的問題,依賴包可以自動從 PyPi 下載安裝
2008 年,pip 工具釋出,開始逐步替代 easy_install,目前已經是 python 包管理的事實標準。pip 希望不再使用 Eggs 格式(雖然它支援 Eggs),而更希望採用 wheel 格式。而且 pip 也支援從程式碼版本倉庫(如 github)安裝模組。
下面我們在來看一下 python 模組的目錄結構,egg 和 wheel 都將安裝檔案分為五大類 purelib、platlib、headers、scripts 和 data 目錄。
目錄 | 安裝位置 | 用途 |
---|---|---|
purelib | $prefix/lib/pythonX.Y/site-packages |
純 python 實現庫 |
platlib | $exec-prefix/lib/pythonX.Y/site-packages |
平臺相關的動態連結庫 |
headers | $prefix/include/pythonX.Yabiflags/distname |
C 標頭檔案 |
script | $prefix/bin |
可執行檔案 |
data | $prefix |
資料檔案。例如 .conf 配置檔案,初始化 SQL 檔案之類的 |
$prefix
和 $exec-prefix
是 python 的編譯器引數,可以通過 sys.prefix
和 sys.exec_prefix
獲得。在 linux 系統下預設值都是 /usr/local
npm
npm 是 nodejs 平臺的包管理工具。npm install 命令將依賴包下載到當前目錄的 node_modules 目錄內,nodejs 執行時依賴的庫可以完全依賴於當前目錄內。但是 nodejs 有些庫依賴本地環境,會在安裝的時候構建。這些本地依賴庫會存在兩個問題,其一,構建環境和執行的環境如果不一致(比如 windows 下構建,linux 下執行),那可能無法執行。其二,假如構建時安裝了一些開發庫和執行庫,這些通過作業系統包管理工具(如 apt-get)在本地安裝的動態連結庫在執行環境的 container 裡可能不存在。
遇到的問題
瞭解了不同語言包管理器的安裝到本地的目錄結構後,再來看看函式計算安裝依賴庫遇到的問題。
依賴安裝在全域性系統目錄
Maven 和 pip 會把依賴包安裝在專案目錄之外的系統目錄。Maven 的構建時會把所以外部依賴都打包進最終交付物。所以 Maven 通常沒有執行時依賴問題。即使不用 Maven 進行工程管理的 Java 專案,在當前目錄或者其子目錄存放依賴的 jar 包,並且最終一起打包也是通常的做法。所以 Java Runtime 不存在這個問題。相比之下 pip 所管理的 Python 環境,就有此問題。pip 會把依賴安裝到系統目錄,而 函式計算的生產環境不可寫(除了 /tmp
目錄),也沒有辦法提供預製環境。
原生依賴
Python 和 Nodejs 常見庫檔案依賴系統的原生環境。需要安裝編譯環境和執行時動態連結庫。這兩種情況的移植性都是很不好的。
在函式計算所使用的 Debain/Ubuntu 系統,使用 apt 包管理系統安裝軟體和庫。預設情況下這些軟體和庫都會被安裝到系統目錄如 /usr/bin
、/usr/lib
、/usr/local/bin
、/usr/local/lib
等。所以原生依賴也需要想辦法安裝到本地目錄。
解決辦法
通常的相應的解法也很直觀:
- 執行依賴安裝的開發系統和生產執行系統保持一致。使用 fcli 提供的 sbox 環境進行依賴安裝。
- 依賴檔案都放到本地目錄。把 pip 的 module,可執行檔案,動態連結庫 .so 檔案都放拷貝到當前目錄
但把依賴檔案放置到當前目錄在實踐過程中往往並不容易。
- pip 和 apt-get 安裝的庫檔案會散落到系統的很多目錄裡,需要對不同包管理系統有深入的瞭解才能找回這些檔案。
- 庫檔案有傳遞依賴,往往安裝某個庫,會把一堆這個庫依賴的庫都安裝進去,手工去遍歷這些依賴是非常繁瑣的。
所以我們的問題歸結到,如何方便地把依賴安裝到當前目錄,減少手工操作。下面我們會分別介紹 pip 和 apt 包管理系統的多種方法,並比較其優劣。
依賴安裝到當前目錄
Python
方法一:使用 --install-option
引數
pip install --install-option="--install-lib=$(pwd)" PyMySQL
--install-option
會將引數傳遞給 setup.py, 而我們知道無論是 .egg 還是 .whl 檔案裡都不存在 setup.py 檔案。--install-option
會觸發基於原始碼包的安裝流程,setup.py 會觸發模組的構建流程。
--install-option
有如下選項
檔案型別 | 可選項 |
---|---|
Python modules | –install-purelib |
extension modules | –install-platlib |
all modules | –install-lib |
scripts | –install-scripts |
data | –install-data |
C headers | –install-headers |
--install-lib
的效果是同時覆蓋 --install-purelib
和 --install-platlib
的值。
另外 --install-option="--prefix=$(pwd)"
也可以安裝在當前目錄,但是這個會在當前目錄建立 lib/python2.7/site-packages
子目錄結構。
優點
- 可以有選擇地將模組裝在本地,比如 purelib
缺點
- 不適用沒有原始碼包的模組
- 觸發構建系統,未體現 wheel 包的優勢
- 需要完整安裝需要設定的引數較多,比較繁瑣
方法二:使用 --target
或者 -t
引數
pip install --target=$(pwd) PyMySQL
--target
是 pip 後來提供的引數,模組會被直接安裝到當前目錄,不會產生 lib/python2.7/site-packages
子目錄解構。該個方法簡單好用,比較適合依賴較少的情況。
方法三:結合使用 PYTHONUSERBASE
和 --user
引數
PYTHONUSERBASE=$(pwd) pip install --user PyMySQL
使用--user
引數,使得模組被安裝到 site.USER_BASE
目錄。該目錄的預設值在 Linux 系統裡是 ~/.local
,MacOS 裡是 ~/Library/Python/X.Y
,Windows 下是 %APPDATA%Python
。PYTHONUSERBASE
環境變數可以修改掉 site.USER_BASE
的值。
--user
的安裝效果和 --prefix=
的效果類似,也會產生 lib/python2.7/site-packages
子目錄結構
方法四:使用 virtualenv
pip install virtualenv
virtualenv path/to/my/virtual-env
source path/to/my/virtual-env/bin/activate
pip install PyMySQL
virutalenv
是 python 社群推薦的玩法,使用 virutalenv 可以不汙染全域性環境。 virtualenv 不但會把需要的模組本地化(如 PyMySQL),也會把包管理相關的工具也本地化,如 setuptools 、pip、wheel。這些模組會增大包的尺寸,但執行時並不需要。
apt-get
apt-get 安裝的連結庫和可執行檔案也需要安裝到本地目錄。網上推薦 chroot
和 apt-get -o RootDir=$(pwd)
的方法,經過一番嘗試都碰到一些問題走不下去。在這個基礎上做了些改進,使用 apt-get
下載 deb 包, dpkg
安裝 deb 包。
apt-get install -d -o=dir::cache=$(pwd) libx11-6 libx11-xcb1 libxcb1
for f in $(ls ./archives/*.deb)
do
dpkg -x $pwd/archives/$f $pwd
done
如何執行
Java 通過設定 classpath 來轉載 jar 和 class 檔案。nodejs 會自動裝載當前目錄下 node_modules 下面的 package 。這些都是常見用法,此處不再贅述。
python
python 會從 sys.path 說指向的目錄列表裡裝載 module 檔案。
> import sys
> print `
`.join(sys.path)
/usr/lib/python2.7
/usr/lib/python2.7/plat-x86_64-linux-gnu
/usr/lib/python2.7/lib-tk
/usr/lib/python2.7/lib-old
/usr/lib/python2.7/lib-dynload
/usr/local/lib/python2.7/dist-packages
/usr/lib/python2.7/dist-packages
由於 sys.path 預設會包含當前目錄,因為使用 --target
或者 -t
引數的方法會將 module 安裝在當前目錄,所以上面提到的方法二無需設定 sys.path。
sys.path 是可以編輯的陣列,所以在程式開始處使用 sys.path.append(dir) 即可。為了讓程式更具備可移植新也可以使用環境變數 PYTHONPATH。
export PYTHONPATH=$PYTHONPATH:$(pwd)/lib/python2.7/site-packages
apt-get
apt-get 安裝的可執行檔案和動態連結庫,需要保證在到 PATH 和 LD_LIBRARY_PATH 環境變數裡設定的目錄列表裡能找到。
PATH
PATH 變數是系統用來查詢可執行程式的路徑列表,比較簡單,把 bin 、usr/bin 和 usr/local/bin 等 bin 或者 sbin 目錄都通通加到 PATH 裡去。
export PATH=$(pwd)/bin:$(pwd)/usr/bin:$(pwd)/usr/local/bin:$PATH
注意上面是 bash 的寫法,在 java,python,nodejs 裡如何修改當前程式的 PATH 環境變數請做響應的調整。
LD_LIBRARY_PATH
LD_LIBRARY_PATH 類似於 PATH,是用來查詢動態連結庫的路徑列表。通常系統會把動態連結放到 /lib
,/usr/lib
和 /usr/local/lib
目錄下。但是有些模組也會放在這些目錄的子目錄裡,比如 /usr/lib/x86_64-linux-gnu
。這些子目錄通常都會記錄在 /etc/ld.so.conf.d/
下的檔案裡。
cat /etc/ld.so.conf.d/x86_64-linux-gnu.conf
# Multiarch support
/lib/x86_64-linux-gnu
/usr/lib/x86_64-linux-gnu
所以 $(pwd)/etc/ld.so.conf.d/
下所有檔案裡宣告的目錄裡的 so 檔案也需要能從 LD_LIBRARY_PATH 環境變數裡的目錄列表裡找到。
注意,執行時修改環境變數 LD_LIBRARY_PATH
可能不生效,至少對於 python 這個問題是已知的。LD_LIBRARY_PATH
變數裡已經預設了 /code/lib
目錄。所以一個可行的辦法是用軟連結把依賴的 so 都軟鏈到 /code/lib
目錄下
小結
本文重點解決的是 pip 和 apt-get 命令如何將庫安裝到本地目錄,而後執行時如何設定環境變數讓本地安裝的庫檔案被程式找到。
python 提供的 4 種方法,對於常見的場景都是適用的。細微的差別也在上文中有提到,使用的繁簡程式也有略有差別的,可能根據自己的偏好選擇使用。
apt-get 也提供了一種可行的辦法,該方法不是唯一的選擇,相比其他可行的方法,該方法考慮到已經安裝在系統裡的 deb 包,就不再安裝了,以節省程式包的尺寸。為了進一步節省尺寸也可以把安裝進去的執行時無關的檔案刪除掉,如使用者手冊 man。
本文是定製更好工具的一個技術積累的過程,基於此,我們會進一步推出更好用的工具,來簡化開發過程。
參考閱讀
- How does python find packages?
- Pip User Guide
- python-lambda-local
- python-lambda
- Python 包管理工具解惑
- Running apt-get for another partition/directory?
相關文章
- 開發函式計算的正確姿勢 —— 依賴安裝方法一覽函式
- 開發函式計算的正確姿勢——使用互動模式安裝依賴函式模式
- ubuntu下安裝nginx時依賴庫zlib,pcre,openssl安裝方法UbuntuNginx
- 函式計算|如何使用層解決依賴包問題?函式
- Hue安裝依賴
- kratos安裝及依賴安裝
- 函式計算Python連線SQLServer小結函式PythonSQLServer
- 當useEffect遇到函式依賴函式
- Serverless 解惑——函式計算如何安裝字型Server函式
- 資料庫系統------函式依賴與正規化資料庫函式
- python 安裝依賴c++PythonC++
- rubymine debug需要安裝依賴
- 以vue依賴統計為核心的框架(函式),mveVue框架函式
- 【python】【安裝包依賴關係】Python
- npm 安裝、刪除依賴命令NPM
- Linux上安裝Nginx依賴環境和庫、Nginx安裝,Nginx服務命令LinuxNginx
- 簡單歡樂的依賴注入函式依賴注入函式
- 解決npm 安裝部分依賴失敗問題總結NPM
- vscode使用npm安裝依賴報錯VSCodeNPM
- python離線安裝外部依賴包Python
- python 離線依賴包打包&安裝Python
- 將你的Gradle依賴轉換為函式Gradle函式
- 函式小結函式
- RoboWare Studio安裝及依賴項解決
- npm 或 yarn安裝依賴報錯 EPERM: operation not permitted, unlink 解決方法NPMYarnMIT
- Golang 依賴注入設計哲學|12.6K 🌟 的依賴注入庫 wireGolang依賴注入
- matchTemplate函式各個方法的計算公式函式公式
- 函式計算支援 MySQL 例項繫結函式MySql
- 如何依賴機器人安裝極狐GitLab機器人Gitlab
- 『手撕Vue-CLI』自動安裝依賴Vue
- ubuntu解決軟體安裝依賴錯誤Ubuntu
- Linux下安裝DB2的包依賴LinuxDB2
- jQuery操作iframe中js函式的方法小結jQueryJS函式
- [KubernetesClient | 底層依賴庫]client
- 不要依賴Mock庫 - ErwinMock
- 交叉編譯庫依賴問題的解決方法編譯
- Vue安裝依賴報錯:checking for Python executable "python" in the PATHVuePython
- 如何在java中使用 Excel 動態函式生成依賴列表JavaExcel函式