Outils pour utilisateurs

Outils du site


sysadmin:docker:creer_image

Docker: créer une image

Il y a plusieurs façons d'aboutir à une nouvelle image:

  • A partir d'un conteneur existant et la commande docker container save, facile à mettre en place mais compliqué à maintenir.
  • From scratch, plus complexe et difficile à maintenir.
  • Via un Dockerfile, un fichier contenant les instructions de création en se basant sur une image existante.

Les instructions du Dockerfile sont listés ci-dessous:

FROM # Pour choisir l'image sur laquelle on se base, toujours en premier
RUN # Permet d'exécuter une commande
CMD # Commande exécutée au démarrage du conteneur par défaut
EXPOSE # Ouvre un port
ENV # Permet d'éditer des variables d'environnement
ARG # Variables utilisées seulement le temps de la construction de l'image
COPY # Permet de copier un fichier ou répertoire de l'hôte vers l'image
ADD # Permet de copier un fichier de l'hôte ou depuis une URL vers l'image, permet également de décompresser une archive tar
LABEL # Des métadonnées utiles pour certains logiciels de gestion de conteneurs, comme rancher ou swarm, ou tout simplement pour mettre des informations sur l'image.
ENTRYPOINT # Commande exécutée au démarrage du conteneur, non modifiable, utilisée pour package une commande
VOLUME # Crée une partition spécifique
WORKDIR # Permet de choisir le répertoire de travail
USER # Choisit l'utilisateur qui lance la commande du ENTRYPOINT ou du CMD
ONBUILD # Crée un step qui sera exécuté seulement si notre image est choisie comme base
HEALTHCHECK # Permet d'ajouter une commande pour vérifier le fonctionnement de votre conteneur
STOPSIGNAL # permet de choisir le [signal](http://man7.org/linux/man-pages/man7/signal.7.html) qui sera envoyé au conteneur lorsque vous ferez un docker container stop

Construire une image

Pour construire l'image on utilisera la commande image build ou build:

docker image build -t [imagename][:tag] aContextFolder
  • La commande accepte un seul argument obligatoire (ici “aContextFolder”), c'est le chemin du build context;
  • L'option --tag, -t permet de définir à la fois le nom de l'image et la version.

A propos du contexte

L'image Docker est construite côté serveur. Le client Docker communique au serveur le Dockerfile et les fichiers présents dans le dossier en argument de la commande docker build: c'est le contexte. Il est possible d'exclure des fichiers du contexte grâce au fichier .dockerignore. Le détail du fonctionnement du contexte est abordé dans le wiki build context.

Exemple

Dans l'exemple ci-dessous, on a écrit un fichier script.sh qui affiche régulièrement un message:

script.sh
while true;
do
        echo $(date +"%d-%m-%Y %H:%M:%S")": [APP version="$APP_VERSION"]"
        sleep 5 
done
exit 0

La variable APP_VERSION non définie dans le script sera présente dans l’environnement d'exécution. On souhaite partir d'une image de busybox existante pour y ajouter notre script

Dockerfile
FROM busybox:1.24-glibc
RUN mkdir -p /usr/local/bin
COPY script.sh /usr/local/bin
CMD /usr/local/bin/script.sh

Pour construire la nouvelle image:

docker image build --tag test_script:v0.1.1 .

On peut ensuite créer le conteneur à partir de notre image:

docker container run --detach --env APP_VERSION="0.1" test_script:v0.1.1

Optimiser la taille des images

Chaque instruction modifiant la structure du système de fichier dans le Dockerfile produit un layer (calque). Un nombre important de layers conduit à une image de taille importante et peu nuire au temps d'accès de lecture/écriture. Pour éviter cela on groupe au maximum les commandes.

Ci dessous un exemple non optimisé:

# layer 1
RUN apt-get update && apt-get install -y --no-install-recommends --no-install-suggests perl \
                                                                        ca-certificates \
                                                                        shared-mime-info \
                                                                        perlmagick \
                                                                        make \
                                                                        gcc \
                                                                        ca-certificates \
                                                                        libssl-dev \
                                                                        git
# layer 2
RUN cpan install Carton

# layer 3
RUN cd / && git clone https://git.framasoft.org/luc/lutim.git

# layer 4
RUN cd /lutim && carton install

# layer 5
RUN apt-get purge -y make gcc ca-certificates libssl-dev git

# layer 6                       
RUN apt-get autoremove --purge -y && apt-get clean

