DockerVoici la deuxième partie du TP Docker dans le cadre de la formation Docker. Dans mon précédent article, nous avons compilé l’économiseur d’écran CMatrix sous Alpine Linux. Aujourd’hui nous allons utiliser l’historique de cette procédure pour rédiger un Dockerfile.

Dans un premier temps, notre approche sera inefficace, pour ne pas dire pataude. Notre façon de procéder va même générer une série d’erreurs, mais c’est tout à fait intentionnel. Petit à petit, nous allons corriger ces erreurs et optimiser notre Dockerfile de façon itérative jusqu’à ce que nous ayons un conteneur léger et rapide.

Ouvrez le Dockerfile du précédent article avec un éditeur de texte et commencez par supprimer les numéros de ligne de l’historique. Ne vous en faites pas si vous n’avez pas exactement le même résultat que moi :

FROM alpine

LABEL org.opencontainers.image.authors="Nicolas Kovacs"
LABEL org.opencontainers.image.description="Container image for CMatrix"

git clone https://github.com/abishekvashok/cmatrix
apk update
apk add git
git clone https://github.com/abishekvashok/cmatrix
cd cmatrix/
ls -l
autoreconf -i
apk add autoconf
autoreconf -i
apk add automake
autoreconf -i
echo $?
./configure LDFLAGS="-static"
apk add alpine-sdk
./configure LDFLAGS="-static"
mkdir -pv /usr/lib/kbd/consolefonts
mkdir -pv /usr/share/consolefonts
apk add ncurses-dev ncurses-static
./configure LDFLAGS="-static"
make 
ls -lh ./cmatrix
./cmatrix
history

Identifiez toutes les étapes où nous avons avancé à tâtons pour expérimenter et/ou qui se sont soldées par un échec et commentez-les :

FROM alpine

LABEL org.opencontainers.image.authors="Nicolas Kovacs"
LABEL org.opencontainers.image.description="Container image for CMatrix"

# git clone https://github.com/abishekvashok/cmatrix
apk update
apk add git
git clone https://github.com/abishekvashok/cmatrix
cd cmatrix/
# ls -l
# autoreconf -i
apk add autoconf
# autoreconf -i
apk add automake
autoreconf -i
# echo $?
# ./configure LDFLAGS="-static"
apk add alpine-sdk
# ./configure LDFLAGS="-static"
mkdir -pv /usr/lib/kbd/consolefonts
mkdir -pv /usr/share/consolefonts
apk add ncurses-dev ncurses-static
./configure LDFLAGS="-static"
make 
# ls -lh ./cmatrix
./cmatrix
# history

Nous voilà avec un bon premier jet de la procédure nécessaire pour compiler un binaire cmatrix depuis le code source.

  • Nous allons ajouter l’instruction RUN à chacune de ces étapes, ce qui exécutera chaque commande en ajoutant à chaque fois un layer à notre image.
  • L’instruction CMD servira à exécuter le binaire résultant cmatrix.
FROM alpine

LABEL org.opencontainers.image.authors="Nicolas Kovacs"
LABEL org.opencontainers.image.description="Container image for CMatrix"

# git clone https://github.com/abishekvashok/cmatrix
RUN apk update
RUN apk add git
RUN git clone https://github.com/abishekvashok/cmatrix
RUN cd cmatrix/
# ls -l
# autoreconf -i
RUN apk add autoconf
# autoreconf -i
RUN apk add automake
RUN autoreconf -i
# echo $?
# ./configure LDFLAGS="-static"
RUN apk add alpine-sdk
# ./configure LDFLAGS="-static"
RUN mkdir -pv /usr/lib/kbd/consolefonts
RUN mkdir -pv /usr/share/consolefonts
RUN apk add ncurses-dev ncurses-static
RUN ./configure LDFLAGS="-static"
RUN make
# ls -lh ./cmatrix
CMD ["./cmatrix"]
# history

Faisons un premier test pour voir ce que ça donne :

