既存のコンテナイメージをdistrolessイメージを使って改善する
はじめに
この記事では既存のコンテナイメージをdistrolessイメージを使って改善していく記事です。
主な内容としては実践したときのメモを中心に書きます。
(忘れやすいことなど)誤りなどがあれば書き直していく予定です。
今回利用したソースコードは以下のリポジトリにあります。
https://github.com/ymd65536/distroless_docker_image
環境
今回は以下の環境で動作確認を行っています。そのほかの環境で動かす場合の参考にしてください。
- MacBook
- Apple Sillicon M1
- Sonoma 14.0
- Rancher Desktop
- v1.7.0
- Python
- Python 3.9
※まずは、Rancher Desktopをインストールしてください。
※Python3.9と記載していますが、dockerイメージにおけるPythonです。
既存のコンテナイメージ
今回改善するイメージは以下です。
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をインストールしています。
イメージをビルドします。
docker build -t test_alpine:1 .
サイズを確認するため、docker images
を実行します。
docker images
実行結果は以下のとおりです。
REPOSITORY TAG IMAGE ID CREATED SIZE
test_alpine 1 374875ee50bb 8 seconds ago 333MB
サイズは333MBです。このコンテナイメージを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 /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
イメージをビルドします。
docker build -t test_distroless:1 .
サイズを確認するため、docker images
を実行します。
docker images
実行結果は以下のとおりです。
REPOSITORY TAG IMAGE ID CREATED SIZE
test_alpine 1 374875ee50bb 8 seconds ago 333MB
test_distroless 1 e6ee09fc6a8e 2 minutes ago 132MB
サイズは132MBです。distrolessイメージを使うことでほぼ同じPythonのパッケージを使いつつ、イメージサイズを大幅に削減できました。
distrolessイメージを作るときのポイント
イメージサイズを削減できたところでdistrolessイメージを作るときのポイントを確認します。
主に以下のポイントです。
- マルチステージビルドを使う
- その他のパッケージに依存したパッケージをインストールしないもしくは代わりを用意する
それぞれ見ていきましょう。
マルチステージビルドを使う
dockerにはマルチステージビルドという機能があります。マルチステージビルドとは複数のFROM
を使ってイメージを構築する機能です。
以下のようなメリットがあります。
- 他のイメージから取得したものをコピー
- 保存されているビルドイメージ(ビルドキャッシュ)から必要なものをコピーすることでビルドの高速化
- パッケージインストール時に発生するダウンロード時間をより短くできる
ただし、以下のようなデメリットもあります。
- 特定のビルドイメージに依存する形になってしまう
- ビルドイメージを保存するため、保存データ容量が大きくなる
また、昨今においてはさまざまなアーキテクチャで開発を行うことが多くなってきているため
ビルドイメージを取得する際にアーキテクチャによってビルドイメージが異なることがあります。
ビルドイメージの構築はCI/CDなどを構築してクラウド上でやるのが良いでしょう。
その他のパッケージに依存したパッケージをインストールしない
distrolessイメージを使うときには、その他のパッケージに依存したパッケージをインストールしないようにする必要があります。
今回の場合はmysqlclientをインストールしているためにmysql-devもインストールしていますが。
distrolessイメージを使うときにはmysqlclientをインストールしないようにする必要があります。
そのため、mysqlclientを削除してmysql-connector-pythonをインストールしています。
mysqlclientをインストールする場合は依存しているapkのパッケージをインストールする必要がありますが
distrolessイメージにはシェルとパッケージシステムが存在しないため、パッケージをインストールできません。
つまり、distrolessイメージを使うときにはその他のパッケージに依存したパッケージをインストールしないようにするかもしくは代わりとなるパッケージを用意する必要があります。
ちなみにmysqlclientをインストールしようとするとpkg-config
が正常に動作せず、インストールに失敗します。
まとめ
今回は既存のコンテナイメージをdistrolessイメージを使って改善しました。
distrolessイメージを使うことでイメージサイズを大幅に削減できるため、使えるところでは使っていきたいです。
ただし、マルチステージビルドを使う必要があり、ビルドイメージをいくつか保存する必要があるため
ビルドイメージの運用には注意が必要です。
イメージをビルドする際はクラウドベースのビルド環境を構築しておきましょう。
Discussion