今回は Docker のマルチステージビルドを使って Wheel が提供されていない Python パッケージを含む Docker イメージを作ってみる。 これだけだと、なんのこっちゃという感じなので、以下で前提から説明しておく。
まず、今の Python のパッケージングにはソースコード配布物 (sdist) と Wheel という二つのフォーマットが主に使われている。 ソースコード配布物は、文字通りソースコードをそのままパッケージングしたもの。 ソースコード配布物では、パッケージの中に Python/C API などで書かれた拡張モジュールがあっても、ソースコードの状態で含まれる。 それに対して Wheel は、拡張モジュールが含まれる場合にはビルドされたバイナリの状態で提供される。 そして、現行の pip はソースコード配布物をインストールするとき、一旦 Wheel にビルドした上でインストールするように振る舞う。 このソースコード配布物を Wheel にビルドするタイミングでは、ランタイムとは別にビルドで必要なツール類の一式が必要になる。
ここで、ソースコード配布物として提供されている Python パッケージを Docker イメージに含めることを考えてみよう。 もし、対象のパッケージが拡張モジュールを含む場合、ビルドに必要なツール類の一式が Docker イメージに必要になってしまう。 Docker イメージは、なるべく不要なものを入れない方が一般的に望ましいとされている。
そこで、上記の問題を解決するのに Docker のマルチステージビルドという機能が使える。 マルチステージビルドでは、複数のイメージを連携させて一つのイメージが作れる。 例えばパッケージのビルドをするステージと、それを組み込むステージを分ければ、後者にはビルドに必要なツールが必要なくなるというわけ。
使った環境は次の通り。
$ sw_vers ProductName: Mac OS X ProductVersion: 10.14.6 BuildVersion: 18G103 $ docker version Client: Docker Engine - Community Version: 19.03.2 API version: 1.40 Go version: go1.12.8 Git commit: 6a30dfc Built: Thu Aug 29 05:26:49 2019 OS/Arch: darwin/amd64 Experimental: false Server: Docker Engine - Community Engine: Version: 19.03.2 API version: 1.40 (minimum version 1.12) Go version: go1.12.8 Git commit: 6a30dfc Built: Thu Aug 29 05:32:21 2019 OS/Arch: linux/amd64 Experimental: true containerd: Version: v1.2.6 GitCommit: 894b81a4b802e4eb2a91d1ce216b8817763c29fb runc: Version: 1.0.0-rc8 GitCommit: 425e105d5a03fabd737a126ad93d62a9eeede87f docker-init: Version: 0.18.0 GitCommit: fec3683
Wheel のビルドについて
実際にマルチステージビルドを試す前に、Wheel に関するオペレーションについて解説しておく。 まず、Python のパッケージ管理ツールの pip は、デフォルトで PyPI というパッケージサイトからパッケージを探してくる。
インストールするパッケージに、使っているプラットフォーム向けの Wheel があれば、それがインストールされる。 もし、ソースコード配布物しか提供されていないときは、それを元に Wheel をビルドした上でインストールされる。 ただし、Wheel が提供されている場合であってもオプションを指定することで、あえてソースコード配布物から自前でビルドすることもできる。
実際に試してみることにしよう。 Docker を使って Ubuntu 18.04 LTS のイメージを起動する。
$ docker run --rm -it ubuntu:18.04 /bin/bash
パッケージシステムの APT を使って Python 3 の pip をインストールする。
# apt update # apt -y install python3-pip
試しに LightGBM というパッケージをソースコード配布物からビルドしてみよう。
pip は wheel サブコマンドを使うことでインストールに必要な Wheel パッケージが取得できる。
その際、--no-binary
オプションを指定すると、ビルド済みの Wheel ではなく自分でソースコード配布物からビルドすることになる。
ちなみに、このオプションは pip install サブコマンドでも有効なので覚えておくと良いかも。
# pip3 wheel --no-binary lightgbm lightgbm
なお、上記のコマンド実行は失敗する。 なぜならビルドに必要なパッケージ類が入っていないため。
# pip3 wheel --no-binary lightgbm lightgbm ... Exception: Please install CMake and all required dependencies first The full version of error log was saved into /root/LightGBM_compilation.log ---------------------------------------- Failed building wheel for lightgbm Running setup.py clean for lightgbm Failed to build lightgbm ERROR: Failed to build one or more wheels
Ubuntu で LightGBM をビルドするのに必要な cmake と gcc をインストールしよう。
# apt -y install cmake gcc
もう一度、先ほどのコマンドを実行すると、今度はエラーにならず上手くいく。
# pip3 wheel --no-binary lightgbm lightgbm
これで、カレントワーキングディレクトリにインストールに必要な Wheel 一式ができあがる。 この中の LightGBM は自分でソースコード配布物からビルドしたもの。
# ls *.whl joblib-0.14.0-py2.py3-none-any.whl lightgbm-2.3.0-py3-none-any.whl numpy-1.17.3-cp36-cp36m-manylinux1_x86_64.whl scikit_learn-0.21.3-cp36-cp36m-manylinux1_x86_64.whl scipy-1.3.1-cp36-cp36m-manylinux1_x86_64.whl
ビルドした Wheel をインストールしてみよう。
# pip3 install lightgbm-2.3.0-py3-none-any.whl
これでインストールしたパッケージが使えるようになる。
# python3 -c "import lightgbm"
シングルステージで Docker イメージをビルドする
続いては、マルチステージビルドを試す前にシングルステージの場合を見ておこう。 これは、ビルドに必要なツールも一緒に Docker イメージに含まれてしまうパターン。
以下のように Dockerfile を用意する。 ビルドに必要なパッケージをインストールした上で、LightGBM をインストールする構成になっている。
FROM ubuntu:18.04 # Use fastest mirror RUN sed -i.bak -e 's%http://[^ ]\+%mirror://mirrors.ubuntu.com/mirrors.txt%g' /etc/apt/sources.list # Install prerequisite apt packages to build RUN apt update \ && apt -yq dist-upgrade \ && apt -yq install \ python3-pip \ cmake \ gcc \ && apt clean \ && rm -rf /var/lib/apt/lists/* # Install Python package from source code distribution RUN pip3 install --no-binary lightgbm lightgbm
上記を元に Docker イメージをビルドする。
$ docker build -t example/singlestage .
ビルドしたイメージを元にコンテナを起動してみよう。
$ docker run --rm -it example/singlestage /bin/bash
このイメージでは、ちゃんとインストールした LightGBM がインポートできる。
# python3 -c "import lightgbm as lgb"
反面、ビルドに使った cmake や gcc もイメージに含まれてしまっている。
# cmake --version cmake version 3.10.2 CMake suite maintained and supported by Kitware (kitware.com/cmake).
マルチステージで Docker イメージをビルドする
それでは、今回の本題となるマルチステージビルドを試してみよう。
マルチステージビルドでは FROM
命令が複数登場する。
それぞれの FROM
命令がステージとなる Docker イメージを表しており、AS
を使って名前をつけられる。
名前をつけた Docker イメージからは COPY
命令を使ってファイルをコピーできる。
以下の Dockerfile は build-stage
と using-stage
という二つのステージに分かれている。
まず、build-stage
では LightGBM の Wheel をビルドしている。
そして、using-stage
でビルドした Wheel をインストールしている。
FROM ubuntu:18.04 AS build-stage # Use fastest mirror RUN sed -i.bak -e 's%http://[^ ]\+%mirror://mirrors.ubuntu.com/mirrors.txt%g' /etc/apt/sources.list # Install prerequisite apt packages to build RUN apt update \ && apt -yq dist-upgrade \ && apt install -yq \ python3-pip \ cmake \ gcc \ && apt clean \ && rm -rf /var/lib/apt/lists/* # Build wheel RUN pip3 wheel -w /tmp/wheelhouse --no-binary lightgbm lightgbm FROM ubuntu:18.04 AS using-stage # Use fastest mirror RUN sed -i.bak -e 's%http://[^ ]\+%mirror://mirrors.ubuntu.com/mirrors.txt%g' /etc/apt/sources.list # Install prerequisite apt packages to build RUN apt update \ && apt -yq dist-upgrade \ && apt install -yq \ python3-pip \ && apt clean \ && rm -rf /var/lib/apt/lists/* # Copy binaries from building stage COPY --from=build-stage /tmp/wheelhouse /tmp/wheelhouse # Install binary package RUN pip3 install /tmp/wheelhouse/lightgbm-*.whl
それでは、上記の Dockerfile をビルドしてみよう。
$ docker build -t example/multistage .
ビルドしたイメージからコンテナを起動する。
$ docker run --rm -it example/multistage /bin/bash
このイメージでは、ちゃんと LightGBM がインポートして使える。
# python3 -c "import lightgbm as lgb"
そして、イメージにはビルド用のツールも含まれていない。
# cmake bash: cmake: command not found
いじょう。
- 作者:もみじあめ
- 発売日: 2020/02/29
- メディア: Kindle版