$ docker build -t kikinovak/cmatrix .
...
 => ERROR [ 8/14] RUN autoreconf -i                     1.2s

La construction de l’image a échoué à la huitième étape. Jetons un œil sur le Dockerfile :

RUN cd cmatrix/

Ici, nous avons utilisé la commande cd pour nous placer dans le répertoire cmatrix. Or, ce changement de répertoire n’est pas persistant pour les instructions RUN subséquentes.

Nous allons utiliser l’instruction WORKDIR qui définit le répertoire de travail pour toutes les instructions RUN, COPY, ADD, CMD et ENTRYPOINT subséquentes. Si le répertoire en question n’existe pas, l’instruction WORKDIR se chargera de le créer :

FROM alpine

LABEL org.opencontainers.image.authors="Nicolas Kovacs"
LABEL org.opencontainers.image.description="Container image for CMatrix"

WORKDIR /cmatrix

# git clone https://github.com/abishekvashok/cmatrix
RUN apk update
RUN apk add git
RUN git clone https://github.com/abishekvashok/cmatrix .
# ls -l
# autoreconf -i
RUN apk add autoconf
# autoreconf -i
RUN apk add automake
RUN autoreconf -i
# echo $?
# ./configure LDFLAGS="-static"
RUN apk add alpine-sdk
# ./configure LDFLAGS="-static"
RUN mkdir -pv /usr/lib/kbd/consolefonts
RUN mkdir -pv /usr/share/consolefonts
RUN apk add ncurses-dev ncurses-static
RUN ./configure LDFLAGS="-static"
RUN make
# ls -lh ./cmatrix
CMD ["./cmatrix"]
# history

ImportantNotez que nous ajoutons un . en argument à notre git clone de manière à ce que le code source de CMatrix soit récupéré dans le répertoire courant.

À partir de là, la construction de l’image devrait se terminer avec succès :

$ docker build -t kikinovak/cmatrix .
[+] Building 42.7s (18/18) FINISHED                                    docker:default
 => [internal] load build definition from Dockerfile                   0.0s
 => => transferring dockerfile: 805B                                   0.0s
 => [internal] load metadata for docker.io/library/alpine:latest       0.0s
 => [internal] load .dockerignore                                      0.0s
 => => transferring context: 2B                                        0.0s
 => CACHED [ 1/14] FROM docker.io/library/alpine:latest                0.0s
 => [ 2/14] WORKDIR /cmatrix                                           0.0s
 => [ 3/14] RUN apk update                                             1.9s
 => [ 4/14] RUN apk add git                                            4.0s
 => [ 5/14] RUN git clone https://github.com/abishekvashok/cmatrix .   1.6s 
 => [ 6/14] RUN apk add autoconf                                       4.0s 
 => [ 7/14] RUN apk add automake                                       1.1s 
 => [ 8/14] RUN autoreconf -i                                          2.4s 
 => [ 9/14] RUN apk add alpine-sdk                                    21.5s 
 => [10/14] RUN mkdir -pv /usr/lib/kbd/consolefonts                    0.3s 
 => [11/14] RUN mkdir -pv /usr/share/consolefonts                      0.2s 
 => [12/14] RUN apk add ncurses-dev ncurses-static                     2.5s 
 => [13/14] RUN ./configure LDFLAGS="-static"                          1.5s 
 => [14/14] RUN make                                                   0.5s 
 => exporting to image                                                 1.0s 
 => => exporting layers                                                1.0s 
 => => writing image sha256:518e202f0446ae04bb20a147eea94dc2d80f1...   0.0s 
 => => naming to docker.io/kikinovak/cmatrix                           0.0s

Jetons un œil sur l’image résultante :

$ docker images
REPOSITORY          TAG       IMAGE ID       CREATED         SIZE
kikinovak/cmatrix   latest    518e202f0446   4 minutes ago   298MB
alpine              latest    91ef0af61f39   6 weeks ago     7.8MB

Qu’est-ce qui se passe si nous lançons un conteneur depuis l’image ? Essayons :

