1
Help us understand the problem. What are the problem?

posted at

マルチステージビルドでPythonのコンテナイメージを軽量にする

仕事でPython/Djgnagoを使ってWEBアプリを開発し、作成したアプリはDockerコンテナにして運用しています。インストールするPythonのライブラリが150個ぐらいあるので、コンテナイメージが大きくなってアップロードやダウンロードに時間がかかっていました。

何とかコンテナイメージを軽くするために、マルチステージビルドを実施(ついでにAWS ECRのイメージスキャンも実施)し、コンテナイメージの容量(と脆弱性)がどのぐらい削減できるのかをそれぞれのパターンで検証しました。

修正前

FROM python:3.7.13-bullseye

ENV PYTHONUNBUFFERED 1
ENV PIPENV_TIMEOUT 600

# githubから直接pipインストールするため、gitインストール
RUN apt-get -y update && \
    apt-get -y upgrade && \
    apt-get -y dist-upgrade && \
    apt-get -y install gcc git mecab libmecab-dev mecab-ipadic mecab-ipadic-utf8 swig fonts-vlgothic poppler-utils libglib2.0-0 libsm6 libxrender1 libxext6 && \
    apt-get autoclean -y && apt-get clean && rm -rf /var/cache/apt/* /var/lib/apt/lists/*

RUN groupadd -r app && useradd --no-log-init -r -g app app
USER app

COPY --chown=app:app . /home/app
WORKDIR /home/app

ENV PATH="/home/app/.local/bin:${PATH}"

RUN pip install --upgrade pip && pip install pipenv
RUN pipenv install --ignore-pipfile --deploy --system

EXPOSE 8000
CMD gunicorn config.wsgi -b 0.0.0.0:8000 --log-level=info

1999.66MB(2重要 + 369その他(詳細))になりました。容量はほぼ2GBになり、criticalの脆弱性も2件含まれています。

軽量なベースイメージを利用

python:3.7.13-bullseyeイメージより軽量なpython:3.7.13-slim-bullseyeイメージを利用します。ちなみにslimがついたイメージは使用頻度の低いツールやライブラリを除外した最小イメージになります。

FROM python:3.7.13-slim-bullseye

ENV PYTHONUNBUFFERED 1
ENV PIPENV_TIMEOUT 600

RUN apt-get -y update && \
    apt-get -y upgrade && \
    apt-get -y dist-upgrade && \
    apt-get -y install gcc git mecab libmecab-dev mecab-ipadic mecab-ipadic-utf8 swig fonts-vlgothic poppler-utils libglib2.0-0 libsm6 libxrender1 libxext6 && \
    apt-get autoclean -y && apt-get clean && rm -rf /var/cache/apt/* /var/lib/apt/lists/*

RUN groupadd -r app && useradd --no-log-init -r -g app app
USER app

COPY --chown=app:app . /home/app
WORKDIR /home/app

ENV PATH="/home/app/.local/bin:${PATH}"

RUN pip install --upgrade pip && pip install pipenv
RUN pipenv install --ignore-pipfile --deploy --system

EXPOSE 8000
CMD gunicorn config.wsgi -b 0.0.0.0:8000 --log-level=info

1805.31MB(1重要 + 236その他(詳細))となり、容量を200MB、criticalの脆弱性も1件削減できました。

マルチステージビルドを利用

マルチステージビルドでコンテナイメージを作成します。pipインストールしたライブラリを実行用のコンテナイメージにコピーしています。

FROM python:3.7.13-bullseye as builder

ENV PYTHONUNBUFFERED 1
ENV PIPENV_TIMEOUT 600

RUN apt-get -y update && \
    apt-get -y upgrade && \
    apt-get -y dist-upgrade && \
    apt-get -y install gcc git mecab libmecab-dev mecab-ipadic mecab-ipadic-utf8 swig fonts-vlgothic poppler-utils libglib2.0-0 libsm6 libxrender1 libxext6

COPY Pipfile Pipfile.lock /app/
WORKDIR /app

RUN pip install --upgrade pip && pip install pipenv
RUN pipenv install --ignore-pipfile --deploy --system


FROM python:3.7.13-slim-bullseye as runner

ENV PYTHONUNBUFFERED 1

RUN apt-get -y update && \
    apt-get -y upgrade && \
    apt-get -y dist-upgrade && \
    apt-get -y install mecab libmecab-dev mecab-ipadic mecab-ipadic-utf8 swig fonts-vlgothic poppler-utils libglib2.0-0 libsm6 libxrender1 libxext6 && \
    apt-get autoclean -y && apt-get clean && rm -rf /var/cache/apt/* /var/lib/apt/lists/*

RUN groupadd -r app && useradd --no-log-init -r -g app app
USER app

COPY --chown=app:app . /home/app
WORKDIR /home/app

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

EXPOSE 8000
CMD gunicorn config.wsgi -b 0.0.0.0:8000 --log-level=info

947.26MB(1高 + 106その他(詳細))となり、容量は当初の半分以下1GBを割り、criticalの脆弱性もなくすことができました。

不要なパッケージをインストールしない

apt-getに--no-install-recommendsをつけて実行します。

Ubuntuは8.10から、インストールしようとしているソフトウェアが依存しているパッケージだけでなく「推奨パッケージ」も一緒にインストールするようになりました。これは大抵の場合はユーザーにとって便利ではあるものの、たまに必要以上に多くのパッケージが一緒にインストールされてしまうこともあります。

aptコマンドに--no-install-recommendsオプションを渡すことで、推奨パッケージのインストールを抑制できます。

引用元:第331回 パッケージ管理のハウツー集

FROM python:3.7.13-bullseye as builder

ENV PYTHONUNBUFFERED 1
ENV PIPENV_TIMEOUT 600

RUN apt-get -y update && \
    apt-get -y upgrade && \
    apt-get -y dist-upgrade && \
    apt-get -y install gcc git mecab libmecab-dev mecab-ipadic mecab-ipadic-utf8 swig fonts-vlgothic poppler-utils libglib2.0-0 libsm6 libxrender1 libxext6

COPY Pipfile Pipfile.lock /app/
WORKDIR /app

RUN pip install --upgrade pip && pip install pipenv
RUN pipenv install --ignore-pipfile --deploy --system


FROM python:3.7.13-slim-bullseye as runner

ENV PYTHONUNBUFFERED 1

RUN apt-get -y update && \
    apt-get -y upgrade && \
    apt-get -y dist-upgrade && \
    apt-get -y install --no-install-recommends mecab libmecab-dev mecab-ipadic mecab-ipadic-utf8 swig fonts-vlgothic poppler-utils libglib2.0-0 libsm6 libxrender1 libxext6 && \
    apt-get autoclean -y && apt-get clean && rm -rf /var/cache/apt/* /var/lib/apt/lists/*

RUN groupadd -r app && useradd --no-log-init -r -g app app
USER app

COPY --chown=app:app . /home/app
WORKDIR /home/app

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

EXPOSE 8000
CMD gunicorn config.wsgi -b 0.0.0.0:8000 --log-level=info

927.14MB(1 高 + 104 その他 (詳細))となり、ダメ押しでさらに20MBほど削減できました。

最後に

上記以外に軽量化のテクニックは様々あると思いますが、マルチステージビルドをするだけで一気に半分くらい容量を削減できるので、コンテナイメージを軽量にしたい場合、まず最初に実践すべき方法と思いました。

参考

Dockerfileを書くためのベストプラクティス【参考訳】v18.09
仕事でPythonコンテナをデプロイする人向けのDockerfile (1): オールマイティ編

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
1
Help us understand the problem. What are the problem?