🇫🇷 Devops

Docker

2024/2025

QRCode to this presentation

Comment utiliser cette présentation ?

  • Pour naviguer, utilisez les flèches en bas à droite (ou celles de votre clavier)

    • Gauche/Droite: changer de chapitre

    • Haut/Bas: naviguer dans un chapitre

  • Pour avoir une vue globale : utiliser la touche "o" (pour "Overview")

Bonjour !

bitmoji hello
hello from docker
La suite: vers le bas ⬇️

Bruno VERACHTEN

cloudbees

Et vous ?

A propos du cours

  • Première itération d’une découverte Docker dans le cadre du DevOps

  • Contenu entièrement libre et open-source

  • Méchamment basé sur le travail de Damien Duportal et Amaury Willemant

  • N’hésitez pas ouvrir des Pull Request si vous voyez des améliorations ou problèmes: sur cette page (😉 wink wink)

Calendrier

🚧 Work in progress…​ 🚧

  • 26 septembre après-midi

  • 24 octobre après-midi

  • 07 novembre après-midi

  • …​

📊 Évaluation

  • Pourquoi ? s’assurer que vous avez acquis un minimum de concepts

  • Quoi ? Sans doute une note sur 20, basée sur une liste de critères exhaustifs

  • Comment ? Un projet Gitlab (probable) ou GitHub (peu probable) à me rendre (timing à déterminer ensemble)

Plan

  • Intro

  • Base

  • Containers

  • Images

  • Fichiers, nommage, inspect

  • Volumes

  • Réseaux

  • Docker Compose

  • Bonus

La suite: vers la droite ➡️

Docker

"La Base"

Pourquoi ?

docker logo monochromatic

🤔 Quel est le problème ?

Pourquoi commencer par un problème ?

docker logo monochromatic

🤔 Commençons plutôt par une définition:

Docker c’est …​

Pourquoi commencer par un problème ?

docker logo monochromatic

🤔 Définition quelque peu datée (2014):

Docker is …​

Pourquoi commencer par un problème ?

docker logo monochromatic

🤔 Définition quelque peu datée (2014):

Docker is a toolset for Linux containers designed to ‘build, ship and run’ distributed applications.

Linux containers ?

docker logo monochromatic

🤔 Nous voilà bien…​

C’est quoi un container?

Linux containers ?

docker logo monochromatic

🤔 Nous voilà bien…​

C’est quoi un container?

Vous voulez la version enfant de 5 ans? Je ne crois pas…​

Docker est vieux

10 ans déjà…​

containerconteneuraccidenteavariejpg 5e8b87ccc8c27

Docker est vieux

Mais moi aussi…​

piWorkshop

Docker on arm

Aucune raison que ça soit réservé aux puissants! La révolution vaincra!

resin ports docker to arm

Docker on *

Linux everywhere…​ And then Docker to follow.

buildx multi arch

On n’avait pas parlé d’un problème?

base du problème sans dev

On n’avait pas parlé d’un problème?

base du problème sans dev et ops

On n’avait pas parlé d’un problème?

base du problème sans stabilité

On n’avait pas parlé d’un problème?

base du problème sans frontièrel

On n’avait pas parlé d’un problème?

base du problème final

On n’avait pas parlé d’un problème?

Histoire vraie.

Hey salut !

On n’avait pas parlé d’un problème?

Histoire vraie.

Hey salut !

cc

On n’avait pas parlé d’un problème?

Histoire vraie.

Hey salut !

cc

Tu peux mettre à jour les packages système steup ? j’ai un truc à tester.

On n’avait pas parlé d’un problème?

Histoire vraie.

Hey salut !

cc

Tu peux mettre à jour les packages système steup ? j’ai un truc à tester.

nan

On n’avait pas parlé d’un problème?

Histoire vraie.

Hey salut !

cc

Tu peux mettre à jour les packages système steup ? j’ai un truc à tester.

nan

???

On n’avait pas parlé d’un problème?

Histoire vraie.

Hey salut !

cc

Tu peux mettre à jour les packages système steup ? j’ai un truc à tester.

nan

???

pas standard dsl

On n’avait pas parlé d’un problème?

Histoire vraie.

Hey salut !

cc

Tu peux mettre à jour les packages système steup ? j’ai un truc à tester.

nan

???

pas standard dsl

1072129428 image16

On n’avait pas parlé d’un problème?

matrixfromhell

Problème de temps exponentiel

Déjà vu ?

L’IT n’est pas la seule industrie à résoudre des problèmes…​

also a matrix from hell

Solution: Le conteneur intermodal

"Separation of Concerns"

blue shipping container

Comment ça marche ?

"Virtualisation Légère"

container vs vm

Comment ça marche ?

Virtualisation

https://medium.com/teamresellerclub/type-1-and-type-2-hypervisors-what-makes-them-different-6a1755d6ae2c

Comment ça marche ?

Virtualisation


https://www.ubackup.com/enterprise-backup/type-1-hypervisor-vs-type-2.html


https://www.ubackup.com/enterprise-backup/type-1-hypervisor-vs-type-2.html

Comment ça marche ?

"Virtualisation Légère"

container vs vm
  • Légère, vraiment?

Légère, vraiment!

Légère, vraiment!

Conteneur != VM

docker n est pas VM
  • Idée erronée mais citée trop fréquemment !

Conteneur != VM

not a vm 9

Conteneur != VM

not a vm 8

Conteneur != VM

not a vm 7

Conteneur != VM

not a vm 6

Conteneur != VM

not a vm 5

Conteneur != VM

not a vm 4

Conteneur != VM

not a vm 3

Conteneur != VM

not a vm 2

Conteneur != VM

not a vm 1

Conteneur != VM

not a vm

Conteneur != VM

"Separation of concerns": 1 "tâche" par conteneur

vm and container

VMs & Conteneurs

Non exclusifs mutuellement

containers and vms together

🤡 Docker, c’est pas un peu une VM quand même?

mobyvm

🤡 Docker, c’est pas un peu une VM quand même?

  • Docker sur Windows pour exécuter des conteneurs Linux :

    • Hyper-V : Sous Windows, Docker utilise souvent Hyper-V pour exécuter des machines virtuelles légères (VM) Linux.

    • Performance : Docker sur Windows pour les conteneurs Linux peut être légèrement moins performant que Docker sur Linux natif.

    • Isolation : Isolation différente de celle des conteneurs Linux natifs.

  • Docker sur Linux pour exécuter des conteneurs Linux :

    • Native : Docker fonctionne nativement car il partage le même noyau Linux.

    • Efficacité : Il est très efficace en termes de consommation de ressources.

    • Isolation : Isolation basée sur les cgroups et les namespaces du noyau Linux.

Cas d’usage

Le bac à sable

bac à sable

Cas d’usage

Le bac à sable

bac à sable
  • Un environnement tout propre tout neuf !

Cas d’usage

Le bac à sable

bac à sable
  • Un environnement tout propre tout neuf !

  • Le poste de travail n’est pas impacté

Cas d’usage

Le bac à sable

bac à sable
  • Un environnement tout propre tout neuf !

  • Le poste de travail n’est pas impacté

  • La configuration reste également isolée sans effet sur le poste de travail

Cas d’usage

La machine de dév

dis le encore une fois

Cas d’usage

La machine de dév

dis le encore une fois
  • Avoir un environnement reproductible pour rendre homogène le développement et les tests.

Cas d’usage

La machine de dév

docker birth

Cas d’usage

La machine de dév

give computer client

Cas d’usage

Déploiement en production

prod probleme

Cas d’usage

Déploiement en production

prod probleme
  • Avec un container, si ca fonctionne en local, ca fonctionne en prod !

Cas d’usage

Outillage jetable

outillage jetable

Cas d’usage

Outillage jetable

outillage jetable
  • On instancie des applications sans les installer

Cas d’usage

Outillage jetable

outillage jetable
  • On instancie des applications sans les installer

  • On crée le container, on l’utilise puis on le jette

Beau boulot! 🤗

bitmoji well done

On a la base, remplissons là maintenant de containers!

La suite: vers la droite ➡️
empty ship

Préparer votre environnement de travail

Outils Nécessaires 🛠

  • Un navigateur web récent (et décent)

  • Un compte sur GitHub

GitPod

GitPod.io : Environnement de développement dans le ☁️ "nuage"

  • But: reproductible

  • Puissance de calcul sur un serveur distant

  • Éditeur de code VSCode dans le navigateur

Gratuit pour 50h par mois (⚠️)
Ça, c’était avant (⚠️)
Gratuit pour 10h par mois (⚠️)
Gratuit pour 50h par mois si compte lié (⚠️)

Démarrer avec GitPod 🚀

  • Rendez vous sur https://gitpod.io

  • Authentifiez vous en utilisant votre compte GitHub:

    • Bouton "Login" en haut à droite

    • Puis choisissez le lien " Continue with GitHub"

⚠️ Pour les "autorisations", passez sur la slide suivante

Autorisations demandées par GitPod 🔓

Lors de votre première connexion, GitPod va vous demander l’accès (à accepter) à votre email public configuré dans GitHub :

gitpod github permissions
⚠️ Passez à la slide suivante avant d’aller plus loin

Validation du Compte GitPod 📱

GitPod vous demande votre numéro de téléphone mobile afin d’éviter les abus (service gratuit). Saisissez un numéro de téléphone valide pour recevoir par SMS un code de déblocage :

gitpod phone validation
⚠️ Passez à la slide suivante avant d’aller plus loin

Choix de l’Éditeur de Code ✎

Choisissez l’éditeur "VSCode Browser" (la première tuile) :

gitpod select editor
⚠️ Passez à la slide suivante avant d’aller plus loin

Workspaces GitPod 🗂

  • Vous arrivez sur la page listant les "workspaces" GitPod :

  • Un workspace est une instance d’un environnement de travail virtuel (C’est un ordinateur distant)

  • ⚠ Faites attention à réutiliser le même workspace tout au long de ce cours ⚠

gitpod workspaces

Permissions GitPod <→ GitHub 🔐

  • Pour les besoins de ce cours, vous devez autoriser GitPod à pouvoir effectuer certaines modification dans vos dépôts GitHub

  • Rendez-vous sur la page des intégrations avec GitPod

  • Éditez les permissions de la ligne "GitHub" (les 3 petits points à droits) et sélectionnez uniquement :

    • user:email

    • public_repo

    • workflow

Démarrer l’environnement GitPod

Cliquez sur le bouton ci-dessous pour démarrer un environnement GitPod personnalisé:

open in gitpod

Après quelques secondes (minutes?), vous avez accès à l’environnement:

  • Gauche: navigateur de fichiers ("Workspace")

  • Haut: éditeur de texte ("Get Started")

  • Bas: Terminal interactif

  • À droite en bas: plein de popups à ignorer (ou pas?)

Checkpoint 🎯

  • Vous devriez pouvoir taper la commande whoami dans le terminal de GitPod:

    • Retour attendu: gitpod

  • Vous devriez pouvoir fermer le fichier "Get Started"…​

    • …​ et ouvrir n’importe quel autre fichier…​

Bien, on peut maintenant fermer ce workspace, il ne s’agirait pas de gaspiller vos 50 heures.

📚 Et Gitlab?

GitPod fonctionne aussi avec gitlab.com, mais pour les instances on premise, il faut l’installer, l’instancier, avoir un "inner cloud"

Bref, on ne l’a pas ici. ¯_(ツ)_/¯

On peut commencer !

Ligne de commande

Guide de survie

🤔 Problématique

  • Communication Humain <→ Machine

  • Base commune de TOUS les outils

CLI

  • 🇬🇧 CLI == "Command Line Interface"

  • 🇫🇷 "Interface de Ligne de Commande"

REPL

Pour les théoriciens et curieux :

  • 🇬🇧 REPL == "Read–eval–print loop"

Anatomie d’une commande

ls --color=always -l /bin
  • Séparateur : l’espace

  • Premier élément (ls) : c’est la commande

  • Les éléments commençant par un tiret - sont des "options" et/ou drapeaux ("flags")

    • "Option" == "Optionnel"

  • Les autres éléments sont des arguments (/bin)

    • Nécessaire (par opposition)

Manuel des commande

  • Afficher le manuel de <commande> :

    # Commande 'man' avec comme argument le nom de ladite commande
    man <commande>
    • Navigation avec les flèches haut et bas

      • Tapez / puis une chaîne de texte pour chercher

      • Touche n pour sauter d’occurrence en occurrence

    • Touche q pour quitter

🎓 Essayez avec ls, chercher le mot color

  • 💡 La majorité des commandes fournit également une option (--help), un flag (-h) ou un argument (help)

  • Google c’est pratique aussi hein !

Raccourcis

Dans un terminal Unix/Linux/WSL :

  • CTRL + C : Annuler le process ou prompt en cours

  • CTRL + L : Nettoyer le terminal

  • CTRL + A : Positionner le curseur au début de la ligne

  • CTRL + E : Positionner le curseur à la fin de la ligne

  • CTRL + R : Rechercher dans l’historique de commandes

🎓 Essayez-les !

Commandes de base 1/2

  • pwd : Afficher le répertoire courant

    • 🎓 Option -P ?

  • ls : Lister le contenu du répertoire courant

    • 🎓 Options -a et -l ?

  • cd : Changer de répertoire

    • 🎓 Sans argument : que se passe t’il ?

  • cat : Afficher le contenu d’un fichier

    • 🎓 Essayez avec plusieurs arguments

  • mkdir : créer un répertoire

    • 🎓 Option -p ?

Commandes de base 2/2

  • echo : Afficher un (des) message(s)

  • rm : Supprimer un fichier ou dossier

  • touch : Créer un fichier

  • grep : Chercher un motif de texte

Arborescence de fichiers 1/2

  • Le système de fichier a une structure d’arbre

    • La racine du disque dur c’est / : 🎓 ls -l /

    • Le séparateur c’est également / : 🎓 ls -l /usr/bin

  • Deux types de chemins :

    • Absolu (depuis la racine): Commence par / (Ex. /usr/bin)

    • Sinon c’est relatif (e.g. depuis le dossier courant) (Ex ./bin ou local/bin/)

linux directory structure

Arborescence de fichiers 2/2

  • Le dossier "courant" c’est . : 🎓 ls -l ./bin # Dans le dossier /usr

  • Le dossier "parent" c’est .. : 🎓 ls -l ../ # Dans le dossier /usr

  • ~ (tilde) c’est un raccourci vers le dossier de l’utilisateur courant : 🎓 ls -l ~

  • Sensible à la casse (majuscules/minuscules) et aux espaces : 🎓

    ls -l /bin
    ls -l /Bin
    mkdir ~/"Accent tué"
    ls -d ~/Accent\ tué

Un language (?)

  • Variables interpolées avec le caractère "dollar" $ :

    echo $MA_VARIABLE
    echo "$MA_VARIABLE"
    echo ${MA_VARIABLE}
    
    # Recommendation
    echo "${MA_VARIABLE}"
    
    MA_VARIABLE="Salut tout le monde"
    
    echo "${MA_VARIABLE}"
  • Sous commandes avec $(<command>):

echo ">> Contenu de /tmp :\n$(ls /tmp)"

Codes de sortie

  • Chaque exécution de commande renvoie un code de retour (🇬🇧 "exit code")

    • Nombre entier entre 0 et 255 (en POSIX)

  • Code accessible dans la variable éphémère $? :

ls /tmp
echo $?

ls /do_not_exist
echo $?

# Une seconde fois. Que se passe-t'il ?
echo $?

Entrée, sortie standard et d’erreur

cli ios

ls -l /tmp
echo "Hello" > /tmp/hello.txt
ls -l /tmp
ls -l /tmp >/dev/null
ls -l /tmp 1>/dev/null

ls -l /do_not_exist
ls -l /do_not_exist 1>/dev/null
ls -l /do_not_exist 2>/dev/null

ls -l /tmp /do_not_exist
ls -l /tmp /do_not_exist 1>/dev/null 2>&1

Pipelines

  • Le caractère "pipe" | permet de chaîner des commandes

    • Le "stdout" de la première commande est branchée sur le "stdin" de la seconde

  • Exemple : Afficher les fichiers/dossiers contenant le lettre d dans le dossier /bin :

ls -l /bin

ls -l /bin | grep "d" --color=auto

Exécution 1/2

  • Les commandes sont des fichier binaires exécutables sur le système :

    command -v cat # équivalent de "which cat"
    
    ls -l "$(command -v cat)"
  • La variable d’environnement $PATH liste les dossiers dans lesquels chercher les binaires

    • 💡 Utiliser cette variable quand une commande fraîchement installée n’est pas trouvée