$ docker run --rm -it kikinovak/cmatrix

CMatrix

Parfait ! Apparemment tout se passe comme prévu. Mais pour l’instant c’est loin d’être parfait. Notre conteneur a un embonpoint conséquent (298 Mo) pour un simple binaire cmatrix.

Jusqu’ici nous n’avons appliqué aucune des bonnes pratiques conseillées pour la rédaction d’un Dockerfile, et nous disposons de bien trop de layers.

Commençons déjà par nous débarrasser de toutes les sections commentées :

FROM alpine

LABEL org.opencontainers.image.authors="Nicolas Kovacs"
LABEL org.opencontainers.image.description="Container image for CMatrix"

WORKDIR /cmatrix

RUN apk update
RUN apk add git
RUN git clone https://github.com/abishekvashok/cmatrix .
RUN apk add autoconf
RUN apk add automake
RUN autoreconf -i
RUN apk add alpine-sdk
RUN mkdir -pv /usr/lib/kbd/consolefonts
RUN mkdir -pv /usr/share/consolefonts
RUN apk add ncurses-dev ncurses-static
RUN ./configure LDFLAGS="-static"
RUN make
CMD ["./cmatrix"]

La commande apk add est invoquée plusieurs fois de suite à tire larigot. Nous pouvons combiner toutes ces commandes en une seule :

FROM alpine

LABEL org.opencontainers.image.authors="Nicolas Kovacs"
LABEL org.opencontainers.image.description="Container image for CMatrix"

WORKDIR /cmatrix

RUN apk update
RUN apk add git autoconf automake alpine-sdk ncurses-dev ncurses-static
RUN git clone https://github.com/abishekvashok/cmatrix .
RUN autoreconf -i
RUN mkdir -pv /usr/lib/kbd/consolefonts
RUN mkdir -pv /usr/share/consolefonts
RUN ./configure LDFLAGS="-static"
RUN make
CMD ["./cmatrix"]

Si nous relançons la construction de l’image, nous voyons que nous n’avons plus qu’une dizaine de layers :

 => [10/10] RUN make                                     0.6s 
 => exporting to image                                   1.3s

La prochaine étape dans notre recherche d’optimisation consiste à combiner les instructions. La barre oblique inversée \ nous permet d’obtenir des instructions multi-lignes :

LABEL org.opencontainers.image.authors="Nicolas Kovacs" \
      org.opencontainers.image.description="Container image for CMatrix"

Nous pouvons faire de même avec l’instruction RUN en combinant les commandes à l’aide de l’opérateur && qui effectue une commande lorsque la commande précédente s’est terminée avec succès :

FROM alpine

LABEL org.opencontainers.image.authors="Nicolas Kovacs" \
      org.opencontainers.image.description="Container image for CMatrix"

WORKDIR /cmatrix

RUN apk update && \
    apk add git autoconf automake alpine-sdk ncurses-dev ncurses-static && \
    git clone https://github.com/abishekvashok/cmatrix . && \
    autoreconf -i && \
    mkdir -pv /usr/lib/kbd/consolefonts && \
    mkdir -pv /usr/share/consolefonts && \
    ./configure LDFLAGS="-static" && \
    make

CMD ["./cmatrix"]

Si nous relançons la construction de l’image, nous voyons que nous en sommes à trois layers, avec un conteneur qui s’exécute toujours correctement.

Le souci, c’est que l’image est toujours énorme, étant donné qu’elle contient toutes les briques logicielles nécessaires à la compilation du code.

AstuceComment est-ce qu’on pourrait réduire tout ça ? Est-ce qu’il n’y aurait pas moyen de compiler le code source pour ensuite extraire juste le résultat de cette opération ? La réponse est oui, et nous allons nous servir d’un multi-stage build (construction en plusieurs étapes) pour ce faire.

Notre Dockerfile sera organisé en deux sections distinctes :

# Build Container Image
FROM alpine AS cmatrixbuilder

WORKDIR /cmatrix