#layer 7
RUN rm -rf /var/lib/apt/lists/* /root/.cpan* /lutim/local/cache/* /lufi/utilities

Les mêmes commandes regroupées conduiront à la création d'un seul calque:

RUN apt-get update \
    && apt-get install -y --no-install-recommends --no-install-suggests perl \
                                                                        ca-certificates \
                                                                        shared-mime-info \
                                                                        perlmagick \
                                                                        make \
                                                                        gcc \
                                                                        ca-certificates \
                                                                        libssl-dev \
                                                                        git \
    && cpan install Carton \
    && cd / \
    && git clone https://git.framasoft.org/luc/lutim.git \
    && cd /lutim \
    && carton install \
    && apt-get purge -y make \
                        gcc \
                        ca-certificates \
                        libssl-dev \
                        git \
    && apt-get autoremove --purge -y \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/* /root/.cpan* /lutim/local/cache/* /lufi/utilities
Depuis les versions 17.09 et supérieures de Docker les commandes COPY/ADD supportent le modificateur --chown. Cela permet d'éviter la création d'un calque supplémentaire lorsqu'une commande de changement de droits d'accès au fichier est enchaînée après la copie.
# Pour l'exemple on crée un fichier de 10 Mo que l'on va ajouter à une image
mkdir test
cd test
dd if=/dev/zero of=file.dat bs=1M count=10

Pour illustrer la problématique on va utiliser une image busybox d'une taille initiale approximative de 1 Mo

# Pour lister les images locales
docker images
REPOSITORY               TAG             IMAGE ID       CREATED         SIZE
phobos/buildenv-ffsync   v1.0            5335d8ff969e   6 weeks ago     144MB
grafana/grafana          8.1.1           c6e5c4f48cf9   7 weeks ago     213MB
bitnami/dokuwiki         latest          1f0bc8445e43   2 months ago    387MB
traefik                  v2.4.12         8b7f6bb63b8a   2 months ago    92MB
...
busybox                  latest          22667f53682a   8 months ago    1.23MB

On crée un Dockerfile pour notre nouveau conteneur basé sur une image busybox à laquelle on ajoute notre fichier de données de 10 Mo:

FROM busybox:latest
COPY file.dat /opt/file.dat
...

On crée l'image à partir de ce Dockerfile:

docker image build -t phobos/layer-test:v1.0 .

Si on affiche à présent les images disponibles:

docker images
REPOSITORY               TAG             IMAGE ID       CREATED          SIZE
phobos/layer-test        v1.0            53be48f115d7   17 seconds ago   11.7MB
...
busybox                  latest          22667f53682a   8 months ago     1.23MB

Comme on pouvait s'y attendre la taille de notre nouvelle image correspond à l'image initiale busybox à laquelle on ajoute le poids de notre fichier de données.

Le phénomène des calques apparaîtra avec la modification suivante dans le Dockerfile:

FROM busybox:latest
COPY file.dat /opt/file.dat
# Pour nos besoins le fichier doit appartenir à l'utilisateur 1003
RUN chown 1003:1003 /opt/file.dat
# création de l'image
docker image build -t phobos/layer-test:v1.1 .
 
# lister les images
docker images
REPOSITORY               TAG             IMAGE ID       CREATED              SIZE
phobos/layer-test        v1.1            776d428be3b6   About a minute ago   22.2MB
phobos/layer-test        v1.0            53be48f115d7   10 minutes ago       11.7MB
...
busybox                  latest          22667f53682a   8 months ago         1.23MB

Ici on voit bien qu'entre les versions v1.0 et v1.1, la taille de l'image a doublé alors qu'aucun nouveau fichier n'a été ajouté à l'image. L'opération de changement de droits a conduit à un nouveau calque contenant le même fichier de 10 Mo avec les droits adéquats.

Pour éviter de créer un nouveau calque et doubler l'occupation disque on peut modifier le Dockerfile comme ci-dessous:

Dockerfile
FROM busybox:latest
COPY --chown=1003:1003 file.dat /opt/file.dat

On crée une nouvelle version avec le Dockerfile modifié

# création de l'image
docker image build -t phobos/layer-test:v1.2 .
 
# lister les images
docker images
REPOSITORY               TAG             IMAGE ID       CREATED          SIZE
phobos/layer-test        v1.2            72ca2ea7cac0   5 seconds ago    11.7MB
phobos/layer-test        v1.1            776d428be3b6   37 minutes ago   22.2MB
phobos/layer-test        v1.0            53be48f115d7   47 minutes ago   11.7MB
...
busybox                  latest          22667f53682a   8 months ago     1.23MB

La version v1.2 obtenue avec la dernière version du Dockerfile n'a pas produit de nouveau calque, on a bien une taille de 11.7Mo pour notre image.

Sécurité

Limiter au maximum les processus s’exécutant en tant que root même dans le container. Utiliser exec, su-exec pour remplacer dès que possible les processus root.

Utilisation des labels

Docker permet d'associer à une image des métadonnées via le mécanisme des labels.

  • Un label est une paire clé/valeur stockée dans une chaîne de caractères.
  • Les noms de labels commencent et se terminent par des lettres minuscules. Ils ne peuvent contenir que des lettre et les caractères point '.' ou trait d'union '-'.
  • La clé du label doit être unique, si elle est définie plusieurs fois, c'est la dernière déclaration qui est conservée. Les labels peuvent ainsi être hérités/redéfinis.
  • Pour éviter les conflits ou redéfinitions involontaires des clés, il est recommandé d'utiliser un espace de nommage basé sur son DNS (org.example.my-key=value).

Confère la documentation officielle Docker à propos des labels

Il existe également un projet OCI 1)

# metadata
LABEL "org.opencontainers.image.authors"="contact@example.org" \
      "org.opencontainers.image.url"="https://example.org/docker-images/app" \
      "org.opencontainers.image.source"="https://example.org/src/app/master" \
      "org.opencontainers.image.vendor"="Phobos, Inc" \
      "org.opencontainers.image.description"="My Lovely App 1.5"

Références

1)
Open Container Initiative
sysadmin/docker/creer_image.txt · Dernière modification : 2023/04/25 12:11 de yoann