TOC
github のレポジトリです。
きっかけ
Python でマルチステージビルドをどうするかという話。特にデータ分析用で Jupyter やLightgbm 等のパッケージを含んだものに関する例が多くないように感じたので、ハマったポイントも含めて備忘録的にまとめました。
最近 NLP 周りのイメージを作ってる時に信じられないくらいにイメージサイズが大きくなってきたので(これはまた別の問題を含んでいるかもしれないが)、イメージサイズの縮小を考えないと漠然と思ってる時に Twitter で見かけたのが以下のブログ記事です。
この記事を参考にして自分のイメージでもマルチステージビルドを試してみることにしました。
Python で Multi-stage Build する
- Dockerfile
FROM python:3.7.7-buster as builder
ENV APP_HOME /workspace
WORKDIR $APP_HOME
COPY requirements.txt $APP_HOME
RUN apt-get -y update \
&& apt-get -y install clang cmake nodejs npm \
&& git clone --recursive https://github.com/microsoft/LightGBM && cd LightGBM && mkdir build && cd build && cmake .. && make -j4 && cd ../python-package && python setup.py install && cd ../.. && rm -r LightGBM \
&& git clone --recursive https://github.com/dmlc/xgboost && cd xgboost && mkdir build && cd build && cmake .. && make -j$(nproc) && cd ../python-package && python setup.py install && cd ../.. && rm -r xgboost \
&& pip install -r requirements.txt \
&& export NODE_OPTIONS=--max-old-space-size=4096 \
&& jupyter labextension install @jupyterlab/toc \
&& jupyter labextension install jupyterlab-plotly \
&& jupyter nbextension enable --py widgetsnbextension
FROM python:3.7.7-slim-buster as runner
ENV APP_HOME /workspace
WORKDIR $APP_HOME
COPY --from=builder /usr/local/lib/python3.7/site-packages /usr/local/lib/python3.7/site-packages
COPY --from=builder /usr/local/bin /usr/local/bin
COPY --from=builder /usr/local/share/jupyter /usr/local/share/jupyter
COPY --from=builder /usr/lib/x86_64-linux-gnu/libgomp.so.1 /usr/lib/x86_64-linux-gnu/libgomp.so.1
RUN apt-get -y update \
&& apt-get -y install git vim \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* \
&& mkdir /root/.kaggle
CMD jupyter lab \
--no-browser \
--port=8888 \
--ip=0.0.0.0 \
--allow-root
- requirements.txt
catboost==0.22 category-encoders==2.1.0 dask==2.9.2 hydra-core==0.11.3 ipywidgets==7.5.1 joblib==0.14.1 jupyterlab==2.0.1 kaggle==1.5.6 matplotlib==3.2.1 mlflow==1.7.2 numba==0.48.0 numpy==1.18.2 pandas==1.0.3 plotly==4.7.1 scikit-learn==0.21.3 seaborn==0.10.0 tqdm==4.42.0
テーブルデータのデータ分析・機械学習モデルの構築には大体こんな感じでイメージを作っています。Lightgbm, Xgboost は pip で入れるとエラーが出た記憶があるのでソースコードがビルドしています。また EDA などデータ可視化する際には Plotly を使うことが時たまあるので nodejs
, npm
を入れたり、jupyter lab の拡張で jupyterlab-plotly
を追加したりしています。
今は別個で作ってますが、そのうちこれをベースイメージにしてNN・Image・NLP・NLP-ja等の別イメージを作って簡単に使い回せるようにしたいと考えています。
ハマったところ
基本的にビルドステージでビルドしたものを、実行ステージに COPY する際に実行に必要なものを渡せないとエラーが出ます。
パッケージ類は以下でコピーして実行用コンテナに渡します。
COPY --from=builder /usr/local/lib/python3.7/site-packages /usr/local/lib/python3.7/site-packages
Jupyter
最初はこれだけ動くかなと思ったら、これだとそもそも Docker が起動しなかった。エラーを読むと jupyter: command not found
と出ていた。調べると Jupyter は /usr/local/bin
以下にあるらしい。なので
COPY --from=builder /usr/local/bin /usr/local/bin
を追加することでコンテナが起動した。
Jupyter Lab
その後 Jupyter Lab を起動して localhost:8888 を叩くと以下のエラーがブラウザに表示された。
JupyterLab Error JupyterLab application assets not found in "/usr/local/share/jupyter/lab" Please run `jupyter lab build` or use a different app directory
サーバは動いてるけど assets がないみたいなので、これはエラーメッセージ通りに
COPY --from=builder /usr/local/share/jupyter /usr/local/share/Jupiter
以上を追加することで Jupyter lab がブラウザに表示された。
Lightgbm
Lightgbm を Import する際に以下のエラーが出た
>>> import lightgbm as lgb Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/python3.7/site-packages/lightgbm/__init__.py", line 8, in <module> from .basic import Booster, Dataset File "/usr/local/lib/python3.7/site-packages/lightgbm/basic.py", line 34, in <module> _LIB = _load_lib() File "/usr/local/lib/python3.7/site-packages/lightgbm/basic.py", line 29, in _load_lib lib = ctypes.cdll.LoadLibrary(lib_path[0]) File "/usr/local/lib/python3.7/ctypes/__init__.py", line 442, in LoadLibrary return self._dlltype(name) File "/usr/local/lib/python3.7/ctypes/__init__.py", line 364, in __init__ self._handle = _dlopen(self._name, mode) OSError: libgomp.so.1: cannot open shared object file: No such file or directory
libgomp.so
が必要だったみたい。このファイルの在処が分かりにくかったけど、以下を追加することで解消した。
COPY --from=builder /usr/lib/x86_64-linux-gnu/libgomp.so.1 /usr/lib/x86_64-linux-gnu/libgomp.so.1
Docker Image のサイズを比較する
シンプルにシングルステージでビルドする場合
FROM python:3.7.7-slim
ENV APP_HOME /workspace
WORKDIR $APP_HOME
ADD requirements.txt $APP_HOME
RUN mkdir /root/.kaggle \
&& apt-get -y update \
&& apt-get -y install clang cmake git nodejs npm vim \
&& git clone --recursive https://github.com/microsoft/LightGBM && cd LightGBM && mkdir build && cd build && cmake .. && make -j4 && cd ../python-package && python setup.py install && cd ../.. && rm -r LightGBM \
&& git clone --recursive https://github.com/dmlc/xgboost && cd xgboost && mkdir build && cd build && cmake .. && make -j$(nproc) && cd ../python-package && python setup.py install && cd ../.. && rm -r xgboost \
&& pip install -r requirements.txt \
&& export NODE_OPTIONS=--max-old-space-size=4096 \
&& jupyter labextension install @jupyterlab/toc \
&& jupyter labextension install jupyterlab-plotly@4.7.1 \
&& jupyter nbextension enable --py widgetsnbextension
CMD jupyter lab \
--no-browser \
--port=8888 \
--ip=0.0.0.0 \
--allow-root
実行に不要なパッケージを削除する場合
上記の Dockerfile の RUN の最後に以下の3行を追加する
&& apt-get -y remove clang cmake nodejs npm \ && apt-get clean \ && rm -rf /var/lib/apt/lists/*
マルチステージビルド
上記の通り
結果
Size | |
---|---|
Single | 2.94GB |
Single-rm | 2.89GB |
Multi | 1.58GB |
まだまだ全然大きいですが、当初の半分くらいになりました。ML周りのパッケージやJupyter周りのパッケージが大きいのかもしれません。/usr/local/bin
以下を全部渡す必要もないのかもしれません。(分からない)
余談
jupyter labextension install の前に export NODE_OPTIONS=--max-old-space-size=4096
をしているのは JavaScript heap out of memory
のエラーが出たためです。node.js に割り当てられるメモリのデフォルトが決まっているらしく、それの上限を上げることで回避できるようです。