Astuces pour écrire des scripts shell et des définitions de conteneurs (Dockerfile, Containerfile)

Je me retrouve régulièrement à faire des petits scripts shell, même si je ne suis pas un expert de cet outil.

Même en 2024, les développeurs sont régulièrement encore confrontés aux scripts shells, parfois sans qu’ils le sachent.

Par exemple, quand on crée des fichiers de définition de conteneurs (Containerfile aussi connu sous le nom originel de Dockerfile), chaque commande définie dans une ligne RUN est exécutée par défaut dans un shell (shell form).

Pour écrire ces fichiers, une bonne pratique est d’éviter d’écrire plusieurs commandes RUN d’affilée dans un Containerfile pour éviter de faire créer trop d’images Docker intermédiaires inutiles pendant la création du conteneur (on évite ainsi de créer trop de snapshots du système de fichiers).

J’ai appris cette bonne pratique a mes débuts avec Docker et je l’ai appliquée, mais je n’ai jamais vraiment réalisé que ce n’était pas juste une ligne de shell que j’écrivais, mais plutôt un script.

Par exemple, quand on construit une image Docker avec une distribution Debian, si on a besoin d’installer un logiciel, il faudrait exécuter trois commandes:

RUN apt update
RUN apt install python3
RUN apt-get clean

La bonne pratique serait de rassembler toutes ces commandes dans une seule ligne RUN:

RUN apt update \
  && apt install python3 \
  && apt-get clean

Depuis la version 20.10.13 de Docker, on peut même utiliser la syntaxe heredoc:

RUN <<EOF
apt update
apt install python3
apt-get clean
EOF

Avec cette nouvelle syntaxe, on se rend compte que, finalement, cette suite de commandes est en réalité un vrai script shell. Il faut donc penser à appliquer les bonnes pratiques pour la création de script shell.

Quand je crée un script shell, je commence toujours par activer les options set -euxo pipefail pour que le script échoue à la moindre erreur.

Récemment, sur LinuxFr.org, Marotte nous a expliqué que ces options s’appellent le bash strict mode et qu’on pourrait l’appeler le script mode, ce que je trouve en effet beaucoup plus parlant.

L’article que Marotte a partagé à propos de ce strict mode est très détaillé et explique d’ailleurs qu’il vaut mieux aussi remplacer la variable IFS par IFS=$'\n\t', ce que je ne connaissais pas.

Enfin, lors de la création d’un script shell, j’utilise également beaucoup l’outil shellcheck, parce qu’il permet de repérer à l’avance tous les pièges courants lors de la création de code shell.

L’exemple de la commande RUN plus haut donnerait donc:

RUN <<EOF
#!/usr/bin/env bash

set -euxo pipefail
IFS=$'\n\t'

apt update
apt install python3
apt-get clean
EOF

Au fait, un autre exemple important où je crée régulièrement des scripts shells, est lorsque je crée des tâches pour l’intégration continue et le déploiement continue de Gitlab.

De la même manière que pour les Containerfile, je ne m’étais pas rendu compte que ce sont des scripts shell, puisque les jobs d’exemples sont assez simples et sont juste une suite de ligne de commandes.

Mais dès qu’une tâche devient un peu plus complexe, il vaudrait mieux passer en script mode et utiliser la syntaxe YAML qui permet aussi de créer des blocs de strings sur plusieurs lignes.

Si on reprend les mêmes commandes que plus haut, dans un bloque script de Gitlab, ça donnerait:

exemple-mise-a-jour:
  script: |
    #!/usr/bin/env bash
    
    set -euxo pipefail
    IFS=$'\n\t'
  
    apt update
    apt install python3
    apt-get clean

Commentaires

Répondez à ce message du Fediverse pour ajouter un commentaire sur cet article.