はじめに
この記事では既存のコンテナイメージをdistrolessイメージを使って改善していく記事です。
主な内容としては実践したときのメモを中心に書きます。
(忘れやすいことなど)誤りなどがあれば書き直していく予定です。
今回利用したソースコードは以下のリポジトリにあります。
distroless_docker_image GitHub
環境
今回は以下の環境で動作確認を行っています。そのほかの環境で動かす場合の参考にしてください。
- MacBook
- Apple Sillicon M1
- Sonoma 14.0
- Python
- Python 3.9
※Python3.9と記載していますが、dockerイメージにおけるPythonです。
なお、Finchのセットアップについてはココを参照してください。
既存のコンテナイメージ
今回改善するイメージは以下です。
FROM python:3.9-alpine as builder
RUN apk add --no-cache musl-dev mysql-dev gcc
WORKDIR /app
COPY . .
RUN pip install --no-cache-dir --upgrade pip && pip install --no-cache-dir -r requirements.txt
CMD ["python", "main.py"]
imageを構築する過程でpipを実行しています。pipでインストールするパッケージは以下のとおりです。
alembic==1.13.1
annotated-types==0.6.0
boto3==1.4.8
botocore==1.8.11
certifi==2023.11.17
charset-normalizer==3.3.2
docutils==0.14
exceptiongroup==1.2.0
functions==0.7.0
greenlet==3.0.3
idna==3.6
iniconfig==2.0.0
jmespath==0.9.3
Mako==1.3.0
MarkupSafe==2.1.3
mysqlclient==2.2.1
packaging==23.2
pluggy==1.3.0
pydantic==2.5.3
pydantic_core==2.14.6
pytest==7.4.4
python-dateutil==2.6.1
python-multipart==0.0.6
pytz==2023.3.post1
requests==2.31.0
s3transfer==0.1.12
schemas==0.7.1
six==1.11.0
SQLAlchemy==2.0.25
SQLAlchemy-Utils==0.41.1
tomli==2.0.1
typing_extensions==4.9.0
urllib3==2.1.0
xmltodict==0.13.0
なお、mysqlclientをインストールしているため、apk
でmysql-devをインストールしています。
イメージをビルドします。
セットアップ
まずはVMを初期化します。
finch vm init
※すでに初期化している場合は下記のような実行結果が表示されます。
FATA[0000] the instance "finch" is already running
python:3.9-alpineイメージをビルドする
では、Finchでイメージをビルドしていきましょう。以下のコマンドを実行します。
finch build -t test_alpine:1 .
サイズを確認するため、finch images
を実行します。
finch images
実行結果は以下のとおりです。
REPOSITORY TAG IMAGE ID CREATED PLATFORM SIZE BLOB SIZE
test_alpine 1 d77ee33a7032 3 seconds ago linux/arm64 337.2 MiB 104.2 MiB
サイズは337MBです。このコンテナイメージをdistrolessイメージに置き換えていきます。
distrolessイメージを使って改善していく
まずはどんな感じのdockerfileになるかを確認します。
FROM python:3.9-alpine as builder
WORKDIR /app
COPY . .
RUN pip --no-cache-dir install --upgrade pip && pip install --no-cache-dir --target site-packages -r requirements.txt
FROM gcr.io/distroless/python3-debian11:nonroot
WORKDIR /app
COPY --from=builder /app .
ENV PYTHONPATH=/app/site-packages
CMD ["./main.py"]
なお、requirements.txtは以下のとおりです。変更点としてはmysqlclientを削除して代わりにmysql-connector-python==8.2.0
をインストールしています。
alembic==1.13.1
annotated-types==0.6.0
boto3==1.4.8
botocore==1.8.11
certifi==2023.11.17
charset-normalizer==3.3.2
docutils==0.14
exceptiongroup==1.2.0
functions==0.7.0
greenlet==3.0.3
idna==3.6
iniconfig==2.0.0
jmespath==0.9.3
Mako==1.3.0
MarkupSafe==2.1.3
# mysqlclient==2.2.1
packaging==23.2
pluggy==1.3.0
pydantic==2.5.3
pydantic_core==2.14.6
pytest==7.4.4
python-dateutil==2.6.1
python-multipart==0.0.6
pytz==2023.3.post1
requests==2.31.0
s3transfer==0.1.12
schemas==0.7.1
six==1.11.0
SQLAlchemy==2.0.25
SQLAlchemy-Utils==0.41.1
tomli==2.0.1
typing_extensions==4.9.0
urllib3==2.1.0
xmltodict==0.13.0
mysql-connector-python==8.2.0
イメージをビルドします。
finch build -t test_distroless:1 .
サイズを確認するため、docker images
を実行します。
finch images
実行結果は以下のとおりです。
test_alpine 1 d77ee33a7032 About a minute ago linux/arm64 337.2 MiB 104.2 MiB
test_distroless 1 b2538550ade5 6 seconds ago linux/arm64 147.1 MiB 41.3 MiB
サイズは147MBです。distrolessイメージを使うことでほぼ同じPythonのパッケージを使いつつ、イメージサイズを大幅に削減できました。
※dockerの時と比較すると10MB程度くらいですが、若干大きい気がします。
distrolessイメージを作るときのポイント
イメージサイズを削減できたところでdistrolessイメージを作るときのポイントを確認します。
主に以下のポイントです。
- マルチステージビルドを使う
- その他のパッケージに依存したパッケージをインストールしないもしくは代わりを用意する
それぞれ見ていきましょう。
マルチステージビルドを使う
dockerにはマルチステージビルドという機能があります。マルチステージビルドとは複数のFROM
を使ってイメージを構築する機能です。
以下のようなメリットがあります。
- ビルドキャッシュから必要なものをコピーすることでビルドの高速化
- パッケージインストール時に発生するダウンロード時間をより短くできる
ただし、以下のようなデメリットもあります。
- 特定のビルドイメージに依存してしまう
- ビルドイメージを保存するため、相対的に保存データ容量が大きくなる
また、昨今においてはさまざまなアーキテクチャで開発を行うことが多くなってきているため
ビルドイメージを取得する際にアーキテクチャによってビルドイメージは異なることがあります。
ビルドイメージの構築はCI/CDなどを構築してクラウド上でやるのが良いでしょう。
その他のパッケージに依存したパッケージをインストールしない
distrolessイメージを使うときには、その他のパッケージに依存したパッケージをインストールしないようにする必要があります。
mysqlclientをインストールする場合は依存しているapkのパッケージをインストールする必要があります。
しかし、distrolessイメージにはシェルとパッケージシステムが存在しないため、apkのパッケージをインストールできません。
ちなみにmysqlclientをインストールしようとするとpkg-config
が正常に動作せず、インストールに失敗します。
まとめ
今回は既存のコンテナイメージをdistrolessイメージを使って改善しました。
distrolessイメージを使うことでイメージサイズを大幅に削減できるため、使えるところでは使っていきたいです。
また、イメージをビルドする際はクラウドベースのビルド環境を構築しておきましょう。