Exécution 2/2

  • Exécution de scripts :

    • Soit appel direct avec l’interprétateur : sh ~/monscript.txt

    • Soit droit d’exécution avec un "shebang" (e.g. #!/bin/bash)

      $ chmod +x ./monscript.sh
      
      $ head -n1 ./monscript.sh
      #!/bin/bash
      
      $ ./monscript.sh
      # Exécution

Git

Guide de survie

🤔 Problématique

  • Support de communication

    • Humain → Machine

    • Humain <→ Humain

  • Besoins de traçabilité, de définition explicite et de gestion de conflits

    • Collaboration requise pour chaque changement (revue, responsabilités)

Tracer le changement dans le code

avec un VCS : 🇬🇧 Version Control System

également connu sous le nom de SCM (🇬🇧 Source Code Management)

Pourquoi un VCS ?

  • Pour conserver une trace de tous les changements dans un historique

    • "Source unique de vérité" (🇬🇧 Single Source of Truth)

  • Pour collaborer efficacement sur un même référentiel

Concepts des VCS

Quel VCS utiliser ?

cloudwords vcs

Nous allons utiliser Git

Git

Git is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency.

git logo

Les 3 états avec Git

  • L’historique ("Version Database") : dossier .git

  • Dossier de votre projet ("Working Directory") - Commande

  • La zone d’index ("Staging Area")

git 3 etats

🎓 Exercice avec Git - 1.1

  • Dans le terminal de votre environnement GitPod:

    • Créez un dossier vide nommé projet-vcs-1 dans le répertoire /workspace, puis positionnez-vous dans ce dossier

      mkdir -p /workspace/projet-vcs-1/
      cd /workspace/projet-vcs-1/
    • Est-ce qu’il y a un dossier .git/ ?

    • Essayez la commande git status ?

  • Initialisez le dépôt git avec git init

    • Est-ce qu’il y a un dossier .git/ ?

    • Essayez la commande git status ?

✅ Solution de l’exercice avec Git - 1.1

mkdir -p /workspace/projet-vcs-1/
cd /workspace/projet-vcs-1/
ls -la # Pas de dossier .git
git status # Erreur "fatal: not a git repository"
git init ./
ls -la # On a un dossier .git
git status # Succès avec un message "On branch main No commits yet"

🎓 Exercice avec Git - 1.2

  • Créez un fichier README.md dedans avec un titre et vos nom et prénoms

    • Essayez la commande git status ?

  • Ajoutez le fichier à la zone d’indexation à l’aide de la commande git add (…​)

    • Essayez la commande git status ?

  • Créez un commit qui ajoute le fichier README.md avec un message, à l’aide de la commande git commit -m <message>

    • Essayez la commande git status ?

✅ Solution de l’exercice avec Git - 1.2

echo "# Read Me\n\nObi Wan" > ./README.md
git status # Message "Untracked file"

git add ./README.md
git status # Message "Changes to be committed"
git commit -m "Ajout du README au projet"
git status  # Message "nothing to commit, working tree clean"

Terminologie de Git - Diff et changeset

diff: un ensemble de lignes "changées" sur un fichier donné

diff

changeset: un ensemble de "diff" (donc peut couvrir plusieurs fichiers)

changeset

Terminologie de Git - Commit

commit: un changeset qui possède un (commit) parent, associé à un message

commit

"HEAD": C’est le dernier commit dans l’historique

scm basics legend
scm basics history

🎓 Exercice avec Git - 2

  • Afficher la liste des commits

  • Afficher le changeset associé à un commit

  • Modifier du contenu dans README.md et afficher le diff

  • Annulez ce changement sur README.md

✅ Solution de l’exercice avec Git - 2

git log

git show # Show the "HEAD" commit
echo "# Read Me\n\nObi Wan Kenobi" > ./README.md

git diff
git status

git checkout -- README.md
git status

Terminologie de Git - Branche

  • Abstraction d’une version "isolée" du code

  • Concrètement, une branche est un alias pointant vers un "commit"

scm branches

🎓 Exercice avec Git - 3

  • Créer une branche nommée feature/html

  • Ajouter un nouveau commit contenant un nouveau fichier index.html sur cette branche

  • Afficher le graphe correspondant à cette branche avec git log --graph

✅ Solution de l’exercice avec Git - 3

git switch --create feature/html
# Ou alors: git branch feature/html && git switch feature/html
echo '<h1>Hello</h1>' > ./index.html
git add ./index.html && git commit --message="Ajout d'une page HTML par défaut" # -m / --message

git log
git log --graph
git lg # cat ~/.gitconfig => regardez la section section [alias], cette commande est déjà définie!

Terminologie de Git - Merge

  • On intègre une branche dans une autre en effectuant un merge

    • Un nouveau commit est créé, fruit de la combinaison de 2 autres commits

scm merge

🎓 Exercice avec Git - 4

  • Merger la branche feature/html dans la branche principale

    • ⚠️ Pensez à utiliser l’option --no-ff

  • Afficher le graphe correspondant à cette branche avec git log --graph

✅ Solution de l’exercice avec Git - 4

git switch main
git merge --no-ff feature/html # Enregistrer puis fermer le fichier 'MERGE_MSG' qui a été ouvert
git log --graph

# git lg

Feature Branch Flow

  • Une seule branche par fonctionnalité

scm feature branch workflow

Exemple d’usages de VCS

Checkpoint 🎯

  • git est un des (plus populaires) de système de contrôle de versions

  • Cet outil vous permet:

    • D’avoir un historique auditable de votre code source

    • De collaborer efficacement sur le code source (conflit git == "PARLEZ-VOUS")

⇒ C’est une ligne de commande (trop?) complète qui nécessite de pratiquer

Containers

🎓 Exercice : Votre premier conteneur

C’est à vous (ouf) !

  • Allez dans Gitpod

  • Dans un terminal, tapez la commande suivante :

docker container run hello-world
# Equivalent de l'ancienne commande 'docker run'

🩻 Anatomie

  • Un service "Docker Engine" tourne en tâche de fond et publie une API REST

  • La commande docker container run …​ a lancé un client docker qui a envoyé une requête POST au service docker

  • Le service a téléchargé une Image Docker depuis le registre DockerHub,

  • Puis a exécuté un conteneur basé sur cette image

🩻 Anatomie

output3 with transparency

🩻 Anatomie

output4 with transparency

🩻 Anatomie

output5 with transparency

🩻 Anatomie

output6 with transparency

🩻 Anatomie

output7 with transparency

🩻 Anatomie

output8 with transparency

🩻 Anatomie

output9 with transparency

🩻 Anatomie

output10 with transparency

🩻 Anatomie

dollar date

🩻 Anatomie

shell dans shell pour date

🎓 Exercice : Où est mon conteneur ?

C’est à vous !

docker container ls --help
# ...
docker container ls
# ...
docker container ls --all

⇒ 🤔 comment comprenez vous les résultats des 2 dernières commandes ?

✅ Solution : Où est mon conteneur ?

Le conteneur est toujours présent dans le "Docker Engine" même en étant arrêté

CONTAINER ID   IMAGE         COMMAND    CREATED          STATUS                      PORTS     NAMES
109a9cdd3ec8   hello-world   "/hello"   33 seconds ago   Exited (0) 17 seconds ago             festive_faraday
  • Un conteneur == une commande "conteneurisée"

    • cf. colonne "COMMAND"

  • Quand la commande s’arrête : le conteneur s’arrête

    • cf. code de sortie dans la colonne "STATUS"

🫁 Rappels d’anatomie

$ docker container run busybox echo hello world

🫁 Rappels d’anatomie

$ docker container run busybox echo hello world
  • Le moteur Docker crée un container à partir de l’image "busybox".

🫁 Rappels d’anatomie

$ docker container run busybox echo hello world
  • Le moteur Docker crée un container à partir de l’image "busybox".

  • Le moteur Docker démarre le container créé.

🫁 Rappels d’anatomie

$ docker container run busybox echo hello world
  • Le moteur Docker crée un container à partir de l’image "busybox".

  • Le moteur Docker démarre le container créé.

  • La commande "echo hello world" est exécutée à l’intérieur du container.

🫁 Rappels d’anatomie

$ docker container run busybox echo hello world
  • Le moteur Docker crée un container à partir de l’image "busybox".

  • Le moteur Docker démarre le container créé.

  • La commande "echo hello world" est exécutée à l’intérieur du container.

  • On obtient le résultat dans la sortie standard de la machine hôte.

🫁 Rappels d’anatomie

$ docker container run busybox echo hello world
  • Le moteur Docker crée un container à partir de l’image "busybox".

  • Le moteur Docker démarre le container créé.

  • La commande "echo hello world" est exécutée à l’intérieur du container.

  • On obtient le résultat dans la sortie standard de la machine hôte.

  • Le container est stoppé.

🍽️ Cas d’usage

Outillage jetable

outillage jetable
  • Tester une version de Maven, de JDK, de NPM, …

🎓 Exercice : Cycle de vie d’un conteneur simple

  • Lancez un nouveau conteneur nommé bonjour

  • Affichez les "logs" du conteneur (==traces d’exécution écrites sur le stdout + stderr de la commande conteneurisée)

  • Lancez le conteneur avec la commande docker container start

    • Regardez le résultat dans les logs

  • Supprimez le container avec la commande docker container rm

✅ Solution : Cycle de vie d’un conteneur simple

docker container run --name=bonjour hello-world
# Affiche le texte habituel

docker container logs bonjour
# Affiche le même texte : pratique si on a fermé le terminal

docker container start bonjour
# N'affiche pas le texte mais l'identifiant unique du conteneur 'bonjour'

docker container logs bonjour
# Le texte est affiché 2 fois !

docker container ls --all
# Le conteneur est présent
docker container rm bonjour
docker container ls --all
# Le conteneur n'est plus là : il a été supprimé ainsi que ses logs

docker container logs bonjour
# Error: No such container: bonjour

🤔 Que contient "hello-world" ?

  • C’est une "image" de conteneur, c’est à dire un modèle (template) représentant une application auto-suffisante.

    • On peut voir ça comme un "paquetage" autonome

  • C’est un système de fichier complet:

    • Il y a au moins une racine /

    • Ne contient que ce qui est censé être nécessaire (dépendances, librairies, binaires, etc.)

Docker Hub

  • https://hub.docker.com/ : C’est le registre d’images "par défaut"

  • 🎓 Cherchez l’image hello-world pour en voir la page de documentation

    • 💡 pas besoin de créer de compte pour ça

  • Il existe d’autre "registres" en fonction des besoins (GitHub GHCR, Google GCR, etc.)

🏓 Lancer un container interactif

docker container run  --interactive --tty alpine

🏓 Lancer un container interactif

docker container run  --interactive --tty alpine
/ $

🏓 Lancer un container interactif

docker container run  --interactive --tty alpine
/ $
  • On lance un container à partir de l’image "alpine"

🏓 Lancer un container interactif

docker container run  --interactive --tty alpine
/ $
  • On lance un container à partir de l’image "alpine"

  • On lance un sh dans ce container

🏓 Lancer un container interactif

docker container run  --interactive --tty alpine
/ $
  • On lance un container à partir de l’image "alpine"

  • On lance un sh dans ce container

  • On redirige l’entrée standard avec -i

🏓 Lancer un container interactif

docker container run  --interactive --tty alpine
/ $
  • On lance un container à partir de l’image "alpine"

  • On lance un sh dans ce container

  • On redirige l’entrée standard avec -i

  • On déclare un pseudo-terminal avec -t

🎓 Exercice : conteneur interactif

  • Quelle distribution Linux est utilisée dans le terminal Gitpod ?

    • 💡 Regardez le fichier /etc/os-release

  • Exécutez un conteneur interactif basé sur alpine:3.18.3 (une distribution Linux ultra-légère) et regardez le contenu du fichier au même emplacement

    • 💡 docker container run --help

    • 💡 Demandez un tty à Docker

    • 💡 Activez le mode interactif

  • Exécutez la même commande dans un conteneur basé sur la même image mais en NON interactif

    • 💡 Comment surcharger la commande par défaut ?

✅ Solution : conteneur interactif

$ cat /etc/os-release
# ... Ubuntu ....

$ docker container run --tty --interactive alpine:3.18.3
/ $ cat /etc/os-release
# ... Alpine ...
# Notez que le "prompt" du terminal est différent DANS le conteneur
/ $ exit
$ docker container ls --all

$ docker container run alpine:3.18.3 cat /etc/os-release
# ... Alpine ...

🏓 Utiliser un container interactif

Revenons dans notre container interactif de tout à l’heure…​

/ $ curl google.fr

🏓 Utiliser un container interactif

/ $ curl google.fr
/bin/sh: curl: not found

🏓 Utiliser un container interactif

/ $ curl google.fr
/bin/sh: curl: not found

cURL n’est pas disponible par défaut sur Alpine. Il faut l’installer au préalable

🏓 Utiliser un container interactif

/ $ curl google.fr
/bin/sh: curl: not found

cURL n’est pas disponible par défaut sur Alpine. Il faut l’installer au préalable

/ $ apk update && apk add curl

🏓 Utiliser un container interactif

/ $ curl google.fr
/bin/sh: curl: not found

cURL n’est pas disponible par défaut sur Alpine. Il faut l’installer au préalable

/ $ apk update && apk add curl
fetch https://dl-cdn.alpinelinux.org/alpine/v3.18/main/x86_64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.18/community/x86_64/APKINDEX.tar.gz
v3.18.3-169-gd5adf7d7d28 [https://dl-cdn.alpinelinux.org/alpine/v3.18/main]
v3.18.3-171-g503977088b4 [https://dl-cdn.alpinelinux.org/alpine/v3.18/community]
OK: 20063 distinct packages available
(1/7) Installing ca-certificates (20230506-r0)
(2/7) Installing brotli-libs (1.0.9-r14)
(3/7) Installing libunistring (1.1-r1)
(4/7) Installing libidn2 (2.3.4-r1)
(5/7) Installing nghttp2-libs (1.55.1-r0)
(6/7) Installing libcurl (8.2.1-r0)
(7/7) Installing curl (8.2.1-r0)
Executing busybox-1.36.1-r2.trigger
Executing ca-certificates-20230506-r0.trigger
OK: 12 MiB in 22 packages

🏓 Utiliser un container interactif

/ $ curl google.fr
<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>301 Moved</TITLE></HEAD><BODY>
<H1>301 Moved</H1>
The document has moved
<A HREF="http://www.google.fr/">here</A>.
</BODY></HTML>

C’est bon, on a cURL 🦱

🏓 Utiliser un container interactif

On peut quitter sh et revenir à la machine hôte !

/ $ exit

🏓 Utiliser un container interactif

On peut quitter sh et revenir à la machine hôte !

/ $ exit

Si on veut réutiliser cURL sur Alpine, c’est simple, on relance le shell, non? 🤔

🏓 Utiliser un container interactif

On peut quitter sh et revenir à la machine hôte !

/ $ exit

Si on veut réutiliser cURL sur Alpine, c’est simple, on relance le shell, non? 🤔

docker container run  --interactive --tty alpine

🏓 Utiliser un container interactif

On peut quitter sh et revenir à la machine hôte !

/ $ exit

Si on veut réutiliser cURL sur Alpine, c’est simple, on relance le shell, non? 🤔

docker container run  --interactive --tty alpine

On relance cURL:

/ $ curl google.fr

🏓 Utiliser un container interactif

On peut quitter sh et revenir à la machine hôte !

/ $ exit

Si on veut réutiliser cURL sur Alpine, c’est simple, on relance le shell, non? 🤔

docker container run  --interactive --tty alpine

On relance cURL:

/ $ curl google.fr
/bin/sh: curl: not found

🏓 Utiliser un container interactif

En fait, c’est logique !

docker container run
  • Cette commande instancie un "nouveau container à chaque fois" !

🏓 Utiliser un container interactif

En fait, c’est logique !

docker container run
  • Cette commande instancie un "nouveau container à chaque fois" !

  • Chaque container est différent.

🏓 Utiliser un container interactif

En fait, c’est logique !

docker container run
  • Cette commande instancie un "nouveau container à chaque fois" !

  • Chaque container est différent.

  • Aucun partage entre les containers à part le contenu de base de l’image.

🏓 Utiliser un container interactif

1460541815 image8

" On m’a vendu un truc qui permet de lancer des tonnes de microservices… mais là, on télécharge nimps et s’amuse à le perdre…"

⛅ Conteneur en tâche de fond

Lançons un container bien particulier…

docker container run  --interactive --tty jpetazzo/clock

⛅ Conteneur en tâche de fond

Lançons un container bien particulier…

docker container run  --interactive --tty jpetazzo/clock
Mon Sep 25 10:33:51 UTC 2023
Mon Sep 25 10:33:52 UTC 2023
Mon Sep 25 10:33:53 UTC 2023
Mon Sep 25 10:33:54 UTC 2023
Mon Sep 25 10:33:55 UTC 2023
Mon Sep 25 10:33:56 UTC 2023
...

⛅ Conteneur en tâche de fond

Lançons un container bien particulier…

docker container run  --interactive --tty jpetazzo/clock
Mon Sep 25 10:33:51 UTC 2023
Mon Sep 25 10:33:52 UTC 2023
Mon Sep 25 10:33:53 UTC 2023
Mon Sep 25 10:33:54 UTC 2023
Mon Sep 25 10:33:55 UTC 2023
Mon Sep 25 10:33:56 UTC 2023
...

Ce container va tourner indéfiniment sauf si on le stoppe avec ⌨️ Ctrl+C

⛅ Conteneur en tâche de fond

Lançons un container bien particulier…

docker container run  --interactive --tty jpetazzo/clock
Mon Sep 25 10:33:51 UTC 2023
Mon Sep 25 10:33:52 UTC 2023
Mon Sep 25 10:33:53 UTC 2023
Mon Sep 25 10:33:54 UTC 2023
Mon Sep 25 10:33:55 UTC 2023
Mon Sep 25 10:33:56 UTC 2023
...

Ce container va tourner indéfiniment sauf si on le stoppe avec ⌨️ Ctrl+C

… mais ca va stopper le container !

lapalissade

⛅ Conteneur en tâche de fond

Lançons un container bien particulier…

docker container run  --interactive --tty jpetazzo/clock
Mon Sep 25 10:33:51 UTC 2023
Mon Sep 25 10:33:52 UTC 2023
Mon Sep 25 10:33:53 UTC 2023
Mon Sep 25 10:33:54 UTC 2023
Mon Sep 25 10:33:55 UTC 2023
Mon Sep 25 10:33:56 UTC 2023
...

Ce container va tourner indéfiniment sauf si on le stoppe avec ⌨️ Ctrl+C

… mais ca va stopper le container !

Oui car quand on stoppe le processus principal d’un container, ce dernier n’a plus de raison d’exister et s’arrête naturellement.

⛅ Conteneur en tâche de fond

La solution : le flag --detach

docker container run --detach jpetazzo/clock

⛅ Conteneur en tâche de fond

La solution : le flag --detach

docker container run --detach jpetazzo/clock
399f3b23bc0585991afa80dfee854cf0a953d782b99153b4e2cbc74ab6b07770

Le retour de cette commande correspond à l’identifiant unique du container.

Cette fois-ci, le container tourne, mais en arrière plan !

⛅ Conteneur en tâche de fond

1460541815 image8

" Okay, maintenant il n’écrit la date nulle part… mais toutes les secondes… à l’aide ! "

⛅ Conteneur en tâche de fond

Le processus principal de ce container écrit dans la sortie standard… du container !

Comment retrouver le contenu de la sortie standard du container ?

⛅ Conteneur en tâche de fond

Le processus principal de ce container écrit dans la sortie standard… du container !

Comment retrouver le contenu de la sortie standard du container ?

docker logs 399f3

⛅ Conteneur en tâche de fond

Le processus principal de ce container écrit dans la sortie standard… du container !

Comment retrouver le contenu de la sortie standard du container ?

docker logs 399f3
Tue Sep 12 12:37:01 UTC 2023
Tue Sep 12 12:37:02 UTC 2023
Tue Sep 12 12:37:03 UTC 2023
Tue Sep 12 12:37:04 UTC 2023
Tue Sep 12 12:37:05 UTC 2023
Tue Sep 12 12:37:06 UTC 2023
Tue Sep 12 12:37:07 UTC 2023
Tue Sep 12 12:37:08 UTC 2023
Tue Sep 12 12:37:09 UTC 2023
Tue Sep 12 12:37:10 UTC 2023
Tue Sep 12 12:37:11 UTC 2023

⛅ Conteneur en tâche de fond

Le processus principal de ce container écrit dans la sortie standard… du container !

Comment retrouver le contenu de la sortie standard du container ?

docker logs 399f3
Tue Sep 12 12:37:01 UTC 2023
Tue Sep 12 12:37:02 UTC 2023
Tue Sep 12 12:37:03 UTC 2023
Tue Sep 12 12:37:04 UTC 2023
Tue Sep 12 12:37:05 UTC 2023
Tue Sep 12 12:37:06 UTC 2023
Tue Sep 12 12:37:07 UTC 2023
Tue Sep 12 12:37:08 UTC 2023
Tue Sep 12 12:37:09 UTC 2023
Tue Sep 12 12:37:10 UTC 2023
Tue Sep 12 12:37:11 UTC 2023

Ouf ! On n’est pas obligé de saisir l’identifiant complet ! Il suffit de fournir le nombre suffisant de caractères pour que ce soit discriminant.

📖 Lister les containers

Comment savoir si j’ai des containers en cours d’exécution ?

💡 Commande vue un peu plus tôt…​

📖 Lister les containers

Comment savoir si j’ai des containers en cours d’exécution ?

docker container ls

📖 Lister les containers

Comment savoir si j’ai des containers en cours d’exécution ?

docker container ls
CONTAINER ID   IMAGE                           COMMAND                  CREATED        STATUS       PORTS                    NAMES
02cbf2fb3721   cours-devops-docker-serve       "/sbin/tini -g gulp …"   3 hours ago    Up 3 hours   0.0.0.0:8000->8000/tcp   cours-devops-docker-serve-1
ebfbe695b2ec   moby/buildkit:buildx-stable-1   "buildkitd"              2 months ago   Up 3 hours                            buildx_buildkit_exciting_williams0

📖 Lister les containers

Comment savoir si j’ai des containers en cours d’exécution ?

docker container ls
CONTAINER ID   IMAGE                           COMMAND                  CREATED        STATUS       PORTS                    NAMES
02cbf2fb3721   cours-devops-docker-serve       "/sbin/tini -g gulp …"   3 hours ago    Up 3 hours   0.0.0.0:8000->8000/tcp   cours-devops-docker-serve-1
ebfbe695b2ec   moby/buildkit:buildx-stable-1   "buildkitd"              2 months ago   Up 3 hours                            buildx_buildkit_exciting_williams0

On obtient un tableau de tous les containers en cours d’exécution.

🛑 🏁 Stop / Start

Il est possible de stopper un container.

docker container stop 399f3

🛑 🏁 Stop / Start

Il est possible de stopper un container.

docker container stop 399f3

Pour redémarrer un container :

docker container start 399f3

📖 Lister tous les containers

Même les 💀.

📖 Lister tous les containers

Comment savoir si j’ai des containers stoppés ?

docker container ls --all

📖 Lister tous les containers

Comment savoir si j’ai des containers stoppés ?

docker container ls --all
CONTAINER ID   IMAGE                           COMMAND                  CREATED          STATUS                        PORTS                    NAMES
90725f661d4e   hello-world                     "/hello"                 13 seconds ago   Exited (0) 12 seconds ago                              hardcore_wescoff
9d0a6586b9e1   busybox                         "echo hello world"       22 seconds ago   Exited (0) 21 seconds ago                              gracious_moser
368ed08a35e3   jpetazzo/clock                  "/bin/sh -c 'while d…"   6 minutes ago    Up 5 minutes                                           sweet_clarke
c036e57bbf05   jpetazzo/clock                  "/bin/sh -c 'while d…"   17 minutes ago   Exited (130) 17 minutes ago                            cool_euclid
02cbf2fb3721   cours-devops-docker-serve       "/sbin/tini -g gulp …"   3 hours ago      Up 3 hours                    0.0.0.0:8000->8000/tcp   cours-devops-docker-serve-1
ebfbe695b2ec   moby/buildkit:buildx-stable-1   "buildkitd"              2 months ago     Up 4 hours                                             buildx_buildkit_exciting_williams0

📖 Lister tous les containers

Comment savoir si j’ai des containers stoppés ?

 docker container ls --all
CONTAINER ID   IMAGE                           COMMAND                  CREATED          STATUS                        PORTS                    NAMES
90725f661d4e   hello-world                     "/hello"                 13 seconds ago   Exited (0) 12 seconds ago                              hardcore_wescoff
9d0a6586b9e1   busybox                         "echo hello world"       22 seconds ago   Exited (0) 21 seconds ago                              gracious_moser
368ed08a35e3   jpetazzo/clock                  "/bin/sh -c 'while d…"   6 minutes ago    Up 5 minutes                                           sweet_clarke
c036e57bbf05   jpetazzo/clock                  "/bin/sh -c 'while d…"   17 minutes ago   Exited (130) 17 minutes ago                            cool_euclid
02cbf2fb3721   cours-devops-docker-serve       "/sbin/tini -g gulp …"   3 hours ago      Up 3 hours                    0.0.0.0:8000->8000/tcp   cours-devops-docker-serve-1
ebfbe695b2ec   moby/buildkit:buildx-stable-1   "buildkitd"              2 months ago     Up 4 hours                                             buildx_buildkit_exciting_williams0

Avec le flag --all , on obtient un tableau de tous les containers quel que soit leur état.

🧽 Nettoyage

Tout container stoppé peut être supprimé.

🧽 Nettoyage

Tout container stoppé peut être supprimé.

docker container rm 90725f661d4e

🧽 Nettoyage

Tout container stoppé peut être supprimé.

docker container rm 90725f661d4e
90725f661d4e

🧽 Nettoyage

Tout container stoppé peut être supprimé.

docker container rm 90725f661d4e
90725f661d4e

Container "auto-nettoyant" 🗑️

🧽 Nettoyage

Tout container stoppé peut être supprimé.

docker container rm 90725f661d4e
90725f661d4e

Container "auto-nettoyant" 🗑️

docker container run  --interactive --tty --rm jpetazzo/clock
Aussitôt stoppé, aussitôt supprimé !

⏰ Rappel: cycle de vie d’un container

output68 with transparency

⏰ Rappel: cycle de vie d’un container

output69 with transparency

⏰ Rappel: cycle de vie d’un container

output70 with transparency

⏰ Rappel: cycle de vie d’un container

output71 with transparency

⏰ Rappel: cycle de vie d’un container

output72 with transparency

🔄 Reprendre le contrôle

Sur un container en arrière-plan

🔄 Reprendre le contrôle

Sur un container en arrière-plan

Il est possible d’interagir avec un container en arrière-plan en cours d’exécution.

🔄 Reprendre le contrôle

Sur un container en arrière-plan

Il est possible d’interagir avec un container en arrière-plan en cours d’exécution.

La commande suivante permet de lancer une commande à l’intérieur d’un container.

🔄 Reprendre le contrôle

Sur un container en arrière-plan

Il est possible d’interagir avec un container en arrière-plan en cours d’exécution.

La commande suivante permet de lancer une commande à l’intérieur d’un container.

docker container exec <containerID> echo "hello"

🔄 Reprendre le contrôle

Sur un container en arrière-plan

Il est possible d’interagir avec un container en arrière-plan en cours d’exécution.

La commande suivante permet de lancer une commande à l’intérieur d’un container.

docker container ls

🔄 Reprendre le contrôle

Sur un container en arrière-plan

Il est possible d’interagir avec un container en arrière-plan en cours d’exécution.

La commande suivante permet de lancer une commande à l’intérieur d’un container.

docker container ls
CONTAINER ID   IMAGE                           COMMAND                  CREATED        STATUS       PORTS                    NAMES
368ed08a35e3   jpetazzo/clock                  "/bin/sh -c 'while d…"   4 hours ago    Up 4 hours                            sweet_clarke
02cbf2fb3721   cours-devops-docker-serve       "/sbin/tini -g gulp …"   7 hours ago    Up 7 hours   0.0.0.0:8000->8000/tcp   cours-devops-docker-serve-1
ebfbe695b2ec   moby/buildkit:buildx-stable-1   "buildkitd"              2 months ago   Up 7 hours                            buildx_buildkit_exciting_williams0

🔄 Reprendre le contrôle

Sur un container en arrière-plan

Il est possible d’interagir avec un container en arrière-plan en cours d’exécution.

La commande suivante permet de lancer une commande à l’intérieur d’un container.

docker container ls
CONTAINER ID   IMAGE                           COMMAND                  CREATED        STATUS       PORTS                    NAMES
368ed08a35e3   jpetazzo/clock                  "/bin/sh -c 'while d…"   4 hours ago    Up 4 hours                            sweet_clarke
02cbf2fb3721   cours-devops-docker-serve       "/sbin/tini -g gulp …"   7 hours ago    Up 7 hours   0.0.0.0:8000->8000/tcp   cours-devops-docker-serve-1
ebfbe695b2ec   moby/buildkit:buildx-stable-1   "buildkitd"              2 months ago   Up 7 hours                            buildx_buildkit_exciting_williams0

docker container exec 368ed08a35e3 echo hello
hello

🔄 Reprendre le contrôle

Sur un container en arrière-plan

1107521466 image9
docker container exec --interactive --tty <containerID> bash

Ca fonctionne aussi en interactif !

🎓 Exercice : conteneur en tâche de fond

Étapes

  • Lancer un container "daemon" jpetazzo/clock

  • Utiliser l’équivalent de tail –f pour lire la sortie standard du container 💡

  • Lancer un second container "daemon" 👹

  • Stocker l’identifiant de ce container dans une variable du shell, en une seule commande et en jouant avec docker container ls

  • Stopper le container avec cet identifiant

  • Afficher les containers lancés 🏃📦

  • Afficher les containers arrêtés 🛑📦

✅ Solution : conteneur en tâche de fond

# Lancer un container "daemon" `jpetazzo/clock`
docker container run --detach --name first-clock jpetazzo/clock

# Utiliser l'équivalent de `tail –f` pour lire la sortie standard du container 💡
docker container logs -f first-clock

# * Lancer un second container "daemon" 👹
docker container run --detach --name second-clock jpetazzo/clock

# Stocker l'identifiant de ce container dans une variable du shell, en une seule commande et en jouant avec `docker container ls`
# --filter "name=second-clock" filters the list of containers to include only those with the name "second-clock."
container_id=$(docker container ls -q --filter "name=second-clock")

# Stopper le container avec cet identifiant
docker container stop "$container_id"

# Afficher les containers lancés  🏃📦
docker container ls

# Afficher les containers arrêtés 🛑📦
docker container ls --filter "status=exited"

🎓 Exercice : conteneur en tâche de fond

  • Relancer un des containers arrêtés.

  • Exécuter un `ps –ef `dans ce container

  • Quel est le PID du process principal ?

  • Vérifier que le container "tourne" toujours

  • Supprimer l’image (tip : docker rmi)

  • Supprimer les containers

  • Supprimer l’image (pour de vrai cette fois)

✅ Solution : conteneur en tâche de fond

# Relancer un des containers arrêtés.
docker container start second-clock

# Exécuter un `ps –ef `dans ce container
docker container exec second-clock ps -ef
PID   USER     TIME  COMMAND
    1 root      0:00 /bin/sh -c while date; do sleep 1; done
   56 root      0:00 sleep 1
   57 root      0:00 ps -ef

# Quel est le PID du process principal ?
# 1

# Vérifier que le container "tourne" toujours
docker container ls
CONTAINER ID   IMAGE                           COMMAND                  CREATED         STATUS              PORTS                    NAMES
7d7085809ad2   cours-devops-docker-serve       "/sbin/tini -g gulp …"   6 minutes ago   Up 5 minutes        0.0.0.0:8000->8000/tcp   cours-devops-docker-serve-1
edea0c46c6d8   jpetazzo/clock                  "/bin/sh -c 'while d…"   9 minutes ago   Up About a minute                            second-clock
ebfbe695b2ec   moby/buildkit:buildx-stable-1   "buildkitd"              2 months ago    Up 11 hours                                  buildx_buildkit_exciting_williams0

# Supprimer l'image (tip : `docker rmi`)
docker rmi jpetazzo/clock
Error response from daemon: conflict: unable to remove repository reference "jpetazzo/clock" (must force) - container edea0c46c6d8 is using its referenced image 7a8965d6553e

# Supprimer les containers
docker stop second-clock
second-clock

docker rm second-clockl
second-clock
# Supprimer l'image (pour de vrai cette fois)
docker rmi jpetazzo/clock

Untagged: jpetazzo/clock:latest
Untagged: jpetazzo/clock@sha256:dc06bbc3744f7200404bff0bbb2516925e7adea115e07de9da8b36bf15fe3dd3
Deleted: sha256:7a8965d6553ea2289485744ef20521e0dd0d12ab51773f271882913b79813750
Deleted: sha256:67f770da229bf16d0c280f232629b0c1f1243a884df09f6b940a1c7288535a6d

🎓 Exercice : conteneur en tâche de fond

  • Exécutez un conteneur, basé sur l’image nginx en tâche de fond ("Background"), nommé webserver-1

    • 💡 On parle de processus "détaché" (ou bien "démonisé") 👹

    • ⚠️ Pensez bien à docker container ls

  • Regardez le contenu du fichier /etc/os-release dans ce conteneur

    • 💡 docker container exec

  • Essayez d’arrêter, démarrer puis redémarrer le conteneur

    • ⚠️ Pensez bien à docker container ls à chaque fois

    • 💡 stop, start, restart

✅ Solution : conteneur en tâche de fond

docker container run --detach --name=webserver-1 nginx
# <ID du conteneur>

docker container ls
docker container ls --all

docker container exec webserver-1 cat /etc/os-release
# ... Debian ...

docker container stop webserver-1
docker container ls
docker container ls --all

docker container start webserver-1
docker container ls
docker container ls --all

docker container start webserver-1
docker container ls

Checkpoint 🎯

Vous savez désormais:

  • Maîtriser le cycle de vie des containers

  • Interagir avec les containers existants

1706694417 image2
1497094497 image10

Checkpoint 🎯

  • Docker essaye de résoudre le problème de l’empaquetage le plus "portable" possible

    • On n’en a pas encore vu les effets, ça arrive !

  • Vous avez vu qu’un containeur permet d’exécuter une commande dans un environnement "préparé"

    • Catalogue d’images Docker par défaut : Le Docker Hub

  • Vous avez vu qu’on peut exécuter des conteneurs selon 3 modes :

    • "One shot"

    • Interactif

    • En tâche de fond

⇒ 🤔 Mais comment ces images sont-elles fabriquées ? Quelle confiance leur accorder ?

Docker Images

multi layered cake

🤔 Pourquoi des images ?

  • Un conteneur est toujours exécuté depuis une image.

  • Une image de conteneur (ou "Image Docker") est un modèle ("template") d’application auto-suffisant.

⇒ Permet de fournir un livrable portable (ou presque).

🤔 C’est quoi une image ?

C’est une collection de fichiers et de metadonnées.

🤔 C’est quoi une image ?

C’est une collection de fichiers et de metadonnées.

C’est une suite de couches superposées.

🤔 C’est quoi une image ?

C’est une collection de fichiers et de metadonnées.

C’est une suite de couches superposées.

images output5 with transparency

🤔 C’est quoi une image ?

C’est une collection de fichiers et de metadonnées.

C’est une suite de couches superposées.

images output6 with transparency

🍰 Détail des couches

Exemple d’une image Apache HTTPd "custom"

images output8 with transparency

🍰 Détail des couches

Exemple d’une image Apache HTTPd "custom"

images output9 with transparency

🍰 Détail des couches

Exemple d’une image Apache HTTPd "custom"

images output10 with transparency

🍰 Détail des couches

Exemple d’une image Apache HTTPd "custom"

images output11 with transparency

🍰 Détail des couches

Exemple d’une image Apache HTTPd "custom"

images output12 with transparency

🍰 Détail des couches

Exemple d’une image Apache HTTPd "custom"

images output13 with transparency

Dicton du jour

"L’image est à la classe ce que le container est à l’objet"

dicton du jour

🤔 Application Auto-Suffisante ?

docker app self sufficient

C’est quoi le principe ?

dockerfile flow

🐋 📖🍳 Le livre de recettes

moby kitchen
dockerfile

🐋 📖🍳 Le livre de recettes

dockerfile

🐋 📖🍳 Le livre de recettes

dockerfile

Un simple fichier nommé "Dockerfile" (majuscule sur le D et pas d’extension).

🐋 📖🍳 Le livre de recettes

dockerfile

Un simple fichier nommé "Dockerfile" (majuscule sur le D et pas d’extension).

C’est du texte, très pratique à stocker dans Git.

🐋 📖🍳 Le livre de recettes

dockerfile

Un simple fichier nommé "Dockerfile" (majuscule sur le D et pas d’extension).

C’est du texte, très pratique à stocker dans Git.

Une suite de clef-valeur.

🤔 Pourquoi fabriquer sa propre image ?

❗️ Problème :

cat /etc/os-release
# ...
git --version
# ...

# Même version de Linux que dans GitPod
docker container run --rm ubuntu:22.04 git --version
# docker: Error response from daemon: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "git": executable file not found in $PATH: unknown.

# En interactif ?
docker container run --rm --tty --interactive ubuntu:22.04 git --version

🎓 Fabriquer sa première image

  • But : fabriquer une image Docker qui contient git

  • Dans votre workspace Gitpod, créez un dossier nommé docker-git/

  • Dans ce dossier, créer un fichier Dockerfile avec le contenu ci-dessous :

    FROM alpine:3.22.0
    
    RUN apk update && apk add --no-cache git
  • Fabriquez votre image avec la commande docker image build --tag=docker-git chemin/vers/docker-git/

  • Testez l’image fraîchement fabriquée

    • 💡 docker image ls

✅ Fabriquer sa première image

cat <<EOF >Dockerfile
FROM alpine:3.22.0

RUN apk update && apk add --no-cache git
EOF

docker image build --tag=docker-git ./

docker image ls | grep docker-git

# Doit fonctionner
docker container run --rm docker-git:latest git --version

🐋 📖🍳 Fabriquer son image

Un peu de cuisine…​

445636559 image7

🐋 📖🍳 Fabriquer son image

💪🏁 DÉFI

Créer une image alpine avec un JRE installé.

📖🍳 RECETTE

  • On part d’une image Alpine

  • On installe un JRE

🐋 📖🍳 Fabriquer son image

images output56 with transparency
FROM alpine:3.22.0
LABEL maintainer="Tony Stark"

RUN apk update

RUN apk add --no-cache openjdk17-jre-headless

🐋 📖🍳 Cuistot, au boulot!

images output57 with transparency

🐋 📖🍳 Cuistot, au boulot!

images output58 with transparency

🐋 📖🍳 Cuistot, au boulot!

images output59 with transparency

🐋 📖🍳 Cuistot, au boulot!

images output59 with transparency
docker image build -t myjava:1.42 .

🪜 Étapes de construction

images output60 with transparency

🪜 Étapes de construction

images output61 with transparency

🪜 Étapes de construction

images output62 with transparency

Et après ?

Quand le build se termine, il se trouve dans le registre local.

docker images
REPOSITORY       TAG                 IMAGE ID
myjava           1.42                d3017f59d5e2

L’image est dispo !

docker container run --interactive --tty myjava:1.42 sh
/ $
/ $ java -version
openjdk version "17.0.8" 2023-07-18
OpenJDK Runtime Environment (build 17.0.8+7-alpine-r0)
OpenJDK 64-Bit Server VM (build 17.0.8+7-alpine-r0, mixed mode, sharing)
278458282 image10

Registre local, mais encore?

On les trouve où, ces images ?

En local, on l’a vu.

Dans les registres Docker.

$ docker images
REPOSITORY                                  TAG                             IMAGE ID       CREATED         SIZE
jenkinsciinfra/jenkins-agent-ubuntu-22.04   latest                          c87afa001ba1   25 hours ago    6.89GB
cours-devops-docker-serve                   latest                          6b7b6a3145fd   27 hours ago    427MB
cours-devops-docker-qrcode                  latest                          43f91abb9cfa   27 hours ago    427MB
jenkins/jenkins                             2.419-rhel-ubi8-jdk11           9e2076ee44fa   3 days ago      507MB
jenkins/jenkins                             2.419-slim                      b93a74bd73b4   3 days ago      394MB
jenkins/jenkins                             2.419                           2193a96f254a   3 days ago      478MB
jenkins/jenkins                             2.419-jdk11                     2193a96f254a   3 days ago      478MB
jenkins/jenkins                             2.419-alpine                    f700f6333bf2   3 days ago      249MB
jenkins/jenkins                             2.419-rhel-ubi9-jdk17           0dbee3b2c2fc   3 days ago      485MB
jenkins/jenkins                             2.419-slim-jdk17                d9a360e0a9bf   3 days ago      393MB
jenkins/jenkins                             2.419-jdk17                     1695080429f5   3 days ago      476MB
jenkins/jenkins                             2.419-alpine-jdk17              2ea0017744c8   3 days ago      249MB
jenkins/jenkins                             2.419-rhel-ubi9-jdk21-preview   66ee1f18309d   3 days ago      494MB
jenkins/jenkins                             2.419-slim-jdk21-preview        e31d85782d2b   3 days ago      414MB
jenkins/jenkins                             2.419-jdk21                     c1e6c123a3c7   3 days ago      485MB
jenkins/jenkins                             2.419-alpine-jdk21-preview      97764348fde6   3 days ago      261MB
jdk21                                       latest                          ba476a3f2cd9   5 days ago      218MB
mycurl                                      1.0                             292bf6b4a4df   6 days ago      13.3MB
myjava                                      1.42                            05b6d3da385e   6 days ago      198MB
<none>                                      <none>                          7bc71c53e776   7 days ago      427MB
<none>                                      <none>                          b45a84c7d06b   7 days ago      427MB
jenkins/jenkins                             latest-jdk21-preview            5b5828e392bf   8 days ago      485MB
moby/buildkit                               buildx-stable-1                 9291fad3b41c   4 weeks ago     172MB
alpine                                      latest                          7e01a0d0a1dc   6 weeks ago     7.34MB
busybox                                     latest                          a416a98b71e2   2 months ago    4.26MB
docker/volumes-backup-extension             1.1.4                           6872a696b721   3 months ago    119MB
portainer/portainer-docker-extension        2.18.3                          3d18fe6d6805   4 months ago    273MB
felipecruz/alpine-tar-zstd                  latest                          31988344315d   12 months ago   6.99MB
justincormack/nsenter1                      latest                          c81481184b1b   5 years ago     101kB

Les registres Docker

Ce sont des plates-formes qui hébergent les images.

Il est possible de créer ses propres registres (ex : registre privé d’entreprise)

🖼️ Les images

Avant d’instancier un container, il faut récupérer l’image en local.

Méthode explicite :

docker image pull mysql
On rapatrie l’image en local mais on n’en fait rien.

Méthode implicite :

docker container run -d mysql
On rapatrie l’image et on démarre un container dans la foulée.

🍰 Un téléchargement par couches

$ docker image pull mysql
Using default tag: latest
latest: Pulling from library/mysql
5f70bf18a086: Pull complete
a734b0ff4ca6: Already exists
ec46eb0ce0a7: Pull complete
a74b383379bc: Pull complete
Digest: sha256:42dc1b67073f7ebab1...8c8d36c9031e408db0d
Status: Downloaded newer image for mysql:latest

Les couches déjà présentes en local ne sont pas téléchargées de nouveau !

🖼🏷️ Conventions de nommage des images

images output26 with transparency

🖼🏷️ Conventions de nommage des images

images output27 with transparency

🖼🏷️ Conventions de nommage des images

images output28 with transparency

🖼🏷️ Conventions de nommage des images

images output29 with transparency

📷🏷️ Conventions de nommage des images

Une même image peut avoir plusieurs noms et tags !

Le tag "latest" est régulièrement réaffecté sur les registres distants.

Plusieurs noms pour une signature

📷🏷️ Conventions de nommage des images

Une même image peut avoir plusieurs noms et tags !

Le tag "latest" est régulièrement réaffecté sur les registres distants.

Plusieurs noms pour une signature

Tags disponibles pour MySQL

mysql tags

📷🏷️ Conventions de nommage des images

Pour résumer…​

[REGISTRY/][NAMESPACE/]NAME[:TAG|@DIGEST]
  • Pas de Registre ? Défaut: registry.docker.com

  • Pas de Namespace ? Défaut: library

  • Pas de tag ? Valeur par défaut: latest

    • ⚠️ Friends don’t let friends use latest 👫

  • Digest: signature unique basée sur le contenu

📷🏷️ Conventions de nommage : Exemples

  • ubuntu:22.04registry.docker.com/library/ubuntu:22.04

  • dduportal/docker-asciidoctorregistry.docker.com/dduportal/docker-asciidoctor:latest

  • ghcr.io/dduportal/docker-asciidoctor:1.3.2@sha256:xxxx

🎓 Utilisons les tags

  • Rappel : ⚠️ Friends don’t let friends use latest 👫

  • Il est temps de "taguer" votre première image !

    docker image tag docker-git:latest docker-git:1.0.0
  • Testez le fonctionnement avec le nouveau tag

  • Comparez les 2 images dans la sortie de docker image ls

✅ Utilisons les tags

docker image tag docker-git:latest docker-git:1.0.0

# 2 lignes
docker image ls | grep docker-git
# 1 ligne
docker image ls | grep docker-git | grep latest
# 1 ligne
docker image ls | grep docker-git | grep '1.0.0'

# Doit fonctionner
docker container run --rm docker-git:1.0.0 git --version

🎓 Mettre à jour votre image (1.1.0)

  • Mettez à jour votre image en version 1.1.0 avec les changements suivants :

    • Ajoutez un LABEL dont la clef est description (et la valeur de votre choix)

    • Configurez git pour utiliser une branche main par défaut au lieu de master (commande git config --global init.defaultBranch main)

  • Indices :

✅ Mettre à jour votre image (1.1.0)

cat ./Dockerfile
FROM ubuntu:22.04
RUN apt-get update && apt-get install --yes --no-install-recommends git
LABEL description="Une image contenant git préconfiguré"
RUN git config --global init.defaultBranch main

docker image build -t docker-git:1.1.0 ./docker-git/
# Sending build context to Docker daemon  2.048kB
# Step 1/4 : FROM ubuntu:22.04
#  ---> e40cf56b4be3
# Step 2/4 : RUN apt-get update && apt-get install --yes --no-install-recommends git
#  ---> Using cache
#  ---> 926b8d87f128
# Step 3/4 : LABEL description="Une image contenant git préconfiguré"
#  ---> Running in 0695fc62ecc8
# Removing intermediate container 0695fc62ecc8
#  ---> 68c7d4fb8c88
# Step 4/4 : RUN git config --global init.defaultBranch main
#  ---> Running in 7fb54ecf4070
# Removing intermediate container 7fb54ecf4070
#  ---> 2858ff394edb
Successfully built 2858ff394edb
Successfully tagged docker-git:1.1.0

docker container run --rm docker-git:1.0.0 git config --get init.defaultBranch
docker container run --rm docker-git:1.1.0 git config --get init.defaultBranch
# main

Cache d’images & Layers

Step 2/4 : RUN apt-get update && apt-get install --yes --no-install-recommends git
  ---> Using cache

🤔 En fait, Docker n’a PAS exécuté cette commande la seconde fois ⇒ ça va beaucoup plus vite !

docker layers

🎓 Essayez de voir les layers avec (dans Gitpod) dive <image>:<tag>

🎓 Cache d’images & Layers

  • But : manipuler le cache d’images

  • Commencez par vérifier que le cache est utilisé : relancez la dernière commande docker image build (plusieurs fois s’il le faut)

  • Invalidez le cache en ajoutant le paquet APT make à installer en même temps que git

    • ⚠️ Tag 1.2.0

  • Vérifiez que le cache est bien présent de nouveau

✅ Cache d’images & Layers

# Build one time
docker image build -t docker-git:1.1.0 ./docker-git/
# Second time is fully cached
docker image build -t docker-git:1.1.0 ./docker-git/

cat Dockerfile
# FROM ubuntu:22.04
# RUN apt-get update && apt-get install --yes --no-install-recommends git make
# LABEL description="Une image contenant git préconfiguré"
# RUN git config --global init.defaultBranch main

# Build one time
docker image build -t docker-git:1.2.0 ./docker-git/
# Second time is fully cached
docker image build -t docker-git:1.2.0 ./docker-git/

## Vérification
# Renvoie une erreur
docker run --rm docker-git:1.1.0 make --version
# Doit fonctionner
docker run --rm docker-git:1.2.0 make --version

Comportement par défaut des containers

Deux instructions permettent de définir la commande à lancer au démarrage du container.

cmd

Comportement par défaut des containers

Deux instructions permettent de définir la commande à lancer au démarrage du container.

cmd entrypoint

Laquelle choisir ???

CMD vs ENTRYPOINT

Cas d’usage : énoncé du besoin.

Besoin : je veux utiliser cURL mais il n’est pas présent sur la machine hôte.

Facile !

docker container run --rm curlimages/curl curl -x http://prx:3128 -L --connect-timeout 60  "http://google.com"
1956754704 image11

CMD vs ENTRYPOINT

Cas d’usage. On va s’outiller!

FROM alpine:3.22
LABEL maintainer="John Doe"
RUN apk update && apk add curl
# Si vous utilisez le proxy de l'Université
CMD ["curl","-x", "http://prx:3128", "-L", "--connect-timeout", "60", "http://google.com"]
# Si vous utilisez votre propre proxy docker
# CMD ["curl","-x", "http://host.docker.internal:3128", "-L", "--connect-timeout", "60", "http://google.com"]
docker image build -t my-curl:1.0 .
docker container run --rm my-curl:1.0
<!doctype html><html [...]
google blahblahblah [...]
</html>
my girl

CMD vs ENTRYPOINT

Cas d’usage.

%auto-animate

CMD vs ENTRYPOINT

Cas d’usage.

%auto-animate
1602759072 image12

CMD vs ENTRYPOINT

Cas d’usage: un cran plus loin.

L’image actuelle, c’est bien mais pas hyper flexible !

Et si on la rendait paramétrable ?

docker container run --rm my-curl:2.0 http://google.com
docker container run --rm my-curl:2.0 http://facebook.com
docker container run --rm my-curl:2.0 http://twitter.com

CMD vs ENTRYPOINT

Cas d’usage, un cran plus loin

Paramétrable, et hop!

FROM alpine:3.22
LABEL maintainer="John Doe"
RUN apk update && apk add curl
# Si vous utilisez le proxy de l'Université
ENTRYPOINT ["curl","-x", "http://prx:3128", "-L", "--connect-timeout", "60"]
# Si vous utilisez votre propre proxy docker
# CMD ["curl","-x", "http://host.docker.internal:3128", "-L", "--connect-timeout", "60"]
docker image build -t my-curl:2.0 .
docker container run --rm my-curl:2.0 http://google.com
<!doctype html><html [...]
google blahblahblah [...]
</html>

CMD vs ENTRYPOINT

Cas d’usage, un cran plus loin

Paramétrable, et hop!

FROM alpine:3.22
LABEL maintainer="John Doe"
RUN apk update && apk add curl
# Si vous utilisez le proxy de l'Université
ENTRYPOINT ["curl","-x", "http://prx:3128", "-L", "--connect-timeout", "60"]
# Si vous utilisez votre propre proxy docker
# CMD ["curl","-x", "http://host.docker.internal:3128", "-L", "--connect-timeout", "60"]
override
docker image build -t my-curl:2.0 .
docker container run --rm my-curl:2.0 http://google.com
<!doctype html><html [...]
google blahblahblah [...]
</html>

Checkpoint 🎯

  • Une image Docker fournit un environnement de système de fichier auto-suffisant (application, dépendances, binaries, etc.) comme modèle de base d’un conteneur

  • Les images Docker ont une convention de nommage permettant d’identifier les images très précisément

  • On peut spécifier une recette de fabrication d’image à l’aide d’un Dockerfile et de la commande docker image build

⇒ 🤔 et si on utilisait Docker pour nous aider dans l’intégration continue ?

📁 Nos fichiers dans les images

📂 Le répertoire de travail

docker container run --interactive --tty httpd pwd /usr/local/apache2

Comment paramétrer le répertoire de travail de tous nos containers ?

📂 WORKDIR

FROM alpine:3.22
WORKDIR /repertoire/travail
RUN echo hello > ./world.txt

📂 WORKDIR

FROM alpine:3.22
WORKDIR /repertoire/travail
RUN echo hello > ./world.txt
fichiers output5 with transparency

📂 WORKDIR

FROM alpine:3.22
WORKDIR /repertoire/travail
RUN echo hello > ./world.txt
fichiers output6 with transparency

📂 WORKDIR

FROM alpine:3.22
WORKDIR /repertoire/travail
RUN echo hello > ./world.txt
docker image build --tag mon-img .
...
$ docker container run mon-img cat /repertoire/travail/world.txt
hello
$ docker container run mon-img pwd
/repertoire/travail
$ docker container run --workdir /home mon-img pwd
/home

📁 Embarquer des fichiers

Cas d’usage.

ls
app.js
$ docker container run --workdir /customdir node app.js
<error : app.js not found>
$ docker container run --workdir /customdir --entrypoint ls node
<0 fichiers présents !>

Comment fournir des fichiers à mes containers ?

📁 Embarquer des fichiers

Copie de fichiers.

FROM node:22.2.0-alpine3.18
WORKDIR /app
COPY ./* /app
docker image build --tag mon-node .
...
$ docker container run --workdir /app --entrypoint ls mon-node
app.js
$ docker container run mon-node app.js
Hello Mad Javascript World!

📁 Embarquer des fichiers

Le mot clé ADD.

FROM image
ADD https://mon-nexus/foo/bar/1.0/package.tar /uncompressed/

Il permet d’ajouter des fichiers aux images, tout comme COPY.

Il est capable de télécharger directement d’une URL

Il est capable de "untar" automatiquement.

On le retrouve dans d’anciens Dockerfile mais il tend à disparaître.

📁 Embarquer des fichiers

📁 Embarquer des fichiers

📁 Embarquer des fichiers

Oui, mais pas la terre entière !

docker image build --tag myjava:1.42 ./
Sending build context to Docker daemon  172.8MB

📁 Embarquer des fichiers

Oui, mais pas la terre entière !

fichiers output19 with transparency

📁 Embarquer des fichiers

Oui, mais pas la terre entière !

fichiers output20 with transparency

📁 Embarquer des fichiers

Oui, mais pas la terre entière !

fichiers output21 with transparency

Si un gros fichier traîne dans le dossier alors qu’il n’est même pas utilisé ni référencé dans le Dockerfile, il sera tout de même envoyé au Docker daemon.

📁 Embarquer des fichiers

Oui, mais pas la terre entière !

"M’en fous ! j’ai une machine de fou et un réseau de fou ! YOLO !"

Et si la CI fait plusieurs builds par minute ?

Et si un vieux Keystore traîne dans les sources ?

.git/ ? .idea/ ? vraiment ?

Attention à l’invalidation de cache d’un step de build à cause de l’ajout d’un fichier inutile !

🙉 .dockerignore

# ignore les dossiers .git et .cache
.git
.cache
# ignore tous les fichiers *.class dans tous les dossiers
**/*.class
# ignore les markdown sauf les README*.md (README-secret.md sera tout de même ignoré par contre)
*.md
!README*.md
README-secret.md

📝 Renommage des images

fichiers output25 with transparency

📝 Renommage des images

fichiers output26 with transparency

📝 Renommage des images

fichiers output27 with transparency

📝 Renommage des images

fichiers output28 with transparency

httpd:latest sera taggué en fedora/httpd:version1.0

🖼️ LES IMAGES

multi layered cake removebg preview

Le bilan

Vous savez désormais:

  • Rédiger un Dockerfile

  • Nommer vos images

  • Créer vos outils

🖼️ LES IMAGES

384835330 image4

Le bilan

Vous savez désormais:

  • Rédiger un Dockerfile

  • Nommer vos images

  • Créer vos outils

🗂️ Les registries

104336496 image5

Golden Rule

La construction d’une image doit être automatisée.

🤖 = 🔒 ? Automatique == sécurisé

Le fait de déléguer la construction des images permet d’ajouter toute une chaîne de traitement, de contrôles des images pour s’assurer qu’elle respecte les règles RSSI.

  • patch management

  • droits sur le FS

  • user

Exemple de mise en place

fichiers output35 with transparency

Exemple de mise en place

fichiers output36 with transparency

Exemple de mise en place

fichiers output37 with transparency

Exemple de mise en place

fichiers output38 with transparency

Exemple de mise en place

fichiers output39 with transparency

➡️ Exemple : continuous build

107490784 image13

🎓 Travaux pratiques #5

✅ Solution Travaux pratiques #5

diff --git a/src/Dockerfile b/src/Dockerfile
index c92c67cd48121705628f67a2af14b2a65e54cdfd..77919a581fffde7c57f43c8b7cbee2a8d84b628b 100644
--- a/src/Dockerfile
+++ b/src/Dockerfile
@@ -9,6 +9,10 @@ COPY start.sh /bin/start.sh
 EXPOSE 4321
 # This line opens port 4321 on the container. It's like installing a door in your house.

+RUN adduser --disabled-password --gecos "" --home /home/www www
+USER www
+WORKDIR /home/www
+
 ENTRYPOINT ["/bin/start.sh"]
 # This line sets the command that will be run when the container starts. It's like setting the alarm clock in your house.

✅ Solution Travaux pratiques #5

diff --git a/src/start.sh b/src/start.sh
index 44006d6cd8414706a5fcf564dbfd1e9c38d4bc0b..a6ed60fdf88759d4962685e7ceb6cdbc21867448 100755
--- a/src/start.sh
+++ b/src/start.sh
@@ -4,7 +4,7 @@
 set -eux
 # These are shell options. 'e' exits on error, 'u' treats unset variables as an error, and 'x' prints each command to the terminal. It's like the script's personal assistant, making sure everything runs smoothly.

-touch /home/www/started.time
+touch /tmp/started.time
 # This creates a file named 'started.time' in the '/home/www' directory. It's like the script's way of marking its territory.

 if [ $? -ne 0 ]; then
@@ -12,7 +12,7 @@ if [ $? -ne 0 ]; then
 fi
 # This checks the exit status of the last command. If it's not zero, which means there was an error, it exits the script. It's like the script's way of saying, "If I can't do it right, I won't do it at all."

-date > /home/www/started.time
+date > /tmp/started.time
 # This writes the current date and time to the 'started.time' file. It's like the script's way of keeping a diary.

 exec "$@"

✅ Solution Travaux pratiques #5

stages:
  - "validator.sh"
  - "docker scout"

variables:
  PROXY: "http://cache-etu.univ-artois.fr:3128"
  IMAGE: "devops-docker-tp05:latest"

validator-job:
  image: cache-ili.univ-artois.fr/proxy_cache/library/docker
  stage: "validator.sh"
  before_script:
    - export HTTP_PROXY="$PROXY"
    - export HTTPS_PROXY="$PROXY"
    - apk add -U bash
  script:
    - errors=$(./validator.sh)
    - echo "$errors"
    - exit $([ -z "$errors" ] && echo 0 || echo 1)
  tags:
    - docker2

# No login needed, using docker desktop with wsl
scout-job:
  stage: "docker scout"
  script:
    - cd ./src
    - docker build . -t "$IMAGE"
    - docker scout cves --format only-packages --only-vuln-packages "$IMAGE"
    - docker rmi "$IMAGE"
  tags:
    - myshell

✅ Solution Travaux pratiques #5

stages:
  - run_script
  - docker_scan

run_script:
  stage: run_script
  script:
    - chmod +x validator.sh src/start.sh && ./validator.sh # Makes the scripts executable and runs validator.sh
    - |
        if [ $? -eq 0 ]; then
          echo "Script exited successfully."
        else
          echo "Script exited with an error."
          exit 1  # Mark the job as a failure
        fi

docker_scan:
  stage: docker_scan
  script:
    - IMG=$(echo img$$)
    - docker image build --tag $IMG ./src > /dev/null
    - echo "Will scan $IMG"
    - echo $DOCKER_TOKEN | docker login -u $DOCKER_USERNAME --password-stdin
    - docker scout cves --format only-packages --only-vuln-packages $IMG # Runs docker scout to scan the image
    - docker scout recommendations $IMG # View base image update recommendations

🔍 Inspection et nommage des containers

1712651753 image15

📛 Nommage des containers

docker container logs

Ne peut-on pas trouver plus 'user-friendly'?

📛 Nommage des containers

fichiers output47 with transparency

📛 Nommage des containers

fichiers output48 with transparency

📛 Nommage des containers

fichiers output49 with transparency

📛 Nommage des containers

📛 Nommage des containers

docker container run --detach --name my-web httpd

Nom du container explicite

docker container logs my-web
docker exec --interactive --tty my-web sh

🦸‍♂️ Renommage des containers

Attention, spoiler pour les n00bs de DC Comics

docker container rename clark_kent superman
kent and superman

🦸‍♂️ Renommage des containers

fichiers output55 with transparency

🦸‍♂️ Renommage des containers

fichiers output56 with transparency

🔍 Inspection des containers

inspecting containers
docker container inspect my-web
[
    {
        "Id": "0ac8cdf8d447c6d316a04bd1a7f74cd2677eea3478f11f0be5241c1bb2d4c7da",
        "Created": "2023-10-24T19:40:11.84788911Z",
        "Path": "httpd-foreground",
        "Args": [],
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 3275,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2023-10-24T19:40:12.363841649Z",
            "FinishedAt": "0001-01-01T00:00:00Z"
        },
...
]

Comment exploiter le JSON ?

JQ est le meilleur outil pour parser du JSON dans le shell

docker container inspect my-web | jq .

Les templates de GO peuvent être utilisés directement

docker container inspect --format '{{json .Created }}' my-web

🎓 Travaux pratiques #6

Étapes:

  • Lancer un container HTTPd

  • Trouver la syntaxe permettant d’extraire le "CMD" qui a été passé au démarrage du container

  • Trouver une seconde méthode pour faire la même chose

✅ Solution Travaux Pratiques #6

On démarre un container nginx avec la commande

# Here we're using the 'docker container run' command to start a new Docker container.
# The '--detach' option tells Docker to run the container in the background and print the container ID.
# The '--name' option allows us to give our container a custom name, in this case 'my-web'.
# Finally, 'nginx' is the name of the Docker image we want to use to create the container.
# So, to sum up, this command will start a new Docker container in the background, using the 'nginx' image, and name it 'my-web'.
docker container run --detach --name my-web nginx

Une solution facile est de faire:

# In this command, we're using 'docker container inspect' to get detailed information about our 'my-web' container.
# The '--format' option allows us to specify the output format. Here, we're using the Go template '{{json .Config.Cmd }}' to get the command used to start the container in JSON format.
# We then pipe this JSON output to 'jq', a powerful command-line JSON processor.
# The '.' in 'jq .' tells 'jq' to take the input JSON and output it as is, effectively pretty-printing it.
# So, to sum up, this command will give us the command used to start the 'my-web' container, in a pretty-printed JSON format.
docker container inspect --format '{{json .Config.Cmd }}' my-web | jq .

Ça nous donne:

[
  "nginx",
  "-g",
  "daemon off;"
]

✅ Solution Travaux Pratiques #6

[
  "nginx",
  "-g",
  "daemon off;"
]

Qu’on pourrait nettoyer pour avoir nginx -g daemon off; avec:

# This command is a symphony in three parts. First, we're using 'docker container inspect' to get detailed information about our 'my-web' container.
# The '--format' option allows us to specify the output format. Here, we're using the Go template '{{json .Config.Cmd }}' to get the command used to start the container in JSON format.
# We then pipe this JSON output to 'jq', a powerful command-line JSON processor. The '-r' option tells 'jq' to output raw strings instead of JSON-encoded strings, and '.[]' tells 'jq' to output the elements of the array.
# But we're not done yet. We then pipe this output to 'tr', which replaces the newlines with spaces, giving us a neat, single-line command.
# So, to sum up, this command will give us the command used to start the 'my-web' container, as a single line of text.
docker container inspect --format '{{json .Config.Cmd }}' my-web | jq -r '.[]' | tr '\n' ' '

qui donne nginx -g daemon off;.

Une autre solution serait:

# This command is using 'docker container ls' to list our Docker containers. The '--filter' option allows us to only show containers with the name 'my-web', and '--no-trunc' prevents Docker from truncating the output.
# We then pipe this output to 'awk', a powerful text processing tool. 'NR==1 {cmd=index($0, "COMMAND"); created=index($0, "CREATED")}' tells 'awk' to find the positions of the 'COMMAND' and 'CREATED' headers in the first line of the output.
# 'NR>1 {print substr($0, cmd, created-cmd)}' then tells 'awk' to print the substring from the start of the 'COMMAND' field to the start of the 'CREATED' field for the rest of the lines.
# So, to sum up, this command will give us the command used to start the 'my-web' container, extracted from the output of 'docker container ls'.
docker container ls --filter "name=my-web" --no-trunc | awk 'NR==1 {cmd=index($0, "COMMAND"); created=index($0, "CREATED")} NR>1 {print substr($0, cmd, created-cmd)}'

✅ Solution Travaux Pratiques #6

# This command is using 'docker container ls' to list our Docker containers. The '--filter' option allows us to only show containers with the name 'my-web', and '--no-trunc' prevents Docker from truncating the output.
# We then pipe this output to 'awk', a powerful text processing tool. 'NR==1 {cmd=index($0, "COMMAND"); created=index($0, "CREATED")}' tells 'awk' to find the positions of the 'COMMAND' and 'CREATED' headers in the first line of the output.
# 'NR>1 {print substr($0, cmd, created-cmd)}' then tells 'awk' to print the substring from the start of the 'COMMAND' field to the start of the 'CREATED' field for the rest of the lines.
# So, to sum up, this command will give us the command used to start the 'my-web' container, extracted from the output of 'docker container ls'.
docker container ls --filter "name=my-web" --no-trunc | awk 'NR==1 {cmd=index($0, "COMMAND"); created=index($0, "CREATED")} NR>1 {print substr($0, cmd, created-cmd)}'

qui donnerait "/docker-entrypoint.sh nginx -g 'daemon off;'".

🌐 Trouver son container en réseau

1233103020 image17

🌐 Le Container en réseau

docker container run --detach nginx
bd12c4d7110d17ce80...`
docker container ls
CONTAINERID     IMAGE   COMMAND      CREATED    STATUS    PORTS
bd12c4d71       nginx   "nginx …"    35 s. ago  Up 33 s.  80/tcp, 443/tcp

Ok, mon container expose un service sur les ports TCP 80 et 443 mais j’appelle comment mon Nginx ?

🔍 Que nous dit docker container inspect ?

"Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "c0fcec43e3e8…eecd6558ac0870a468a3",
                    "EndpointID": "0ec8fb237a10e9227359b4…db23edc32",
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.2",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:11:00:02",
                    "DriverOpts": null
                }
            }

🔍 Que nous dit docker container inspect ?

fichiers output66 with transparency

🔍 Que nous dit docker container inspect ?

fichiers output67 with transparency
docker container inspect --format "{{.NetworkSettings.IPAddress}}" <ContainerID>

Et voilà !

curl -I --noproxy '*' http://172.17.0.2:80
HTTP/1.1 200 OK
....
`Server: nginx/1.25 ...
`

Il est maintenant facile d’interagir avec notre serveur web !

🗺️ Mapping de Port

fichiers output71 with transparency

🗺️ Mapping de Port

fichiers output72 with transparency

🗺️ Mapping de Port

fichiers output73 with transparency
curl -I --noproxy '*' http://172.17.0.2:80

🗺️ Mapping de Port

curl -I --noproxy '*' http://172.17.0.2:80
HTTP/1.1 200 OK
Server: nginx/1.25.2
Date: Wed, 18 Oct 2023 11:54:49 GMT
Content-Type: text/html
Content-Length: 284237
Last-Modified: Tue, 10 Oct 2023 12:12:13 GMT
Connection: keep-alive
ETag: "65253f9d-4564d"
Accept-Ranges: bytes
$ curl -I --noproxy '*' http://localhost:8000`
HTTP/1.1 200 OK
Server: nginx/1.25.2
Date: Wed, 18 Oct 2023 11:57:25 GMT
Content-Type: text/html
Content-Length: 284237
Last-Modified: Tue, 10 Oct 2023 12:12:13 GMT
Connection: keep-alive
ETag: "65253f9d-4564d"
Accept-Ranges: bytes

🎓 Travaux pratiques #7

Étapes:

✅ Solution travaux pratiques #7

<html><head></head><body><header>
<title>http://info.cern.ch</title>
</header>

<h1>http://info.cern.ch - home of the first website</h1>
<p>From here you can:</p>
<ul>
<li><a href="http://info.cern.ch/hypertext/WWW/TheProject.html">Browse the first website</a></li>
<li><a href="http://line-mode.cern.ch/www/hypertext/WWW/TheProject.html">Browse the first website using the line-mode browser simulator</a></li>
<li><a href="http://home.web.cern.ch/topics/birth-web">Learn about the birth of the web</a></li>
<li><a href="http://home.web.cern.ch/about">Learn about CERN, the physics laboratory where the web was born</a></li>
</ul>
</body></html>
docker run --name my-nginx -v /fully/qualified/path/on/my/computer/:/usr/share/nginx/html:ro -d -p 8000:80 nginx
curl --noproxy '*' http://localhost:8000
<html><head></head><body><header>
<title>http://info.cern.ch</title>
</header>

<h1>http://info.cern.ch - home of the first website</h1>
<p>From here you can:</p>
<ul>
<li><a href="http://info.cern.ch/hypertext/WWW/TheProject.html">Browse the first website</a></li>
<li><a href="http://line-mode.cern.ch/www/hypertext/WWW/TheProject.html">Browse the first website using the line-mode browser simulator</a></li>
<li><a href="http://home.web.cern.ch/topics/birth-web">Learn about the birth of the web</a></li>
<li><a href="http://home.web.cern.ch/about">Learn about CERN, the physics laboratory where the web was born</a></li>
</ul>
</body></html>

✅ Solution travaux pratiques #7

wget https://gitlab.univ-artois.fr/bruno.verachten/devops-docker-tp07/-/raw/master/sample.war
# Used to run a Docker container with the name "wildfly-container" using the "jboss/wildfly" image. The container is
# configured to expose ports 8080 and 9990 on the host machine, which will be mapped to the corresponding ports inside
# the container. Additionally, a volume is mounted to the container, linking the
# "/fully/qualified/path/on/my/computer/sample.war" file to "/opt/jboss/wildfly/standalone/deployments/sample.war"
# path within the container. This allows the WildFly server running inside the container to access and deploy the
# "sample.war" file. The "-d" flag is used to run the container in detached mode, meaning it will run in the
# background.
docker run -d -p 8080:8080 -p 9990:9990 --name wildfly-container -v /fully/qualified/path/on/my/computer/sample.war:/opt/jboss/wildfly/standalone/deployments/sample.war jboss/wildfly

Visitez avec votre navigateur http://localhost:8080/sample/.

✅ Solution travaux pratiques #7

git clone https://github.com/spring-guides/gs-spring-boot.git

C’est un début…​

cd gs-spring-boot/complete
mvn package
java -jar target/spring-boot-complete-0.0.1-SNAPSHOT.jar

Ok, il y a de l’idée…​

✅ Solution travaux pratiques #7

Assemblons tout ça dans un Dockerfile.

# Use a Maven image for building the application
FROM maven:3.9.8-eclipse-temurin-22-alpine
WORKDIR /app

# Clone the Spring Boot application repository
RUN apk add git && git clone https://github.com/spring-guides/gs-spring-boot.git

# Change directory to the application's complete directory
WORKDIR /app/gs-spring-boot/complete

# Build the application using Maven
RUN mvn package

WORKDIR /app/gs-spring-boot/complete/target

# Command to run the Spring Boot application
CMD ["java", "-jar", "spring-boot-complete-0.0.1-SNAPSHOT.jar"]

Spoiler alert: le nom de fichier donne une indication…​

docker build -t spring-boot-app --file Dockerfile.ugly .

Et logiquement…​

docker run -d -p 8080:8080 --name spring-boot-container spring-boot-app

⏳ Attends un peu…

1779074283 image19

Ça fonctionne, mais c’est comme si je laissais la grue dans la chambre une fois que j’avais fini de construire ma maison !

✅ Solution travaux pratiques #7

On essaye un Dockerfile moins BTP?

# Use a Maven image for building the application
FROM maven:3.9.8-eclipse-temurin-22-alpine AS build
WORKDIR /app

# Clone the Spring Boot application repository
RUN apk add git && git clone https://github.com/spring-guides/gs-spring-boot.git

# Change directory to the application's complete directory
WORKDIR /app/gs-spring-boot/complete

# Build the application using Maven
RUN mvn package

# Use a lightweight Java image for running the application
FROM eclipse-temurin:24.0.1_9-jre-alpine
WORKDIR /app

# Copy the built JAR file from the previous build stage
COPY --from=build /app/gs-spring-boot/complete/target/spring-boot-complete-0.0.1-SNAPSHOT.jar ./

# Command to run the Spring Boot application
CMD ["java", "-jar", "spring-boot-complete-0.0.1-SNAPSHOT.jar"]

Le multistage, c’est la classe à Arras. On en reparle après.

🧩 Une première solution

Avec un autre exemple…​ Une image pour construire l’appli:

FROM golang:1.24

WORKDIR /go/src/github.com/alexellis/href-counter/

COPY go.mod .
COPY go.sum .
COPY app.go	.


RUN CGO_ENABLED=0 GOOS=linux go build  -ldflags "-s -w" -o app .

Une image pour faire tourner l’appli

FROM alpine:3.22.0
RUN apk --no-cache add ca-certificates

WORKDIR /root/

COPY app    .

CMD ["./app"]

🧩 Une première solution

Avec un autre exemple

#!/bin/sh
echo Building alexellis2/href-counter:build

docker image build --build-arg https_proxy=$https_proxy --build-arg http_proxy=$http_proxy \
    -t alexellis2/href-counter:build . -f Dockerfile.build
docker container create --name extract alexellis2/href-counter:build
docker container cp extract:/go/src/github.com/alexellis/href-counter/app ./app
docker container rm -f extract

echo Building alexellis2/href-counter:latest
docker image build --no-cache -t alexellis2/href-counter:latest .
rm ./app

🧩 Une première solution

Avec un autre exemple

fichiers output81 with transparency

🧩 Une première solution

Avec un autre exemple

fichiers output82 with transparency

🧩 Une première solution

Avec un autre exemple

fichiers output83 with transparency

🧩 Une première solution

Avec un autre exemple

fichiers output83 with transparency
1956754704 image20

🪜 Multistage build

FROM golang:1.19

WORKDIR /go/src/github.com/alexellis/href-counter/

COPY go.mod .
COPY go.sum .
COPY app.go	.

RUN CGO_ENABLED=0 GOOS=linux go build  -ldflags "-s -w" -o app .

FROM alpine:3.17.1
RUN apk --no-cache add ca-certificates

WORKDIR /root/

COPY --from=0 /go/src/github.com/alexellis/href-counter/app    .

CMD ["./app"]

🪜 Multistage build

FROM golang:1.19

WORKDIR /go/src/github.com/alexellis/href-counter/

COPY go.mod .
COPY go.sum .
COPY app.go	.

RUN CGO_ENABLED=0 GOOS=linux go build  -ldflags "-s -w" -o app .

FROM alpine:3.17.1
RUN apk --no-cache add ca-certificates

WORKDIR /root/

COPY --from=0 /go/src/github.com/alexellis/href-counter/app    .

CMD ["./app"]

On copie les fichiers depuis le premier stage

📦 LES CONTAINERS

Le bilan : achievement unlocked

Vous savez désormais:

  • Maîtriser le cycle de vie des containers

  • Interagir avec les containers existants

  • Nommer les containers

  • Inspecter les containers

  • Appeler les containers

  • Obtenir des containers légers

1706694417 image21

Les volumes

1300236655 image2

🔴🔵🟢 3 types de gestion de données

3 types gestions donnees

🔴🔵🟢 3 types de gestion de données

mapping fs

Partage d’un répertoire de votre système hôte avec un conteneur Docker.

  • Utile pour partager des fichiers ou des données de configuration avec un conteneur.

  • Données stockées sur le système hôte, mais accessibles depuis le conteneur.

docker container run -v /chemin/du/systeme/hote:/chemin/dans/le/conteneur

🔴🔵🟢 3 types de gestion de données

volumes

Stockage persistant ⇒ données en dehors du système de fichiers du conteneur.

  • Utile pour stocker des données qui doivent survivre à l’arrêt ou à la suppression d’un conteneur.

  • Volumes indépendants du cycle de vie du conteneur.

docker container run -v nom_du_volume:/chemin/dans/le/conteneur

🔴🔵🟢 3 types de gestion de données

fs ram

Système de fichiers temporaire stocké en RAM.

  • Utile lorsque des données temporaires doivent être stockées en mémoire pour des performances élevées.

  • Les données n’existent que tant que le conteneur est en cours d’exécution.

docker container run --tmpfs /chemin/dans/le/conteneur

🔴🔵🟢 3 types de gestion de données

1329201553 image3

🗺️📂 Mapping de FileSystem

293990221 image4

🗺️📂 Mapping de FileSystem

"Créer une image avec les sources de mon application web juste pour distribuer des ressources statiques via Apache ! Merci Docker !"

Un étudiant excédé

1105931517 image5

🗺️📂 Mapping de FileSystem

🗺️📂 Mapping de FileSystem

Partage de dossier

partage dossier

Partage de fichier

partage fichier
Le chemin dans la machine hôte doit être un chemin absolu !
L’exemple ci-dessus ne peut donc pas fonctionner

📦📁 Les volumes

726805516 image8

📦📁 Les volumes

Il est possible de créer des "espaces mémoire" que l’on peut mettre à disposition des containers.

docker volume create --name my-data
docker run -v my-data:/data busybox ls /data
my_data n’est plus un chemin absolu, mais juste le nom du volume

📦📁 Les volumes

Lister tous les volumes

docker volume ls

Supprimer un volume

docker volume rm my-data

Inspecter un volume

docker volume inspect my-data

📦📁🕶️ Les volumes "anonymes"

FROM alpine:3.22
WORKDIR /repertoire/travail
VOLUME ["/data"]
RUN echo hello > ./world.txt

Création d’un volume anonyme mappé sur le répertoire /data des containers basés sur cette image

💾🧠 FileSystem en RAM

docker container run
  -d
  --read-only
  --tmpfs /run/httpd
  --tmpfs /tmp
  httpd
Les données sont perdues à l’arrêt du container.

💾🧠 FileSystem en RAM

volumes output21 with transparency
Les données sont perdues à l’arrêt du container.

💾🧠 FileSystem en RAM

volumes output22 with transparency
Les données sont perdues à l’arrêt du container.

📊📝 Bilan de compétences

  • Création d’images

  • Création des containers

    • cycle de vie

    • nommage

    • débug

    • réseau

    • volumes

📊📝 Bilan de compétences

  • Création d’images ✅

  • Création des containers ✅

    • cycle de vie ✅

    • nommage ✅

    • débug ✅

    • réseau ✅

    • volumes ✅

🎓 Travaux pratiques #8

Lancer un container `nginx `et lui faire distribuer une page web externe au container.

1763880002 image10

✅ Solution travaux pratiques #8

En utilisant un montage de répertoire (Bind Mount) :

Utilisez un montage de répertoire pour mapper un répertoire de l’hôte vers le conteneur Nginx.

Placez vos fichiers HTML dans un répertoire sur votre machine hôte, puis mappez ce répertoire vers le conteneur Nginx.

docker container run -d -p 80:80 --name mon-nginx -v /chemin/vers/la/page/web/sur/lhote:/usr/share/nginx/html nginx

Remplacez /chemin/vers/la/page/web/sur/lhote par le chemin réel vers le répertoire contenant vos fichiers de la page web sur l’hôte.

✅ Solution travaux pratiques #8

En utilisant un volume nommé (Named Volume) :

Créez un volume nommé et copiez vos fichiers de la page web dedans.

Montez ce volume nommé dans le conteneur Nginx.

Tout d’abord, créez un volume nommé :

docker volume create mon-nginx-html

Copiez vos fichiers de la page web dans ce volume :

docker container run --rm \
  --volume mon-nginx-html:/cible \   # Montez le volume nommé "mon-nginx-html" dans le conteneur sous le répertoire "/cible".
  --volume /chemin/vers/la/page/web/sur/lhote:/html \ # Montez le répertoire où se trouve le fichier html
  --workdir /cible \                # Définissez le répertoire de travail à "/cible" dans le conteneur.
  busybox cp -r /html .   # Copiez récursivement le contenu depuis "/chemin/vers/la/page/web/sur/lhote" dans le répertoire actuel du conteneur.

Maintenant, lancez le conteneur Nginx et montez le volume nommé :

docker container run -d -p 80:80 --name mon-nginx -v mon-nginx-html:/usr/share/nginx/html nginx

🎓 Travaux pratiques #9

Étapes:

  • Créer un volume nommé "data"

  • Démarrer un container attaché à ce volume

  • Lancer un processus qui écrit un fichier dans le volume

  • Démarrer un second container attaché à ce volume

  • Lire les données du volume depuis le second container

  • Supprimer le volume

✅ Solution travaux pratiques #9

Créer un volume nommé "data" :

docker volume create data

Démarrer un premier conteneur attaché à ce volume :

docker container run -it --name premier-container -v data:/donnees busybox

Dans le premier conteneur, lancez un processus qui écrit un fichier dans le volume (par exemple, un fichier texte) :

echo "Données du premier conteneur" > /donnees/mon-fichier.txt

Quittez le premier conteneur.

Démarrer un second conteneur attaché au même volume :

docker container run -it --name second-container -v data:/donnees busybox

✅ Solution travaux pratiques #9

Démarrer un second conteneur attaché au même volume :

docker container run -it --name second-container -v data:/donnees busybox

Dans le second conteneur, lisez les données du volume (le fichier texte) :

cat /donnees/mon-fichier.txt

Vous devriez voir le contenu du fichier affiché à l’écran.

Quittez le second conteneur.

Vous pouvez maintenant supprimer le volume "data" car vous n’en avez plus besoin :

docker volume rm data

🎓 Travaux pratiques #10

Écrire un Dockerfile qui:

  • Ajoute des fichiers dans un dossier

  • Déclare ce dossier comme volume anonyme

  • Ajoute d’autres fichiers dans ce dossier

Que verra-ton dans ce dossier au sein de nos containers ?

✅ Solution travaux pratiques #10

Tout d’abord, créez un répertoire pour votre projet et placez-vous dedans.

Créez un Dockerfile avec le contenu suivant :

FROM alpine:3.22

# Ajoutez des fichiers dans un dossier
RUN mkdir /mon-dossier
RUN echo "Contenu du fichier 1" > /mon-dossier/fichier1.txt
RUN echo "Contenu du fichier 2" > /mon-dossier/fichier2.txt

# Déclarez ce dossier comme un volume anonyme
VOLUME ["/mon-dossier"]

# Ajoutez d'autres fichiers dans ce dossier
RUN echo "Nouveau contenu du fichier 3" > /mon-dossier/fichier3.txt

Ensuite, vous pouvez créer une image Docker à partir de ce Dockerfile en exécutant la commande suivante dans le même répertoire que le Dockerfile :

docker image build -t mon-image .

✅ Solution travaux pratiques #10

docker image build -t mon-image .

Assurez-vous que "mon-image" est le nom que vous souhaitez donner à votre image Docker. Après avoir créé l’image, vous pouvez exécuter un conteneur basé sur cette image :

docker container run -it mon-image

Lorsque vous êtes dans le conteneur, accédez au dossier /mon-dossier :

cd /mon-dossier

Vous verrez les fichiers "fichier1.txt", "fichier2.txt" et "fichier3.txt" dans ce dossier.

ls
fichier1.txt  fichier2.txt  fichier3.txt
cat *
Contenu du fichier 1
Contenu du fichier 2
Nouveau contenu du fichier 3

✅ Solution travaux pratiques #10

cat *
Contenu du fichier 1
Contenu du fichier 2
Nouveau contenu du fichier 3

Vous devriez voir la liste des fichiers et leurs contenus.

Cela montre que les fichiers que vous avez ajoutés dans le dossier, même après avoir déclaré ce dossier comme un volume anonyme, restent accessibles dans le conteneur.

C’est ainsi que vous pouvez ajouter des fichiers dans un dossier, le déclarer comme un volume anonyme, puis ajouter d’autres fichiers dans ce dossier tout en y ayant accès à l’intérieur du conteneur.

Réseaux et Docker

Interagir avec le Docker ENGINE

2375

🤝 Interagir avec le Docker Engine

reseaux output2 with transparency

🤝 Interagir avec le Docker Engine

reseaux output3 with transparency

🤝 Interagir avec le Docker Engine

reseaux output4 with transparency

🤝 Interagir avec le Docker Engine

reseaux output5 with transparency

🤝 Interagir avec le Docker Engine

reseaux output6 with transparency

Ex : intégration IntelliJ

Les réseaux de containers

1435904594 image8

🗺️🔀 Mapping de Port : Rappel

docker container run –d -p 8000:80 nginx

🗺️🔀 Mapping de Port : Rappel

reseaux output11 with transparency

🗺️🔀 Mapping de Port : Rappel

reseaux output12 with transparency
curl -I --noproxy '*' http://172.17.0.2:80
It works!
curl -I --noproxy '*' http://localhost:8000
It works too!

🌐 Les réseaux

docker network ls
NETWORK ID     NAME                                                             DRIVER    SCOPE
11b7af7b16e4   bridge                                                           bridge    local
1112832d205b   cours-devops-docker_default                                      bridge    local
7ab15cc28199   docker_volumes-backup-extension-desktop-extension_default        bridge    local
34caf4674478   host                                                             host      local
2a179c7be3b3   none                                                             null      local
0c577a792771   portainer_portainer-docker-extension-desktop-extension_default   bridge    local
les réseaux

🌐 Les réseaux

docker network ls
NETWORK ID     NAME                                                             DRIVER    SCOPE
11b7af7b16e4   bridge                                                           bridge    local
1112832d205b   cours-devops-docker_default                                      bridge    local
7ab15cc28199   docker_volumes-backup-extension-desktop-extension_default        bridge    local
34caf4674478   host                                                             host      local
2a179c7be3b3   none                                                             null      local
0c577a792771   portainer_portainer-docker-extension-desktop-extension_default   bridge    local
les réseaux

🔍🌐 Les réseaux : inspection

docker network inspect bridge
[
    {
        "Name": "bridge",
        "Id": "11b7af7b16e4cf9fe42733aa1b6900ed876407e3b55e692c9dfe03505e2af19f",
        "Created": "2023-10-31T15:08:44.054140179Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16",
                    "Gateway": "172.17.0.1"
                }
            ]
        },
        ...

🌐✨ Les réseaux : création

docker network create mon-reseau
docker network ls
NETWORK ID     NAME                                                             DRIVER    SCOPE
11b7af7b16e4   bridge                                                           bridge    local
1112832d205b   cours-devops-docker_default                                      bridge    local
7ab15cc28199   docker_volumes-backup-extension-desktop-extension_default        bridge    local
34caf4674478   host                                                             host      local
46953dc9b3d5   mon-reseau                                                       bridge    local
2a179c7be3b3   none                                                             null      local
0c577a792771   portainer_portainer-docker-extension-desktop-extension_default   bridge    local

🐳🔗 Attacher un container à un réseau particulier

docker run –d --net=mon-reseau --name=app img

Il est opportun de nommer un container quand on l’attache à un réseau.

Docker fournit un DNS interne dans les réseaux custom. Les containers d’un même réseau peuvent se "voir" et "s’appeler" par leur noms.

🖥️🔍 MicroDNS

docker network create mynet
docker container run -d --name web --net mynet nginx
docker container run --net mynet alpine ping web
PING web (172.21.0.2): 56 data bytes
64 bytes from 172.21.0.2: seq=0 ttl=64 time=0.442 ms
64 bytes from 172.21.0.2: seq=1 ttl=64 time=0.105 ms
64 bytes from 172.21.0.2: seq=2 ttl=64 time=0.099 ms
64 bytes from 172.21.0.2: seq=3 ttl=64 time=0.150 ms
64 bytes from 172.21.0.2: seq=4 ttl=64 time=0.114 ms
64 bytes from 172.21.0.2: seq=5 ttl=64 time=0.098 ms
64 bytes from 172.21.0.2: seq=6 ttl=64 time=0.099 ms
64 bytes from 172.21.0.2: seq=7 ttl=64 time=0.100 ms
64 bytes from 172.21.0.2: seq=8 ttl=64 time=0.114 ms
64 bytes from 172.21.0.2: seq=9 ttl=64 time=0.097 ms
^C
--- web ping statistics ---
10 packets transmitted, 10 packets received, 0% packet loss
round-trip min/avg/max = 0.097/0.141/0.442 ms

🖥️🔍 MicroDNS

appeler son voisin

📶🔌 Se connecter à un réseau

docker network connect mynet myapp

Cette commande permet d’attacher un container à un réseau après sa création.

🎓 Travaux pratiques #11

Étapes:

  • Lister et inspecter les réseaux existants

  • Lancer un container `nginx `nommé "web1"

  • A quel réseau est-il attaché ?

  • Créer un réseau de type bridge et l’inspecter

  • Lancer un container nginx nommé "web2" et l’attacher au réseau créé

  • L’inspection confirme-t-elle l’attachement ?

  • Lancer un bash dans le container "web1"

  • Est-ce que "web1" peut voir "web2" ?

  • Corriger pour que ce soit le cas

✅ Solution travaux pratiques #11

Étape 1 : Lister et inspecter les réseaux existants

# Liste tous les réseaux Docker existants
docker network ls

# Inspecte un réseau spécifique (par exemple, le réseau bridge, d'autres réseaux peuvent être inspectés)
docker network inspect bridge

Étape 2 : Lancer un container "nginx" nommé "web1"

# Lance un conteneur "nginx" nommé "web1"
docker container run --name web1 -d nginx

Étape 3 : Vérifier le réseau auquel "web1" est attaché

# Récupère l'identifiant de réseau complet pour le container "web1"
network_id=$(docker container inspect -f '{{.NetworkSettings.Networks.bridge.NetworkID}}' web1)

# Raccourcit l'identifiant du réseau aux premiers 12 caractères
shortened_network_id=$(echo $network_id | cut -c 1-12)

# Liste tous les réseaux docker, en filtrant par l'identifiant du réseau
docker network ls --format "table {{.ID}}\t{{.Name}}" | awk -v network_id="$shortened_network_id" '$1 == network_id {print $2}'

✅ Solution travaux pratiques #11

Étape 3 : Vérifier le réseau auquel "web1" est attaché

# Récupère l'identifiant de réseau complet pour le container "web1"
network_id=$(docker container inspect -f '{{.NetworkSettings.Networks.bridge.NetworkID}}' web1)

# Raccourcit l'identifiant du réseau aux premiers 12 caractères
shortened_network_id=$(echo $network_id | cut -c 1-12)

# Liste tous les réseaux docker, en filtrant par l'identifiant du réseau
docker network ls --format "table {{.ID}}\t{{.Name}}" | awk -v network_id="$shortened_network_id" '$1 == network_id {print $2}'
bridge

Étape 4 : Créer un réseau de type bridge et l’inspecter

# Crée un réseau Docker de type bridge nommé "mon-reseau"
docker network create mon-reseau

# Inspecte le réseau "mon-reseau"
docker network inspect mon-reseau

Étape 5 : Lancer un container "nginx" nommé "web2" et l’attacher au réseau créé

# Lance un conteneur "nginx" nommé "web2" et l'attache au réseau "mon-reseau"
docker container run --name web2 -d --network mon-reseau nginx

✅ Solution travaux pratiques #11

Étape 5 : Lancer un container "nginx" nommé "web2" et l’attacher au réseau créé

# Lance un conteneur "nginx" nommé "web2" et l'attache au réseau "mon-reseau"
docker container run --name web2 -d --network mon-reseau nginx

Étape 6 : Vérifier l’attachement du container "web2" au réseau

# Inspecte le conteneur "web2" pour vérifier le réseau auquel il est attaché
docker container inspect web2 | grep NetworkMode

Étape 7 : Lancer un bash dans le container "web1"

# Lance un shell interactif dans le conteneur "web1"
docker container exec -it web1 bash

Étape 8 : Vérifier si "web1" peut voir "web2"

# À l'intérieur du conteneur "web1," essayez de faire une requête HTTP vers "web2"
curl web2
curl: (6) Could not resolve host: web2

✅ Solution travaux pratiques #11

Étape 9 : Corriger pour permettre la communication entre "web1" et "web2"

# Sortez du shell du conteneur "web1" en tapant "exit"

# Attachez "web1" au même réseau "mon-reseau"
docker network connect mon-reseau web1

Après avoir suivi ces étapes, les conteneurs "web1" et "web2" devraient être attachés au même réseau "mon-reseau" et être capables de communiquer entre eux.

# Lance un shell interactif dans le conteneur "web1"
docker container exec -it web1 bash
# À l'intérieur du conteneur "web1," essayez de faire une requête HTTP vers "web2"
curl web2
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
[...]

🐋🐙 Docker compose

2073552164 image2

🌐🏡 Un cas de la vraie vie

Une application riche repose souvent sur plusieurs éléments techniques à coordonner ensemble.

(Apache, Tomcat, Node, MongoDB, ElasticSearch, Logstash, etc.)

Comment automatiser ces déploiements ?

📜💻 On pourrait tout scripter…

#!/bin/sh
docker network create my-net
docker container run –d --net=my-net –p 3306:3306 mysql
docker container run –d --net=my-net –v /docs:/docs --name col1 httpd:2.4.58-bookworm
docker container run –d --net=my-net –v /docs:/docs --name col2 httpd:2.4.58-bookworm

📜💻 Tout scripter

351794078 image3
  • Et tout lancer indépendamment ?

  • Et tout monitorer un par un ?

  • Peut mieux faire, non ?

🐋🐙 docker compose.yml

services:

🐋🐙 docker compose.yml

services:
  apache:







  middle:



  db:

🐋🐙 docker compose.yml

services:
  apache:
    image: "my-httpd:2.4"






  middle:



  db:

🐋🐙 docker compose.yml

services:
  apache:
    image: "my-httpd:2.4"
    ports:
      - "80:80"




  middle:



  db:

🐋🐙 docker compose.yml

services:
  apache:
    image: "my-httpd:2.4"
    ports:
      - "80:80"
    environment:
      - MIDDLE_HOST_1=middle


  middle:



  db:

🐋🐙 docker compose.yml

services:
  apache:
    image: "my-httpd:2.4"
    ports:
      - "80:80"
    environment:
      - MIDDLE_HOST_1=middle
    volumes:
      - ./etc/httpd/workers.properties:/etc/httpd/conf/workers.properties
  middle:



  db:

🐋🐙 docker compose.yml

services:
  apache:
    image: "my-httpd:2.4"
    ports:
      - "80:80"
    environment:
      - MIDDLE_HOST_1=middle
    volumes:
      - ./etc/httpd/workers.properties:/etc/httpd/conf/workers.properties
  middle:
    build: .


  db:

🐋🐙 docker compose.yml

services:
  apache:
    image: "my-httpd:2.4"
    ports:
      - "80:80"
    environment:
      - MIDDLE_HOST_1=middle
    volumes:
      - ./etc/httpd/workers.properties:/etc/httpd/conf/workers.properties
  middle:
    build: .
    environment:
      - DB_HOST=db
  db:

🐋🐙 docker compose.yml

services:
  apache:
    image: "my-httpd:2.4"
    ports:
      - "80:80"
    environment:
      - MIDDLE_HOST_1=middle
    volumes:
      - ./etc/httpd/workers.properties:/etc/httpd/conf/workers.properties
  middle:
    build: .
    environment:
      - DB_HOST=db
  db:
    image: "mysql:5.6"

🐋🐙 docker compose.yml

services:
  apache:
    image: "my-httpd:2.4"
    ports:
      - "80:80"
    environment:
      - MIDDLE_HOST_1=middle
    volumes:
      - ./etc/httpd/workers.properties:/etc/httpd/conf/workers.properties
  middle:
    build: .
    environment:
      - DB_HOST=db
  db:
    image: "mysql:5.6"
    ports:
      - "3306:3306"

🤔➡️ Et après ?

docker compose up -d
docker compose build
docker compose logs
docker compose stop
docker compose restart
docker compose down

🏁🔢 Ordre de démarrage

L’ordre de démarrage fait référence à la séquence dans laquelle les services définis dans un fichier Docker Compose sont lancés.

🏁🔢 Ordre de démarrage

Pourquoi est-ce important ?

Certains services peuvent dépendre d’autres services pour fonctionner correctement.

Par exemple, une application web peut avoir besoin qu’une base de données soit opérationnelle avant de pouvoir démarrer.

🏁🔢 Ordre de démarrage

Comment Docker Compose gère-t-il l’ordre de démarrage ?

Par défaut, Docker Compose démarre les services dans l’ordre dans lequel ils sont définis dans le fichier Docker Compose.

Cependant, cela ne garantit pas que les services dépendants seront prêts à être utilisés lorsque les services qui en dépendent seront lancés.

🏁🔢 Ordre de démarrage

Comment gérer les dépendances entre services ?

Docker Compose offre deux directives pour gérer les dépendances entre services : depends_on et healthcheck.

  • depends_on : Cette directive peut être utilisée pour indiquer qu’un service dépend d’un autre service. Cependant, cela ne garantit pas que le service dépendant sera prêt à être utilisé lorsque le service qui en dépend sera lancé.

  • healthcheck : Cette directive peut être utilisée pour vérifier l’état de santé d’un service. En combinaison avec depends_on, elle peut aider à s’assurer qu’un service est prêt à être utilisé avant de lancer les services qui en dépendent.

🏁🔢 Ordre de démarrage

Exemple Voici un exemple de fichier Docker Compose qui utilise depends_on et healthcheck pour gérer l’ordre de démarrage des services :

version: '3'
services:
  web:
    build: .
    depends_on:
      db:
        condition: service_healthy
  db:
    image: postgres
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 30s
      timeout: 30s
      retries: 3

Dans cet exemple, le service web dépend du service db. Le service db est vérifié toutes les 30 secondes pour s’assurer qu’il est prêt à être utilisé. Le service web ne sera lancé que lorsque le service db sera en bonne santé.

🏁🔢 Ordre de démarrage❓🤔 Conditions.

Rappel: La directive depends_on dans un fichier Docker Compose est utilisée pour indiquer qu’un service dépend d’un autre service. Cela signifie que le service dépendant ne sera pas démarré tant que les services dont il dépend n’auront pas été démarrés.

🏁🔢 Ordre de démarrage❓🤔 Autres conditions.

En plus de la condition service_healthy, il existe d’autres conditions que vous pouvez utiliser avec depends_on:

service_started : Cette condition signifie que le service dépendant ne sera pas démarré tant que le service dont il dépend n’aura pas été démarré.

service_completed_successfully: Cette condition indique qu’un service dépendant ne doit pas démarrer avant qu’un autre service ait terminé avec succès. Cela peut être utile dans des scénarios où un service doit effectuer une tâche unique qui doit être terminée avant que d’autres services puissent débuter.

Cependant, cela ne garantit pas que le service dont il dépend est prêt à être utilisé.

🏁🔢 Ordre de démarrage❓🤔 Autre condition.

Voici un exemple de comment vous pourriez utiliser service_started dans un fichier Docker Compose :

version: '3'
services:
  web:
    build: .
    depends_on:
      db:
        condition: service_started
  db:
    image: postgres

Dans cet exemple, le service web ne sera démarré que lorsque le service db aura été démarré.

🏁🔢 Ordre de démarrage & 🩺✅ healthcheck

Rappel healthcheck est une instruction dans un Dockerfile qui permet de vérifier l’état de santé d’un service. Il peut être utilisé pour déterminer si un service est prêt à être utilisé ou non.

Voici un exemple de healthcheck dans un Dockerfile :

FROM postgres
HEALTHCHECK --interval=5m --timeout=3s \
  CMD pg_isready -U postgres || exit 1

Dans cet exemple, pg_isready -U postgres est la commande utilisée pour vérifier l’état de santé du service. Si cette commande réussit, le service est considéré comme sain. Sinon, il est considéré comme malsain.

🏁🔢 Ordre de démarrage & 🩺✅ healthcheck

Rappel depends_on est une directive dans un fichier Docker Compose qui indique qu’un service dépend d’un autre service. Il peut être utilisé pour contrôler l’ordre de démarrage des services.

Voici un exemple de depends_on dans un fichier Docker Compose :

version: '3'
services:
  web:
    build: .
    depends_on:
      db:
        condition: service_healthy
  db:
    image: postgres

Dans cet exemple, le service web dépend du service db. Le service web ne sera démarré que lorsque le service db sera en bonne santé.

🏁🔢 🩺✅ Comment healthcheck et depends_on travaillent ensemble ?

En combinant healthcheck et depends_on, vous pouvez contrôler l’ordre de démarrage des services en fonction de leur état de santé. Par exemple, vous pouvez vous assurer qu’un service de base de données est prêt à être utilisé avant de démarrer une application web qui en dépend.

🏁🔢 Ordre de démarrage 📊📝 Pour résumer

L’ordre de démarrage fait référence à la séquence dans laquelle les services définis dans un fichier Docker Compose sont lancés. Par défaut, Docker Compose démarre les services dans l’ordre dans lequel ils sont définis dans le fichier Docker Compose.

Docker Compose offre deux directives pour gérer les dépendances entre services : depends_on et healthcheck.

🏁🔢 Ordre de démarrage 📊📝 Pour résumer

  • depends_on : Cette directive peut être utilisée pour indiquer qu’un service dépend d’un autre service. Elle peut être utilisée avec deux conditions : service_started et service_healthy.

  • service_started : Cette condition signifie que le service dépendant ne sera pas démarré tant que le service dont il dépend n’aura pas été démarré. Cependant, cela ne garantit pas que le service dont il dépend est prêt à être utilisé.

  • service_healthy : Cette condition est utilisée avec la directive healthcheck dans un Dockerfile pour vérifier l’état de santé d’un service. Si la commande healthcheck réussit, Docker considère le service comme sain. Sinon, il est considéré comme malsain.

🏁🔢 Ordre de démarrage 📊📝 Pour résumer

Exemple Voici un exemple de fichier Docker Compose qui utilise depends_on et healthcheck pour gérer l’ordre de démarrage des services :

version: '3'
services:
  web:
    build: .
    depends_on:
      db:
        condition: service_healthy
  db:
    image: postgres
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 30s
      timeout: 30s
      retries: 3

🏁🔢 Étude de cas 📚🔍

Un exemple de fichier docker-compose.yml utilisé dans Jenkins:

sidekick_service:
    # Configuration for the sidekick service
    image: ${DOCKERHUB_USERNAME}/jenkinsci-tutorials:sidekick_
    stdin_open: true
    tty: true
    entrypoint: sh -c "/usr/local/bin/keygen.sh /ssh-dir"  # Runs the keygen.sh script and specifies the output directory
    volumes:
      - agent-ssh-dir:/ssh-dir  # Mounts the agent-ssh-dir volume to the /ssh-dir path inside the container
    healthcheck:
      test: [ "CMD-SHELL", "[ -f /ssh-dir/conductor_ok ] || exit 1" ]  # Checks if the conductor_ok file exists in the /ssh-dir path
      interval: 5s
      timeout: 10s
      retries: 5

Les options stdin_open: true et tty: true sont utilisées pour garder l’entrée standard du conteneur ouverte et pour allouer un pseudo-TTY au conteneur, respectivement. C’est généralement fait lorsque vous voulez interagir avec le service en cours d’exécution.

🏁🔢 Étude de cas 📚🔍

sidekick_service:
    # Configuration for the sidekick service
    image: ${DOCKERHUB_USERNAME}/jenkinsci-tutorials:sidekick_
    stdin_open: true
    tty: true
    entrypoint: sh -c "/usr/local/bin/keygen.sh /ssh-dir"  # Runs the keygen.sh script and specifies the output directory
    volumes:
      - agent-ssh-dir:/ssh-dir  # Mounts the agent-ssh-dir volume to the /ssh-dir path inside the container
    healthcheck:
      test: [ "CMD-SHELL", "[ -f /ssh-dir/conductor_ok ] || exit 1" ]  # Checks if the conductor_ok file exists in the /ssh-dir path
      interval: 5s
      timeout: 10s
      retries: 5
  • La directive entrypoint est utilisée pour spécifier la commande qui sera exécutée lorsque le conteneur démarre.

  • Dans ce cas, elle exécute une commande shell qui exécute un script nommé keygen.sh situé à /usr/local/bin/keygen.sh.

  • Le script reçoit un argument /ssh-dir, qui est le répertoire où le script effectuera ses opérations.

🏁🔢 Étude de cas 📚🔍

sidekick_service:
    # Configuration for the sidekick service
    image: ${DOCKERHUB_USERNAME}/jenkinsci-tutorials:sidekick_
    stdin_open: true
    tty: true
    entrypoint: sh -c "/usr/local/bin/keygen.sh /ssh-dir"  # Runs the keygen.sh script and specifies the output directory
    volumes:
      - agent-ssh-dir:/ssh-dir  # Mounts the agent-ssh-dir volume to the /ssh-dir path inside the container
    healthcheck:
      test: [ "CMD-SHELL", "[ -f /ssh-dir/conductor_ok ] || exit 1" ]  # Checks if the conductor_ok file exists in the /ssh-dir path
      interval: 5s
      timeout: 10s
      retries: 5
  • La directive volumes est utilisée pour monter un volume nommé agent-ssh-dir sur le chemin /ssh-dir à l’intérieur du conteneur.

  • Cela permet de conserver les données entre les redémarrages du conteneur et peut également être utilisé pour partager des données entre les conteneurs.

🏁🔢 Étude de cas 📚🔍

sidekick_service:
    # Configuration for the sidekick service
    image: ${DOCKERHUB_USERNAME}/jenkinsci-tutorials:sidekick_
    stdin_open: true
    tty: true
    entrypoint: sh -c "/usr/local/bin/keygen.sh /ssh-dir"  # Runs the keygen.sh script and specifies the output directory
    volumes:
      - agent-ssh-dir:/ssh-dir  # Mounts the agent-ssh-dir volume to the /ssh-dir path inside the container
    healthcheck:
      test: [ "CMD-SHELL", "[ -f /ssh-dir/conductor_ok ] || exit 1" ]  # Checks if the conductor_ok file exists in the /ssh-dir path
      interval: 5s
      timeout: 10s
      retries: 5
  • Enfin, un healthcheck est défini pour le service. Il s’agit d’une commande que Docker exécutera à l’intérieur du conteneur pour vérifier sa santé.

  • Dans ce cas, la commande vérifie si un fichier nommé conductor_ok existe dans le répertoire /ssh-dir.

  • Si le fichier existe, la commande réussit et Docker considère le service comme sain.

  • Si le fichier n’existe pas, la commande échoue et Docker considère le service comme malsain.

  • Docker exécutera cette vérification de santé toutes les 5 secondes (interval: 5s), et si elle ne répond pas dans les 10 secondes (timeout: 10s), Docker la considérera comme un échec. Docker réessaiera une vérification de santé échouée 5 fois (retries: 5) avant de considérer le service comme malsain.

🏁🔢 Étude de cas 📚🔍

services:
  sidekick_service: [...]

  jenkins_controller: [...]
    depends_on:
      sidekick_service:
        condition: service_completed_successfully  # Depends on the successful completion of the sidekick_service
    healthcheck:
      test: [ "CMD-SHELL", "[ -f /ssh-dir/conductor_ok ] || exit 1" ]  # Checks if the conductor_ok file exists in the /ssh-dir path
      interval: 5s
      timeout: 10s
      retries: 5

  default_agent: [...]
    depends_on:
      sidekick_service:
        condition: service_completed_successfully  # Depends on the successful completion of the sidekick_service
      jenkins_controller:
        condition: service_started
    healthcheck:
      test: [ "CMD-SHELL", "[ -f /home/jenkins/.ssh/authorized_keys ] || exit 1" ]  # Checks if the authorized_keys file exists in the /home/jenkins/.ssh path
      interval: 5s
      timeout: 10s
      retries: 5
    volumes:
      - agent-ssh-dir:/home/jenkins/.ssh:ro  # Mounts the agent-ssh-dir volume to the /home/jenkins/.ssh path inside the container as read-only
  • Service jenkins_controller

    • Ce service dépend du sidekick_service.

    • Il ne démarrera que lorsque le sidekick_service aura terminé avec succès.

    • C’est ce que signifie la condition service_completed_successfully.

🏁🔢 Étude de cas 📚🔍

services:
  sidekick_service: [...]

  jenkins_controller: [...]
    depends_on:
      sidekick_service:
        condition: service_completed_successfully  # Depends on the successful completion of the sidekick_service
    healthcheck:
      test: [ "CMD-SHELL", "[ -f /ssh-dir/conductor_ok ] || exit 1" ]  # Checks if the conductor_ok file exists in the /ssh-dir path
      interval: 5s
      timeout: 10s
      retries: 5

  default_agent: [...]
    depends_on:
      sidekick_service:
        condition: service_completed_successfully  # Depends on the successful completion of the sidekick_service
      jenkins_controller:
        condition: service_started
    healthcheck:
      test: [ "CMD-SHELL", "[ -f /home/jenkins/.ssh/authorized_keys ] || exit 1" ]  # Checks if the authorized_keys file exists in the /home/jenkins/.ssh path
      interval: 5s
      timeout: 10s
      retries: 5
    volumes:
      - agent-ssh-dir:/home/jenkins/.ssh:ro  # Mounts the agent-ssh-dir volume to the /home/jenkins/.ssh path inside the container as read-only

Un healthcheck est également défini pour ce service.

  • Docker vérifie si un fichier nommé conductor_ok existe dans le chemin /ssh-dir.

  • Si le fichier existe, Docker considère le service comme sain.

  • Sinon, il est considéré comme malsain.

🏁🔢 Étude de cas 📚🔍

services:
  sidekick_service: [...]

  jenkins_controller: [...]
    depends_on:
      sidekick_service:
        condition: service_completed_successfully  # Depends on the successful completion of the sidekick_service
    healthcheck:
      test: [ "CMD-SHELL", "[ -f /ssh-dir/conductor_ok ] || exit 1" ]  # Checks if the conductor_ok file exists in the /ssh-dir path
      interval: 5s
      timeout: 10s
      retries: 5

  default_agent: [...]
    depends_on:
      sidekick_service:
        condition: service_completed_successfully  # Depends on the successful completion of the sidekick_service
      jenkins_controller:
        condition: service_started
    healthcheck:
      test: [ "CMD-SHELL", "[ -f /home/jenkins/.ssh/authorized_keys ] || exit 1" ]  # Checks if the authorized_keys file exists in the /home/jenkins/.ssh path
      interval: 5s
      timeout: 10s
      retries: 5
    volumes:
      - agent-ssh-dir:/home/jenkins/.ssh:ro  # Mounts the agent-ssh-dir volume to the /home/jenkins/.ssh path inside the container as read-only

Service default_agent

  • Ce service dépend également du sidekick_service et du jenkins_controller.

  • Il ne démarrera que lorsque le sidekick_service aura terminé avec succès et que le jenkins_controller aura démarré.

  • C’est ce que signifient les conditions service_completed_successfully et service_started.

🏁🔢 Étude de cas 📚🔍

services:
  sidekick_service: [...]

  jenkins_controller: [...]
    depends_on:
      sidekick_service:
        condition: service_completed_successfully  # Depends on the successful completion of the sidekick_service
    healthcheck:
      test: [ "CMD-SHELL", "[ -f /ssh-dir/conductor_ok ] || exit 1" ]  # Checks if the conductor_ok file exists in the /ssh-dir path
      interval: 5s
      timeout: 10s
      retries: 5

  default_agent: [...]
    depends_on:
      sidekick_service:
        condition: service_completed_successfully  # Depends on the successful completion of the sidekick_service
      jenkins_controller:
        condition: service_started
    healthcheck:
      test: [ "CMD-SHELL", "[ -f /home/jenkins/.ssh/authorized_keys ] || exit 1" ]  # Checks if the authorized_keys file exists in the /home/jenkins/.ssh path
      interval: 5s
      timeout: 10s
      retries: 5
    volumes:
      - agent-ssh-dir:/home/jenkins/.ssh:ro  # Mounts the agent-ssh-dir volume to the /home/jenkins/.ssh path inside the container as read-only

Un healthcheck est également défini pour ce service.

  • Docker vérifie si un fichier nommé authorized_keys existe dans le chemin /home/jenkins/.ssh.

  • Si le fichier existe, Docker considère le service comme sain.

  • Sinon, il est considéré comme malsain.

🏁🔢 Étude de cas 📚🔍

services:
  sidekick_service: [...]

  jenkins_controller: [...]
    depends_on:
      sidekick_service:
        condition: service_completed_successfully  # Depends on the successful completion of the sidekick_service
    healthcheck:
      test: [ "CMD-SHELL", "[ -f /ssh-dir/conductor_ok ] || exit 1" ]  # Checks if the conductor_ok file exists in the /ssh-dir path
      interval: 5s
      timeout: 10s
      retries: 5

  default_agent: [...]
    depends_on:
      sidekick_service:
        condition: service_completed_successfully  # Depends on the successful completion of the sidekick_service
      jenkins_controller:
        condition: service_started
    healthcheck:
      test: [ "CMD-SHELL", "[ -f /home/jenkins/.ssh/authorized_keys ] || exit 1" ]  # Checks if the authorized_keys file exists in the /home/jenkins/.ssh path
      interval: 5s
      timeout: 10s
      retries: 5
    volumes:
      - agent-ssh-dir:/home/jenkins/.ssh:ro  # Mounts the agent-ssh-dir volume to the /home/jenkins/.ssh path inside the container as read-only

Enfin, un volume nommé agent-ssh-dir est monté sur le chemin /home/jenkins/.ssh à l’intérieur du conteneur en lecture seule.

🏁🔢 Étude de cas 📚🔍

À étudier au calme chez vous:

47642838 image4

Transformer son application en docker compose

Deux approches différentes et complémentaires :

🎓 Travaux pratiques #12

🎓 Travaux pratiques #12-BIS 🔨🔥

Ça vous dirait d’avoir votre propre forge Gitlab ?

😏 Non, et bien c’est parti quand même.

  • Créez un nouveau fichier appelé docker-compose.yml.

  • Dans ce fichier, définissez trois services : gitlab, gitlab-runner-1 et gitlab-runner-2.

  • Pour le service gitlab:

    • utilisez l’image gitlab/gitlab-ce:latest (préfixée par le cache spécifique ILI)

    • redémarrez toujours le conteneur s’il s’arrête,

    • définissez le nom d’hôte sur localhost,

    • définissez l’URL externe sur http://localhost et exposez les ports 80, 443 et 22.

🎓 Travaux pratiques #12-BIS

  • Pour les services gitlab-runner-1 et gitlab-runner-2:

    • utilisez l’image gitlab/gitlab-runner:latest (préfixée par le cache spécifique ILI),

    • redémarrez toujours le conteneur s’il s’arrête,

    • dépendez du service gitlab et montez le socket Docker et le fichier de configuration de GitLab Runner.

  • Créez des volumes pour:

    • les fichiers de configuration,

    • les fichiers journaux

    • et les fichiers de données de GitLab,

    • ainsi que pour les fichiers de configuration de GitLab Runner.

🎓 Travaux pratiques #12-BIS 🔨🔥

Pas assez détaillé? 😨

  • Ouvrez votre éditeur de texte préféré et créez un nouveau fichier.

  • Nommez ce fichier docker-compose.yml.

  • Commencez le fichier avec la ligne services: pour commencer à définir les services de votre application.

  • Commencez à définir votre premier service en tapant gitlab: sur une nouvelle ligne. Ce sera le service pour votre serveur GitLab.

  • Sous gitlab:, ajoutez les détails de votre service.

    • Par exemple, image: 'gitlab/gitlab-ce:latest' pour spécifier l’image Docker à utiliser pour ce service.

🎓 Travaux pratiques #12-BIS 🔨🔥

  • Sous gitlab:, ajoutez les détails de votre service.

    • Par exemple, image: 'gitlab/gitlab-ce:latest' pour spécifier l’image Docker à utiliser pour ce service.

    • Continuez à ajouter des détails pour le service gitlab, comme restart: always pour toujours redémarrer le conteneur s’il s’arrête, et hostname: 'localhost' pour définir le nom d’hôte du serveur GitLab.

  • Définissez l’URL externe de votre serveur GitLab en ajoutant environment: et GITLAB_OMNIBUS_CONFIG: | sur de nouvelles lignes,

  • puis external_url 'http://localhost' sur la ligne suivante.

  • Exposez les ports nécessaires en ajoutant ports: sur une nouvelle ligne,

  • puis - '80:80', - '443:443' et - '22:22' sur les lignes suivantes.

🎓 Travaux pratiques #12-BIS 🔨🔥

  • Montez les volumes nécessaires en ajoutant volumes: sur une nouvelle ligne,

    • puis - 'gitlab_config:/etc/gitlab',

    • - 'gitlab_logs:/var/log/gitlab'

    • et - 'gitlab_data:/var/opt/gitlab' sur les lignes suivantes.

  • Répétez ces étapes pour les services gitlab-runner-1 et gitlab-runner-2, en remplaçant gitlab par gitlab-runner-1 et gitlab-runner-2 respectivement.

🎓 Travaux pratiques #12-BIS 🔨🔥

  • Pour les services gitlab-runner-1 et gitlab-runner-2, remplacez l’URL externe par une dépendance au service gitlab en ajoutant depends_on: sur une nouvelle ligne, puis - gitlab sur la ligne suivante.

  • Montez le socket Docker et le fichier de configuration de GitLab Runner

    • en ajoutant - '/var/run/docker.sock:/var/run/docker.sock'

    • et - 'gitlab-runner-1-config:/etc/gitlab-runner' (ou - 'gitlab-runner-2-config:/etc/gitlab-runner' pour gitlab-runner-2) sous volumes:.

🎓 Travaux pratiques #12-BIS 🔨🔥

Enfin, définissez les volumes pour votre application en ajoutant volumes: sur une nouvelle ligne à la fin de votre fichier, puis

  • - gitlab_config:,

  • - gitlab_logs:,

  • - gitlab_data:,

  • - gitlab-runner-1-config:

  • et - gitlab-runner-2-config: sur les lignes suivantes.

✅ Solution Travaux pratiques #12-BIS 🔨🔥

# This is a Docker Compose file for setting up a GitLab server and two GitLab runners.

services:
  # The GitLab server service
  gitlab:
    # The Docker image to use for the GitLab server
    image: 'gitlab/gitlab-ce:16.6.0-ce.0'
    # Always restart the container if it stops
    restart: always
    # The hostname for the GitLab server
    hostname: 'localhost'
    # Environment variables for the GitLab server
    environment:
      # Configuration for the GitLab Omnibus package
      GITLAB_OMNIBUS_CONFIG: |
        # The external URL for the GitLab server
        external_url 'http://localhost'
    # The ports to expose from the GitLab server
    ports:
      - '80:80'   # HTTP
      - '443:443' # HTTPS
      - '22:22'   # SSH
    # The volumes to mount for the GitLab server
    volumes:
      - 'gitlab_config:/etc/gitlab'       # Configuration files
      - 'gitlab_logs:/var/log/gitlab'     # Log files
      - 'gitlab_data:/var/opt/gitlab'     # Data files

  # The first GitLab runner service
  gitlab-runner-1:
    # The Docker image to use for the GitLab runner
    image: 'gitlab/gitlab-runner:alpine3.16-v16.6.0'
    # Always restart the container if it stops
    restart: always
    # The services this service depends on
    depends_on:
      - gitlab # Depends on the GitLab server
    # The volumes to mount for the GitLab runner
    volumes:
      - '/var/run/docker.sock:/var/run/docker.sock' # Docker socket for running Docker commands
      - 'gitlab-runner-1-config:/etc/gitlab-runner' # Configuration files

  # The second GitLab runner service
  gitlab-runner-2:
    # The Docker image to use for the GitLab runner
    image: 'gitlab/gitlab-runner:alpine3.16-v16.6.0'
    # Always restart the container if it stops
    restart: always
    # The services this service depends on
    depends_on:
      - gitlab # Depends on the GitLab server
    # The volumes to mount for the GitLab runner
    volumes:
      - '/var/run/docker.sock:/var/run/docker.sock' # Docker socket for running Docker commands
      - 'gitlab-runner-2-config:/etc/gitlab-runner' # Configuration files

# The volumes to create for the services
volumes:
  gitlab_config:         # Volume for GitLab server configuration files
  gitlab_logs:           # Volume for GitLab server log files
  gitlab_data:           # Volume for GitLab server data files
  gitlab-runner-1-config: # Volume for GitLab runner 1 configuration files
    name: gitlab-runner-1-config
  gitlab-runner-2-config: # Volume for GitLab runner 2 configuration files
    name: gitlab-runner-2-config

🔨🔥 J’ai ma forge! Bon, et maintenant? 😐

when

🔨🔥 J’ai ma forge! Bon, et maintenant? 😐

Il va falloir se logguer, lier les runners au serveur, créer un projet, etc…​

🔨🔥 J’ai ma forge! Bon, et maintenant? 😐

gitlab login

Pour trouver le mot de passe, il va falloir le demander gentiment à Gitlab:

docker compose -f tp12-bis.yml exec -it gitlab grep 'Password:' /etc/gitlab/initial_root_password
Password: qaPxXU+RqioolV3bAljvs2VYnyYa4jO/UYcis/UXLAk=

Ensuite, il va falloir restreindre l’accès:

sign up restrictions

🔨🔥 J’ai ma forge! Bon, et maintenant? 😐

Pour utiliser le runner GitLab dans GitLab, vous devez le configurer.

  • Pour une configuration correcte, nous aurons besoin d’un jeton copié depuis le portail.

  • Pire que ça, pas un jeton, mais carrément une commande à adapter à docker compose.

  • Pour ce faire, allez à l’adresse : http://localhost/admin/runners et cliquez sur le bouton "New instance runner".

  • Choisissez "Linux".

  • "Run untagged jobs"

  • Donnez une description au runner.

  • Cliquez sur le bouton "Create runner"

  • Vous avez ensuite une commande à copier et modifier pour docker compose.

🔨🔥 J’ai ma forge! Bon, et maintenant? 😐

docker compose -f tp12-bis.yml exec gitlab-runner-1 gitlab-runner register --url http://gitlab  --token glrt-PF5rLbUKzku5g8BL2y7J
Runtime platform                                    arch=amd64 os=linux pid=103 revision=853330f9 version=16.5.0
Running in system-mode.

Enter the GitLab instance URL (for example, https://gitlab.com/):
[http://gitlab]:
Verifying runner... is valid                        runner=PF5rLbUKz
Enter a name for the runner. This is stored only in the local config.toml file:
[3c2ee0d97d2b]: runner 1
Enter an executor: parallels, docker-autoscaler, docker+machine, custom, docker, docker-windows, shell, ssh, virtualbox, instance, kubernetes:
shell
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!

Configuration (with the authentication token) was saved in "/etc/gitlab-runner/config.toml"

👤 Profils dans Docker Compose 🐙

On a vu déjà l’instruction depends_on dans un fichier Docker Compose.

  • Elle permet de définir des dépendances entre les services.

  • Mais que faire si on veut définir un lot de services qui fonctionnent ensemble, sans pour autant être en interdépendance ?

  • Les profils dans Docker Compose permettent de définir des groupes de services qui peuvent être activés ou désactivés ensemble.

  • Cela peut être utile pour gérer des environnements de développement, de test et de production différents dans le même fichier Docker Compose.

  • Pour définir un profil pour un service, vous pouvez ajouter la clé profiles à la définition du service dans votre fichier Docker Compose.

👤 Profils dans Docker Compose 🐙

Par exemple :

services:
  mon_service:
    image: mon_image
    profiles:
      - dev

Dans cet exemple, le service mon_service appartient au profil dev.

👤 Profils dans Docker Compose 🐙

  • Pour démarrer seulement les services qui appartiennent à un certain profil, vous pouvez utiliser l’option --profile avec la commande docker compose up.

  • Par exemple :

docker compose up --profile dev

Cette commande démarrera seulement les services qui appartiennent au profil dev.

👤 Profils dans Docker Compose 🐙

  • Les profils peuvent rendre votre fichier Docker Compose plus organisé et flexible.

  • Ils vous permettent de définir différents environnements dans le même fichier et de choisir facilement quels services démarrer en fonction de vos besoins.

  • Jetons un coup d’œil à un exemple de fichier Docker Compose avec des profils :

services:
  service_dev:
    image: mon_image_dev
    profiles:
      - dev
  service_prod:
    image: mon_image_prod
    profiles:
      - prod
  • Dans cet exemple, nous avons deux services : service_dev et service_prod.

  • Chacun appartient à un profil différent.

👤 Profils dans Docker Compose 🐙

services:
  service_dev:
    image: mon_image_dev
    profiles:
      - dev
  service_prod:
    image: mon_image_prod
    profiles:
      - prod
  • Nous pouvons choisir de démarrer seulement les services de développement avec docker compose up --profile dev, ou uniquement les services de production avec docker compose up --profile prod.

  • Les profils dans Docker Compose sont un outil puissant pour gérer différents environnements dans le même fichier Docker Compose.

  • Ils peuvent rendre votre développement et vos tests plus efficaces et organisés.

🔨🔥 Une autre forge, ça vous dit? 😐

😏 Non, et bien, c’est parti quand même.

services:
  # Le service sidekick est responsable de la génération des clés SSH et de la vérification de leur existence.
  sidekick_service:
    build: dockerfiles/sidekick/.  # Le Dockerfile pour construire l'image du service sidekick.
    stdin_open: true  # Permet au service de garder STDIN ouvert même s'il n'est pas attaché.
    tty: true  # Alloue un pseudo-TTY pour le service.
    entrypoint: sh -c "/usr/local/bin/keygen.sh /ssh-dir"  # La commande que le service exécute lorsqu'il démarre.
    volumes:
      - agent-ssh-dir:/ssh-dir  # Monte le volume agent-ssh-dir au chemin /ssh-dir à l'intérieur du conteneur.
    healthcheck:  # La commande de vérification de santé pour le service.
      test: [ "CMD-SHELL", "[ -f /ssh-dir/conductor_ok ] || exit 1" ]  # Vérifie si le fichier conductor_ok existe dans le chemin /ssh-dir.
      interval: 5s  # Le temps entre les vérifications de santé.
      timeout: 10s  # Le temps à attendre avant de considérer que la vérification a échoué.
      retries: 5  # Le nombre d'échecs consécutifs nécessaires pour considérer un service comme malsain.

  # Le service jenkins_controller est responsable de la gestion des jobs et des configurations Jenkins.
  jenkins_controller:
    build: dockerfiles/.  # Le Dockerfile pour construire l'image du service jenkins_controller.
    restart: on-failure  # Le service sera redémarré s'il quitte en raison d'une erreur.
    ports:
      - "8080:8080"  # Expose le port 8080 du service à l'hôte.
    volumes:
      - jenkins_home:/var/jenkins_home  # Monte le volume jenkins_home au chemin /var/jenkins_home à l'intérieur du conteneur.
      - agent-ssh-dir:/ssh-dir  # Monte le volume agent-ssh-dir au chemin /ssh-dir à l'intérieur du conteneur.
    depends_on:  # Les services dont ce service dépend.
      sidekick_service:
        condition: service_completed_successfully  # Le sidekick_service doit se terminer avec succès avant que ce service ne démarre.
    healthcheck:  # La commande de vérification de santé pour le service.
      test: [ "CMD-SHELL", "[ -f /ssh-dir/conductor_ok ] || exit 1" ]  # Vérifie si le fichier conductor_ok existe dans le chemin /ssh-dir.
      interval: 5s  # Le temps entre les vérifications de santé.
      timeout: 10s  # Le temps à attendre avant de considérer que la vérification a échoué.
      retries: 5  # Le nombre d'échecs consécutifs nécessaires pour considérer un service comme malsain.

  # Le service default_agent est un agent Jenkins avec un JDK installé.
  default_agent:
    image: jenkins/ssh-agent:5.5.0-jdk17  # L'image Docker pour le service.
    container_name: desktop-jenkins_agent-1  # Le nom du conteneur.
    depends_on:  # Les services dont ce service dépend.
      sidekick_service:
        condition: service_completed_successfully  # Le sidekick_service doit se terminer avec succès avant que ce service ne démarre.
      jenkins_controller:
        condition: service_started  # Le service jenkins_controller doit démarrer avant que ce service ne démarre.
    healthcheck:  # La commande de vérification de santé pour le service.
      test: [ "CMD-SHELL", "[ -f /home/jenkins/.ssh/authorized_keys ] || exit 1" ]  # Vérifie si le fichier authorized_keys existe dans le chemin /home/jenkins/.ssh.
      interval: 5s  # Le temps entre les vérifications de santé.
      timeout: 10s  # Le temps à attendre avant de considérer que la vérification a échoué.
      retries: 5  # Le nombre d'échecs consécutifs nécessaires pour considérer un service comme malsain.
    volumes:
      - agent-ssh-dir:/home/jenkins/.ssh:ro  # Monte le volume agent-ssh-dir au chemin /home/jenkins/.ssh à l'intérieur du conteneur en lecture seule.

  # Le service maven est un agent Jenkins avec Maven installé.
  maven:
    build: dockerfiles/maven/.  # Le Dockerfile pour construire l'image du service Maven.
    container_name: desktop-jenkins_agent-1  # Le nom du conteneur.
    profiles:
      - maven  # Les profils à appliquer au service. Cela permet de personnaliser le comportement du service en fonction des besoins spécifiques.
    depends_on:  # Les services dont ce service dépend.
      sidekick_service:
        condition: service_completed_successfully  # Le sidekick_service doit se terminer avec succès avant que ce service ne démarre.
      jenkins_controller:
        condition: service_started  # Le service jenkins_controller doit démarrer avant que ce service ne démarre.
    healthcheck:  # La commande de vérification de santé pour le service.
      test: [ "CMD-SHELL", "[ -f /home/jenkins/.ssh/authorized_keys ] || exit 1" ]  # Vérifie si le fichier authorized_keys existe dans le chemin /home/jenkins/.ssh.
      interval: 5s  # Le temps entre les vérifications de santé.
      timeout: 10s  # Le temps à attendre avant de considérer que la vérification a échoué.
      retries: 5  # Le nombre d'échecs consécutifs nécessaires pour considérer un service comme malsain.
    volumes:
      - agent-ssh-dir:/home/jenkins/.ssh:ro  # Monte le volume agent-ssh-dir au chemin /home/jenkins/.ssh à l'intérieur du conteneur en lecture seule.

  # Le service python est un agent Jenkins avec Python installé.
  python:
    build: dockerfiles/python/.  # Le Dockerfile pour construire l'image du service Python.
    container_name: desktop-jenkins_agent-1  # Le nom du conteneur.
    profiles:
      - python  # Les profils à appliquer au service. Cela permet de personnaliser le comportement du service en fonction des besoins spécifiques.
    depends_on:  # Les services dont ce service dépend.
      sidekick_service:
        condition: service_completed_successfully  # Le sidekick_service doit se terminer avec succès avant que ce service ne démarre.
      jenkins_controller:
        condition: service_started  # Le service jenkins_controller doit démarrer avant que ce service ne démarre.
    healthcheck:  # La commande de vérification de santé pour le service.
      test: [ "CMD-SHELL", "[ -f /home/jenkins/.ssh/authorized_keys ] || exit 1" ]  # Vérifie si le fichier authorized_keys existe dans le chemin /home/jenkins/.ssh.
      interval: 5s  # Le temps entre les vérifications de santé.
      timeout: 10s  # Le temps à attendre avant de considérer que la vérification a échoué.
      retries: 5  # Le nombre d'échecs consécutifs nécessaires pour considérer un service comme malsain.
    volumes:
      - agent-ssh-dir:/home/jenkins/.ssh:ro  # Monte le volume agent-ssh-dir au chemin /home/jenkins/.ssh à l'intérieur du conteneur en lecture seule.

  # Le service node est un agent Jenkins avec Node.js installé.
  node:
    build: dockerfiles/node/.  # Le Dockerfile pour construire l'image du service Node.js.
    environment:
      - GITPOD_WORKSPACE_URL=${GITPOD_WORKSPACE_URL}  # Les variables d'environnement pour le service.
    container_name: desktop-jenkins_agent-1  # Le nom du conteneur.
    profiles:
      - node  # Les profils à appliquer au service. Cela permet de personnaliser le comportement du service en fonction des besoins spécifiques.
    depends_on:  # Les services dont ce service dépend.
      sidekick_service:
        condition: service_completed_successfully  # Le sidekick_service doit se terminer avec succès avant que ce service ne démarre.
      jenkins_controller:
        condition: service_started  # Le service jenkins_controller doit démarrer avant que ce service ne démarre.
    ports:
      - "3000:3000"  # Expose le port 3000 du service à l'hôte.
    healthcheck:  # La commande de vérification de santé pour le service.
      test: [ "CMD-SHELL", "[ -f /home/jenkins/.ssh/authorized_keys ] || exit 1" ]  # Vérifie si le fichier authorized_keys existe dans le chemin /home/jenkins/.ssh.
      interval: 5s  # Le temps entre les vérifications de santé.
      timeout: 10s  # Le temps à attendre avant de considérer que la vérification a échoué.
      retries: 5  # Le nombre d'échecs consécutifs nécessaires pour considérer un service comme malsain.
    volumes:
      - agent-ssh-dir:/home/jenkins/.ssh:ro  # Monte le volume agent-ssh-dir au chemin /home/jenkins/.ssh à l'intérieur du conteneur en lecture seule.

  # Le service multi_jenkins_controller est un contrôleur Jenkins pour gérer plusieurs jobs et configurations Jenkins.
  multi_jenkins_controller:
    build: 06_multibranch_pipeline/dockerfiles/.  # Le Dockerfile pour construire l'image du service multi Jenkins controller.
    restart: on-failure  # Le service sera redémarré s'il quitte en raison d'une erreur.
    ports:
      - "8080:8080"  # Expose le port 8080 du service à l'hôte.
    volumes:
      - jenkins_home:/var/jenkins_home  # Monte le volume jenkins_home au chemin /var/jenkins_home à l'intérieur du conteneur.
      - agent-ssh-dir:/ssh-dir  # Monte le volume agent-ssh-dir au chemin /ssh-dir à l'intérieur du conteneur.
    depends_on:  # Les services dont ce service dépend.
      sidekick_service:
        condition: service_completed_successfully  # Le sidekick_service doit se terminer avec succès avant que ce service ne démarre.
    healthcheck:  # La commande de vérification de santé pour le service.
      test: [ "CMD-SHELL", "[ -f /ssh-dir/conductor_ok ] || exit 1" ]  # Vérifie si le fichier conductor_ok existe dans le chemin /ssh-dir.
      interval: 5s  # Le temps entre les vérifications de santé.
      timeout: 10s  # Le temps à attendre avant de considérer que la vérification a échoué.
      retries: 5  # Le nombre d'échecs consécutifs nécessaires pour considérer un service comme malsain.

  # Le service multi est un agent Jenkins pour gérer plusieurs jobs et configurations Jenkins.
  multi:
    build: 06_multibranch_pipeline/dockerfiles/agent/.  # Le Dockerfile pour construire l'image du service multi Jenkins agent.
    environment:
      - GITPOD_WORKSPACE_URL=${GITPOD_WORKSPACE_URL}  # Les variables d'environnement pour le service.
    container_name: desktop-jenkins_agent-1  # Le nom du conteneur.
    profiles:
      - multi  # Les profils à appliquer au service. Cela permet de personnaliser le comportement du service en fonction des besoins spécifiques.
    depends_on:  # Les services dont ce service dépend.
      sidekick_service:
        condition: service_completed_successfully  # Le sidekick_service doit se terminer avec succès avant que ce service ne démarre.
      multi_jenkins_controller:
        condition: service_started  # Le service multi_jenkins_controller doit démarrer avant que ce service ne démarre.
    ports:
      - "3000:3000"  # Expose le port 3000 du service à l'hôte.
      - "5000:5000"  # Expose le port 5000 du service à l'hôte.
    healthcheck:  # La commande de vérification de santé pour le service.
      test: [ "CMD-SHELL", "[ -f /home/jenkins/.ssh/authorized_keys ] || exit 1" ]  # Vérifie si le fichier authorized_keys existe dans le chemin /home/jenkins/.ssh.
      interval: 5s  # Le temps entre les vérifications de santé.
      timeout: 10s  # Le temps à attendre avant de considérer que la vérification a échoué.
      retries: 5  # Le nombre d'échecs consécutifs nécessaires pour considérer un service comme malsain.
    volumes:
      - agent-ssh-dir:/home/jenkins/.ssh:ro  # Monte le volume agent-ssh-dir au chemin /home/jenkins/.ssh à l'intérieur du conteneur en lecture seule.

# Les volumes utilisés par les services.
volumes:
  jenkins_home:  # Un volume pour stocker les données de Jenkins.
  agent-ssh-dir:  # Un volume pour stocker les clés SSH.
    name: agent-ssh-dir  # Le nom du volume.

🔨🔥 Une autre forge, ça vous dit? 😐

Indigeste? 😨

Ne relevons que les points importants:

  # Le service maven est un agent Jenkins avec Maven installé.
  maven:
    build: dockerfiles/maven/.  # Le Dockerfile pour construire l'image du service Maven.
    container_name: desktop-jenkins_agent-1  # Le nom du conteneur.
    profiles:
      - maven  # Les profils à appliquer au service. Cela permet de personnaliser le comportement du service en fonction des besoins spécifiques.
    depends_on:  # Les services dont ce service dépend.
      sidekick_service:
        condition: service_completed_successfully  # Le sidekick_service doit se terminer avec succès avant que ce service ne démarre.
      jenkins_controller:
        condition: service_started  # Le service jenkins_controller doit démarrer avant que ce service ne démarre.
    healthcheck:  # La commande de vérification de santé pour le service.
      test: [ "CMD-SHELL", "[ -f /home/jenkins/.ssh/authorized_keys ] || exit 1" ]  # Vérifie si le fichier authorized_keys existe dans le chemin /home/jenkins/.ssh.
      interval: 5s  # Le temps entre les vérifications de santé.
      timeout: 10s  # Le temps à attendre avant de considérer que la vérification a échoué.
      retries: 5  # Le nombre d'échecs consécutifs nécessaires pour considérer un service comme malsain.
    volumes:
      - agent-ssh-dir:/home/jenkins/.ssh:ro  # Monte le volume agent-ssh-dir au chemin /home/jenkins/.ssh à l'intérieur du conteneur en lecture seule.

🔨🔥 Une autre forge, ça vous dit? 😐

Maven

FROM jenkins/ssh-agent:6.17.0-jdk17 as ssh-agent

# ca-certificates because curl will need it later on for the maven installation
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates curl && apt-get clean && \
  rm -rf /var/lib/apt/lists/*

# Now time to install maven
ARG MAVEN_VERSION=3.9.2

# Set SHELL flags for RUN commands to allow -e and pipefail
# Rationale:https://github.com/hadolint/hadolint/wiki/DL4006
SHELL ["/bin/bash", "-eo", "pipefail", "-c"]

# Add a checksum for the maven binary
RUN curl -sS -L -O --output-dir /tmp/ --create-dirs  https://archive.apache.org/dist/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz \
    && printf "%s" "$(sha512sum /tmp/apache-maven-${MAVEN_VERSION}-bin.tar.gz)" | sha512sum -c - \
    && curl -sS -L -O --output-dir /tmp/ --create-dirs  https://downloads.apache.org/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz.sha512 \
    && printf "%s /tmp/apache-maven-${MAVEN_VERSION}-bin.tar.gz" "$(cat /tmp/apache-maven-${MAVEN_VERSION}-bin.tar.gz.sha512)" | sha512sum --check --status - \
    && tar xzf "/tmp/apache-maven-${MAVEN_VERSION}-bin.tar.gz" -C /opt/ \
    && rm "/tmp/apache-maven-${MAVEN_VERSION}-bin.tar.gz" \
    && ln -s /opt/apache-maven-${MAVEN_VERSION} /opt/maven \
    && ln -s /opt/maven/bin/mvn /usr/bin/mvn \
    && mkdir -p /etc/profile.d \
    && echo "export JAVA_HOME=$JAVA_HOME \n \
             export M2_HOME=/opt/maven \n \
             export PATH=${M2_HOME}/bin:${PATH}" > /etc/profile.d/maven.sh
ENV M2_HOME="/opt/maven"
ENV PATH="${M2_HOME}/bin/:${PATH}"
RUN echo "PATH=${PATH}" >> /etc/environment && chown -R jenkins:jenkins "${JENKINS_AGENT_HOME}"

🔨🔥 Une autre forge, ça vous dit? 😐

Python

  # Le service python est un agent Jenkins avec Python installé.
  python:
    build: dockerfiles/python/.  # Le Dockerfile pour construire l'image du service Python.
    container_name: desktop-jenkins_agent-1  # Le nom du conteneur.
    profiles:
      - python  # Les profils à appliquer au service. Cela permet de personnaliser le comportement du service en fonction des besoins spécifiques.
    depends_on:  # Les services dont ce service dépend.
      sidekick_service:
        condition: service_completed_successfully  # Le sidekick_service doit se terminer avec succès avant que ce service ne démarre.
      jenkins_controller:
        condition: service_started  # Le service jenkins_controller doit démarrer avant que ce service ne démarre.
    healthcheck:  # La commande de vérification de santé pour le service.
      test: [ "CMD-SHELL", "[ -f /home/jenkins/.ssh/authorized_keys ] || exit 1" ]  # Vérifie si le fichier authorized_keys existe dans le chemin /home/jenkins/.ssh.
      interval: 5s  # Le temps entre les vérifications de santé.
      timeout: 10s  # Le temps à attendre avant de considérer que la vérification a échoué.
      retries: 5  # Le nombre d'échecs consécutifs nécessaires pour considérer un service comme malsain.
    volumes:
      - agent-ssh-dir:/home/jenkins/.ssh:ro  # Monte le volume agent-ssh-dir au chemin /home/jenkins/.ssh à l'intérieur du conteneur en lecture seule.

🔨🔥 Une autre forge, ça vous dit? 😐

# Use the base image
FROM jenkins/ssh-agent:5.12.0-jdk17 as ssh-agent

# Install necessary Dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
    binutils ca-certificates curl git python3 python3-pip python3-setuptools python3-wheel python3-dev wget

# Create an alias for python3 as python
RUN ln -s /usr/bin/python3 /usr/bin/python

# Install required python packages
RUN pip install docker-py feedparser nosexcover prometheus_client pycobertura pylint pytest pytest-cov requests setuptools sphinx pyinstaller

# Add the Jenkins agent user to the environment
RUN echo "PATH=${PATH}" >> /etc/environment

# Set ownership for Jenkins agent home directory
RUN chown -R jenkins:jenkins "${JENKINS_AGENT_HOME}"

🔨🔥 Une autre forge, ça vous dit? 😐

À vous maintenant!

git clone https://github.com/jenkins-docs/quickstart-tutorials.git

À vous de modifier les sources de façon à passer par le cache ILI

Ensuite, ne restera qu’à lancer la "forge":

docker compose up --build -d --force-recreate maven

Ou encore si vous voulez tout construire localement:

docker compose -f build-docker-compose.yaml up --build -d --force-recreate maven

Autre étude de cas 📚🔍

Et pourquoi pas s’attaquer au docker compose qui a généré ce cours ?

Autre étude de cas 📚🔍

Et pourquoi pas s’attaquer au docker compose qui a généré ce cours ?

# Ceci est un fichier Docker Compose pour un projet qui comprend plusieurs services.

# 'x-slides-base' est une ancre YAML qui définit une configuration commune pour plusieurs services.
x-slides-base: &slides-base
  # La configuration de construction pour l'image Docker.
  build:
    # Le contexte de construction est le répertoire courant.
    context: ./
    args:
      # Active la fonction de mise en cache en ligne de Docker BuildKit.
      BUILDKIT_INLINE_CACHE: 1
  # Variables d'environnement pour le conteneur Docker.
  environment:
    - PRESENTATION_URL=${PRESENTATION_URL}
    - REPOSITORY_URL=${REPOSITORY_URL}
  # L'ID utilisateur qui exécute les commandes à l'intérieur du conteneur.
  user: ${CURRENT_UID}
  # Un montage tmpfs pour des opérations d'E/S plus rapides.
  tmpfs:
    - ${BUILD_DIR}
  # Volumes pour le conteneur Docker.
  volumes:
    - ./content:/app/content
    - ./assets:/app/assets
    - ${DIST_DIR}:/app/dist
    - ./gulp/gulpfile.js:/app/gulpfile.js
    - ./gulp/tasks:/app/tasks
    - ./npm-packages:/app/npm-packages

# Les services qui composent l'application.
services:
  # Le service 'serve'.
  serve:
    # Utilise la configuration commune définie par l'ancre 'x-slides-base'.
    <<: *slides-base
    # Expose le port 8000 du conteneur à l'hôte.
    ports:
      - "8000:8000"

  # Le service 'build'.
  build:
    <<: *slides-base
    # Ce service dépend du service 'qrcode'.
    depends_on:
      qrcode:
        # Le service 'qrcode' doit se terminer avec succès avant que ce service ne démarre.
        condition: service_completed_successfully
    # Le point d'entrée pour le conteneur Docker.
    entrypoint: >
      sh -xc 'gulp build && cp -r "${BUILD_DIR}"/* /app/dist/'

  # Le service 'qrcode'.
  qrcode:
    <<: *slides-base
    # Le point d'entrée pour le conteneur Docker.
    entrypoint: /app/node_modules/.bin/qrcode
    # La commande à exécuter dans le conteneur Docker.
    command: >
      -t png -o /app/content/media/qrcode.png ${PRESENTATION_URL}

  # Le service 'pdf'.
  pdf:
    # L'image Docker pour ce service.
    image: ghcr.io/astefanutti/decktape:3.7.0
    # Ce service dépend du service 'build'.
    depends_on:
      build:
        # Le service 'build' doit se terminer avec succès avant que ce service ne démarre.
        condition: service_completed_successfully
    # L'ID utilisateur qui exécute les commandes à l'intérieur du conteneur.
    user: ${CURRENT_UID}
    # Volumes pour le conteneur Docker.
    volumes:
      - ${DIST_DIR}:/slides
    # La commande à exécuter dans le conteneur Docker.
    command: >
      /slides/index.html /slides/slides.pdf --size='1024x768' --pause 0

Autre étude de cas 📚🔍

Et pourquoi pas s’attaquer au docker compose qui a généré ce cours ?

Indigeste? 😨

Ne relevons que les points importants:

Les ancres…​

# Ceci est un fichier Docker Compose pour un projet qui comprend plusieurs services.

# 'x-slides-base' est une ancre YAML qui définit une configuration commune pour plusieurs services.
x-slides-base: &slides-base
  # La configuration de construction pour l'image Docker.
  build:
    # Le contexte de construction est le répertoire courant.
    context: ./
    args:
      # Active la fonction de mise en cache en ligne de Docker BuildKit.
      BUILDKIT_INLINE_CACHE: 1
  # Variables d'environnement pour le conteneur Docker.
  environment:
    - PRESENTATION_URL=${PRESENTATION_URL}
    - REPOSITORY_URL=${REPOSITORY_URL}
  # L'ID utilisateur qui exécute les commandes à l'intérieur du conteneur.
  user: ${CURRENT_UID}
  # Un montage tmpfs pour des opérations d'E/S plus rapides.
  tmpfs:
    - ${BUILD_DIR}
  # Volumes pour le conteneur Docker.
  volumes:
    - ./content:/app/content
    - ./assets:/app/assets
    - ${DIST_DIR}:/app/dist
    - ./gulp/gulpfile.js:/app/gulpfile.js
    - ./gulp/tasks:/app/tasks
    - ./npm-packages:/app/npm-packages

➡️🔄 Les ancres dans docker compose

Docker Compose permet de définir et de gérer plusieurs services Docker dans un seul fichier YAML.

  • Les ancres (&) et les alias (*) sont des fonctionnalités YAML qui peuvent être utilisées dans Docker Compose pour réutiliser des configurations.

  • Une ancre est une référence à un objet ou à une valeur dans un fichier YAML. Elle est définie en utilisant l’opérateur & suivi d’un nom unique.

x-slides-base: &slides-base
  build:
    context: ./

Dans cet exemple, x-slides-base est une ancre qui représente un objet avec une clé build.

➡️🔄 Les ancres dans docker compose

Les ancres peuvent être référencées ailleurs dans le fichier YAML en utilisant l’opérateur *.

services:
  serve:
    <<: *slides-base
  • Les ancres permettent de réutiliser des configurations, ce qui rend le fichier Docker Compose plus lisible et plus facile à maintenir.

  • Si vous devez modifier une configuration qui est utilisée à plusieurs endroits, vous pouvez simplement modifier l’ancre.

  • En conclusion, les ancres et les alias sont des outils puissants pour gérer des configurations complexes dans Docker Compose. Ils permettent de réduire la duplication et d’améliorer la lisibilité de votre fichier Docker Compose.

Autre étude de cas 📚🔍

Le fameux qrcode…​

  # Le service 'qrcode'.
  qrcode:
    <<: *slides-base
    # Le point d'entrée pour le conteneur Docker.
    entrypoint: /app/node_modules/.bin/qrcode
    # La commande à exécuter dans le conteneur Docker.
    command: >
      -t png -o /app/content/media/qrcode.png ${PRESENTATION_URL}

Autre étude de cas 📚🔍

Le service qui construit les slides à partir d'`asciidoc` avec reveal.js.

  # Le service 'build'.
  build:
    <<: *slides-base
    # Ce service dépend du service 'qrcode'.
    depends_on:
      qrcode:
        # Le service 'qrcode' doit se terminer avec succès avant que ce service ne démarre.
        condition: service_completed_successfully
    # Le point d'entrée pour le conteneur Docker.
    entrypoint: >
      sh -xc 'gulp build && cp -r "${BUILD_DIR}"/* /app/dist/'

Autre étude de cas 📚🔍

Le service qui construit les slides à partir d'`asciidoc` avec reveal.js.

  # Le service 'serve'.
  serve:
    # Utilise la configuration commune définie par l'ancre 'x-slides-base'.
    <<: *slides-base
    # Expose le port 8000 du conteneur à l'hôte.
    ports:
      - "8000:8000"

Autre étude de cas 📚🔍

Le service qui construit le pdf à partir des slides.

  # Le service 'pdf'.
  pdf:
    # L'image Docker pour ce service.
    image: ghcr.io/astefanutti/decktape:3.7.0
    # Ce service dépend du service 'build'.
    depends_on:
      build:
        # Le service 'build' doit se terminer avec succès avant que ce service ne démarre.
        condition: service_completed_successfully
    # L'ID utilisateur qui exécute les commandes à l'intérieur du conteneur.
    user: ${CURRENT_UID}
    # Volumes pour le conteneur Docker.
    volumes:
      - ${DIST_DIR}:/slides
    # La commande à exécuter dans le conteneur Docker.
    command: >
      /slides/index.html /slides/slides.pdf --size='1024x768' --pause 0

Autre étude de cas 📚🔍

Pour rappel, l’ancre contenait :

# 'x-slides-base' est une ancre YAML qui définit une configuration commune pour plusieurs services.
x-slides-base: &slides-base
  # La configuration de construction pour l'image Docker.
  build:
    # Le contexte de construction est le répertoire courant.
    context: ./
    args:
      # Active la fonction de mise en cache en ligne de Docker BuildKit.
      BUILDKIT_INLINE_CACHE: 1
  # Variables d'environnement pour le conteneur Docker.
  environment:
    - PRESENTATION_URL=${PRESENTATION_URL}
    - REPOSITORY_URL=${REPOSITORY_URL}
  # L'ID utilisateur qui exécute les commandes à l'intérieur du conteneur.
  user: ${CURRENT_UID}
  # Un montage tmpfs pour des opérations d'E/S plus rapides.
  tmpfs:
    - ${BUILD_DIR}
  # Volumes pour le conteneur Docker.
  volumes:
    - ./content:/app/content
    - ./assets:/app/assets
    - ${DIST_DIR}:/app/dist
    - ./gulp/gulpfile.js:/app/gulpfile.js
    - ./gulp/tasks:/app/tasks
    - ./npm-packages:/app/npm-packages

➡️🔄 Build quoi? 🛠️📦

  • Docker BuildKit est un outil de construction de Docker qui apporte de nombreuses améliorations par rapport à l’ancien système de construction.

  • L’une de ces améliorations est la mise en cache en ligne.

  • La mise en cache en ligne est une fonctionnalité de Docker BuildKit qui permet de réutiliser les couches de cache existantes lors de la construction d’une image Docker.

multi layered cake removebg preview

Elle est activée en définissant l’argument BUILDKIT_INLINE_CACHE à 1.

args:
  BUILDKIT_INLINE_CACHE: 1

➡️🔄 Environnement 🌍🌿

  # Variables d'environnement pour le conteneur Docker.
  environment:
    - PRESENTATION_URL=${PRESENTATION_URL}
    - REPOSITORY_URL=${REPOSITORY_URL}

Deux variables d’environnement sont définies pour le conteneur Docker : PRESENTATION_URL et REPOSITORY_URL.

Ces variables sont définies à l’aide de la syntaxe ${VARIABLE_NAME}, qui est une manière standard d’accéder aux variables d’environnement dans les fichiers de configuration YAML.

➡️🔄 Environnement 🌍🌿

  # Variables d'environnement pour le conteneur Docker.
  environment:
    - PRESENTATION_URL=${PRESENTATION_URL}
    - REPOSITORY_URL=${REPOSITORY_URL}

Lorsque Docker Compose rencontre cette syntaxe, il cherche la valeur de la variable d’environnement dans plusieurs endroits, en suivant un ordre spécifique :

  • Il vérifie d’abord si la variable est définie dans le shell courant.

  • Si c’est le cas, il utilise cette valeur.

  • Si la variable n’est pas définie dans le shell, Docker Compose cherche ensuite dans un fichier .env situé dans le même répertoire que le fichier docker-compose.yml.

  • Si la variable est définie dans ce fichier, Docker Compose utilise cette valeur.

➡️🔄 Environnement 🌍🌿

  # Variables d'environnement pour le conteneur Docker.
  environment:
    - PRESENTATION_URL=${PRESENTATION_URL}
    - REPOSITORY_URL=${REPOSITORY_URL}
  • Si la variable n’est définie ni dans le shell ni dans le fichier .env, Docker Compose utilise la valeur par défaut spécifiée dans le fichier docker-compose.yml (s’il y en a une).

  • Dans notre cas, aucune valeur par défaut n’est spécifiée, donc si la variable n’est définie ni dans le shell ni dans le fichier .env, Docker Compose générera une erreur.

Autre étude de cas 📚🔍

  # L'ID utilisateur qui exécute les commandes à l'intérieur du conteneur.
  user: ${CURRENT_UID}
  • Dans Docker, chaque instruction dans le Dockerfile est exécutée par un utilisateur particulier.

  • Par défaut, cet utilisateur est root, mais pour des raisons de sécurité, il est souvent recommandé d’exécuter les processus en tant qu’utilisateur non root.

  • Cette ligne définit l’ID de l’utilisateur qui exécutera les commandes à l’intérieur du conteneur Docker à la valeur de la variable d’environnement CURRENT_UID.

  • La syntaxe ${CURRENT_UID} est utilisée pour accéder à la valeur d’une variable d’environnement.

  • Dans ce cas, Docker Compose recherchera une variable d’environnement nommée CURRENT_UID et utilisera sa valeur.

  • Si CURRENT_UID n’est pas défini dans l’environnement où Docker Compose est exécuté, Docker Compose renverra une erreur.

Autre étude de cas 📚🔍

# Ce Dockerfile met en place un environnement Node.js avec des outils et dépendances supplémentaires.
# Il est basé sur l'image Docker officielle de Node.js (version 22, variante Alpine).

FROM node:24-alpine

# Installer la dernière version des dépendances requises
# hadolint ignore=DL3018
RUN apk add --no-cache \
  curl \ # Outil pour transférer des données avec des URLs
  git \ # Système de contrôle de version distribué
  tini \ # Un init minuscule mais valide pour les conteneurs
  unzip # Outil pour décompresser les fichiers zip

# Installer les dépendances NPM globalement (dernières versions)
# hadolint ignore=DL3016,DL3059
RUN npm install --global npm npm-check-updates # Mettre à jour npm à la dernière version et installer npm-check-updates

# Copier les dépendances NPM de l'application dans l'image Docker
COPY ./npm-packages /app/npm-packages
# Créer des liens symboliques pour package.json et package-lock.json à la racine de /app
# Cela permet d'exécuter les opérations npm sans erreur ENOENT
RUN ln -s /app/npm-packages/package.json /app/package.json \
  && ln -s /app/npm-packages/package-lock.json /app/package-lock.json

# Définir le répertoire de travail dans l'image Docker à /app
WORKDIR /app

# Télécharger et installer une version spécifique de FontAwesome
ARG FONTAWESOME_VERSION=6.4.0
RUN curl --silent --show-error --location --output /tmp/fontawesome.zip \
    "https://use.fontawesome.com/releases/v${FONTAWESOME_VERSION}/fontawesome-free-${FONTAWESOME_VERSION}-web.zip" \
  && unzip -q /tmp/fontawesome.zip -d /tmp \
  && mv /tmp/"fontawesome-free-${FONTAWESOME_VERSION}-web" /app/fontawesome \
  && rm -rf /tmp/font*

# Installer les dépendances NPM en utilisant le package-lock.json
# Si l'installation échoue, revenir à une installation npm régulière
RUN { npm install-clean && npx update-browserslist-db@latest; } || npm install

# Lier la commande gulp pour qu'elle soit disponible dans le PATH
# hadolint ignore=DL3059
RUN npm link gulp

# Copier les tâches gulp et la configuration dans l'image Docker
COPY ./gulp/tasks /app/tasks
COPY ./gulp/gulpfile.js /app/gulpfile.js

# Définir un volume pour le répertoire /app
VOLUME ["/app"]

# Exposer le port 8000 pour HTTP
EXPOSE 8000

# Utiliser tini comme point d'entrée, et exécuter gulp par défaut
ENTRYPOINT ["/sbin/tini","-g","gulp"]
CMD ["default"]

Autre étude de cas 📚🔍

RUN npm install --global npm npm-check-updates # Mettre à jour npm à la dernière version et installer npm-check-updates

# Copier les dépendances NPM de l'application dans l'image Docker
COPY ./npm-packages /app/npm-packages
# Créer des liens symboliques pour package.json et package-lock.json à la racine de /app
# Cela permet d'exécuter les opérations npm sans erreur ENOENT
RUN ln -s /app/npm-packages/package.json /app/package.json \
  && ln -s /app/npm-packages/package-lock.json /app/package-lock.json
  • Ce Dockerfile met en place un environnement Node.js avec des outils et dépendances supplémentaires.

  • Il installe les dernières versions de npm et npm-check-updates globalement, copie les dépendances npm de l’application dans l’image Docker, et crée des liens symboliques pour package.json et package-lock.json à la racine de /app.

Autre étude de cas 📚🔍

ARG FONTAWESOME_VERSION=6.4.0
RUN curl --silent --show-error --location --output /tmp/fontawesome.zip \
    "https://use.fontawesome.com/releases/v${FONTAWESOME_VERSION}/fontawesome-free-${FONTAWESOME_VERSION}-web.zip" \
  && unzip -q /tmp/fontawesome.zip -d /tmp \
  && mv /tmp/"fontawesome-free-${FONTAWESOME_VERSION}-web" /app/fontawesome \
  && rm -rf /tmp/font*
  • Il télécharge également et installe une version spécifique de FontAwesome

Autre étude de cas 📚🔍

# Installer les dépendances NPM en utilisant le package-lock.json
# Si l'installation échoue, revenir à une installation npm régulière
RUN { npm install-clean && npx update-browserslist-db@latest; } || npm install

# Lier la commande gulp pour qu'elle soit disponible dans le PATH
# hadolint ignore=DL3059
RUN npm link gulp
  • Il installe les dépendances npm en utilisant le package-lock.json (en revenant à une installation npm régulière si nécessaire), et lie la commande gulp pour qu’elle soit disponible dans le PATH.

Autre étude de cas 📚🔍

# Copier les tâches gulp et la configuration dans l'image Docker
COPY ./gulp/tasks /app/tasks
COPY ./gulp/gulpfile.js /app/gulpfile.js

# Définir un volume pour le répertoire /app
VOLUME ["/app"]

# Exposer le port 8000 pour HTTP
EXPOSE 8000
  • Les tâches gulp et la configuration sont copiées dans l’image Docker, un volume est défini pour le répertoire /app, et le port 8000 est exposé pour HTTP.

Autre étude de cas 📚🔍

# Utiliser tini comme point d'entrée, et exécuter gulp par défaut
ENTRYPOINT ["/sbin/tini","-g","gulp"]
CMD ["default"]
  • Le point d’entrée est défini sur tini, et gulp est exécuté par défaut.

🐋 Tini dans Docker

🤔 Qu’est-ce que Tini ?

Tini est un init minuscule mais valide pour les conteneurs. Il est conçu pour être le système init le plus simple possible.

🛠️ Que fait Tini ?

Tini fait deux choses :

  1. Il génère votre processus en tant que son enfant (directement, pas en tant que petit-enfant, comme le ferait un shell).

  2. Il attend ensuite les signaux et les transmet au processus enfant.

🎯 Pourquoi Tini est-il utile dans Docker ?

  1. Docker exécute un seul processus dans un conteneur par défaut. Si ce processus génère des processus enfants et ne les récolte pas correctement, ils deviennent des processus zombies.

  2. Tini assure que ces processus zombies sont correctement récoltés, améliorant ainsi le comportement du conteneur et réduisant la probabilité de cas limites.

🐋 Tini dans notre Dockerfile

Dans notre Dockerfile, nous utilisons Tini comme point d’entrée :

ENTRYPOINT ["/sbin/tini","-g","gulp"]
  1. Cela signifie que Tini est le premier processus qui est lancé dans notre conteneur.

  2. Il lancera ensuite gulp en tant que processus enfant.

  3. Tout signal envoyé au conteneur sera transmis par Tini à gulp.

  4. Si gulp génère des processus enfants et ne les récolte pas, Tini le fera.

🐋 Docker peut travailler main dans la main avec d’autres outils…​ 👫🤝

# Ce Makefile est utilisé pour gérer le projet Docker Compose.

# Définir les valeurs par défaut pour les variables DIST_DIR et REPOSITORY_URL.
DIST_DIR ?= $(CURDIR)/dist
REPOSITORY_URL ?= file://$(CURDIR)
export REPOSITORY_URL DIST_DIR

# Activer Docker BuildKit pour une construction plus rapide et la mise en cache des images.
DOCKER_BUILDKIT ?= 1
COMPOSE_DOCKER_CLI_BUILD ?= 1
export DOCKER_BUILDKIT COMPOSE_DOCKER_CLI_BUILD

# Définition des commandes shell réutilisables pour Docker Compose.

# compose_cmd est une fonction qui exécute la commande 'docker compose' avec le fichier docker-compose.yml du répertoire courant.
# Elle prend un argument $(1) qui représente les options supplémentaires à passer à la commande 'docker compose'.
# $(CURDIR) est une variable d'environnement dans le Makefile qui représente le répertoire courant dans lequel
# le Makefile est exécuté. C'est une fonctionnalité intégrée de GNU Make. Elle est souvent utilisée pour référencer
# des fichiers ou des répertoires relatifs au répertoire courant.
compose_cmd = docker compose --file=$(CURDIR)/docker-compose.yml $(1)

# compose_up est une fonction qui utilise compose_cmd pour exécuter 'docker compose up'.
# Elle prend un argument $(1) qui représente les options supplémentaires à passer à la commande 'docker compose up'.
# L'option '--build' est toujours incluse, ce qui signifie que Docker construira les images avant de démarrer les conteneurs.
compose_up = $(call compose_cmd, up --build $(1))

# compose_run est une fonction qui utilise compose_cmd pour exécuter 'docker compose run'.
# Elle prend un argument $(1) qui représente les options supplémentaires à passer à la commande 'docker compose run'.
# L'option '--user=0' est toujours incluse, ce qui signifie que les commandes seront exécutées en tant que root à l'intérieur du conteneur.
compose_run = $(call compose_cmd, run --user=0 $(1))

# La cible par défaut. Elle nettoie le projet, le construit et le vérifie.
all: clean build verify

# Construire le projet à l'intérieur d'un conteneur Docker.
# Cette règle Makefile utilise la fonction compose_up définie précédemment pour exécuter 'docker compose up' avec l'option '--exit-code-from=build'.
# L'option '--exit-code-from=build' signifie que la commande 'docker compose up' renverra le code de sortie du service 'build'.
# Si le service 'build' se termine avec un code de sortie non nul (ce qui signifie qu'une erreur s'est produite), alors 'docker compose up' se terminera également avec un code de sortie non nul.
# Cela permet à Make de savoir si la construction du projet a réussi ou non.
build:
 @$(call compose_up,--exit-code-from=build build)

# Vérifier le projet. Actuellement désactivé.
verify:
 @echo "Vérification désactivée"

# Démarrer les services 'serve' et 'qrcode'.
serve:
 @$(call compose_up, --force-recreate serve qrcode)

# Démarrer un shell à l'intérieur du conteneur du service 'serve'.
shell:
 @$(call compose_run,--entrypoint=sh --rm serve)

# Mettre à jour le fichier de verrouillage pour les dépendances npm.
dependencies-lock-update:
 @$(call compose_run,--entrypoint=npm --rm serve install --package-lock)

# Mettre à jour les dépendances npm et le fichier de verrouillage.
dependencies-update:
 @$(call compose_run,--entrypoint=ncu --workdir=/app/npm-packages --rm serve -u)
 @make -C $(CURDIR) dependencies-lock-update

# Construire une version PDF du projet.
pdf:
 @$(call compose_up, --exit-code-from=pdf pdf)

# Nettoyer le projet en arrêtant et en supprimant les conteneurs Docker, les réseaux et les volumes.
clean:
 @$(call compose_cmd, down -v --remove-orphans)
 @rm -rf $(DIST_DIR)

# Démarrer le service 'qrcode'.
qrcode:
 @$(call compose_up, qrcode)

# Déclarer des cibles factices.
.PHONY: all build verify serve qrcode pdf dependencies-update dependencies-lock-update

🐋 Docker peut travailler main dans la main avec d’autres outils…​ 👫🤝

# Activer Docker BuildKit pour une construction plus rapide et la mise en cache des images.
DOCKER_BUILDKIT ?= 1
COMPOSE_DOCKER_CLI_BUILD ?= 1
export DOCKER_BUILDKIT COMPOSE_DOCKER_CLI_BUILD

Cette configuration de docker compose concerne Docker BuildKit, une fonctionnalité de Docker qui améliore les performances de construction des images Docker.

  • La ligne DOCKER_BUILDKIT ?= 1 vérifie si la variable d’environnement DOCKER_BUILDKIT est déjà définie.

    • Si ce n’est pas le cas, elle lui attribue la valeur 1, ce qui active Docker BuildKit.

  • De même, COMPOSE_DOCKER_CLI_BUILD ?= 1 vérifie si la variable d’environnement COMPOSE_DOCKER_CLI_BUILD est déjà définie.

    • Si ce n’est pas le cas, elle lui attribue la valeur 1.

    • Cette variable d’environnement est utilisée pour activer l’utilisation de Docker CLI lors de l’utilisation de Docker Compose.

    • C’est nécessaire car Docker Compose ne supporte pas BuildKit par défaut, donc cette variable d’environnement est une solution de contournement pour l’activer. En résumé, ces deux lignes de code activent Docker BuildKit pour améliorer les performances de construction des images Docker lors de l’utilisation de Docker Compose.

🐋 Docker peut travailler main dans la main avec d’autres outils…​ 👫🤝

# Définition des commandes shell réutilisables pour Docker Compose.

# compose_cmd est une fonction qui exécute la commande 'docker compose' avec le fichier docker-compose.yml du répertoire courant.
# Elle prend un argument $(1) qui représente les options supplémentaires à passer à la commande 'docker compose'.
# $(CURDIR) est une variable d'environnement dans le Makefile qui représente le répertoire courant dans lequel
# le Makefile est exécuté. C'est une fonctionnalité intégrée de GNU Make. Elle est souvent utilisée pour référencer
# des fichiers ou des répertoires relatifs au répertoire courant.
compose_cmd = docker compose --file=$(CURDIR)/docker-compose.yml $(1)

# compose_up est une fonction qui utilise compose_cmd pour exécuter 'docker compose up'.
# Elle prend un argument $(1) qui représente les options supplémentaires à passer à la commande 'docker compose up'.
# L'option '--build' est toujours incluse, ce qui signifie que Docker construira les images avant de démarrer les conteneurs.
compose_up = $(call compose_cmd, up --build $(1))

# compose_run est une fonction qui utilise compose_cmd pour exécuter 'docker compose run'.
# Elle prend un argument $(1) qui représente les options supplémentaires à passer à la commande 'docker compose run'.
# L'option '--user=0' est toujours incluse, ce qui signifie que les commandes seront exécutées en tant que root à l'intérieur du conteneur.
compose_run = $(call compose_cmd, run --user=0 $(1))

🐋 Docker peut travailler main dans la main avec d’autres outils…​ 👫🤝

# Construire le projet à l'intérieur d'un conteneur Docker.
# Cette règle Makefile utilise la fonction compose_up définie précédemment pour exécuter 'docker compose up' avec l'option '--exit-code-from=build'.
# L'option '--exit-code-from=build' signifie que la commande 'docker compose up' renverra le code de sortie du service 'build'.
# Si le service 'build' se termine avec un code de sortie non nul (ce qui signifie qu'une erreur s'est produite), alors 'docker compose up' se terminera également avec un code de sortie non nul.
# Cela permet à Make de savoir si la construction du projet a réussi ou non.
build:
 @$(call compose_up,--exit-code-from=build build)
  • Cette règle Makefile est utilisée pour construire le projet à l’intérieur d’un conteneur Docker.

  • Elle utilise la fonction compose_up pour exécuter la commande docker compose up avec l’option --exit-code-from=build.

  • Cette option permet à la commande docker compose up de renvoyer le code de sortie du service build, ce qui permet à Make de savoir si la construction du projet a réussi ou non.

📖🔚 Fin du chapitre docker compose 🐋🐙

bitmoji whale friend

Bonus

Les petits plus

902050214 image2
902050214 image2
902050214 image2
902050214 image2
902050214 image2
902050214 image2

"Il en remet une couche"

docker history nginx
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
f35646e83998        4 weeks ago         /bin/sh -c #(nop)  CMD ["nginx" "-g" "daemon…   0B
<missing>           4 weeks ago         /bin/sh -c #(nop)  STOPSIGNAL SIGTERM           0B
<missing>           4 weeks ago         /bin/sh -c #(nop)  EXPOSE 80                    0B
<missing>           4 weeks ago         /bin/sh -c #(nop)  ENTRYPOINT ["/docker-entr…   0B
<missing>           4 weeks ago         /bin/sh -c #(nop) COPY file:0fd5fca330dcd6a7…   1.04kB
<missing>           4 weeks ago         /bin/sh -c #(nop) COPY file:1d0a4127e78a26c1…   1.96kB
<missing>           4 weeks ago         /bin/sh -c #(nop) COPY file:e7e183879c35719c…   1.2kB
<missing>           4 weeks ago         /bin/sh -c set -x     && addgroup --system -…   63.6MB
<missing>           4 weeks ago         /bin/sh -c #(nop)  ENV PKG_RELEASE=1~buster     0B
<missing>           4 weeks ago         /bin/sh -c #(nop)  ENV NJS_VERSION=0.4.4        0B
<missing>           4 weeks ago         /bin/sh -c #(nop)  ENV NGINX_VERSION=1.19.3     0B
<missing>           4 weeks ago         /bin/sh -c #(nop)  LABEL maintainer=NGINX Do…   0B
<missing>           4 weeks ago         /bin/sh -c #(nop)  CMD ["bash"]                 0B
<missing>           4 weeks ago         /bin/sh -c #(nop) ADD file:0dc53e7886c35bc21…   69.2MB

"Il en remet une couche"

docker history nginx
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
f35646e83998        4 weeks ago         /bin/sh -c #(nop)  CMD ["nginx" "-g" "daemon…   0B
<missing>           4 weeks ago         /bin/sh -c #(nop)  STOPSIGNAL SIGTERM           0B
<missing>           4 weeks ago         /bin/sh -c #(nop)  EXPOSE 80                    0B
<missing>           4 weeks ago         /bin/sh -c #(nop)  ENTRYPOINT ["/docker-entr…   0B
<missing>           4 weeks ago         /bin/sh -c #(nop) COPY file:0fd5fca330dcd6a7…   1.04kB
<missing>           4 weeks ago         /bin/sh -c #(nop) COPY file:1d0a4127e78a26c1…   1.96kB
<missing>           4 weeks ago         /bin/sh -c #(nop) COPY file:e7e183879c35719c…   1.2kB
<missing>           4 weeks ago         /bin/sh -c set -x     && addgroup --system -…   63.6MB
<missing>           4 weeks ago         /bin/sh -c #(nop)  ENV PKG_RELEASE=1~buster     0B
<missing>           4 weeks ago         /bin/sh -c #(nop)  ENV NJS_VERSION=0.4.4        0B
<missing>           4 weeks ago         /bin/sh -c #(nop)  ENV NGINX_VERSION=1.19.3     0B
<missing>           4 weeks ago         /bin/sh -c #(nop)  LABEL maintainer=NGINX Do…   0B
<missing>           4 weeks ago         /bin/sh -c #(nop)  CMD ["bash"]                 0B
<missing>           4 weeks ago         /bin/sh -c #(nop) ADD file:0dc53e7886c35bc21…   69.2MB

Il est préférable de limiter le nombre de couches pour limiter la bande passante.

"Il en remet une couche"

RUN apk add \
     patch \
     tar \
     mercurial \
     git \
     ruby \
     ruby-devel \
     rubygem-bundler \
     make \
     gcc-c++ \
     zlib-devel \
     libxml2-devel \
     docker \
     nodejs \
     npm \
     sssd \
 && mkdir -p /var/lib/docker-builder \
 && mkdir -p /etc/docker-builder

Ne pas charger inutilement !

 && apk remove make gcc maven ... \
 && rm -f previously-downloaded.tar.gz \
 && apk clean all \
 && rm -rf /tmp/*

Il est utile, pour des raisons d’espace, de nettoyer le filesystem de ses futurs containers, en donnant les bonnes instructions dans le Dockerfile.

Documentez !

Les mots clés EXPOSE et LABEL peuvent fournir de précieuses informations aux utilisateurs de vos images !

(les ports exposés par défaut, les auteurs de l’image, la version d’un middleware embarqué, …)

Préparez-vous au read-only

Il est possible de lister toutes les modifications qui ont été apportées au filesystem d’un container.

docker diff 29f1c4
C /run
A /run/nginx.pid
C /var
C /var/lib
C /var/lib/nginx
C /var/lib/nginx/tmp
A /var/lib/nginx/tmp/client_body
A /var/lib/nginx/tmp/fastcgi
A /var/lib/nginx/tmp/proxy
A /var/lib/nginx/tmp/scgi
A /var/lib/nginx/tmp/uwsgi
C /var/log
C /var/log/nginx
A /var/log/nginx/access.log
A /var/log/nginx/error.log

Une bonne piste pour savoir quels volumes déclarer !

HealthCheck

Savoir que mon PID 1 est toujours en cours d’exécution n’est peut-être pas la meilleure piste pour savoir si mon conteneur est en bonne santé !

FROM ghost:3
RUN apt update && apt install curl -y \
        && rm -rf /var/lib/apt/lists/*

HEALTHCHECK --interval=1m --timeout=30s --retries=3 CMD curl --fail http://localhost:2368 || exit 1

Debugging

1939424428 image3

Une tonne d’outils !

Les logs

  • des containers

  • du démon Docker

docker container exec
docker inspect
docker cp
docker history
docker stats

Debugging : Les logs

rappel : docker container logs permet de lister les logs des conteneurs.

docker container logs 47d6
Fri Nov 20 00:39:52 UTC 2023
Fri Nov 20 00:39:53 UTC 2023

Concentrateur de logs

Par défaut, les logs des conteneurs sont stockés dans des fichiers json. Mais comment faire pour les envoyer vers un concentrateur ?

docker container run -d
--log-driver=gelf
--log-opt gelf-address=udp://localhost:12201
-p 88:80
nginx

on spécifie un driver

on configure le driver

Travaux pratiques #13

Étapes

Lancer une stack ELK grâce aux fichiers fournis dans le repo https://gitlab.univ-artois.fr/bruno.verachten/devops-docker-tp13

Alimenter en logs avec la commande docker container run --log-driver=gelf --log-opt gelf-address=udp://localhost:12201 alpine bash -c 'seq 1 100'

Créer un index "timestamp" sur Kibana puis aller à l’écran "discover"

Lancer un conteneur nginx et concentrez ses logs dans ELK

GitLab et Dependabot

press kit icon
27347476?s=200&v=4

🤔 Qu’est-ce que Dependabot ?

Dependabot est un outil qui vous aide à maintenir vos dépendances à jour.

27347476?s=200&v=4

Il ouvre automatiquement des merge requests pour les mises à jour de dépendances dans vos projets GitLab.

🛠️ Comment Dependabot fonctionne-t-il avec Docker ?

Dependabot peut analyser vos Dockerfiles et vos fichiers docker-compose pour trouver les dépendances qui peuvent être mises à jour.

Il crée ensuite des merge requests pour chaque mise à jour de dépendance.

🎯 Pourquoi utiliser Dependabot avec Docker ?

  1. Garder vos dépendances à jour est crucial pour la sécurité et la stabilité de vos applications.

  2. Dependabot automatise ce processus, vous faisant gagner du temps et réduisant le risque d’oublier une mise à jour importante.

  3. Dependabot n’est pas magique, il faut que votre CI soit capable de construire votre application avec les nouvelles dépendances.

  4. Ce qui n’est pas testé ne fonctionne pas.

🚀 Comment configurer Dependabot pour GitLab ? 🦊🛠️

  • Malheureusement, il n’y a pas encore de support officiel pour GitLab dans Dependabot (et vice versa).

  • Notre forge Gitlab est définie dans un fichier docker-compose.yml, et Dependabot aussi.

  • Dependabot doit avoir accès à notre forge GitLab pour pouvoir ouvrir des merge requests.

  • Suivons donc la documentation officielle.

🚀 Comment configurer Dependabot pour GitLab ? 🦊🛠️

Notez bien le token, vous ne le reverrez plus jamais.

L’étape suivante, c’est de positionner les valeurs de certaines variables d’environnement. Ça peut être dans un .env, ou dans les variables d’environnement de votre machine.

export SETTINGS__GITLAB_URL=http://localhost
export SETTINGS__GITLAB_ACCESS_TOKEN=glpat-LXUfrxeFXuRJXNTNNifD

🚀 Comment configurer Dependabot pour GitLab ? 🦊🛠️

Démarrez l’application avec docker compose:

curl -s https://gitlab.com/dependabot-gitlab/dependabot/-/raw/v3.8.0-alpha.1/docker-compose.yml | docker compose -f - up -d
dependabot cant see gitlab

😢

🚀 Comment configurer Dependabot pour GitLab ? 🦊🛠️

Vous vous souvenez du chapitre sur les réseaux Docker ?

  • Dans Docker, chaque conteneur a son propre espace de nom réseau, ce qui signifie que localhost à l’intérieur d’un conteneur fait référence au conteneur lui-même, et non à la machine hôte ou à d’autres conteneurs.

  • Lorsque vous essayez de faire un ping sur gitlab-instance-gitlab-1 depuis le conteneur web, il ne connaît pas le nom d’hôte gitlab-instance-gitlab-1 car il n’est pas dans le même espace de nom réseau.

  • Pour permettre aux conteneurs de communiquer entre eux, ils doivent être dans le même réseau Docker.

🚀 Comment configurer Dependabot pour GitLab ? 🦊🛠️

  • Lorsque vous utilisez Docker Compose, il crée automatiquement un réseau par défaut pour votre application et tout service défini dans le docker-compose.yml peut atteindre les autres en utilisant le nom du service comme nom d’hôte.

  • Dans notre cas, le conteneur web et le conteneur gitlab-instance-gitlab-1 ne sont pas dans le même réseau Docker.

  • Vous pouvez vérifier cela en inspectant les réseaux de chaque conteneur à l’aide de la commande docker inspect.

  • S’ils ne sont pas dans le même réseau, nous pouvons créer un réseau et ajouter les deux services à celui-ci dans notre fichier docker-compose.yml.

🚀 Comment configurer Dependabot pour GitLab ? 🦊🛠️

  • Voici un exemple :

version: '3'
services:
  web:
    image: web
    networks:
      - mynetwork
  gitlab-instance-gitlab-1:
    image: gitlab
    networks:
      - mynetwork
networks:
  mynetwork:
  • Après avoir mis à jour votre fichier docker-compose.yml, vous devez recréer vos conteneurs pour que les modifications prennent effet.

  • Vous pouvez le faire avec la commande docker compose up -d --force-recreate.

  • Après cela, vous devriez pouvoir faire un ping sur gitlab-instance-gitlab-1 depuis le conteneur web.

🚀 Comment configurer Dependabot pour GitLab ? 🦊🛠️

  web:
    image: *base_image
    networks:
[...]
networks:
  mynetwork:

Et…​

  gitlab:
    # The Docker image to use for the GitLab server
    image: 'gitlab/gitlab-ce:16.6.0-ce.0'
    networks:
      - mynetwork
[...]
networks:
  mynetwork:

🚀 Comment configurer Dependabot pour GitLab ? 🦊🛠️

Sauf que…​

root@95def7cb933d:/home/dependabot/app# nmap -sn 172.30.0.0/16
Starting Nmap 7.80 ( https://nmap.org ) at 2023-11-21 20:45 UTC
Nmap scan report for 172.30.0.1
Host is up (0.0000090s latency).
MAC Address: 02:42:50:A6:7C:5A (Unknown)
Nmap scan report for symbiosis-gitlab-1.symbiosis_mynetwork (172.30.0.2)
Host is up (0.000012s latency).
MAC Address: 02:42:AC:1E:00:02 (Unknown)
Nmap scan report for symbiosis-gitlab-runner-1-1.symbiosis_mynetwork (172.30.0.3)
Host is up (0.000015s latency).
MAC Address: 02:42:AC:1E:00:03 (Unknown)
Nmap scan report for symbiosis-gitlab-runner-2-1.symbiosis_mynetwork (172.30.0.4)
Host is up (0.000032s latency).
MAC Address: 02:42:AC:1E:00:04 (Unknown)
Nmap scan report for symbiosis-redis-1.symbiosis_mynetwork (172.30.0.5)
Host is up (0.0000070s latency).
MAC Address: 02:42:AC:1E:00:05 (Unknown)
Nmap scan report for symbiosis-mongodb-1.symbiosis_mynetwork (172.30.0.6)
Host is up (0.000016s latency).
MAC Address: 02:42:AC:1E:00:06 (Unknown)
Nmap scan report for symbiosis-docker-1.symbiosis_mynetwork (172.30.0.7)
Host is up (0.000021s latency).
MAC Address: 02:42:AC:1E:00:07 (Unknown)
Nmap scan report for symbiosis-worker-1.symbiosis_mynetwork (172.30.0.9)
Host is up (0.000024s latency).
MAC Address: 02:42:AC:1E:00:09 (Unknown)
Nmap scan report for 95def7cb933d (172.30.0.10)
Host is up.

🚀 Comment configurer Dependabot pour GitLab ? 🦊🛠️

Victoire?

docker compose -f docker-compose-dependabot.yml exec -it -u root web b
ash
root@95def7cb933d:/home/dependabot/app# ping symbiosis-gitlab-1.symbiosis_mynetwork
PING symbiosis-gitlab-1.symbiosis_mynetwork (172.30.0.2) 56(84) bytes of data.
64 bytes from symbiosis-gitlab-1.symbiosis_mynetwork (172.30.0.2): icmp_seq=1 ttl=64 time=0.165 ms
64 bytes from symbiosis-gitlab-1.symbiosis_mynetwork (172.30.0.2): icmp_seq=2 ttl=64 time=0.053 ms
64 bytes from symbiosis-gitlab-1.symbiosis_mynetwork (172.30.0.2): icmp_seq=3 ttl=64 time=0.039 ms
64 bytes from symbiosis-gitlab-1.symbiosis_mynetwork (172.30.0.2): icmp_seq=4 ttl=64 time=0.040 ms
64 bytes from symbiosis-gitlab-1.symbiosis_mynetwork (172.30.0.2): icmp_seq=5 ttl=64 time=0.056 ms
^C
--- symbiosis-gitlab-1.symbiosis_mynetwork ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4165ms
rtt min/avg/max/mdev = 0.039/0.070/0.165/0.047 ms
dependabot 404 gitlab

On y est presque!

Debugging : docker exec

rappel : `docker container exec `permet de lancer une commande dans un container

docker exec <containerID> echo "hello"
docker exec –it <containerID> bash

Debugging : docker inspect

rappel : `docker inspect `permet de lister toutes les caractéristiques d’une image ou d’un container.

On peut filtrer le retour de la commande avec jq ou l’option --format.

Debugging : docker cp

Cette commande permet d’échanger des fichiers entre un conteneur et la machine hôte.

docker cp --help
Usage:  docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-
        docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH
Copy files/folders between a container and the local filesystem
Options:
  -a, --archive       Archive mode (copy all uid/gid information)
  -L, --follow-link   Always follow symbol link in SRC_PATH

Debugging : docker history

Cette commande permet d’afficher la concaténation de tous les Dockerfiles qui ont abouti à cette image.

docker history nginx
IMAGE               CREATED             CREATED BY                                      SIZE                f35646e83998        4 weeks ago         /bin/sh -c #(nop)  CMD ["nginx" "-g" "daemon…   0B
<missing>           4 weeks ago         /bin/sh -c #(nop)  STOPSIGNAL SIGTERM           0B
<missing>           4 weeks ago         /bin/sh -c #(nop)  EXPOSE 80                    0B
<missing>           4 weeks ago         /bin/sh -c #(nop)  ENTRYPOINT ["/docker-entr…   0B
<missing>           4 weeks ago         /bin/sh -c #(nop) COPY file:0fd5fca330dcd6a7…   1.04kB
<missing>           4 weeks ago         /bin/sh -c #(nop) COPY file:1d0a4127e78a26c1…   1.96kB
<missing>           4 weeks ago         /bin/sh -c #(nop) COPY file:e7e183879c35719c…   1.2kB
<missing>           4 weeks ago         /bin/sh -c set -x     && addgroup --system -…   63.6MB
<missing>           4 weeks ago         /bin/sh -c #(nop)  ENV PKG_RELEASE=1~buster     0B
<missing>           4 weeks ago         /bin/sh -c #(nop)  ENV NJS_VERSION=0.4.4        0B
<missing>           4 weeks ago         /bin/sh -c #(nop)  ENV NGINX_VERSION=1.19.3     0B
<missing>           4 weeks ago         /bin/sh -c #(nop)  LABEL maintainer=NGINX Do…   0B
<missing>           4 weeks ago         /bin/sh -c #(nop)  CMD ["bash"]                 0B
<missing>           4 weeks ago         /bin/sh -c #(nop) ADD file:0dc53e7886c35bc21…   69.2MB

Debugging : docker stats

Cette commande permet d’avoir les stats en temps réel d’un conteneur.

docker stats 42f128
CONTAINER           CPU %     MEM USAGE/LIMIT     MEM %     NET I/O
42f128              0.00%     1.454 MB/4.145 GB   0.04%     648 B/648 B

🎓 Travaux pratiques #14

Notre valeureux collègue Jean-Michel s’initiait à l’art mystérieux de Docker, et généreusement partageait ses créations sous forme d’images Docker pour l’entreprise.

Michel a soudainement décroché le jackpot du Loto, laissant derrière lui son bureau du jour au lendemain, alors qu’il était sur le point de nous offrir une image Nginx.

Désormais, nous n’avons que le binaire de son chef-d’œuvre, accessible à cette adresse :

Rassurez-vous, on nous a dit que docker load est la première étape de la formule magique pour percer les secrets de son œuvre.

À vos claviers ! 🧙‍♂️✨

✅ Solution travaux pratiques #14🕵️‍♂️🔍

docker load est la première étape de la formule magique

Okay…​

🧙‍♂️✨

docker load -i bad-nginx.dkr
Loaded image: bad-nginx:1

Nous voilà bien avancés…​

On essaye de lancer un conteneur basé dessus ?

docker container run -d --name bad-1 bad-nginx:1
f4caa4a641729c0ab3d7b5974fa735c128047f75292c7f16495b72c7c6808502

C’est lancé ?

✅ Solution travaux pratiques #14🕵️‍♂️🔍

docker container ls --format '{{.Names}}' | grep "bad"

Oups, il n’est pas là…​

Essayons encore.

docker container ls -a --format '{{.Names}}' | grep "bad"
bad-1

C’est donc lancé, mais il a crashé.

Allons donc chercher les logs…​

docker container logs bad-1
Error : nginx is not executable

On a trouvé le problème !

Spoiler alert : on a trouvé UN problème, pas LE problème.

✅ Solution travaux pratiques #14🕵️‍♂️🔍

Je ne sais pas ce qu’a fait Jean-Michel, mais il a cassé le binaire de Nginx.

Est-ce un problème de permission, de dépendance, de version, de compilation ?

En tous cas, c’est cassé.

Regardons un peu ce que nous dit docker inspect de tout ça…​

docker image inspect bad-nginx:1
[
    {
        "Id": "sha256:a469e4d1cb0c61324298fc6fde09f78f211420a1848b690dec35d9ac324a6cab",
        "RepoTags": [
            "bad-nginx:1"
        ],
        [...]
        "ContainerConfig": {
            [...]
            "ExposedPorts": {
                "80/tcp": {}
            },

Port 80 exposé, c’est bon signe.

✅ Solution travaux pratiques #14🕵️‍♂️🔍

            [...]
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
            ],

On a un PATH, c’est bon signe.

            "Cmd": [
                "/bin/sh",
                "-c",
                "#(nop) ",
                "CMD [\"/bin/sh\" \"-c\" \"nginx\"]"
            ],

On a un CMD, c’est bon signe.

✅ Solution travaux pratiques #14🕵️‍♂️🔍

            [...]
            "Entrypoint": [
                "/bin/sh",
                "-c",
                "/tmp/entrypoint.sh"
            ],

On a un Entrypoint, c’est bon signe.

            [...]
            "Labels": {
                [...]
                "org.label-schema.name": "CentOS Base Image",
                [...]
            }
        },
        "DockerVersion": "18.09.0",
        [...]
        "Architecture": "amd64",
        "Os": "linux",
        [...]
    }
]

✅ Solution travaux pratiques #14🕵️‍♂️🔍

            [...]
            "Labels": {
                [...]
                "org.label-schema.name": "CentOS Base Image",
                [...]
            }
        },
        "DockerVersion": "18.09.0",
        [...]
        "Architecture": "amd64",
        "Os": "linux",
        [...]
    }
]

C’est bien une image Linux amd64 (donc pour ton PC, mais pas pour ton Mac M1, toi là-bas), mais argh, c’est basé sur CentOS, pas sur Alpine ou Debian !

Ça a été construit avec une vieille version de Docker.

Qu’est-ce qu’on fait maintenant ?

✅ Solution travaux pratiques #14🕵️‍♂️🔍

Tournons-nous vers l’histoire !

1200x768 stephane bern arrive elysee 4 octobre 2017
docker history bad-nginx:1
IMAGE          CREATED       CREATED BY                                      SIZE      COMMENT
a469e4d1cb0c   3 years ago   /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "ngin…   0B
<missing>      3 years ago   /bin/sh -c #(nop)  ENTRYPOINT ["/bin/sh" "-c…   0B
<missing>      3 years ago   /bin/sh -c #(nop)  EXPOSE 80                    0B
<missing>      3 years ago   /bin/sh -c #(nop) COPY file:b0fc89bcaf962602…   98B
<missing>      3 years ago   /bin/sh -c yum install -y nginx && yum clean…   56.7MB
<missing>      3 years ago   /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B
<missing>      3 years ago   /bin/sh -c #(nop)  LABEL org.label-schema.sc…   0B
<missing>      3 years ago   /bin/sh -c #(nop) ADD file:538afc0c5c964ce0d…   215MB

Ça se lit de bas en haut…​

Le premier ADD est l’image de base, CentOS.

La deuxième ligne, c’est une métadata, on s’en fiche un peu.

La troisième ligne, c’est le CMD de l’image de base, qui est bash.

La quatrième ligne, c’est l’installation de Nginx par l’infâme yum de Centos (déjà repéré dans le docker inspect).

✅ Solution travaux pratiques #14🕵️‍♂️🔍

docker history bad-nginx:1
IMAGE          CREATED       CREATED BY                                      SIZE      COMMENT
a469e4d1cb0c   3 years ago   /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "ngin…   0B
<missing>      3 years ago   /bin/sh -c #(nop)  ENTRYPOINT ["/bin/sh" "-c…   0B
<missing>      3 years ago   /bin/sh -c #(nop)  EXPOSE 80                    0B
<missing>      3 years ago   /bin/sh -c #(nop) COPY file:b0fc89bcaf962602…   98B
<missing>      3 years ago   /bin/sh -c yum install -y nginx && yum clean…   56.7MB
<missing>      3 years ago   /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B
<missing>      3 years ago   /bin/sh -c #(nop)  LABEL org.label-schema.sc…   0B
<missing>      3 years ago   /bin/sh -c #(nop) ADD file:538afc0c5c964ce0d…   215MB

La cinquième ligne, c’est sans doute la copie du fichier index.html.

La sixième ligne, c’est l' EXPOSE de l’image finale, qui est 80 (déjà repéré dans le docker inspect).

La septième ligne, c’est le ENTRYPOINT de l’image finale, qui est /bin/sh -c /tmp/entrypoint.sh (déjà repéré dans le docker inspect).

✅ Solution travaux pratiques #14🕵️‍♂️🔍

Résumons-nous…​ On a:

  • une image de base CentOS (à remplacer par Alpine ou Debian)

  • une installation de nginx.

  • un port 80 exposé

  • un ENTRYPOINT qui lance un script nginx

  • un fichier HTML, qu’on n’a pas encore récupéré Ça progresse…​ On peut donc créer un premier 💀 Dockerfile

✅ Solution travaux pratiques #14🕵️‍♂️🔍💀

# Utiliser Alpine ou Debian comme image de base
FROM alpine

# Mettre à jour le système et installer nginx
RUN apk update && apk add nginx

# Exposer le port 80
EXPOSE 80

# Ajouter un fichier HTML (remplacer /chemin/vers/votre/fichier.html par le chemin réel vers votre fichier HTML)
ADD /chemin/vers/votre/fichier.html /usr/share/nginx/html

# Définir le point d'entrée pour lancer nginx
ENTRYPOINT ["nginx", "-g", "daemon off;"]

Comment récupérer ce fichu fichier HTML?

✅ Solution travaux pratiques #14🕵️‍♂️🔍

Essayons de lancer un conteneur basé sur notre image, et de récupérer le fichier HTML.

Le binaire nginx est non fonctionnel, essayons de lancer un conteneur avec un shell.

docker container run -it --name bad-2 --entrypoint=sh bad-nginx:1 sh
/usr/bin/sh: /usr/bin/sh: cannot execute binary file
francais queendugif

Oui, je sais, il y a une faute dans la légende du gif…​

Jean-Micheeeeeeeeeeeeeeeeel, j’ai deux mots à te dire! 😡

✅ Solution travaux pratiques #14🕵️‍♂️🔍

Le conteneur qui tourne, on oublie, donc…​ Jean-Michel a laissé un terrain miné derrière lui. 💣

Mais on a d’autres armes en réserve…​ Le docker container create par exemple !

docker container create --name temp_container bad-nginx:1
9149745197df413f7966181f6ca517b68713edd32ff7c35cea6e958d549a7553

Utilisons maintenant docker cp pour récupérer le fichier HTML.

Quel est le répertoire où nginx s’attend à trouver ses fichiers HTML à servir?

/usr/share/nginx/html

mkdir /tmp/html && cd /tmp/html
docker cp temp_container:/usr/share/nginx/html .

✅ Solution travaux pratiques #14🕵️‍♂️🔍

head html/index.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
    <head>
        <title>Test Page for the Nginx HTTP Server on Red Hat Enterprise Linux</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <style type="text/css">
            /*<![CDATA[*/
            body {
                background-color: #fff;

Jean-Michel n’avait même pas fini de remplir son fichier HTML…​ C’est encore le fichier de base de nginx sous CentOs. 😞

Jean-Michel, t’assures pas une 🥜, là…​

✅ Solution travaux pratiques #14🕵️‍♂️🔍

# Ce Dockerfile met en place un serveur web simple en utilisant Nginx sur Alpine Linux.

# Utilise Alpine Linux comme image de base. Alpine Linux est une distribution Linux légère et orientée sécurité.
FROM alpine

# Met à jour la liste des paquets du système et installe Nginx.
# Nginx est un serveur web populaire qui peut également être utilisé comme proxy inverse, équilibreur de charge et cache HTTP.
RUN apk update && apk add nginx && rm -rf /var/cache/apk/*

# Expose le port 80 au monde extérieur. C'est le port standard pour le trafic HTTP.
EXPOSE 80

# Ajoute un fichier HTML à la racine du document Nginx.
# Remplacez /chemin/vers/votre/fichier.html par le chemin réel vers votre fichier HTML.
ADD /chemin/vers/votre/fichier.html /usr/share/nginx/html

# Définit le point d'entrée pour le conteneur. Cette commande sera exécutée lorsque le conteneur démarre.
# La commande "nginx" démarre le serveur Nginx.
# Le flag "-g" nous permet de définir des directives globales. Dans ce cas, nous disons à Nginx de fonctionner au premier plan (daemon off;).
ENTRYPOINT ["nginx", "-g", "daemon off;"]

Finalement, le 💀 de notre Dockerfile était déjà bon…​

Ou presque…​ Rien ne vous gêne dans ce Dockerfile?

👫 don’t let 👫…​

🐳😴 Lazy Docker

Conseils pour l’éval'

Conseils pour l’eval'

dont panic

Vous devez avoir les compétences suivantes:

Savoir lancer un container avec toutes les options

  • -v, -p, -w, -e, --rm, etc.

Écrire un Dockerfile optimisé pour "dockeriser" une application

  • pas trop gourmand, facile à modifier, paramétrable.

Étudier une image ou un conteneur pour tout débug éventuel.

Savoir s’outiller pour avoir des images qui servent d’environnement d’exécution à votre CI.

Monter un écosystème applicatif complexe via docker-compose.

Que revoir ?

🤔👉 One more thing

415439355 image7

Bibliographie

Ligne de commande

Git / VCS

Intégration Continue

Docker

DevOps

Merci !

QRCode to this presentation