Aller au contenu principal

Optimiser la taille des images python

· 3 minutes de lecture
Stéphane ROBERT
Consultant DevOps

Ce matin en parcourant la documentation de pipenv j'ai redécouvert qu'il était possible de packager l'application avec l'option --deploy. Et la le déclic et si cette méthode permettait de réduire la taille de mes containers utilisant python. En parcourant quelques sites j'ai vu que cela pouvait fonctionner. Cela vient compléter mon précédent billet d'optimisation des images docker.

Mise en pratique

J'ai repris l'image d'ansible-lint et j'ai modifié l'image de build pour qu'elle :

  • installe pipenv.
  • copie le fichier Pipfile
  • et installe les packages dans le répertoire venv
FROM alpine:latest as builder
ENV PYROOT=/venv
ENV PYTHONUSERBASE=$PYROOT
COPY Pipfile* ./

RUN apk update && apk add --no-cache bc cargo gcc libffi-dev musl-dev openssl-dev rust python3-dev py3-pip \
  && pip3 install --no-cache-dir --no-compile pipenv && pipenv lock && PIP_USER=1 pipenv sync --system

FROM alpine:latest as default

RUN apk add --no-cache git python3
COPY --from=builder /venv /venv

# Make sure we use the virtualenv:
ENV PATH="/venv/bin:$PATH"
ENV PYTHONPATH="/venv/lib/python3.9/site-packages/"

WORKDIR /src
ENTRYPOINT ["ansible-lint", "-p", "-v"]
CMD ["--version"]

Le contenu du Pipfile:

[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]
ansible-lint = "==5.2.0"
yaml-lint = "*"
ansible = "==2.9.26"

[dev-packages]

[requires]
python_version = "3.9"

Je lance le build :

docker build -t ansible-lint-opt:5.2.0 .

Je lance le container pour la première fois:

docker run ansible-lint-opt:5.2.0
ansible-lint unknown using ansible 2.9.26

Oh il n'affiche pas la version. Un détail? Fonctionne t'il ?

On le teste sur un playbook :

docker run -v $PWD:/src ansible-lint-opt:5.2.0 provision-playbook.yml
INFO     Running ansible-galaxy role install --force --roles-path /root/.cache/ansible-lint/56c9e1/roles -vr requirements.yml
INFO     Running ansible-galaxy collection install --force -p /root/.cache/ansible-lint/56c9e1/collections -vr requirements.yml
INFO     Added ANSIBLE_COLLECTIONS_PATHS=/root/.cache/ansible-lint/56c9e1/collections
INFO     Added ANSIBLE_ROLES_PATH=~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:roles
INFO     Running ansible-galaxy role install --force --roles-path /root/.cache/ansible-lint/56c9e1/roles -vr requirements.yml
INFO     Running ansible-galaxy collection install --force -p /root/.cache/ansible-lint/56c9e1/collections -vr requirements.yml
INFO     Added ANSIBLE_COLLECTIONS_PATHS=/root/.cache/ansible-lint/56c9e1/collections:/root/.cache/ansible-lint/56c9e1/collections
INFO     Added ANSIBLE_ROLES_PATH=~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:roles:roles
INFO     Executing syntax check on provision-playbook.yml (5.48s)

Ca marche !!!!

La taille de l'image est de :

docker images |grep ansible-lint

ansible-lint-opt                                5.2.0        c5c2efc297ad   14 seconds ago   146MB
registry.gitlab.com/dockerfiles6/ansible-lint   5.2.0        369443c79b4a   2 days ago       451MB

Le choc on est passé de 1.9Gb à 451Mb avec venv et maintenant à 146Mb avec pipenv. Good job! A tester sur d'autres projets python.

Mais attention la taille ne fait pas tout. Utiliser une Alpine Linux peut parfois avoir des effets secondaires. En effet, par exemple sur les projets python contrairement à d'autres distributions Alpine ne met à disposition qu'un seul package wheel (binaire précompilé). Du coup il recompile tout à partir du code source et parfois ca peut être très très long ........ Un exemple les packages cryptography et lxml.

Donc faut trouver le bon compromis.

Source