TSM - Crearea de Dockerfile-uri eficiente

Bogdan George Barna - DevOps Engineer @ 3PillarGlobal Romania

A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image. Using docker build users can create an automated build that executes several command-line instructions in succession. [0]

Așadar, un Dockerfile eficient presupune o imagine eficientă. De ce ai vrea o imagine de Docker eficientă? Și ce înseamnă, de fapt, o imagine eficientă?

Conform glosarului oficial,

Docker images are the basis of containers[^1]. An Image is an ordered collection of root filesystem changes and the corresponding execution parameters for use within a container runtime. An image typically contains a union of layered filesystems stacked on top of each other. An image does not have state and it never changes.

O imagine eficientă:

Exemplu

În continuare, vom itera ideile de mai sus peste un Dockerfile simplu, dar ineficient.

FROM ubuntu
COPY . /app
RUN apt-get update -yq
RUN apt-get install -yq cowsay
RUN cd /app/ && pip install -r
    requirements.txt

CMD ["python", "/app/main.py"]

Dockerfile-ul final arată în felul următor:

# Multi-stage build using wheel lets us compile on 
# the first image,
# create wheel files for all dependencies
# and install them in the second image without installing the compilers.

### Base
FROM python:3.7.3-stretch as base
LABEL maintainer="IDK "

COPY requirements/prod-requirements.txt /tmp/
RUN \
  apt-get update -yq && \
  apt-get install -yq --no-install-recommends \
    cowsay=3.* && \
  apt-get -y clean && \
  rm -rf /var/lib/apt/lists/ * && \
  pip --no-cache-dir install 
      -r /tmp/prod-requirements.txt

SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# fail if error at any stage in the pipe below

RUN echo "this was a multi-stage build..." | 
  /usr/games/cowsay

COPY src /app

### Test
FROM base as test
COPY requirements/test-requirements.txt /tmp/
RUN pip --no-cache-dir install -r 
    /tmp/test-requirements.txt

RUN \
  pylint --disable=R,C,W /app/hello.py && \
  bandit -v -r app && \
  radon mi -s app && \
  radon cc -s app

### Release
FROM gcr.io/distroless/python3:debug as release
COPY --from=base /app /app
COPY --from=base 
  /usr/local/lib/python3.7/site-packages 
  /usr/local/lib/python3.7/site-packages

ENV PYTHONPATH=/usr/local/lib/python3.7/site-packages

WORKDIR /app
USER 1001
CMD [ "hello.py" ]

Referințe

[0] https://docs.docker.com/engine/reference/builder/

[1] https://docs.docker.com/glossary/

[2] https://en.wikipedia.org/wiki/Systems_development_life_cycle

[3] https://docs.docker.com/engine/reference/commandline/system_prune/

[4] https://docs.docker.com/engine/reference/commandline/tag/

[5] https://thenewstack.io/understanding-the-docker-cache-for-faster-builds/

[6] https://medium.com/@mccode/understanding-how-uid-and-gid-work-in-docker-containers-c37a01d01cf/

[7] https://en.wikipedia.org/wiki/Privilege_escalation/

[8] https://docs.docker.com/v17.09/engine/userguide/eng-image/dockerfile_best-practices/#workdir

[9] https://docs.docker.com/develop/develop-images/multistage-build/