はじめに
MIERUNEアドベントカレンダー22日目です!
いよいよ終盤ですね!
今回はDockerのマルチステージビルドについてやっていきます!
いやいやGIS関係ないんかい!と思う方もいるかもしれませんが、そんなことはありません!
GISに関わるプログラミングをするときにPythonはもはや必須ですし、WebGISをやっていく上でDockerも必須です。
なのでまぁDockerの話題はほぼGISですね。
ということで早速やっていきましょう!
作成するファイルはこちらに置いていますので、せっかちな方はご利用ください。
https://github.com/nokonoko1203/minimize-docker-image
必要なファイルを作成する
まずは適当にディレクトリを作成しましょう!
mkdir minimize-docker-image
cd minimize-docker-image
次に適当なPipfileを作成して書き込みます。
Pythonのバージョンはなんでも良いですが、今回はpython3.9のdockerイメージを利用するので、3.9系を指定しました。
マイクロバージョンはほんとになんでも良いです。
touch Pipfile
- Pipfile
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true
[dev-packages]
autopep8 = "*"
flake8 = "*"
isort = "*"
mypy = "*"
[packages]
requests = "==2.24.0"
[requires]
python_version = "3.9.9"
作成したPipfileを利用してlockファイルを作ります。
pipenv lock
最後にDockerfileを作成しましょう。
- Dockerfile
FROM python:3.9-buster
ENV PYTHONUNBUFFERED=1
RUN mkdir app
WORKDIR /app
COPY Pipfile.lock /app/
RUN pip install -U pip && \
pip install pipenv==2021.5.29 && \
pipenv sync --system && \
pip uninstall --yes pipenv
これだけでPythonの実行環境が概ね完成しました!
Dockerってすごいですね!
イメージを作成して起動してみる
作成したDockerfileを利用してDockerイメージを作成しましょう!
python-sample
のところは作成したいイメージ名を指定しますので、任意で構いません。
docker image build . -t python-sample
Dockerイメージからコンテナを起動してPythonのパッケージがインストールされているか確認してみましょう!
% docker container run --rm python-sample pip list
Package Version
--------------------------------- ---------
backports.entry-points-selectable 1.1.1
certifi 2021.10.8
chardet 3.0.4
distlib 0.3.4
filelock 3.4.0
idna 2.10
pip 21.3.1
platformdirs 2.4.0
requests 2.24.0
setuptools 57.5.0
six 1.16.0
urllib3 1.25.11
virtualenv 20.10.0
virtualenv-clone 0.5.7
wheel 0.37.0
Pythonもちゃんと入っていそうです。
% docker container run --rm python-sample python -V
Python 3.9.9
Dockerイメージをスリムにする
先程作成したDockerイメージのサイズを確認してみましょう。
% docker image ls | grep python-sample
python-sample latest abd1e5ec8c95 15 minutes ago 945MB
dockerイメージの容量は比較的大きめ(945MB)です。
基本的な原則として、イメージサイズが大きいと良いことは少ないです。
イメージのビルドに時間がかかる上、SSDを圧迫します。
配布する際にはアップロードにもダウンロードにも時間がかかってしまいますね。
なので、特に理由がなければコンパクトにするのが望ましいです。
ということでDockerfileを編集してイメージサイズを下げていきましょう!
Dockerfileを改修して軽量なイメージを利用する
Dockerfileを以下のように修正します。
FROM python:3.9-slim-buster
ENV PYTHONUNBUFFERED=1
RUN mkdir app
WORKDIR /app
COPY Pipfile.lock /app/
RUN pip install -U pip && \
pip install pipenv==2021.5.29 && \
pipenv sync --system && \
pip uninstall --yes pipenv
利用するイメージをpython:3.9-buster
からpython:3.9-slim-buster
に変更してみました。
このままイメージをビルドしてみましょう。
docker image build . -t python-sample-minimize
動作確認
% docker container run --rm python-sample-minimize pip list
Package Version
--------------------------------- ---------
backports.entry-points-selectable 1.1.1
certifi 2021.10.8
chardet 3.0.4
distlib 0.3.4
filelock 3.4.0
idna 2.10
pip 21.3.1
platformdirs 2.4.0
requests 2.24.0
setuptools 57.5.0
six 1.16.0
urllib3 1.25.11
virtualenv 20.10.0
virtualenv-clone 0.5.7
wheel 0.37.0
そのままイメージサイズを確認してみます。
% docker image ls | grep python-sample
python-sample-minimize latest 733101d08d8f 12 seconds ago 174MB
python-sample latest abd1e5ec8c95 4 hours ago 945MB
!!!
なんとびっくりイメージサイズが1/5以下に!!!!!
というのは当たり前で…
python:3.9-buster
: よく利用される(Python以外の)ツールなどもてんこ盛りでプリインストールされている便利なイメージ
python:3.9-slim-buster
: 利用頻度の低いツールを削ぎ落とした軽量イメージ
です。
ツールがあまりインストールされていないので、当然イメージサイズも小さい、ということになります。
今回は特に問題ないので利用しましたが、slim-busterを利用する際には自分の必要とするツールがプリインストールされているのかどうかよく確認してから利用する必要があります。
また、運用中のコンテナをbusterからslim-busterへ移行する場合にはより一層注意が必要です。
さらなる軽量化を目指す
ということで、(利用したいツールが全て揃っているのであれば)slim-busterを利用するだけでだいぶイメージサイズを軽量化できることがわかりましたが、今回はもう少し攻めてみましょう。
Dockerfileを以下のように修正します。
FROM python:3.9-buster as builder
ENV PYTHONUNBUFFERED=1
RUN mkdir app
WORKDIR /app
COPY Pipfile.lock /app/
RUN pip install -U pip && \
pip install pipenv==2021.5.29 && \
pipenv sync --system && \
pip uninstall --yes pipenv
FROM python:3.9-slim-buster as production
ENV PYTHONUNBUFFERED=1
RUN mkdir app
WORKDIR /app
COPY --from=builder /usr/local/lib/python3.9/site-packages /usr/local/lib/python3.9/site-packages/
COPY --from=builder /app /app/
Dockerfileの中にFROMが2回出てくるのが見慣れないという方もいるかもしれませんね。
これはマルチステージビルドといって、FROM文を複数使い、各ステージにbuilder・productionのような名前をつけて段階的にビルドしていくことです。
今回の例では、まずbuilderステージでpython:3.9-buster
を利用し、pipからのライブラリのインストールなどを完了させます。
その後、python:3.9-slim-buster
を利用したproductionステージで、builderステージでインストールしたライブラリが格納されたディレクトリごとコピーしてきています。
こうすることでpipやpipenvなど、アプリケーションの実行に不要なファイルを除外することができます。
では実際にイメージをビルドしてみましょう。
% docker image build . -t python-sample-minimize
その後、イメージサイズを確認してみましょう。
% docker image ls | grep python-sample-minimize
python-sample-minimize latest f9ab98960d48 32 seconds ago 138MB
174MBから138MBまで減らすことができました!
終わりに
複雑な依存関係をもたず、簡単なスクリプト実行のためにDockerを利用するのであれば、案外サクッとイメージサイズを下げることができます。
今回はマルチステージ前後で40MB程度しか減りませんでしたが、実行ファイルをビルドする必要があったり、Next.jsを利用して静的ファイルを吐き出す際の利用など、「ビルド時には多くのライブラリが必要だが、アプリケーションの実行にはほとんど依存関係が必要ない」場合にはものすごい効果を発揮します。
皆さんもどんどん利用していきましょう!