アリババクラウド機能計算のための依存ライブラリのインストール

この記事では、既存のツールを使用して、最小限の手動介入で**Function Compute**プロジェクトに依存ライブラリをインストールする方法を説明します。

*本ブログは英語版からの翻訳です。オリジナルは[こちら](https://www.alibabacloud.com/blog/installing-a-dependency-library-for-function-compute_594410)からご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。*

#パッケージマネージャのインストールディレクトリ
現在、[Function Compute](https://www.alibabacloud.com/ja/products/function-compute)はJava、Python、Node.js環境をサポートしています。これら3つの言語のパッケージマネージャは、それぞれMaven、pip、NPMです。以下では、これらのパッケージマネージャのそれぞれのインストールディレクトリを説明します。

**Maven**
MavenはJavaのパッケージマネージャです。Mavenは、プロジェクトファイルpom.xmlで宣言された依存関係を、中央リポジトリまたはプライベートリポジトリから`$M2_HOME/repository`ディレクトリにダウンロードします。`M2_HOME`のデフォルト値は`$HOME/.m2`です。開発マシン上のすべてのJavaプロジェクトは、このローカルリポジトリディレクトリの下にあるJARパッケージを共有します。`mvn package`の段階で、依存するすべてのJARパッケージは最終的な成果物にパッケージ化されます。したがって、Javaプロジェクトは`$M2_HOME/repository`ディレクトリ内のファイルに依存することなく実行されます。

**pip**
現在、Pythonのパッケージマネージャーとして最も人気があり、推奨されているのはpipです。インストールパッケージをローカルディレクトリにインストールする方法を理解する前に、Python パッケージマネージャについての理解を深める必要があります。理解を深めるために、以下ではまずPythonパッケージマネージャの開発履歴を説明します。

2004年以前は、Pythonのインストールにはsetup.pyが推奨されていました。これを使用するには、任意のモジュールをダウンロードし、そのモジュールに付属のsetup.pyファイルを使用します。

```
python setup.py install
```
setup.pyは[Distutils](https://docs.python.org/3/library/distutils.html?spm=a2c65.11461447.0.0.63a7136bbSr8mo)から開発されています。2000年にPython標準リポジトリの一部としてリリースされたDistutilsは、Pythonモジュールのビルドやインストールに利用されています。

そのため、setup.pyを使ってPythonモジュールや

```
python setup.py sdist
```
モジュールをRPMやEXEファイルにパッケージ化することもできます。

```
python setup.py bdist_rpm
python setup.py bdist_wininst
```
MakeFileと同様に、setup.pyもビルドとインストールに利用できます。しかし、ビルドとインストールのプロセスが統合されているため、モジュールをインストールする際には必ずモジュールをビルドしなければならず、リソースを無駄にしてしまいます。2004年にPythonコミュニティは、easy_installツールを含むsetuptoolsをリリースしました。その後、PythonはEGG形式のサポートを開始し、オンラインリポジトリPyPiを導入しました。

オンラインモジュールリポジトリPyPiには、2つの重要な利点があります。

1、コンパイル済みのEGGパッケージをインストールするだけで済むので、効率が向上します。
2、依存パッケージを自動的にPyPiからダウンロードしてインストールします。

2008年にリリースされて以来、pipは徐々にeasy_installに取って代わり、事実上の標準的なPythonパッケージマネージャとなりました。EGG形式と互換性があるため、pipはWheel形式を好み、コードバージョンリポジトリ(例えばGitHub)からのモジュールのインストールをサポートしています。

以下では、Pythonモジュールのディレクトリ構造を説明します。EGG形式とWheel形式の両方のインストールファイルのディレクトリは、purelib、platlib、ヘッダ、スクリプト、データの5種類に分類されます。

| Directory | Installation location | Purpose |
|:--|:--|:--|
| purelib | $prefix/lib/pythonX.Y/site-packages | Pure Python implementation library |
| platlib | $exec-prefix/lib/pythonX.Y/site-packages | Platform-related DLL |
| headers | $prefix/include/pythonX.Yabiflags/distname | C header files |
| script | $prefix/bin | Executable files |
| data | $prefix | Data files, such as .conf configuration files and SQL initialization files |

`prefix`と `$exec-prefix` は Python コンパイラのパラメータで、 `sys.prefix` と `sys.exec_prefix` から取得できます。Linuxでのデフォルトはどちらも`/usr/local`です。

**npm**
npmはNode.jsのパッケージマネージャーです。npm installコマンドを実行すると、依存パッケージがカレントディレクトリ以下のnode_modulesサブディレクトリにダウンロードされます。Node.jsのランタイム依存パッケージはすべてカレントディレクトリに含まれています。ただし、Node.jsのライブラリの中には、モジュールをインストールしたときに構築されたローカル環境に依存するものがあります。ビルド環境(Windowsなど)とランタイム環境(Linuxなど)が異なる場合、ローカルに依存するライブラリは実行できません。また、ビルド時に開発ライブラリやランタイムライブラリがインストールされている場合、オペレーティングシステムのパッケージマネージャでローカルにインストールされていたDDL（apt-getなど）がランタイム環境下のコンテナに存在しない場合があります。

#トラブルシューティングの問題点
次に、Function Computeの依存ライブラリがインストールされている場合に発生する問題を解決する方法を見ていきます。

####グローバルシステムディレクトリにインストールされた依存ライブラリ####
Mavenとpipは、依存パッケージをプロジェクトディレクトリ以外のシステムディレクトリにインストールします。プロジェクトがビルドされると、Mavenは外部の依存パッケージをすべて最終的な成果物にパッケージ化します。そのため、Mavenで管理されているプロジェクトは、実行時に依存関係の問題から解放されます。Mavenで管理されていないJAVAプロジェクトでは、依存性のあるJARパッケージをカレントディレクトリまたはそのサブディレクトリに配置し、最終的な成果物にパッケージ化することも一般的です。このようにして、Java実行時に依存関係の問題が発生するのを防ぐことができます。しかし、このような問題は pip で管理された Python 環境で発生します。 pip は依存関係をシステムディレクトリにインストールしますが、Function Compute の本番環境（`/tmp` ディレクトリを除く）は読み込み専用で、プレハブ環境を構築することができません。

####ネイティブの依存関係####
一般的なPythonやNode.jsのライブラリファイルは、システムのネイティブ環境に依存しています。コンパイル環境とランタイム環境のDDLをインストールしなければならず、結果としてどちらの場合も移植性が悪くなります。

Function ComputeがDebianやUbuntu上で実行される場合、システムのインストールプログラムやライブラリを管理するためにAPTパッケージが使用されます。デフォルトでは，これらのプログラムやライブラリは，システムディレクトリ（例えば，`/usr/bin`，`/usr/lib`，`/usr/local/bin`，`/usr/local/lib`）にインストールされます．したがって、ネイティブの依存関係もローカルディレクトリにインストールする必要があります。

#推奨ソリューション
以下にいくつかの直感的な解決策を説明します。

1、依存関係をインストールするための開発システムが本番の実行システムと一致していることを確認します。fcli sboxを使用して依存関係をインストールします。
2、すべての依存関係ファイルをローカルディレクトリに配置します。モジュール、実行ファイル、.dlまたは.soファイルをpipからカレントディレクトリにコピーします。
しかし、実際には依存関係ファイルをカレントディレクトリに配置するのは難しいです。

1、pipやapt-getでインストールしたライブラリファイルは、異なるディレクトリに散らばっています。これは、これらのファイルを見つけるためには、異なるパッケージマネージャに精通していなければならないことを意味します。
2、ライブラリファイルには遷移的な依存関係があります。ライブラリがインストールされると、そのライブラリが依存する他のライブラリもインストールされます。このため、これらの依存関係を手動で取得するのは非常に面倒です。
この場合、手動による介入を最小限に抑えて、カレントディレクトリに依存関係を手動でインストールするにはどうすればよいのでしょうか。以下では、pip と APT パッケージマネージャが使用するいくつかの方法を説明し、長所と短所を比較します。

#現在のディレクトリへの依存関係のインストール
####Python
#####方法 1: --install-option パラメータを使用します。

```
pip install --install-option="--install-lib=$(pwd)" PyMySQL
```
`--install-option`を使用すると、setup.pyにパラメータが渡されます。しかし、.eggファイルや.whlファイルにはsetup.pyファイルは含まれていません。そのため、`--install-option` を使用するとソースコードパッケージに基づいてインストール手順が起動し、setup.py はモジュールの構築プロセスを起動します。

`--install-option`は以下のオプションをサポートしています。

| File type | 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`というサブディレクトリが作成されます。

利点。

1、モジュールをpurelibなどのローカルディレクトリにインストールできます。
デメリット: モジュールを purelib のようなローカルディレクトリにインストールすることができます。

1、ソースコードパッケージを含まないモジュールには適用できません。
2、Wheelパッケージをフル活用せずにシステムを構築してしまう。
3、モジュールを完全にインストールするためには、より多くのパラメータを設定する必要があり、面倒です。

**方法 2: `--target` または `-t` パラメータを使用する**

```
pip install --target=$(pwd) PyMySQL
```
`--target`はpipによって新たに提供されたパラメータです。このパラメータを使うと、`lib/python2.7/site-packages`というサブディレクトリを作らずに、モジュールをカレントディレクトリに直接インストールします。この方法は使いやすく、いくつかの依存関係を持つモジュールに適用できます。

**方法3: `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` の値を変更することができます。

`prefix=`と同様に、`--user`を使用すると、`lib/python2.7/site-packages`というサブディレクトリが作成されます。

**方法4: virtualenvを使用する**

```
pip install virtualenv
virtualenv path/to/my/virtual-env
source path/to/my/virtual-env/bin/activate
pip install PyMySQL
```
`virutalenv` は、グローバル環境を汚染しないという点で Python コミュニティから推奨されている方法です。virtualenv を使用すると、目的のモジュール (PyMySQL など) とパッケージマネージャ (setuptools, pip, wheel など) の両方がローカルディレクトリに保存されます。これらのモジュールはパッケージのサイズを大きくしますが、実行時には使用されません。

**apt-get**
apt-getでインストールしたDDLや実行ファイルもローカルディレクトリにインストールする必要があります。インターネット上で推奨されている `chroot` や `apt-get -o RootDir=$(pwd)` の方法を試してみましたが、欠陥があるので破棄しました。これまでの方法をベースに改良を加え、`apt-get` を使って DEB パッケージをダウンロードし、 `dpkg` を使ってインストールする方法を設計しました。

```
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はクラスパスを設定することでjarファイルやクラスファイルをロードしますが、nodejsはカレントディレクトリのnode_modules以下のパッケージを自動的にロードします。ここでは、これらの一般的な操作は省略します。

**Python**
Pythonはsys.pathが指すディレクトリリストからモジュールファイルをロードします。

```
> import sys
> print '\n'.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にはカレントディレクトリが含まれています。そのため、2番目のメソッドでは `-- target `や `-t` パラメータを使用した場合、モジュールはカレントディレクトリにインストールされているので、sys.path の設定を無視することができます。

sys.pathは編集可能な配列なので、プログラム起動時にsys.path.append(dir)を使用することができます。プログラムの移植性を向上させるために、環境変数 PYTHONPATH を使用することもできます。

```
export PYTHONPATH=$PYTHONPATH:$(pwd)/lib/python2.7/site-packages
```
**apt-get**
apt-get を使ってインストールされた実行ファイルや DDL が、環境変数 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、node.jsの場合は、現在のプロセスの環境変数PATHを変更する際に適宜調整してください。

**LD_LIBRARY_PATH**

PATHと同様に、LD_LIBRARY_PATHはDDLを検索できるディレクトリリストです。通常、システムは` /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 `を変更しても、その変更は反映されない可能性があることに注意してください。`LD_LIBRARY_PATH`環境変数では、`/code/lib`ディレクトリがあらかじめ設定されています。そのため、依存するsoファイルをすべて`/code/lib`ディレクトリにソフトリンクすることができます。

#結論
このドキュメントでは、プログラムがインストールされたローカルライブラリファイルを見つけることができるように、pipやapt-getコマンドを実行してローカルディレクトリにライブラリをインストールしたり、実行時に環境変数を設定したりする方法を説明しています。

Pythonが提供する4つの方法は、どのような一般的なシナリオにも適用可能です。上記で説明したわずかな違いにもかかわらず、ニーズに応じて適切な方法を選択することができます。

apt-getはもう一つの方法です。他の方法と比較して、この方法はすでにシステムにインストールされているdebパッケージをインストールする必要がないため、パッケージサイズを小さくすることができます。さらにサイズを小さくするには、ユーザーマニュアルなどのインストール済みの不要なファイルを削除することができます。

本書は、より良いツールをカスタマイズするための技術蓄積の一環です。これをベースに、将来的にはより良いツールを提供し、開発を簡素化していきたいと考えています。

#参考文献
1、[python はどのようにしてパッケージを見つけるのですか?](https://leemendelowitz.github.io/blog/how-does-python-find-packages.html?spm=a2c65.11461447.0.0.63a7136bYOoE5l)
2、[Pip ユーザーガイド](https://pip.pypa.io/en/stable/user_guide/?spm=a2c65.11461447.0.0.63a7136beJDCpr#user-installs)
3、[python-lambda-local](https://github.com/HDE/python-lambda-local?spm=a2c65.11461447.0.0.63a7136bG7BKxP)
4、[python-lambda](https://github.com/nficano/python-lambda?spm=a2c65.11461447.0.0.63a7136bNU76Lq)
5、[Pythonのパッケージ管理ツールガイド](https://blog.zengrong.net/post/python_packaging/)
6、[別のパーティション/ディレクトリのために apt-get を実行していますか？](https://stackoverflow.com/questions/3954584/running-apt-get-for-another-partition-directory?spm=a2c65.11461447.0.0.63a7136bBYYVR2)

*アリババクラウドは日本に２つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
[アリババクラウドジャパン公式ページ](https://www.alibabacloud.com/ja)*