Make python container image lighter with multi-stage build

DevOps

I develop web apps using Python/Django for my work and run them as Docker containers. There are about 150 Python libraries to install, so the container images are getting large and taking a long time to upload and download.

I use a multi-stage build (and an AWS ECR image scan) to reduce the container image size. Finally, I test how much I could be reduced the container image size (and vulnerability) for each pattern.

Original dockerfile

FROM python:3.7.13-bullseye

ENV PYTHONUNBUFFERED 1
ENV PIPENV_TIMEOUT 600

# Install git command because some python libraries install directly from GitHub
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 critical + 369 others (details)). The volume is almost 2 GB and includes two critical vulnerabilities.

Use lightweight base images

I use the python:3.7.13-slim-bullseye image, which is lighter than the python:3.7.13-bullseye image. Note that the image with “slim” is the smallest image that excludes infrequently used tools and libraries.

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 critical + 236 others (details)), reducing volume by 200MB and one critical vulnerability.

Use multi-stage build

I create a container image with a multi-stage build, copying the pip-installed libraries into the runtime container image.

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.26 MB (1 high + 106 others (details)). The volume is less than 1GB, and the container image has no critical vulnerability.

Do not install unnecessary packages.

Run apt-get with –no-install-recommends. You can suppress the installation of recommended packages by passing the –no-install-recommends option to the apt command.

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.14 MB (1high + 104 other (details)), and the volume is reduced by another 20MB.

Finally

There may be various other lightweight techniques besides those mentioned above. Still, I think this is the first method you should practice if you want to make container images light because you can reduce half the volume by doing a multi-stage build.

コメント

タイトルとURLをコピーしました