Voici le premier volet d’un petit atelier pratique que j’aime bien faire avec mes étudiants ou mes stagiaires dans le cadre de mes cours sur Docker. Il s’agit en gros de compiler le code source de l’économiseur d’écran CMatrix sous Alpine Linux, une distribution ultra-légère spécialement conçue pour servir de base aux conteneurs Docker.
Pour commencer, récupérez la dernière image d’Alpine Linux :
$ docker pull alpine Using default tag: latest latest: Pulling from library/alpine 43c4264eed91: Pull complete Digest: sha256:beefdbd8a1da6d2915566fde36db9db0b524eb737fc57cd1367effd16dc0d06d Status: Downloaded newer image for alpine:latest docker.io/library/alpine:latest
Sur mon système, l’image pèse moins de 8 Mo. C’est un excellent point de départ pour construire une image Docker légère :
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
alpine latest 91ef0af61f39 5 weeks ago 7.8MB
La prochaine étape consiste à créer un Dockerfile
. C’est en quelque sorte la recette de cuisine de notre image Docker : un fichier texte qui contient toutes les instructions nécessaires pour construire l’image.
$ mkdir -v cmatrix mkdir: created directory 'cmatrix' $ cd cmatrix/ $ vim Dockerfile
L’instruction FROM
nous indique l’image de base à utiliser pour les instructions subséquentes :
FROM alpine
Pour les métadonnées de l’image, la bonne pratique consiste à utiliser l’instruction LABEL
en conformité avec les standards décrits ici. Commencez par renseigner l’auteur de l’image :
LABEL org.opencontainers.image.authors="Nicolas Kovacs"
Ajoutez une description succincte :
LABEL org.opencontainers.image.description="Container image for CMatrix"
Pour l’instant notre image ne fait pas grand-chose, mais rien ne nous empêche de construire un premier jet pour autant :
$ docker build -t kikinovak/cmatrix .
Nos deux images sont de la même taille. C’est normal, puisque nous avons uniquement ajouté les métadonnées pour l’instant.
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE alpine latest 91ef0af61f39 5 weeks ago 7.8MB kikinovak/cmatrix latest 355beea785e4 5 weeks ago 7.8MB
À présent nous allons démarrer un conteneur basé sur cette image. Dans un premier temps, notre démarche consistera à essayer de compiler CMatrix directement dans le shell du conteneur, en avançant pas à pas dans une approche de tentative et échec.
Alpine Linux est plus exigeant qu’une base Ubuntu ou Debian. Au départ nous ne disposons même pas d’un shell Bash, et la plupart des outils que nous pouvons avoir l’habitude d’utiliser sont également absents :
$ docker run --rm -it kikinovak/cmatrix sh / #
Nous avons besoin du code source de CMatrix. Essayons de le récupérer :
# git clone https://github.com/abishekvashok/cmatrix sh: git: not found
Alpine Linux utilise le gestionnaire de paquets APK (Alpine Package Keeper) pour installer et gérer les paquets logiciels. Nous allons donc l’utiliser pour récupérer Git :
# apk update # apk add git
Récupérez le code source de CMatrix :
# git clone https://github.com/abishekvashok/cmatrix Cloning into 'cmatrix'...
Notez dans un coin de votre tête qu’à ce stade nous avons effectué trois étapes dans la construction de notre image :
- la mise à jour des métadonnées des paquets
- l’installation du paquet
git
- la récupération du code source de CMatrix
Continuons allègrement et jetons un œil au code source :
# cd cmatrix/
# ls -l
total 176
-rw-r--r-- 1 root root 65 Oct 18 08:00 AUTHORS
-rw-r--r-- 1 root root 3141 Oct 18 08:00 CMakeLists.txt
-rw-r--r-- 1 root root 3220 Oct 18 08:00 CODE_OF_CONDUCT.md
-rw-r--r-- 1 root root 4235 Oct 18 08:00 CONTRIBUTING.md
-rw-r--r-- 1 root root 35149 Oct 18 08:00 COPYING
-rw-r--r-- 1 root root 65 Oct 18 08:00 ChangeLog
-rw-r--r-- 1 root root 7831 Oct 18 08:00 INSTALL
...
Lorsque vous construisez une image Docker, c’est tout à fait normal d’avancer à tâtons et de rencontrer une succession d’obstacles. Je ne vais pas forcément rentrer dans les détails de tous les obstacles que j’ai pu rencontrer lors de la compilation de CMatrix.
Pour compiler le code source de CMatrix, commençons par invoquer autoreconf -i
:
# autoreconf -i sh: autoreconf: not found
La commande n’est pas disponible non plus. Je vais donc l’installer :
# apk add autoconf (1/7) Installing m4 (1.4.19-r3) (2/7) Installing libbz2 (1.0.8-r6) (3/7) Installing perl (5.38.2-r0) (4/7) Installing autoconf (2.72-r0) (5/7) Installing perl-error (0.17029-r2) (6/7) Installing perl-git (2.45.2-r0) (7/7) Installing git-perl (2.45.2-r0) Executing busybox-1.36.1-r29.trigger OK: 63 MiB in 34 packages
Notez la taille de tous ces paquets dans un autre coin de votre tête. Tous ces outils contribuent à agrandir considérablement notre image de conteneur.
Et voici le prochain obstacle :
# autoreconf -i Can't exec "aclocal": No such file or directory at /usr/share/autoconf/Autom4te/FileUtils.pm line 299. autoreconf: error: aclocal failed with exit status: 2
Ici il nous faut installer le paquet automake
:
# apk add automake
Et c’est reparti pour un tour :
# autoreconf -i configure.ac:5: warning: 'AM_CONFIG_HEADER': this macro is obsolete. configure.ac:5: You should use the 'AC_CONFIG_HEADERS' macro instead. ./lib/autoconf/general.m4:2434: AC_DIAGNOSE is expanded from... aclocal.m4:745: AM_CONFIG_HEADER is expanded from... configure.ac:5: the top level configure.ac:27: warning: The macro 'AC_HEADER_STDC' is obsolete. configure.ac:27: You should run autoupdate. ./lib/autoconf/headers.m4:663: AC_HEADER_STDC is expanded from... configure.ac:27: the top level configure.ac:31: warning: The macro 'AC_TYPE_SIGNAL' is obsolete. configure.ac:31: You should run autoupdate. ./lib/autoconf/types.m4:805: AC_TYPE_SIGNAL is expanded from... configure.ac:31: the top level configure.ac:172: warning: AC_OUTPUT should be used without arguments. configure.ac:172: You should run autoupdate. configure.ac:18: installing './compile' configure.ac:8: installing './config.guess' configure.ac:8: installing './config.sub' configure.ac:6: installing './install-sh' configure.ac:6: installing './missing' Makefile.am: installing './depcomp'
Si vous n’avez pas trop l’habitude de compiler du code source, ce gloubi-boulga vous semblera probablement inquiétant. Or, les choses se passent plutôt bien. Pour en avoir le cœur net, affichez le code retour de la dernière commande :
# echo $? 0
Le code retour 0
nous indique ici que la commande précédente s’est correctement effectuée.
La prochaine étape consiste à lancer ./configure
, une procédure classique dans la compilation d’applications en C. Notez tout de même que je souhaite construire un binaire statique qui contient toutes les dépendances nécessaires à son exécution :
# ./configure LDFLAGS="-static" checking for a BSD-compatible install... /usr/bin/install -c checking whether build environment is sane... yes checking for a race-free mkdir -p... /bin/mkdir -p checking for gawk... no checking for mawk... no checking for nawk... no checking for awk... awk checking whether make sets $(MAKE)... no checking whether make supports nested variables... no checking build system type... x86_64-pc-linux-musl checking host system type... x86_64-pc-linux-musl checking for gcc... no checking for cc... no checking for cl.exe... no checking for clang... no configure: error: in '/cmatrix': configure: error: no acceptable C compiler found in $PATH See 'config.log' for more details
Cette étape échoue également pour la simple raison que notre système réduit ne dispose pas de compilateur pour l’instant :
checking for gcc... no checking for cc... no
Sous Alpine Linux, les compilateurs de base sont fournis par le méta-paquet alpine-sdk
. Là encore, notez la taille relativement importante des paquets installés par rapport au système de base initial :
# apk add alpine-sdk
...
OK: 282 MiB in 68 packages
On retente le coup :
# ./configure LDFLAGS="-static" checking for a BSD-compatible install... /usr/bin/install -c checking whether build environment is sane... yes checking for a race-free mkdir -p... /bin/mkdir -p ... configure: WARNING: *** No termcap lib available, consider getting the official ncurses *** distribution from ftp://ftp.gnu.org/pub/gnu/ncurses if you get *** errors compiling cmatrix. ... configure: WARNING: *** You do not appear to have a consolefonts directory in a standard location *** (/usr/lib/kbd or /usr/share), even though you appear to have the *** consolechars and/or setfont command. The matrix font for the console *** will not be installed. This means you will not be able to use the *** matrix console font (and the -l command line switch) unless the font *** is located in your current directory when you run CMatrix. ... config.status: creating Makefile config.status: creating cmatrix.spec config.status: creating config.h config.status: executing depfiles commands
C’est déjà pas mal, à deux détails près :
- Il nous manque deux répertoires sur le système.
- Il manque également une dépendance NCurses.
Créez les deux répertoires pour les polices d’affichage :
# mkdir -pv /usr/lib/kbd/consolefonts created directory: '/usr/lib/kbd/' created directory: '/usr/lib/kbd/consolefonts' # mkdir -pv /usr/share/consolefonts created directory: '/usr/share/consolefonts'
Installez les dépendances NCurses :
# apk add ncurses-dev ncurses-static (1/8) Installing ncurses-terminfo-base (6.4_p20240420-r1) (2/8) Installing libncursesw (6.4_p20240420-r1) (3/8) Installing libformw (6.4_p20240420-r1) (4/8) Installing libmenuw (6.4_p20240420-r1) (5/8) Installing libpanelw (6.4_p20240420-r1) (6/8) Installing libncurses++ (6.4_p20240420-r1) (7/8) Installing ncurses-dev (6.4_p20240420-r1) (8/8) Installing ncurses-static (6.4_p20240420-r1) Executing busybox-1.36.1-r29.trigger OK: 290 MiB in 76 packages
Notez au passage que le paquet ncurses-terminfo-base
est également installé. Ce paquet va constituer une dépendance d’exécution de notre application. Nous aurons l’occasion d’en reparler en temps et en heure.
On relance la commande ./configure
:
# ./configure LDFLAGS="-static"
...
"Using ncurses as the termcap library"
checking for use_default_colors in -lncurses... yes
checking for resizeterm in -lncurses... yes
checking for wresize in -lncurses... yes
checking for consolechars... no
checking for setfont... /usr/sbin/setfont
checking for /usr/lib/kbd/consolefonts... yes
checking for /usr/share/consolefonts... yes
checking for mkfontdir... no
checking for /usr/share/fonts/misc... no
checking for /usr/share/X11/fonts/misc... no
checking for /usr/X11R6/lib/X11/fonts/misc... no
checking that generated files are newer than configure... done
configure: creating ./config.status
config.status: creating Makefile
config.status: creating cmatrix.spec
config.status: creating config.h
config.status: executing depfiles commands
Tout a l’air parfait. On passe à la compilation :
# make
...
gcc -g -O2 -static -o cmatrix cmatrix.o -lncurses -lncurses
make[1]: Leaving directory '/cmatrix'
Là aussi, tout a l’air parfait. La compilation n’a retourné aucune erreur, et nous disposons à présent d’un binaire cmatrix
dans le répertoire de travail :
# ls -lh ./cmatrix -rwxr-xr-x 1 root root 777.9K Oct 18 13:39 ./cmatrix
Nous pouvons l’exécuter pour avoir une première idée de ce que ça donne :
# ./cmatrix
Nous avons réussi à compiler CMatrix avec succès. Pour retrouver la procédure de construction du binaire depuis le code source, il nous suffit d’afficher l’historique du shell :
# history 0 git clone https://github.com/abishekvashok/cmatrix 1 apk update 2 apk add git 3 git clone https://github.com/abishekvashok/cmatrix 4 cd cmatrix/ 5 ls -l 6 autoreconf -i 7 apk add autoconf 8 autoreconf -i 9 apk add automake 10 autoreconf -i 11 echo $? 12 ./configure LDFLAGS="-static" 13 apk add alpine-sdk 14 ./configure LDFLAGS="-static" 15 mkdir -pv /usr/lib/kbd/consolefonts 16 mkdir -pv /usr/share/consolefonts 17 apk add ncurses-dev ncurses-static 18 ./configure LDFLAGS="-static" 19 make 20 ls -lh ./cmatrix 21 ./cmatrix 22 history
Copiez l’intégralité de cet historique dans le presse-papier de votre environnement graphique et quittez le conteneur.
N’oubliez pas que le conteneur a été créé avec l’option --rm
. À partir du moment où nous le quittons (exit
), l’arrêt provoque également la suppression. Tout ce que nous avons fait jusqu’ici est donc parti au paradis des octets… ou presque.
Collez le contenu de l’historique tel quel dans le Dockerfile
, à la suite des trois premières instructions :
FROM alpine LABEL org.opencontainers.image.authors="Nicolas Kovacs" LABEL org.opencontainers.image.description="Container image for CMatrix" 0 git clone https://github.com/abishekvashok/cmatrix 1 apk update 2 apk add git 3 git clone https://github.com/abishekvashok/cmatrix 4 cd cmatrix/ 5 ls -l 6 autoreconf -i 7 apk add autoconf 8 autoreconf -i 9 apk add automake 10 autoreconf -i 11 echo $? 12 ./configure LDFLAGS="-static" 13 apk add alpine-sdk 14 ./configure LDFLAGS="-static" 15 mkdir -pv /usr/lib/kbd/consolefonts 16 mkdir -pv /usr/share/consolefonts 17 apk add ncurses-dev ncurses-static 18 ./configure LDFLAGS="-static" 19 make 20 ls -lh ./cmatrix 21 ./cmatrix 22 history
La suite au prochain numéro, où nous allons voir en détail la rédaction d’un Dockerfile
propre et efficace depuis notre historique du shell.
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