RUN apk update && \
    apk add git autoconf automake alpine-sdk ncurses-dev ncurses-static && \
    git clone https://github.com/abishekvashok/cmatrix . && \
    autoreconf -i && \
    mkdir -pv /usr/lib/kbd/consolefonts && \
    mkdir -pv /usr/share/consolefonts && \
    ./configure LDFLAGS="-static" && \
    make

# CMatrix Container Image
FROM alpine

LABEL org.opencontainers.image.authors="Nicolas Kovacs" \
      org.opencontainers.image.description="Container image for CMatrix"

COPY --from=cmatrixbuilder /cmatrix/cmatrix /cmatrix

CMD ["./cmatrix"]

Relançons la construction de l’image et voyons le résultat :

$ docker images
REPOSITORY          TAG       IMAGE ID       CREATED          SIZE
kikinovak/cmatrix   latest    a0bc0930a33f   37 seconds ago   8.59MB
alpine              latest    91ef0af61f39   6 weeks ago      7.8MB

La réduction en termes de taille est assez spectaculaire. Mais ne nous réjouissons pas trop tôt. Voici ce que nous obtenons lorsque nous lançons un conteneur :

$ docker run --rm -it kikinovak/cmatrix
Error opening terminal: xterm.

Nous en avons parlé dans le précédent article : CMatrix a besoin de NCurses, et plus précisément du paquet ncurses-terminfo-base comme dépendance d’exécution. Il va donc falloir ajouter ce paquet à notre image :

# Build Container Image
FROM alpine AS cmatrixbuilder

WORKDIR /cmatrix

RUN apk update --no-cache && \
    apk add git autoconf automake alpine-sdk ncurses-dev ncurses-static && \
    git clone https://github.com/abishekvashok/cmatrix . && \
    autoreconf -i && \
    mkdir -pv /usr/lib/kbd/consolefonts && \
    mkdir -pv /usr/share/consolefonts && \
    ./configure LDFLAGS="-static" && \
    make

# CMatrix Container Image
FROM alpine

LABEL org.opencontainers.image.authors="Nicolas Kovacs" \
      org.opencontainers.image.description="Container image for CMatrix"

RUN apk update --no-cache && apk add ncurses-terminfo-base

COPY --from=cmatrixbuilder /cmatrix/cmatrix /cmatrix

CMD ["./cmatrix"]

AstuceNotez en passant que j’ai ajouté l’option --no-cache à la commande apk update. Comme son nom le suggère, cette option permet de ne pas garder les méta-informations dans le cache et de réduire encore plus la taille de l’image résultante.

On relance la construction. L’image a augmenté un tout petit peu en taille :

$ docker images
REPOSITORY          TAG       IMAGE ID       CREATED          SIZE
kikinovak/cmatrix   latest    77530482fd12   18 seconds ago   11MB
alpine              latest    91ef0af61f39   6 weeks ago      7.8MB

En revanche, elle fonctionne parfaitement :

$ docker run --rm -it kikinovak/cmatrix

CMatrix

La prochaine étape dans le perfectionnement de notre image concerne l’utilisateur. Pour l’instant notre conteneur s’exécute en tant que root :

$ docker run --rm -it kikinovak/cmatrix whoami
root

Nous allons ajouter un utilisateur « commun mortel » pour exécuter notre application :

# Build Container Image
FROM alpine AS cmatrixbuilder

WORKDIR /cmatrix

RUN apk update --no-cache && \
    apk add git autoconf automake alpine-sdk ncurses-dev ncurses-static && \
    git clone https://github.com/abishekvashok/cmatrix . && \
    autoreconf -i && \
    mkdir -pv /usr/lib/kbd/consolefonts && \
    mkdir -pv /usr/share/consolefonts && \
    ./configure LDFLAGS="-static" && \
    make

# CMatrix Container Image
FROM alpine

LABEL org.opencontainers.image.authors="Nicolas Kovacs" \
      org.opencontainers.image.description="Container image for CMatrix"

RUN apk update --no-cache && \
    apk add ncurses-terminfo-base && \
    adduser -g "Thomas Anderson" -s /usr/sbin/nologin -D -H thomas

RUN apk update --no-cache && apk add ncurses-terminfo-base

COPY --from=cmatrixbuilder /cmatrix/cmatrix /cmatrix

USER thomas

CMD ["./cmatrix"]
  • -g pour le nom complet de l’utilisateur
  • -s pour le shell de l’utilisateur
  • -D pour désactiver le mot de passe
  • -H pour éviter la création d’un répertoire utilisateur

Je reconstruis l’image et je vérifie :

$ docker run --rm -it kikinovak/cmatrix whoami                                  
thomas     

Il nous reste un dernier point de détail à régler. C’est que l’application cmatrix est susceptible d’être lancée avec toute une série d’options. Voyez par vous-même :

$ docker run --rm -it kikinovak/cmatrix sh
/ $ ./cmatrix --help
 Usage: cmatrix -[abBcfhlsmVxk] [-u delay] [-C color] [-t tty] [-M message]
 -a: Asynchronous scroll
 -b: Bold characters on
 -B: All bold characters (overrides -b)
 -c: Use Japanese characters as seen in the original matrix. Requires appropriate fonts
 -f: Force the linux $TERM type to be on
 -l: Linux mode (uses matrix console font)
 -L: Lock mode (can be closed from another terminal)
 -o: Use old-style scrolling
 -h: Print usage and exit
 -n: No bold characters (overrides -b and -B, default)
 -s: "Screensaver" mode, exits on first keystroke
 -x: X window mode, use if your xterm is using mtx.pcf
 -V: Print version information and exit
 -M [message]: Prints your message in the center of the screen. Overrides -L's default message.
 -u delay (0 - 10, default 4): Screen update delay
 -C [color]: Use this color for matrix (default green)
 -r: rainbow mode
 -m: lambda mode
 -k: Characters change while scrolling. (Works without -o opt.)
 -t [tty]: Set tty to use

Affichez des caractères gras :

$ ./cmatrix -b

Les caractères peuvent changer pendant le défilement :

$ ./cmatrix -k

Combinez ces options avec -r comme rainbow (arc-en-ciel) :

$ ./cmatrix -k -b -r

Pour l’instant notre image Docker permet la seule exécution de cmatrix sans options. Nous allons donc modifier notre approche. CMD fournit un paramètre par défaut pour exécuter un paramètre, mais nous pouvons très bien le remplacer par ENTRYPOINT. Dans ce cas, CMD fournira la ou les options par défaut à utiliser :

ENTRYPOINT ["./cmatrix"]
CMD ["-b"]

À partir de là, les arguments que nous fournirons à l’exécution du conteneur constitueront autant de paramètres pour ENTRYPOINT avec le mode -b (bold pour les caractères gras) activé par défaut :

$ docker run --rm -it kikinovak/cmatrix

Testez le défilement asynchrone :

$ docker run --rm -it kikinovak/cmatrix -a

Combinez le défilement asynchrone avec les caractères gras :

$ docker run --rm -it kikinovak/cmatrix -ab

Accélérez le défilement :

$ docker run --rm -it kikinovak/cmatrix -ab -u 2

Modifiez la couleur de l’affichage :

$ docker run --rm -it kikinovak/cmatrix -ab -u 2 -C magenta

CMatrix

La suite au prochain numéro, où nous allons voir en détail la construction de notre image dans le but de l’utiliser sur différentes architectures : processeurs Intel & AMD, processeurs Mac Silicon, Raspberry Pi, etc.

Remerciements

Cet atelier pratique s’inspire d’une série de cours de James Spurin, un formateur Linux & Open Source assez exceptionnel. Je tiens à le remercier ici.


La rédaction de cette documentation demande du temps et des quantités significatives de café espresso. Vous appréciez ce blog ? Offrez un café au rédacteur en cliquant sur la tasse.

 


0 commentaire

Laisser un commentaire

Emplacement de l’avatar

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *