đŸ‡«đŸ‡· Devops

Docker

2026/2027

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

xcpng

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…​ 🚧

  • 22 septembre aprĂšs-midi

  • 29 septembre aprĂšs-midi

  • 06 octobre 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 Ă  me rendre

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

Depuis mars 2013, soit 13 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

SchĂ©ma comparant un hyperviseur de type 1, posĂ© directement sur le matĂ©riel, et un hyperviseur de type 2, posĂ© sur un systĂšme d’exploitation hĂŽte

Comment ça marche ?

Virtualisation

Logos d’hyperviseurs de type 1 : VMware vSphere, Microsoft Hyper-V, XenServer, XCP-ng, KVM et Proxmox VE
Logos d’hyperviseurs de type 2 : VMware Workstation, VirtualBox, VMware Fusion, QEMU et Microsoft Virtual PC

Un type 1 que vous pouvez installer

Logo de XCP-ng
⚖ Transparence : je travaille chez Vates, l’Ă©diteur de XCP-ng et de Xen Orchestra. Je les montre parce qu’ils sont libres et ouvrables, pas parce qu’il faudrait les acheter.
  • BĂątie sur l’hyperviseur Xen : elle s’installe sur le matĂ©riel nu, donc bien du type 1

  • NĂ©e d’un fork de XenServer, aujourd’hui projet en incubation du Xen Project, hĂ©bergĂ© par la Linux Foundation

  • L’ISO se tĂ©lĂ©charge et s’installe sans licence : un type 1 que vous pouvez vraiment essayer, sur une machine que vous lui dĂ©diez — elle ne fera plus que ça

On ne pilote pas XCP-ng Ă  la main

  • Sur un hĂŽte XCP-ng, ni bureau ni fenĂȘtre : la machine ne montre qu’une console

  • L’administration passe par un outil sĂ©parĂ©, qui parle Ă  l’hĂŽte par le rĂ©seau

  • Xen Orchestra est celui de XCP-ng : visualiser, gĂ©rer, sauvegarder et dĂ©lĂ©guer un parc, depuis un navigateur

  • Sauvegardes complĂštes ou incrĂ©mentales, rĂ©plication, instantanĂ©s tournants — qui ne remplacent pas une sauvegarde

  • Deux façons de l’obtenir : l’appliance clĂ© en main, ou une installation depuis les sources

VM et conteneur ne s’opposent pas

┌──────────────────────┐
│ vos conteneurs de TP │
├───────────────────────
│  la VM du Codespace  │
├───────────────────────
│   un hyperviseur,    │
│   chez l'hĂ©bergeur   │
├───────────────────────
│     le matĂ©riel      │
└──────────────────────┘
  • Depuis le premier TP, vos conteneurs tournent dĂ©jĂ  dans une machine virtuelle : GitHub hĂ©berge chaque Codespace sur une VM

  • La question n’est donc jamais « VM ou conteneur ? Â», mais « qu’est-ce que je mets Ă  quel Ă©tage ? Â»

  • Dans les TP, on ne travaillera qu’Ă  l’Ă©tage du haut

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

Particularités du réseau

Utiliser Docker derriĂšre un proxy ou un cache universitaire

Pourquoi ? – Le Problùme

Lorsque nous sommes sur le rĂ©seau de l’universitĂ©, Docker a du mal Ă  tĂ©lĂ©charger directement les images depuis Docker Hub Ă  cause des restrictions rĂ©seau. Nous sortons tous avec la mĂȘme adresse IP, et en peu de temps, elle sera blacklistĂ©e pour un moment, ce qui nous pĂ©nalisera tous. Pour rĂ©soudre ce problĂšme, l’universitĂ© met Ă  disposition un serveur de cache local.

1. Concepts de base Docker

Avant toute configuration, introduisons les notions essentielles, que nous verrons en détail par la suite :

# Que sont les images Docker ?
docker image pull hello-world
docker container run hello-world

# Comment les images Docker sont nommées ?
[registry/][username/]image-name[:tag]

# Exemples :
library/ubuntu:22.04   # Image officielle (library est implicite)
ubuntu:22.04           # Identique Ă  ci-dessus
user/mon-app:latest    # Image personnalisée

2. Le concept de cache de registre

Au lieu de rĂ©cupĂ©rer les images directement depuis Docker Hub, nous utilisons le cache fourni par l’universitĂ©.

2. Le concept de cache de registre

Au lieu de rĂ©cupĂ©rer les images directement depuis Docker Hub, nous utilisons le cache fourni par l’universitĂ©.

Avant (problĂšme) :

docker image pull ubuntu:22.04   # Échec sur le rĂ©seau universitaire, ou succĂšs temporaire avant d'ĂȘtre blacklistĂ©s

2. Le concept de cache de registre

Au lieu de rĂ©cupĂ©rer les images directement depuis Docker Hub, nous utilisons le cache fourni par l’universitĂ©.

Avant (problĂšme) :

docker image pull ubuntu:22.04   # Échec sur le rĂ©seau universitaire, ou succĂšs temporaire avant d'ĂȘtre blacklistĂ©s

AprĂšs (solution) :

docker image pull cache-ili.univ-artois.fr:80/proxy_cache/library/ubuntu:22.04

3. Approche simplifiée de configuration

Option A : utiliser un registry mirror.

3. Approche simplifiée de configuration

Option A : utiliser un registry mirror.

Modifier /etc/docker/daemon.json :

{
  "registry-mirrors": ["http://cache-ili.univ-artois.fr:80/proxy_cache/"],
  "insecure-registries": ["cache-ili.univ-artois.fr:80"]
}

3. Approche simplifiée de configuration

Option A : utiliser un registry mirror.

Modifier /etc/docker/daemon.json :

{
  "registry-mirrors": ["http://cache-ili.univ-artois.fr:80/proxy_cache/"],
  "insecure-registries": ["cache-ili.univ-artois.fr:80"]
}

Désormais, les commandes Docker standards fonctionnent :

docker image pull ubuntu:22.04   # Utilise automatiquement le cache !
docker container run gradle      # Fonctionne de maniĂšre transparente

4. Script étape par étape

Étape 1 – Comprendre le problùme

  • Docker Hub nous voit tous avec la mĂȘme adresse IP, qui mĂšne au blacklist

  • Nous devons utiliser le serveur de cache local.

4. Script étape par étape

Étape 1 – Comprendre le problùme

  • Docker Hub nous voit tous avec la mĂȘme adresse IP, qui mĂšne au blacklist

  • Nous devons utiliser le serveur de cache local.

Étape 2 – Configurer Docker une seule fois

sudo nano /etc/docker/daemon.json

4. Script étape par étape

Étape 1 – Comprendre le problùme

  • Docker Hub nous voit tous avec la mĂȘme adresse IP, qui mĂšne au blacklist

  • Nous devons utiliser le serveur de cache local.

Étape 2 – Configurer Docker une seule fois

sudo nano /etc/docker/daemon.json

Ajouter :

{
  "registry-mirrors": ["http://cache-ili.univ-artois.fr:80/proxy_cache/"],
  "insecure-registries": ["cache-ili.univ-artois.fr:80"]
}

4. Script étape par étape

Étape 1 – Comprendre le problùme

  • Docker Hub nous voit tous avec la mĂȘme adresse IP, qui mĂšne au blacklist

  • Nous devons utiliser le serveur de cache local.

Étape 2 – Configurer Docker une seule fois

sudo nano /etc/docker/daemon.json

Ajouter :

{
  "registry-mirrors": ["http://cache-ili.univ-artois.fr:80/proxy_cache/"],
  "insecure-registries": ["cache-ili.univ-artois.fr:80"]
}

Étape 3 – RedĂ©marrer Docker

sudo systemctl restart docker

4. Script étape par étape

Étape 3 – RedĂ©marrer Docker

sudo systemctl restart docker

Étape 4 – Utiliser Docker normalement

docker image pull ubuntu:22.04    # Utilise automatiquement le cache
docker container run python:3.9   # Fonctionne sans problĂšme

4. Script étape par étape

Étape 4 – Utiliser Docker normalement

docker image pull ubuntu:22.04    # Utilise automatiquement le cache
docker container run python:3.9   # Fonctionne sans problĂšme

Étape 5 – Explication visuelle

Votre client Docker → Cache universitaire → Docker Hub (si nĂ©cessaire) (cache-ili.univ-artois.fr)

Avantages : - Téléchargements plus rapides (images déjà en cache) - Fonctionne derriÚre le pare-feu universitaire - Pas besoin de changer vos commandes

4. Script étape par étape

Étape 5 – Explication visuelle

Votre client Docker → Cache universitaire → Docker Hub (si nĂ©cessaire) (cache-ili.univ-artois.fr)

Avantages : - Téléchargements plus rapides (images déjà en cache) - Fonctionne derriÚre le pare-feu universitaire - Pas besoin de changer vos commandes

Étape 6 – Problùmes courants

# Permission refusée ? Ajoutez votre utilisateur au groupe docker :
sudo usermod -aG docker $USER
newgrp docker   # Ou déconnectez-vous / reconnectez-vous

# Toujours un souci ? Testez le cache :
docker image pull cache-ili.univ-artois.fr:80/proxy_cache/library/hello-world

5. Proxy HTTP pour les conteneurs et les builds

Le cache de registre ne concerne que les images (docker image pull, FROM).

Ce qui se passe dans un conteneur ou dans un RUN (apk add, pip install, curl) sort sur le rĂ©seau et doit passer par le proxy HTTP de l’universitĂ© : cache-etu.univ-artois.fr:3128.

5. Proxy HTTP pour les conteneurs et les builds

Option 1 – ~/.docker/config.json (RecommandĂ©)

Créer ou modifier ~/.docker/config.json :

{
  "proxies": {
    "default": {
      "httpProxy": "http://cache-etu.univ-artois.fr:3128",
      "httpsProxy": "http://cache-etu.univ-artois.fr:3128",
      "noProxy": "localhost,127.0.0.1"
    }
  }
}

5. Proxy HTTP pour les conteneurs et les builds

Option 1 – ~/.docker/config.json (RecommandĂ©)

Le client Docker ajoute alors les variables de proxy :

  • Ă  chaque nouveau conteneur (docker container run) ;

  • Ă  chaque docker image build, comme arguments de build.

docker container run --rm alpine:3.24.2 env | grep -i proxy
# HTTP_PROXY=http://cache-etu.univ-artois.fr:3128
# HTTPS_PROXY=... NO_PROXY=... et les formes en minuscules

L’image construite, elle, ne garde rien : docker image inspect ne montre que PATH dans Config.Env. Aucun redĂ©marrage n’est nĂ©cessaire, et le Dockerfile reste le mĂȘme partout.

5. Proxy HTTP pour les conteneurs et les builds

Option 1 – Deux limites

  • Les noms de conteneurs : noProxy ne les couvre pas. Un curl web2 entre deux conteneurs part vers le proxy. Utilisez curl --noproxy '*' web2.

  • Maven ignore HTTP_PROXY et HTTPS_PROXY. Il lui faut un settings.xml (voir les travaux pratiques #7). apk, wget, curl et git, eux, suivent les variables.

5. Proxy HTTP pour les conteneurs et les builds

Option 2 – Arguments de build, pour un build ponctuel

docker image build \
  --build-arg HTTP_PROXY=http://cache-etu.univ-artois.fr:3128 \
  --build-arg HTTPS_PROXY=http://cache-etu.univ-artois.fr:3128 .

Dockerfile correspondant, sans ligne ARG :

FROM alpine:3.24.2
RUN apk add --no-cache curl

HTTP_PROXY, HTTPS_PROXY, NO_PROXY (et leurs minuscules) sont des arguments prédéfinis : les RUN les voient sans déclaration.

5. Proxy HTTP pour les conteneurs et les builds

Option 2 – Arguments de build : le piùge de ARG

Si le Dockerfile dĂ©clare ARG HTTPS_PROXY, la valeur apparaĂźt dans l’historique de l’image :

docker image history --no-trunc --format '{{.CreatedBy}}' mon-image | grep -i proxy
# RUN |1 HTTPS_PROXY=http://cache-etu.univ-artois.fr:3128 /bin/sh -c apk add --no-cache curl # buildkit
# ARG HTTPS_PROXY=http://cache-etu.univ-artois.fr:3128

Sans la ligne ARG, les arguments prédéfinis sont exclus de docker image history. Ne déclarez donc pas les variables de proxy dans le Dockerfile.

5. Proxy HTTP pour les conteneurs et les builds

Option 3 – Proxy avec identifiants : secret de build

Un proxy qui demande un login contient le mot de passe dans son URL. Pour le passer Ă  un seul RUN sans qu’il reste nulle part, BuildKit propose les secrets de build :

FROM alpine:3.24.2
RUN --mount=type=secret,id=proxy,env=HTTPS_PROXY \
    apk add --no-cache curl
read -rsp 'URL du proxy : ' PROXY_URL; echo   # saisie masquée
PROXY_URL="$PROXY_URL" docker image build --secret id=proxy,env=PROXY_URL .
unset PROXY_URL

Le mot de passe ne passe ni par l’historique du shell, ni par un export : seul ce docker image build le reçoit.

Le secret n’existe que pendant ce RUN : ni docker image history, ni Config.Env ne le contiennent.

5. Proxy HTTP pour les conteneurs et les builds

Option 4 – ENV dans le Dockerfile : Ă  Ă©viter

FROM alpine:3.24.2
ENV HTTPS_PROXY=http://cache-etu.univ-artois.fr:3128
RUN apk add --no-cache curl

Ça fonctionne (ENV aprĂšs FROM et avant le RUN), mais la valeur est gravĂ©e dans l’image :

  • elle apparaĂźt dans docker image history et dans Config.Env ;

  • chaque conteneur lancĂ© depuis cette image, partout, essaiera de passer par le proxy de l’universitĂ© ;

  • si l’URL contient un mot de passe, il est livrĂ© avec l’image.

À rĂ©server au dĂ©pannage, sur une image que vous ne partagez pas.

5. Proxy HTTP pour les conteneurs et les builds

Option 5 – daemon.json : le proxy du dĂ©mon seulement

{
  "proxies": {
    "http-proxy": "http://cache-etu.univ-artois.fr:3128",
    "https-proxy": "http://cache-etu.univ-artois.fr:3128",
    "no-proxy": "localhost,127.0.0.1"
  }
}

Ce bloc de /etc/docker/daemon.json sert au dĂ©mon : pull et push vers les registres, communication entre nƓuds d’un swarm. Il ne s’applique ni aux processus des conteneurs, ni aux RUN d’un build : apk add Ă©choue toujours. Docker Desktop l’ignore (le proxy se rĂšgle dans ses paramĂštres).

6. En résumé

MĂ©thodeConteneursRUN d’un buildDans l’image

~/.docker/config.json

oui

oui

non

--build-arg, sans ARG

non

oui

non

--build-arg + ARG

non

oui

historique

secret de build

non

un RUN

non

ENV

oui

oui

oui

daemon.json

non

non

non

6. En résumé (suite)

  • Pour la salle de TP : ~/.docker/config.json.

  • Pour un proxy avec mot de passe : un secret de build.

  • Le cache de registre (registry-mirrors) reste utile, mais pour les images seulement.

Environnement Cloud : GitHub Codespaces ☁

Outils NĂ©cessaires 🛠

  • Un navigateur web rĂ©cent (et dĂ©cent)

  • Un compte sur GitHub

GitHub Codespaces

GitHub Codespaces : Environnement de dĂ©veloppement dans le ☁ "nuage"

  • But: reproductible et instantanĂ©

  • Puissance de calcul sur un serveur distant

  • Éditeur VSCode dans le navigateur

  • Docker dĂ©jĂ  installĂ© et configurĂ© 🐳

Gratuit : 60h/mois pour compte GitHub standard 🎉
Gratuit : 120h/mois avec GitHub Student Developer Pack 🎓

Pourquoi Codespaces ? đŸ€”

  • ✅ Pas d’installation locale nĂ©cessaire

  • ✅ Docker prĂ©-configurĂ© et prĂȘt Ă  l’emploi

  • ✅ Environnement identique pour tous les Ă©tudiants

  • ✅ Accessible depuis n’importe quel ordinateur

  • ✅ Sauvegarde automatique dans le cloud

  • ✅ Gratuit et gĂ©nĂ©reux en ressources

PrĂ©requis 📋

Vous avez dĂ©jĂ  ce qu’il faut ! ✓

  • Compte GitHub (créé prĂ©cĂ©demment)

  • 🌐 Navigateur web moderne (Chrome, Firefox, Edge, Safari)

  • đŸ“¶ Connexion Internet stable

💡 Astuce : PrĂ©fĂ©rez Chrome ou Edge pour une meilleure compatibilitĂ© VSCode

DĂ©marrer avec Codespaces 🚀

codespaces create button
  1. Rendez-vous sur un dépÎt GitHub

  2. Cliquez sur le bouton "Code" (en haut Ă  droite) âŹ‡ïž

  3. SĂ©lectionnez l’onglet "Codespaces"

  4. Cliquez sur "Create codespace on main"

⚠ Patientez quelques secondes…​ ⏳
⚠ Passez Ă  la slide suivante pour voir l’interface

Interface Codespaces đŸ’»

codespaces interface placeholder
Gauche : Explorateur de fichiers
→ Arborescence, Git

Centre : Éditeur de code
→ Coloration, autocomplĂ©tion

Bas : Terminal intégré
→ Bash/Zsh, Docker

💡 Identique à VSCode Desktop !

Terminal Codespaces đŸ–„ïž

Le terminal est votre outil principal pour Docker !

# Vérifier l'utilisateur
whoami
# Résultat attendu : codespace

# Vérifier Docker
docker --version
# Résultat attendu : Docker version XX.XX.X

# Tester Docker
docker container run hello-world
⚠ Passez Ă  la slide suivante pour comprendre la configuration
codespaces terminal output

Configuration Codespaces (Optionnelle) ⚙

Codespaces peut ĂȘtre personnalisĂ© avec .devcontainer/

// .devcontainer/devcontainer.json
{
  "name": "Docker DevOps Course",
  "image": "mcr.microsoft.com/devcontainers/base:ubuntu",
  "features": {
    "ghcr.io/devcontainers/features/docker-in-docker:2": {}
  }
}
💡 Pour ce cours : Pas besoin de configuration spĂ©ciale !
Docker est dĂ©jĂ  disponible par dĂ©faut 🎉

Checkpoint 🎯

Vérifiez que tout fonctionne :

  1. ✓ Terminal ouvert (Ctrl+`)

  2. ✓ Commande whoami retourne codespace

  3. ✓ Commande docker --version affiche la version

  4. ✓ Commande docker container run hello-world s’exĂ©cute avec succĂšs

✅ Si tout fonctionne : vous ĂȘtes prĂȘt pour la suite ! 🚀
❌ Si un problùme : levez la main ou consultez la documentation

Gestion de votre Codespace 🔧

Optimisez votre quota gratuit de 60h/mois :

  • 🛑 ArrĂȘter le Codespace quand vous ne l’utilisez pas

  • ♻ RĂ©utiliser le mĂȘme Codespace (pas besoin d’en crĂ©er un nouveau)

  • 📊 Surveiller votre consommation : GitHub Settings > Billing

  • ⏱ ArrĂȘt automatique : Par dĂ©faut aprĂšs 30 min d’inactivitĂ©

💡 Astuce : 60h = 2h/semaine pendant 30 semaines (largement suffisant !)

Avantages de Codespaces 🌟

Pour vous :

  • 🚀 DĂ©marrage instantanĂ©

  • đŸ’Ÿ Pas de configuration locale

  • 🔄 Environnement cohĂ©rent

  • đŸ“± Accessible partout

Pour le cours :

  • ✅ MĂȘme environnement pour tous

  • 🐛 Debugging simplifiĂ©

  • 🎓 Focus sur Docker, pas sur l’installation

  • 🌐 Collaboration facile

Limites et Alternatives ⚠

Limites de Codespaces :

  • đŸ“¶ NĂ©cessite une connexion Internet stable

  • ⏱ Quota mensuel (60h gratuit)

  • 🔒 DĂ©pendance Ă  GitHub

Alternative : Installation locale de Docker

  • ✅ Pas de limite de temps

  • ✅ Fonctionne hors-ligne

  • ❌ Configuration plus complexe

💡 Vous pouvez combiner les deux approches !

Ressources et Aide 📚

Documentation officielle :

Support :

  • 🙋 Questions pendant le cours

  • 💬 Forum/Discord du cours (si disponible)

  • 🐛 GitHub Issues pour bugs

💡 N’hĂ©sitez jamais Ă  poser des questions !

Recap : GitHub Codespaces ✅

Ce que vous devez retenir :

  • ☁ Environnement de dĂ©veloppement cloud

  • 🐳 Docker prĂ©-installĂ© et prĂȘt

  • 🆓 60h/mois gratuit (largement suffisant)

  • 🚀 DĂ©marrage en 30 secondes

  • đŸ’» Interface VSCode familiĂšre

  • 🛑 Penser Ă  arrĂȘter pour Ă©conomiser

âžĄïž PrĂȘts pour commencer Ă  utiliser Docker !

🐳 Installation locale de Docker

Installer Docker sur Ubuntu 26.04 🐧

Le piĂšge : apt install docker.io n’installe pas ce qu’il faut

⚠ Ni docker compose, ni docker buildx, et Docker demande de retirer ce paquet

Pourquoi pas docker.io ? đŸ€”

Ce que donne apt install docker.io

docker compose version
# docker: unknown command: docker compose

docker buildx version
# docker: unknown command: docker buildx
Les deux plugins sont en Suggests:, donc apt ne les installe pas

Et la doc Docker demande de le retirer

docker.io figure sur la liste officielle des paquets Ă  dĂ©sinstaller avant d’installer Docker, aux cĂŽtĂ©s de podman-docker.
âžĄïž On utilise donc le dĂ©pĂŽt apt officiel Docker

⚠ D’abord : ĂȘtes-vous derriĂšre le proxy ? đŸ«

Sur le rĂ©seau de l’universitĂ©, apt et curl doivent passer par le proxy. DĂ©finir http_proxy dans son shell ne suffit pas : sudo efface les variables d’environnement.

sudo tee /etc/apt/apt.conf.d/99proxy <<'PROXY'
Acquire::http::Proxy "http://cache-etu.univ-artois.fr:3128/";
Acquire::https::Proxy "http://cache-etu.univ-artois.fr:3128/";
PROXY

export http_proxy=http://cache-etu.univ-artois.fr:3128/
export https_proxy=http://cache-etu.univ-artois.fr:3128/
🏠 Chez vous, rien de tout ça : passez directement Ă  l’Ă©tape 1

Étape 1 : retirer les paquets en conflit đŸ§č

sudo apt remove $(dpkg --get-selections \
  docker.io docker-compose docker-compose-v2 \
  docker-doc docker-buildx podman-docker \
  containerd runc | cut -f1)
✅ Sans effet si aucun n’est installĂ© — la commande ne retire rien et se termine normalement

Étape 2 : ajouter le dĂ©pĂŽt officiel 🔑

sudo apt update
sudo apt install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings

curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /tmp/docker.asc
sudo install -m 0644 /tmp/docker.asc /etc/apt/keyrings/docker.asc
🔑 La clĂ© GPG sert Ă  vĂ©rifier la signature des paquets tĂ©lĂ©chargĂ©s
⚠ curl sans sudo, volontairement : lancĂ© en root il perdrait votre proxy

Étape 2 (suite) : dĂ©clarer la source 📝

sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF

sudo apt update

Étape 3 : installer et vĂ©rifier ✅

sudo apt install docker-ce docker-ce-cli containerd.io \
  docker-buildx-plugin docker-compose-plugin

sudo usermod -aG docker $USER
⚠ Logout/login requis pour que le groupe docker s’applique

Ça marche ? 🎯

docker --version
docker compose version
docker buildx version
docker container run hello-world
✅ Cette fois docker compose et docker buildx rĂ©pondent

Et si Podman est installĂ© ? đŸ€”

SymptÎme : docker semble installé, mais répond Cannot connect to the Docker daemon

C’est le paquet podman-docker qui pose un /usr/bin/docker redirigeant vers Podman. Podman seul ne fournit aucune commande docker : on obtiendrait command not found.
# Qui répond, au juste ?
command -v docker
echo $DOCKER_HOST
docker context ls

# Retirer le wrapper suffit
sudo apt remove podman-docker
⚠ Ubuntu n’installe pas Podman par dĂ©faut : ce cas ne concerne que qui l’a installĂ© lui-mĂȘme

Et si le tĂ©lĂ©chargement bloque ? đŸ•łïž

SymptĂŽme : curl ou apt restent muets, puis expirent. Pourtant echo $http_proxy affiche bien le proxy, et la mĂȘme commande sans sudo passe.

sudo repart d’un environnement neuf : sudoers(5) appelle ça env_reset, actif par dĂ©faut. http_proxy n’est dans aucune liste conservĂ©e, donc le curl lancĂ© en root sort en direct et se fait jeter par le rĂ©seau.
# Le proxy est-il visible par root ?
sudo printenv http_proxy
# (aucune sortie)

# Trois sorties possibles
sudo -E curl ...
curl ... puis sudo install ...
/etc/apt/apt.conf.d/99proxy
💡 Rien dans le message d’erreur ne nomme sudo : c’est ce qui rend la panne longue Ă  diagnostiquer

Environnement prĂȘt ! ✅

Votre environnement Docker est maintenant configurĂ© 🎉

  • ✅ GitHub Codespaces : Docker dĂ©jĂ  installĂ©

  • ✅ Ubuntu 26.04 : Docker installĂ© par le dĂ©pĂŽt officiel

  • ✅ PrĂȘt pour la suite !

Prochaine Ă©tape 🚀

Avant de dĂ©couvrir Docker, un dĂ©tour essentiel…​

La ligne de commande (CLI)

Docker s’utilise exclusivement en ligne de commande !

Pourquoi la CLI ? đŸ’»

  • đŸ–„ïž Interface fondamentale pour Docker

  • 🔧 Base de tous les outils DevOps

  • đŸ’Ș CompĂ©tence indispensable pour automatiser

âžĄïž Prochain chapitre : Guide de survie de la ligne de commande

đŸ€– Et quand une machine s’en charge : GitHub Actions

La mĂȘme image, ailleurs đŸ€–

Votre Codespace tourne. Docker répond. Tout marche.

Et sur la machine du voisin ?

Et sur le serveur qui construira le projet Ă  3 h du matin, sans vous ?

GitHub Actions en une diapo 📄

Un fichier dans .github/workflows/, et GitHub exécute vos commandes à chaque push.

name: Bonjour
on:
  - push
jobs:
  dire_bonjour:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7 # RécupÚre le contenu du dépÎt correspondant au commit du workflow en cours
runs-on : la machine que GitHub vous prĂȘte. steps : ce qu’elle fait.

Le runner n’est pas votre Codespace ⚠

Votre Codespace

devcontainers/base:ubuntu

Vos outils, votre version

Le runner par défaut

ubuntu-latest

Les outils de GitHub, leurs versions

Deux environnements diffĂ©rents. Les mĂȘmes commandes n’y donnent pas le mĂȘme rĂ©sultat.

La mĂȘme image, en CI ✅

    container:
      image: mcr.microsoft.com/devcontainers/base:ubuntu # La mĂȘme image que le Codespace
Trois lignes. Le job ne tourne plus sur le runner, mais dans le conteneur.

Trois piĂšges, mesurĂ©s 🔍

sudo : inutile, dans un conteneur le job tourne déjà en root (uid=0)
cowsay — installĂ© dans /usr/games, absent du PATH d’un shell non interactif
README.md — ce dĂ©pĂŽt n’en a pas. Il a un README.adoc

🎓 Exercice : la mĂȘme image partout

  • But : faire dire le README du dĂ©pĂŽt par une vache, depuis la CI

  • đŸ‘·đŸœâ€â™€ïž À vous d’Ă©crire le workflow :

    • 💡 le job doit tourner dans la mĂȘme image que votre Codespace

    • 💡 cowsay n’est pas installĂ© dans l’image : il faut l’ajouter

    • 💡 pensez Ă  rĂ©cupĂ©rer le contenu du dĂ©pĂŽt avant de le lire

✅ Solution

name: Bonjour
on:
  - push
jobs:
  dire_bonjour:
    runs-on: ubuntu-latest
    container:
      image: mcr.microsoft.com/devcontainers/base:ubuntu # La mĂȘme image que le Codespace
    steps:
      - uses: actions/checkout@v7 # RécupÚre le contenu du dépÎt correspondant au commit du workflow en cours
      - run: | # Pas de `sudo` : dans un conteneur, le job tourne déjà en root
          apt-get update
          apt-get install -y cowsay
      - run: cat README.adoc | /usr/games/cowsay # `cowsay` s'installe dans /usr/games, absent du PATH

Ce cours est construit comme ça đŸ—ïž

Les diapos que vous regardez sortent de .github/workflows/build-workflow.yml

docker compose version → make build → make verify → publication
À chaque push. Y compris celui qui a corrigĂ© la faute de frappe que vous venez de lire.

RĂ©capitulatif 📌

Un workflow, c’est un fichier YAML dans .github/workflows/
container: fait tourner le job dans votre image, pas celle de GitHub
Ce qui marche à la main ne marche pas forcément en CI. Exécuter, pas relire.

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 Codespace :

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

      mkdir -p /workspaces/projet-vcs-1/
      cd /workspaces/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 /workspaces/projet-vcs-1/
cd /workspaces/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' n'existe pas par défaut : c'est un alias, à définir une fois pour toutes
git config --global alias.lg "log --graph --oneline --decorate --all"
git lg # cat ~/.gitconfig => l'alias apparaĂźt maintenant dans la section [alias]

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 votre Codespace

  • Dans un terminal, tapez la commande suivante :

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

🧭 Deux Ă©critures pour la mĂȘme commande

Depuis Docker 1.13 (2017), les commandes qui agissent sur un objet (conteneur, image, rĂ©seau, volume) s’Ă©crivent aussi docker OBJET VERBE, qui dit sur quel type d’objet on agit.

Raccourci historiqueForme de gestion

docker run

docker container run

docker ps

docker container ls

docker images

docker image ls

docker rmi

docker image rm

docker pull

docker image pull

🧭 Laquelle utiliser ?

  • Les deux formes marchent, et les raccourcis ne sont pas dĂ©prĂ©ciĂ©s

    • docker container cp --help liste d’ailleurs les deux sous Aliases:

  • ⚠ Le raccourci cache parfois l’objet : docker pull tĂ©lĂ©charge une image, donc la forme longue est docker image pull, pas docker container pull

  • đŸš« Les commandes qui n’agissent sur aucun objet gardent leur forme unique : docker login, docker version, docker search

  • 📏 Dans ce cours, on Ă©crit toujours la forme de gestion. Vous rencontrerez les raccourcis partout ailleurs : sachez les lire.

đŸ©» 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

anatomie 1

đŸ©» Anatomie

anatomie 2

đŸ©» Anatomie

anatomie 3

đŸ©» Anatomie

anatomie 4

đŸ©» Anatomie

anatomie 5

đŸ©» Anatomie

anatomie 6

đŸ©» Anatomie

anatomie 7

đŸ©» Anatomie

anatomie 8

đŸ©» 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 Codespaces ?

    • 💡 Regardez le fichier /etc/os-release

  • ExĂ©cutez un conteneur interactif basĂ© sur alpine:3.24.2 (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.24.2
/ $ 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.24.2 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
v3.24.2-58-g29b9ec24b1b [https://dl-cdn.alpinelinux.org/alpine/v3.24/main]
v3.24.2-61-g887b5770c3e [https://dl-cdn.alpinelinux.org/alpine/v3.24/community]
OK: 28651 distinct packages available
(1/9) Installing brotli-libs (1.2.0-r1)
(2/9) Installing c-ares (1.34.8-r0)
(3/9) Installing libunistring (1.4.2-r0)
(4/9) Installing libidn2 (2.3.8-r0)
(5/9) Installing nghttp2-libs (1.69.0-r0)
(6/9) Installing libpsl (0.21.5-r3)
(7/9) Installing zstd-libs (1.5.7-r2)
(8/9) Installing libcurl (8.22.0-r0)
(9/9) Installing curl (8.22.0-r0)
Executing busybox-1.37.0-r31.trigger
OK: 13.1 MiB in 25 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 container 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 container 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 container 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 image rm)

  • Supprimer les containers

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

✅ Solution : conteneur en tñche de fond (1/2)

# 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
# (sortie abrégée : autres conteneurs et colonnes CREATED, PORTS retirés)
CONTAINER ID   IMAGE            COMMAND                  STATUS              NAMES
edea0c46c6d8   jpetazzo/clock   "/bin/sh -c 'while d
"   Up About a minute   second-clock

✅ Solution : conteneur en tñche de fond (2/2)

# Supprimer l'image (tip : `docker image rm`)
docker image rm jpetazzo/clock
Error response from daemon: conflict: unable to delete jpetazzo/clock:latest (must be forced)
 - container edea0c46c6d8 is using its referenced image dc06bbc3744f

# Supprimer les containers (first-clock utilise aussi l'image)
docker container stop first-clock second-clock
first-clock
second-clock

docker container rm first-clock second-clock
first-clock
second-clock

# Supprimer l'image (pour de vrai cette fois)
docker image rm jpetazzo/clock
Untagged: jpetazzo/clock:latest
Deleted: sha256:dc06bbc3744f7200404bff0bbb2516925e7adea115e07de9da8b36bf15fe3dd3

💡 Et docker image rm -f ?

Le message d’erreur suggĂšre de forcer. Voici ce que fait vraiment -f quand un conteneur utilise encore l’image :

docker image rm -f jpetazzo/clock
Untagged: jpetazzo/clock:latest
  • Seule l'Ă©tiquette est retirĂ©e, que le conteneur soit arrĂȘtĂ© ou en cours d’exĂ©cution (il continue de tourner).

  • docker container ls --all affiche ensuite l’ID de l’image Ă  la place de son nom.

  • Les couches restent sur le disque : l’espace n’est libĂ©rĂ© qu’aprĂšs suppression des conteneurs qui les utilisent.

🎓 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 Codespaces
docker container run --rm ubuntu:24.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:24.04 git --version

🎓 Fabriquer sa premiùre image

  • But : fabriquer une image Docker qui contient git

  • Dans votre Codespace, crĂ©ez un dossier nommĂ© docker-git/

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

    FROM alpine:3.24.2
    
    RUN 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.24.2

RUN 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

Structure d’un Dockerfile : couche de base, metadata, commandes d’installation
FROM alpine:3.24.2
LABEL maintainer="Tony Stark"

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 image ls
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 image ls
REPOSITORY                            TAG                     IMAGE ID       CREATED         SIZE
cours-devops-docker-serve             latest                  654fc65c0913   13 hours ago    668MB
cours-devops-docker-qrcode            latest                  a3ff53e5d677   13 hours ago    668MB
cours-devops-docker-build             latest                  a8b5055c6b8a   13 hours ago    668MB
jenkins/jenkins                       2.504-slim-jdk17        2f264c295874   44 hours ago    669MB
jenkins/jenkins                       2.504-slim              4db3d90f94da   44 hours ago    691MB
jenkins/jenkins                       2.504-slim-jdk21        4db3d90f94da   44 hours ago    691MB
jenkins/jenkins                       2.504                   c6458a4308c9   44 hours ago    807MB
jenkins/jenkins                       2.504-jdk21             c6458a4308c9   44 hours ago    807MB
jenkins/jenkins                       2.504-jdk17             a44b80b5278d   44 hours ago    785MB
jenkins/jenkins                       2.504-jdk25             df290633b1b1   44 hours ago    709MB
jenkins/jenkins                       2.504-slim-jdk25        1b3e857e0e80   44 hours ago    593MB
jenkins/jenkins                       2.504-rhel-ubi9-jdk21   5bf47807e933   44 hours ago    807MB
jenkins/jenkins                       2.504-rhel-ubi9-jdk17   7c663bf36a0b   44 hours ago    785MB
jenkins/jenkins                       2.504-rhel-ubi9-jdk25   7ade79f2d5d6   44 hours ago    709MB
jenkins/jenkins                       2.504-alpine-jdk17      f8bb1d21d3b3   44 hours ago    458MB
jenkins/jenkins                       2.504-alpine            9796a49cd5ad   44 hours ago    480MB
jenkins/jenkins                       2.504-alpine-jdk21      9796a49cd5ad   44 hours ago    480MB
jenkins/jenkins                       2.504-alpine-jdk25      713be11d8079   44 hours ago    384MB
tomcat                                9.0                     bfe8385d954f   4 days ago      608MB
tomcat                                latest                  2b3894dce12e   4 days ago      607MB
img1758726197                         latest                  69b8e1bd3710   6 days ago      259MB
ubuntu                                latest                  9cbed7541129   6 weeks ago     117MB
nginx                                 latest                  d5f28ef21aab   6 weeks ago     279MB
hello-world                           latest                  54e66cc1dd1f   7 weeks ago     20.3kB
alpine                                latest                  4bcff63911fc   2 months ago    12.8MB
<none>                                <none>                  8feb4d8ca535   5 months ago    109MB
moby/buildkit                         buildx-stable-1         14aa1b4dd92e   8 months ago    306MB
ghcr.io/jenkinsci/ssh-agents-plugin   baseb2fb086             2bc6d82ca2b4   9 months ago    1.36GB
<none>                                <none>                  dfa54ef35e43   21 months ago   6.89MB
registry                              2                       a3d8aaa63ed8   24 months ago   37.2MB
jboss/wildfly                         latest                  35320abafdec   3 years ago     1.17GB

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.04 ⇒ registry.docker.com/library/ubuntu:22.04

  • dduportal/docker-asciidoctor ⇒ registry.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 alpine:3.24.1
RUN apk add --no-cache 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 alpine:3.24.1
#  ---> e40cf56b4be3
# Step 2/4 : RUN apk add --no-cache 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 apk add --no-cache 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 dive. Il n’est pas installĂ© dans Codespaces, mais s’utilise en conteneur sans rien installer :

docker container run --rm --tty --interactive \
  --volume /var/run/docker.sock:/var/run/docker.sock \
  wagoodman/dive:latest <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 APK 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 alpine:3.24.1
# RUN apk add --no-cache 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 container run --rm docker-git:1.1.0 make --version
# Doit fonctionner
docker container 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://cache-etu.univ-artois.fr:3128 -L --connect-timeout 60  "http://google.com"
1956754704 image11

CMD vs ENTRYPOINT

Cas d’usage. On va s’outiller!

FROM alpine:3.24
LABEL maintainer="John Doe"
RUN apk add --no-cache curl
# Si vous utilisez le proxy de l'Université
CMD ["curl", "-L", "--connect-timeout", "60", \
  "-x", "http://cache-etu.univ-artois.fr:3128", \
  "http://google.com"]
# Si vous utilisez votre propre proxy docker
# (sous Linux, lancer avec --add-host=host.docker.internal:host-gateway)
# CMD ["curl", "-L", "--connect-timeout", "60", \
#   "-x", "http://host.docker.internal:3128", \
#   "http://google.com"]
docker image build -t my-curl:1.0 .
docker container run --rm \
  my-curl:1.0
  % Total    % Received [...]
100  84301   0  84301   [...]
<!doctype html><html [...]
[...]
[...]</script></body></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://example.com

CMD vs ENTRYPOINT

Cas d’usage, un cran plus loin

Paramétrable, et hop!

FROM alpine:3.24
LABEL maintainer="John Doe"
RUN apk add --no-cache curl
# Si vous utilisez le proxy de l'Université
ENTRYPOINT ["curl", "-L", "--connect-timeout", "60", \
  "-x", "http://cache-etu.univ-artois.fr:3128"]
# Si vous utilisez votre propre proxy docker
# (sous Linux, lancer avec --add-host=host.docker.internal:host-gateway)
# ENTRYPOINT ["curl", "-L", "--connect-timeout", "60", \
#   "-x", "http://host.docker.internal:3128"]
docker image build -t my-curl:2.0 .
docker container run --rm \
  my-curl:2.0 http://google.com
  % Total    % Received [...]
100  84301   0  84301   [...]
<!doctype html><html [...]
[...]
[...]</script></body></html>

CMD vs ENTRYPOINT

Cas d’usage, un cran plus loin

Paramétrable, et hop!

FROM alpine:3.24
LABEL maintainer="John Doe"
RUN apk add --no-cache curl
# Si vous utilisez le proxy de l'Université
ENTRYPOINT ["curl", "-L", "--connect-timeout", "60", \
  "-x", "http://cache-etu.univ-artois.fr:3128"]
# Si vous utilisez votre propre proxy docker
# (sous Linux, lancer avec --add-host=host.docker.internal:host-gateway)
# ENTRYPOINT ["curl", "-L", "--connect-timeout", "60", \
#   "-x", "http://host.docker.internal:3128"]
override
docker image build -t my-curl:2.0 .
docker container run --rm \
  my-curl:2.0 http://google.com
  % Total    % Received [...]
100  84301   0  84301   [...]
<!doctype html><html [...]
[...]
[...]</script></body></html>

ENTRYPOINT + CMD

Une URL par défaut, toujours remplaçable.

ENTRYPOINT : la commande.

CMD : ses arguments par défaut.

FROM alpine:3.24
LABEL maintainer="John Doe"
RUN apk add --no-cache curl
# Si vous utilisez le proxy de l'Université
ENTRYPOINT ["curl", "-L", "--connect-timeout", "60", \
  "-x", "http://cache-etu.univ-artois.fr:3128"]
# Si vous utilisez votre propre proxy docker
# (sous Linux, lancer avec
#  --add-host=host.docker.internal:host-gateway)
# ENTRYPOINT ["curl", "-L", "--connect-timeout", "60", \
#   "-x", "http://host.docker.internal:3128"]
CMD ["http://example.com"]
docker image build -t my-curl:3.0 .
docker container run --rm \
  my-curl:3.0
  % Total    % Received [...]
100    713   0    713   [...]
<!doctype html><html [...]
[...]<title>Example Domain</title>
docker container run --rm \
  my-curl:3.0 http://neverssl.com
  % Total    % Received [...]
100   3961 100   3961   [...]
<html>
  <head>
    <title>NeverSSL [...]

Forme exec ou forme shell ?

# Forme exec (JSON)
ENTRYPOINT ["curl", "-L", ...]

# Forme shell
ENTRYPOINT curl -L ...

La forme shell passe par /bin/sh -c.

CMD et les arguments de run sont ignorés.

docker container run --rm \
  my-curl:shell http://neverssl.com
curl: (2) no URL specified
docker image inspect my-curl:shell \
  --format '{{json .Config.Entrypoint}}'
["/bin/sh","-c","curl -L [...]"]

RĂšgle : forme exec pour ENTRYPOINT et CMD.

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.24
WORKDIR /repertoire/travail
RUN echo hello > ./world.txt

📂 WORKDIR

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

📂 WORKDIR

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

📂 WORKDIR

FROM alpine:3.24
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:26-alpine
WORKDIR /app
COPY ./* /app
docker image build --tag mon-node .
...
$ docker container run --workdir /app --entrypoint ls mon-node
Dockerfile
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

nommage 1

📛 Nommage des containers

nommage 2

📛 Nommage des containers

nommage 3

📛 Nommage des containers

📛 Nommage des containers

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

Nom du container explicite

docker container logs my-web
docker container 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 nginx

  • 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
CONTAINER ID    IMAGE   COMMAND                  CREATED    STATUS    PORTS
bd12c4d71       nginx   "/docker-entrypoint.
"   35 s. ago  Up 33 s.  80/tcp

Ok, mon container expose un service sur le port TCP 80, 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 '{{range .NetworkSettings.Networks}}{{println .IPAddress}}{{end}}' <ContainerID>

🔍 Que nous dit docker container inspect ?

L’ancienne forme, encore dans beaucoup de tutoriels, Ă©choue avec Docker 29 :

docker container inspect --format "{{.NetworkSettings.IPAddress}}" <ContainerID>
template parsing error: [...] map has no entry for key "IPAddress"

L’adresse n’est plus que sous .NetworkSettings.Networks.<rĂ©seau>, d’oĂč le range.

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 container run --name my-nginx -v /fully/qualified/path/on/my/computer/:/usr/share/nginx/html:ro -d -p 8000:80 nginx:1.31.6-alpine3.24
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 -O sample.war https://github.com/gounthar/tp07-sample/releases/download/v2026.09.07-083440/sample-2026.09.07-083440.war
# wget sort en 0 mĂȘme s'il a reçu une page HTML : vĂ©rifier que c'est bien une archive
file sample.war
sample.war: Zip archive data, made by v2.0 UNIX, ...
# Used to run a Docker container with the name "tomcat-container" using the "tomcat:11.0.26-jdk25-temurin-noble" image. The container is
# configured to expose port 8080 on the host machine, which will be mapped to the corresponding port inside
# the container. Additionally, a volume is mounted to the container, linking the
# "/fully/qualified/path/on/my/computer/sample.war" file to "/usr/local/tomcat/webapps/sample.war"
# path within the container. This allows the Tomcat 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 container run -d -p 8080:8080 --name tomcat-container -v /fully/qualified/path/on/my/computer/sample.war:/usr/local/tomcat/webapps/sample.war tomcat:11.0.26-jdk25-temurin-noble

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

DerriĂšre le proxy de l’universitĂ©, mvn package Ă©choue : Maven ignore HTTP_PROXY et HTTPS_PROXY. Il lit son proxy dans ~/.m2/settings.xml.

<!-- Proxy de l'université pour Maven, qui ignore HTTP_PROXY et HTTPS_PROXY -->
<settings>
  <proxies>
    <proxy>
      <id>univ</id>
      <active>true</active>
      <protocol>http</protocol>
      <host>cache-etu.univ-artois.fr</host>
      <port>3128</port>
    </proxy>
  </proxies>
</settings>
mkdir -p ~/.m2 && cp settings.xml ~/.m2/settings.xml

Dans un Dockerfile, on le monte comme secret de build, le temps du seul RUN mvn package : il ne reste pas dans l’image. Sans --secret (hors de l’universitĂ©), le montage est simplement absent.

✅ Solution travaux pratiques #7

Assemblons tout ça dans un Dockerfile.

# Use a Maven image for building the application
FROM maven:3.9-eclipse-temurin-25-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; settings.xml (proxy) is mounted as a build secret, if provided
RUN --mount=type=secret,id=maven-settings,target=/root/.m2/settings.xml 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 image build --secret id=maven-settings,src=settings.xml \
  -t spring-boot-app --file Dockerfile.ugly .

✅ Solution travaux pratiques #7

Et logiquement…​

docker container 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-eclipse-temurin-25-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; settings.xml (proxy) is mounted as a build secret, if provided
RUN --mount=type=secret,id=maven-settings,target=/root/.m2/settings.xml mvn package

# Use a lightweight Java image for running the application
FROM eclipse-temurin:25-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.27

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.24.2
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.27

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.24.2
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.27

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.24.2
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 container 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.24
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 /usr/local/apache2/logs \
  --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 :

# Copie le *contenu* du dossier hÎte (monté sur /html) dans le volume (monté sur /cible).
# "/html/." et non "/html", sinon les fichiers atterrissent dans un sous-répertoire.
docker container run --rm \
  --volume mon-nginx-html:/cible \
  --volume /chemin/vers/la/page/web/sur/lhote:/html \
  --workdir /cible \
  busybox cp -r /html/. .

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 --rm --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 --rm --name second-container -v data:/donnees busybox

✅ Solution travaux pratiques #9

Dans le second conteneur (dĂ©marrĂ© Ă  l’Ă©tape prĂ©cĂ©dente), 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-t-on 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.24

# 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.

fichier3.txt est lĂ  : avec BuildKit, le constructeur par dĂ©faut, un RUN Ă©crit dans le dossier mĂȘme aprĂšs VOLUME.

Ce n’est pas vrai de tous les constructeurs : voir la diapositive suivante.

✅ Solution TP #10 : selon le constructeur

Le mĂȘme Dockerfile, construit avec l’ancien constructeur (dĂ©prĂ©ciĂ©) :

DOCKER_BUILDKIT=0 docker image build -t mon-image-legacy .
docker container run --rm mon-image-legacy ls /mon-dossier
fichier1.txt
fichier2.txt

Le RUN qui suit VOLUME a été exécuté, mais ses modifications du dossier ont été jetées.

La documentation le dit : modifications Ă©cartĂ©es avec l’ancien constructeur, conservĂ©es avec BuildKit.

Pour ne pas dépendre du constructeur, placez VOLUME aprÚs les instructions qui remplissent le dossier.

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

đŸ€ Interagir avec le Docker Engine

Sauf que…​

reseaux docker desktop 2375 configuration

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
HTTP/1.1 200 OK
...
curl -I --noproxy '*' http://localhost:8000
HTTP/1.1 200 OK
...

🌐 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 container 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

🚹 Piùge NGINX : le problùme

NGINX, contrairement Ă  d’autres outils (curl, wget, applications), rĂ©sout les noms Ă©crits en dur (bloc upstream, proxy_pass http://nom:port) une seule fois, au chargement de la configuration.

  • Si le service amont n’est pas encore dĂ©marrĂ© → NGINX Ă©choue au dĂ©marrage : c’est un problĂšme d'ordre de dĂ©marrage

  • Si un conteneur redĂ©marre et change d’IP → NGINX continue d’envoyer vers l'ancienne IP, mĂȘme si un autre conteneur l’a rĂ©cupĂ©rĂ©e

  • Erreur typique : host not found in upstream "nom-service:port"

🚹 PiĂšge NGINX : configuration dĂ©faillante

events {
    worker_connections 1024;
}

http {
    # ❌ AUCUNE directive resolver !

    upstream backend {
        server app-server:8080;  # ❌ ÉCHEC au dĂ©marrage !
    }

    server {
        listen 80;
        location / {
            proxy_pass http://backend;
        }
    }
}

Résultat : nginx: [emerg] host not found in upstream "app-server:8080"

Ajouter resolver 127.0.0.11; Ă  cette configuration ne change rien : mĂȘme message, mĂȘme arrĂȘt.

🚹 Piùge NGINX : resolver + variable

events {
    worker_connections 1024;
}

http {
    # DNS interne Docker pour résolution dynamique
    resolver 127.0.0.11 valid=30s ipv6=off;

    server {
        listen 80;
        location / {
            # ✅ Utiliser une variable pour forcer la rĂ©solution dynamique
            set $backend_host "app-server:8080";
            proxy_pass http://$backend_host;
        }
    }
}

🚹 PiĂšge NGINX : rĂ©solution Ă  l’exĂ©cution

resolver n’est lu que par la rĂ©solution Ă  l’exĂ©cution. Un nom Ă©crit en dur est rĂ©solu au chargement, sans lui.

Deux façons de passer Ă  la rĂ©solution Ă  l’exĂ©cution :

  1. Variable (diapositive précédente) : set $var "nom-service:port" puis proxy_pass http://$var

  2. upstream avec resolve (NGINX 1.27.3 et plus) : garde le bloc upstream, utile pour répartir la charge

🚹 Piùge NGINX : resolve

http {
    resolver 127.0.0.11 valid=30s ipv6=off;

    upstream backend {
        zone backend 64k;               # obligatoire avec resolve
        server app-server:8080 resolve;
    }
    # server { ... proxy_pass http://backend; ... }
}
  • NGINX dĂ©marre mĂȘme si app-server n’existe pas encore (502 en attendant)

  • Le nom est rĂ©-interrogĂ© Ă  l’Ă©chĂ©ance de valid : jusqu’Ă  30 s de 502 aprĂšs l’arrivĂ©e du service

  • L’approche variable, elle, rĂ©pond dĂšs la requĂȘte suivante

🚹 Piùge NGINX : quand faut-il resolver ?

Situationresolver ?Raison

upstream ou proxy_pass avec nom écrit en dur

❌ SANS EFFET

Résolu au chargement, resolver ignoré

proxy_pass avec variable ($backend_host)

✅ OBLIGATOIRE

RĂ©solution Ă  l’exĂ©cution

upstream avec server …​ resolve

✅ OBLIGATOIRE

RĂ©solution Ă  l’exĂ©cution (+ zone)

upstream avec IP fixe

❌ NON

Pas de résolution DNS

🚹 Piùge NGINX : paramùtres de resolver

resolver 127.0.0.11 valid=30s ipv6=off;
#        ^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^
#        Adresse DNS  Cache TTL Désactiver IPv6
  • 127.0.0.11 : DNS interne Docker (sur les rĂ©seaux dĂ©finis par l’utilisateur, dont ceux de Compose)

  • valid=30s : DurĂ©e de cache imposĂ©e (sans valid, NGINX suit le TTL de la rĂ©ponse DNS)

  • ipv6=off : DĂ©sactive IPv6 (optionnel, Ă©vite warnings)

Le rĂ©seau bridge par dĂ©faut n’a pas ce DNS : crĂ©er un rĂ©seau avec docker network create, ou laisser Compose le faire.

🚹 Piùge NGINX : exercice (1/2)

Créez un projet avec :

  1. NGINX (proxy) vers webapp

  2. webapp (serveur web simple, port 80 dans le conteneur, 8080 sur l’hĂŽte)

services:
  webapp:
    image: nginx:1.31.6-alpine3.24
    ports:
      - "8080:80"

  proxy:
    image: nginx:1.31.6-alpine3.24
    ports:
      - "8000:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro

🚹 Piùge NGINX : exercice (2/2)

Test 1 : bloc upstream vers webapp:80, sans resolver. DĂ©marrer le proxy seul (docker compose up proxy) → Observer l’Ă©chec NGINX

Test 1 bis : ajouter resolver 127.0.0.11; au test 1 → MĂȘme Ă©chec, mĂȘme message

Test 2 : resolver 127.0.0.11 et proxy_pass par variable (diapositive « resolver + variable »). Le proxy dĂ©marre seul, puis rĂ©pond dĂšs que webapp tourne → SuccĂšs !

🚹 PiĂšge NGINX : en rĂ©sumĂ©

RĂšgle d’or : un nom Ă©crit en dur est rĂ©solu au chargement. Pour rĂ©soudre Ă  l’exĂ©cution : variable ou resolve, et alors resolver 127.0.0.11 obligatoire.

Avec un nom écrit en dur, resolver ou pas :

  • ❌ Échec au dĂ©marrage si le service amont n’est pas encore lancĂ©

  • ❌ IP obsolĂšte aprĂšs redĂ©marrage du conteneur amont

  • ❌ Erreur : host not found in upstream

đŸ“¶đŸ”Œ 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"
# --noproxy : web2 est un nom interne, la requĂȘte ne doit pas partir vers le proxy
curl --noproxy '*' 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 --noproxy '*' web2
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
[...]

🚹 Piùge : localhost vs nom de service dans Docker

La Confusion Fatale

TermeHors Docker (machine locale)Dans Docker (environnement conteneurisé)

localhost

Votre ordinateur

Le conteneur actuel uniquement

127.0.0.1

Votre ordinateur

Le conteneur actuel uniquement

nom_service

(n’existe pas)

Un autre conteneur du mĂȘme rĂ©seau

nom_service:port

(n’existe pas)

Communication inter-conteneurs

Dans Docker : localhost ≠ autre conteneur

localhost = seulement le conteneur oĂč vous ĂȘtes !

Exemples d’Erreurs RĂ©elles

Ces erreurs proviennent d’examens rĂ©els (2025-2026).

Erreur #1 : Logging GELF vers le nom de service

Code cassé :

services:
  backend:
    image: mon-app
    logging:
      driver: gelf
      options:
        gelf-address: "udp://logstash:12201"  # ❌ FAUX !
        tag: "backend"

  logstash:
    image: logstash:9.5.3
    ports:
      - "12201:12201/udp"

ProblĂšme : le pilote gelf ne s’exĂ©cute pas dans le conteneur. Il s’exĂ©cute dans le dĂ©mon Docker de l’hĂŽte, qui n’est pas sur le rĂ©seau Compose et ne connaĂźt donc pas le nom logstash.

Le conteneur ne dĂ©marre mĂȘme pas :

Error response from daemon: failed to create task for container:
failed to initialize logging driver: gelf: cannot connect to GELF endpoint:
logstash:12201 dial udp: lookup logstash on ...: no such host

Solution :

services:
  backend:
    logging:
      driver: gelf
      options:
        gelf-address: "udp://localhost:12201"  # ✅ Adresse vue depuis l'hîte

  logstash:
    image: logstash:9.5.3
    ports:
      - "12201:12201/udp"                      # ✅ le port publiĂ© rend logstash joignable depuis l'hĂŽte

L’adresse d’un pilote de log se lit depuis l’hĂŽte, pas depuis le conteneur.

C’est la seule exception de ce chapitre Ă  la rĂšgle « autre conteneur → nom du service ». Les options logging: sont interprĂ©tĂ©es par le dĂ©mon Docker avant que le conteneur existe : il lui faut une adresse joignable depuis l’hĂŽte, d’oĂč le port publiĂ© 12201:12201/udp et l’adresse localhost.

Sans ports:, le conteneur dĂ©marre quand mĂȘme — et les logs partent dans le vide.

GELF utilise UDP : le dĂ©mon n’ouvre aucune connexion, il Ă©met des datagrammes. Si personne n’Ă©coute sur localhost:12201, rien ne le signale : le conteneur dĂ©marre normalement, son code de sortie ne dĂ©pend que du processus qu’il exĂ©cute, et Kibana reste vide.

Vérifiez toujours que les logs arrivent, pas seulement que le conteneur démarre.

localhost ne dĂ©signe pas une seule adresse, et le pilote gelf ne s’en plaint pas.

localhost peut ĂȘtre rĂ©solu en 127.0.0.1 (IPv4) ou en ::1 (IPv6). La publication 12201:12201/udp Ă©coute sur les deux familles — 0.0.0.0:12201 et [::]:12201 — donc localhost tombe juste quoi qu’il arrive.

Restreindre la publication Ă  une seule famille change tout. Avec 127.0.0.1:12201:12201/udp, le dĂ©mon n’Ă©coute plus qu’en IPv4 : si localhost est rĂ©solu en ::1, les datagrammes ne trouvent personne. MesurĂ© sur un dĂ©mon 27.5.1 : zĂ©ro log reçu, trois essais sur trois, sans un message d’erreur nulle part.

C’est le mĂȘme piĂšge qu’au-dessus, avec une cause de plus : ici le port est publiĂ©, et l’Ă©criture localhost suffit Ă  le manquer.

docker container port <conteneur>    # montre les familles réellement écoutées

En cas de doute, écrire 127.0.0.1 plutÎt que localhost : la famille est explicite, la résolution ne peut plus surprendre.

Erreur #2 : Health Check avec localhost IPv6

Code cassé :

services:
  webapp:
    image: node:18
    healthcheck:
      test: ["CMD", "curl", "-f", "http://[::1]:3000/health"]  # ❌ FAUX !

ProblĂšme : [::1] (localhost IPv6) pointe vers le conteneur webapp lui-mĂȘme, mais si l’app Ă©coute sur IPv4…​

Solution :

healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost:3000/health"]  # ✅ OK (mĂȘme conteneur)
  # OU
  test: ["CMD", "curl", "-f", "http://127.0.0.1:3000/health"]  # ✅ OK

Health check = test dans le mĂȘme conteneur → localhost est correct ici !

C’est quand on veut accĂ©der Ă  un autre service qu’il faut utiliser le nom du service.

Erreur #3 : Backend URL avec localhost

Code cassé (frontend) :

// frontend/src/config.js
const API_URL = "http://localhost:5000/api";  // ❌ FAUX !

ProblĂšme : Le navigateur du client exĂ©cute ce code → localhost pointe vers l’ordinateur du client, pas le backend Docker !

Solution 1 : Utiliser un reverse proxy (NGINX)

const API_URL = "/api";  // ✅ Relatif → passe par NGINX

Solution 2 : Variable d’environnement (build-time)

const API_URL = process.env.REACT_APP_API_URL || "/api";
services:
  frontend:
    build:
      context: ./frontend
      args:
        # ✅ URL publique qui passe par le reverse proxy
        REACT_APP_API_URL: "/api"

React client-side ≠ SSR/Next.js :

  • React client-side (CRA, Vite) : Code s’exĂ©cute dans le navigateur → Ne peut PAS rĂ©soudre backend:5000 → Utiliser URL publique : /api ou http://localhost:8080/api

  • SSR/Next.js : Code s’exĂ©cute sur le serveur Node.js → Peut rĂ©soudre backend:5000 (dans le rĂ©seau Docker) → Mais prĂ©fĂ©rez quand mĂȘme le reverse proxy pour la cohĂ©rence

Recommandation : Toujours utiliser le reverse proxy (Solution 1) pour éviter la confusion !

Tableau de Référence Rapide

SituationUtiliserExemple

Health check (mĂȘme conteneur)

localhost ou 127.0.0.1

curl http://localhost:8080/health

Communication entre services Docker

Nom du service

http://backend:5000

Logging GELF (options logging:)

Adresse vue depuis l’hĂŽte

gelf-address: "udp://localhost:12201" + ports: - "12201:12201/udp"

Adresse de connexion base de données

Nom du service

DATABASE_URL=postgresql://db:5432/mydb

API frontend → backend (mĂȘme rĂ©seau Docker)

Nom du service

API_URL=http://backend:5000

API frontend → backend (navigateur client)

Reverse proxy

API_URL=/api (NGINX route vers backend)

RÚgle Mnémotechnique

Localhost = Local Container

  • localhost dans un conteneur = ce conteneur uniquement

  • Pour parler Ă  un autre conteneur = utiliser son nom de service

  • Pour parler depuis le navigateur = utiliser reverse proxy ou URL publique

  • Dans un bloc logging: = adresse vue depuis l’hĂŽte (le pilote tourne cĂŽtĂ© dĂ©mon)

🎓 Exercice Pratique

Corrigez ce docker-compose.yml cassé :

services:
  webapp:
    image: node:18
    environment:
      DATABASE_URL: "postgresql://localhost:5432/mydb"  # ❌ Erreur 1
      REDIS_URL: "redis://127.0.0.1:6379"              # ❌ Erreur 2
    logging:
      driver: gelf
      options:
        gelf-address: "udp://localhost:12201"          # ⚠ PiĂšge : cette ligne est correcte

  database:
    image: postgres:15

  cache:
    image: redis:7

  logstash:
    image: logstash:9.5.3
    ports:
      - "12201:12201/udp"

Solution : remplacer les localhost des variables d’environnement par les noms de services, et laisser l’adresse GELF telle quelle — le pilote de log est interprĂ©tĂ© par le dĂ©mon de l’hĂŽte (voir Erreur #1).

📊 En RĂ©sumĂ©

Erreurs fréquentes :

  • ❌ localhost:5432 pour PostgreSQL → Utiliser database:5432

  • ❌ localhost:6379 pour Redis → Utiliser cache:6379

  • ❌ localhost dans variables d’environnement → Utiliser nom de service

  • ❌ logstash:12201 pour GELF logging → Utiliser localhost:12201 + port publiĂ©

La derniĂšre ligne va Ă  contre-courant des trois autres, et c’est voulu.

Le bloc logging: est la seule option de ce chapitre qui soit interprĂ©tĂ©e par le dĂ©mon Docker de l’hĂŽte et non depuis le rĂ©seau Compose. C’est prĂ©cisĂ©ment parce que la rĂšgle gĂ©nĂ©rale est bien apprise que cette exception est ratĂ©e Ă  l’examen.

RĂšgle d’or :

  • MĂȘme conteneur → localhost ✅

  • Autre conteneur → nom du service ✅

  • Depuis navigateur → reverse proxy ✅

  • Bloc logging: → adresse joignable depuis l’hĂŽte ✅

🐋🐙 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.

⚠ PiĂšge : Outils manquants dans les images de base

Le ProblĂšme

Si la commande utilisĂ©e dans un health check n’existe pas dans l’image, le health check Ă©chouera !

  • Les images slim et alpine sont minimalistes pour rĂ©duire la taille

  • Elles ne contiennent PAS les outils courants (curl, wget, bash, etc.)

  • Health check avec outil manquant → Status "unhealthy" → Conteneur inutilisable

Exemples d’Images et Outils Disponibles

Image de BaseOutils InclusOutils ABSENTS

debian:12

sh, bash, ps, grep

❌ wget, curl, netcat (installables via apt)

debian:12-slim

sh, bash, ps

❌ curl, wget, netcat

alpine:3.19

sh, wget, ps

❌ curl, bash

python:3.11

python, pip, bash, ps, wget, curl

(image complĂšte)

python:3.11-slim

python, pip, sh, bash, ps

❌ curl, wget

python:3.11-alpine

python, pip, sh, wget, ps

❌ curl, bash

node:18

node, npm, bash, ps, wget, curl

(image complĂšte)

node:18-alpine

node, npm, sh, wget, ps

❌ curl, bash

postgres:15

pg_isready, psql, bash

(include outils PostgreSQL)

redis:7

redis-cli, bash

(include outils Redis)

nginx:latest

wget, bash, ps, curl

(version latest complÚte, nginx:alpine plus limité)

Images officielles de bases de données (postgres, mysql, redis, mongodb) incluent généralement leurs propres outils de diagnostic.

SymptĂŽmes d’un Health Check avec Outil Manquant

Quand un health check Ă©choue Ă  cause d’un outil manquant :

docker compose ps
NAME            IMAGE                  STATUS
myapp-backend   python:3.11-slim      Up 30 seconds (unhealthy)
docker container inspect myapp-backend | grep -A 10 Health
"Health": {
    "Status": "unhealthy",
    "FailingStreak": 5,
    "Log": [
        {
            "ExitCode": 127,  # ← Code 127 = "Command not found"
            "Output": "/bin/sh: curl: not found"
        }
    ]
}

ExitCode 127 = La commande n’existe pas dans l’image !

Solution 1 : Installer l’Outil dans le Dockerfile

La mĂ©thode la plus fiable : installer l’outil manquant.

FROM python:3.11-slim

WORKDIR /app

# Installer curl pour le health check
RUN apt-get update && \
    apt-get install -y --no-install-recommends curl && \
    rm -rf /var/lib/apt/lists/*  # ← Nettoyer le cache apt

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 5000

CMD ["python", "app.py"]
services:
  backend:
    build: ./backend
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:5000/health"]  # ✅ Fonctionne !
      interval: 10s
      timeout: 5s
      retries: 3

Alpine : Utiliser apk add au lieu de apt-get

FROM python:3.11-alpine

RUN apk add --no-cache curl

Solution 2 : Utiliser des Alternatives Natives

Utilisez les outils dĂ©jĂ  prĂ©sents dans l’image.

BesoinAu lieu de…​Utiliser

Tester connexion HTTP

curl http://localhost:8080

wget --spider --quiet http://localhost:8080

Tester port ouvert

curl -f localhost:8080

nc -z localhost 8080 (si netcat disponible)
cat < /dev/tcp/localhost/8080 (bash uniquement)

Tester fichier existe

curl -f http://...

[ -f /path/to/file ] || exit 1

Tester processus

curl http://...

ps aux | grep "mon-process"

Exemple avec wget (disponible dans Alpine) :

services:
  frontend:
    image: node:18-alpine
    healthcheck:
      test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:3000 || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3

Solution 3 : Utiliser Python/Node Built-in

Cette solution s’applique uniquement aux images qui contiennent dĂ©jĂ  Python ou Node.js (ex: python:3.11-slim, node:18-alpine, images d’applications Python/Node). N’installez pas Python/Node.js juste pour le health check - utilisez plutĂŽt les Solutions 1 ou 2.

Health check avec Python (pour images Python) :

services:
  backend:
    image: python:3.11-slim  # ← DĂ©jĂ  contient Python
    healthcheck:
      test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:5000/health')"]
      interval: 10s
      timeout: 5s
      retries: 3

Health check avec Node.js (pour images Node) :

services:
  api:
    image: node:18-alpine  # ← DĂ©jĂ  contient Node.js
    healthcheck:
      test: ["CMD", "node", "-e", "require('http').get('http://localhost:8080/health', (r) => process.exit(r.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))"]
      interval: 10s
      timeout: 5s
      retries: 3

🎓 Exercice Pratique

Corrigez ce docker-compose.yml qui échoue avec "unhealthy" :

services:
  webapp:
    image: python:3.11-slim
    # Dockerfile n'installe PAS curl !
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:5000/health"]
      interval: 10s

3 solutions possibles :

  1. Ajouter RUN apt-get update && apt-get install -y curl dans Dockerfile

  2. Remplacer par wget --spider --quiet http://localhost:5000/health

  3. Remplacer par health check Python natif (Solution 3)

📊 En RĂ©sumĂ© (Checklist)

Avant d’Ă©crire un health check :

  1. ✅ VĂ©rifier : La commande existe-t-elle dans l’image ?

  2. ✅ Tester : docker container run --rm image commande pour vĂ©rifier

  3. ✅ Choisir une solution :

    • Installer l’outil (Solution 1) → RecommandĂ© pour production

    • Utiliser alternative native (Solution 2) → LĂ©ger, pas d’install

    • Utiliser Python/Node built-in (Solution 3) → Images avec runtime uniquement

Erreurs fréquentes :

  • ❌ curl dans python:3.11-slim → ExitCode 127

  • ❌ bash dans Alpine → Utiliser sh

  • ❌ Oublier --no-cache lors de l’install → Image plus lourde

RĂšgle d’or : docker container run --rm mon-image commande

Si la commande Ă©choue → Elle Ă©chouera aussi dans le health check !

🏁🔱 É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 de la commande docker compose, placĂ©e avant la sous-commande up.

  • Par exemple :

docker compose --profile dev up

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 --profile dev up, ou uniquement les services de production avec docker compose --profile prod up.

  • 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.
    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.
    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:9.0.0-jdk21 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:8.6.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}
    # Le fichier racine Ă  construire : SOURCE_FILE=index-examen.adoc make build
    - SOURCE_FILE=${SOURCE_FILE}
  # L'ID utilisateur qui exécute les commandes à l'intérieur du conteneur.
  user: ${CURRENT_UID}
  # Un montage tmpfs pour des E/S plus rapides ; 1777 car le conteneur n'est pas root.
  tmpfs:
    - ${BUILD_DIR}:mode=1777
  # 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 est construite localement, Ă  partir de decktape : voir dockerfiles/pdf/.
    build:
      context: ./dockerfiles/pdf
    # 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}
    environment:
      # Chromium veut un HOME inscriptible pour sa base crashpad. L'image n'en
      # déclare un que pour l'uid 1000 : tout autre uid tombe sur HOME=/ et le
      # navigateur refuse de démarrer.
      - HOME=/tmp
    # Volumes pour le conteneur Docker.
    volumes:
      - ${DIST_DIR}:/slides

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}
    # Le fichier racine Ă  construire : SOURCE_FILE=index-examen.adoc make build
    - SOURCE_FILE=${SOURCE_FILE}
  # L'ID utilisateur qui exécute les commandes à l'intérieur du conteneur.
  user: ${CURRENT_UID}
  # Un montage tmpfs pour des E/S plus rapides ; 1777 car le conteneur n'est pas root.
  tmpfs:
    - ${BUILD_DIR}:mode=1777
  # 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 de 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 de 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 est construite localement, Ă  partir de decktape : voir dockerfiles/pdf/.
    build:
      context: ./dockerfiles/pdf
    # 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}
    environment:
      # Chromium veut un HOME inscriptible pour sa base crashpad. L'image n'en
      # déclare un que pour l'uid 1000 : tout autre uid tombe sur HOME=/ et le
      # navigateur refuse de démarrer.
      - HOME=/tmp
    # Volumes pour le conteneur Docker.
    volumes:
      - ${DIST_DIR}:/slides

Pourquoi une image construite, et pas l’image publiĂ©e ? 📄

L’image publiĂ©e ghcr.io/astefanutti/decktape ne suffit plus telle quelle.

  • Le PDF passe par deux Ă©tapes : decktape rend les diapositives en 2048x1536, puis ghostscript rĂ©duit le fichier. L’image publiĂ©e n’a pas ghostscript.

  • Chromium ne dĂ©marre dans le conteneur qu’avec --no-sandbox et --disable-gpu. Ces options vivent dans entrypoint.sh, d’oĂč l’absence de command: dans le service.

  • L’image ne dĂ©clare un HOME que pour l’uid 1000. Avec un autre uid, Chromium Ă©choue sur chrome_crashpad_handler: --database is required : d’oĂč HOME=/tmp. Un petit Dockerfile FROM ghcr.io/astefanutti/decktape rĂšgle les deux premiers points ; Compose le construit avec build:.

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}
    # Le fichier racine Ă  construire : SOURCE_FILE=index-examen.adoc make build
    - SOURCE_FILE=${SOURCE_FILE}
  # L'ID utilisateur qui exécute les commandes à l'intérieur du conteneur.
  user: ${CURRENT_UID}
  # Un montage tmpfs pour des E/S plus rapides ; 1777 car le conteneur n'est pas root.
  tmpfs:
    - ${BUILD_DIR}:mode=1777
  # 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}
    # Le fichier racine Ă  construire : SOURCE_FILE=index-examen.adoc make build
    - SOURCE_FILE=${SOURCE_FILE}

Trois variables d’environnement sont dĂ©finies pour le conteneur Docker : PRESENTATION_URL, REPOSITORY_URL et SOURCE_FILE. SOURCE_FILE choisit le fichier racine : SOURCE_FILE=index-examen.adoc make build construit le sujet d’examen.

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}
    # Le fichier racine Ă  construire : SOURCE_FILE=index-examen.adoc make build
    - SOURCE_FILE=${SOURCE_FILE}

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}
    # Le fichier racine Ă  construire : SOURCE_FILE=index-examen.adoc make build
    - SOURCE_FILE=${SOURCE_FILE}
  • 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. Docker Compose n’Ă©choue pas pour autant : il Ă©met un avertissement, substitue une chaĂźne vide, et continue.

  • The "PRESENTATION_URL" variable is not set. Defaulting to a blank string. — code de sortie 0.

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, Docker Compose n’Ă©choue pas : mĂȘme avertissement, mĂȘme chaĂźne vide. user: reçoit une valeur vide.

  • Le conteneur dĂ©marre donc en root — exactement l’inverse de ce que cette ligne cherchait Ă  obtenir.

đŸ›Ąïž Rendre une variable vraiment obligatoire

Permissif :

user: ${CURRENT_UID}

Obligatoire :

user: ${CURRENT_UID:?CURRENT_UID est obligatoire}
  • ${VAR} est permissif : variable absente, valeur vide, on continue.

  • ${VAR:?message} Ă©choue pour de bon, dĂšs docker compose config : le message s’affiche et le code de sortie est 1.

  • Rien ne dĂ©marre, donc rien ne dĂ©marre en root.

  • C’est la forme Ă  Ă©crire quand la valeur est obligatoire.

  • C’est celle qu’utilise le docker-compose.yml Ă  la racine de ce dĂ©pĂŽt : sans CURRENT_UID (que make fournit), il s’arrĂȘte au lieu de construire en root.

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 (variante Alpine).

FROM node:26-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

# Les conteneurs de construction écrivent dans dist/ à travers un montage bind :
# ils doivent donc tourner sous l'utilisateur qui lance make. Laissés en root, ils
# y déposent des fichiers que `clean` ne peut plus supprimer depuis l'hÎte, et
# `make all` meurt sur sa premiÚre cible. Calculé ici plutÎt qu'écrit en dur : cet
# identifiant n'est pas le mĂȘme sur un poste, dans un Codespace ou sur un agent
# d'intégration continue.
CURRENT_UID ?= $(shell id -u):$(shell id -g)
export CURRENT_UID

# 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) 2>/dev/null || { \
	  echo "NOTE: dist/ contient des fichiers appartenant Ă  root, suppression depuis un conteneur"; \
	  docker run --rm --volume $(DIST_DIR):/dist alpine:3 \
	    sh -c 'rm -rf /dist/* /dist/.[!.]*' >/dev/null 2>&1; \
	  rm -rf $(DIST_DIR); \
	}

# Démarrer le service 'qrcode'.
qrcode:
	@$(call compose_up, qrcode)

# Déclarer des cibles factices.
.PHONY: all build verify serve shell clean qrcode pdf dependencies-update dependencies-lock-update

🐋 Docker peut travailler main dans la main avec d’autres outils…​ đŸ‘«đŸ€

# Les conteneurs de construction écrivent dans dist/ à travers un montage bind :
# ils doivent donc tourner sous l'utilisateur qui lance make. Laissés en root, ils
# y déposent des fichiers que `clean` ne peut plus supprimer depuis l'hÎte, et
# `make all` meurt sur sa premiÚre cible. Calculé ici plutÎt qu'écrit en dur : cet
# identifiant n'est pas le mĂȘme sur un poste, dans un Codespace ou sur un agent
# d'intégration continue.
CURRENT_UID ?= $(shell id -u):$(shell id -g)
export CURRENT_UID
  • VoilĂ  d’oĂč vient le CURRENT_UID que docker-compose.yml consomme quelques diapos plus haut.

  • Compose ne le calcule pas, et ne se plaint pas non plus s’il manque : c’est au Makefile de le poser.

  • $(shell id -u):$(shell id -g) le lit Ă  chaque appel, au lieu de le figer dans .env.

🐋 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

Sécurité et Production Docker

security production header

Objectifs de la session

  • Comprendre et appliquer les bonnes pratiques de sĂ©curitĂ© Docker

  • Optimiser les conteneurs pour la production

  • GĂ©rer les registres d’images Docker

  • DĂ©ployer une application sĂ©curisĂ©e et production-ready

Projet Fil Rouge : SecureVote

SecureVote : Une application de vote en ligne moderne

  • Frontend React (interface de vote)

  • Backend API Python Flask

  • Base de donnĂ©es PostgreSQL

  • Redis pour le cache

  • Nginx comme reverse proxy

Architecture de l’application

services:
  frontend:    # React app
  backend:     # Flask API
  database:    # PostgreSQL
  cache:       # Redis
  proxy:       # Nginx

Architecture multi-tiers classique pour DevOps

Votre mission

  • Phase 1 (30 min) : DĂ©marrer l’application "dangereuse"

  • Phase 2 (1h15) : SĂ©curiser l’application

  • Phase 3 (1h) : Optimiser pour la production

L’application initiale contient de nombreuses vulnĂ©rabilitĂ©s volontaires !

Phase 1 : Découverte (30 min)

git clone https://github.com/gounthar/securevote.git
cd securevote/phase1
docker compose up -d

# Vérifier l'état des services
docker compose ps

# Suivre les logs (Ctrl+C pour quitter)
docker compose logs -f
  • Attendre que tous les services soient UP (~30 secondes)

  • AccĂ©dez Ă  http://localhost:8080

  • Testez l’application de vote

Analyse des vulnérabilités

Explorez les fichiers et identifiez les problĂšmes :

  • Qui exĂ©cute les conteneurs ?

  • OĂč sont stockĂ©s les secrets ?

  • Les images sont-elles Ă  jour ?

  • Y a-t-il des ports exposĂ©s inutilement ?

Points d’attention

À vĂ©rifier :

  • docker compose ps

  • docker container inspect <container>

  • Contenu des Dockerfile

  • Variables d’environnement

Questions : Quels risques identifiez-vous ? Comment exploiter ces failles ?

Sécurité Docker : Les fondamentaux

Les 3 piliers de la sécurité Docker :

  1. Images sûres : Bases fiables, sans vulnérabilités

  2. Runtime sécurisé : Isolation, utilisateurs non-root

  3. Secrets protégés : Pas de mots de passe en clair

Principe du moindre privilĂšge

Un conteneur ne devrait avoir QUE les permissions nécessaires à son fonctionnement

  • Pas d’exĂ©cution en tant que root

  • CapacitĂ©s Linux minimales

  • SystĂšme de fichiers en lecture seule quand possible

  • RĂ©seau isolĂ©

Scan de vulnérabilités

Les images Docker peuvent contenir des vulnérabilités connues (CVE)

Outils de scan :

  • Docker Scout (intĂ©grĂ© Ă  Docker Desktop)

  • Trivy (open source, trĂšs populaire)

  • Snyk, Grype

Docker Scout en action

# Scanner une image locale
docker scout cves python:3.11

# Comparer deux images
docker scout compare python:3.11 --to python:3.11-slim
Démonstration live recommandée

Installation de Trivy

Méthode rapide (pour le TP) :

# Installation directe du binaire
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sudo sh -s -- -b /usr/local/bin
trivy --version
curl | sudo sh est pratique MAIS dangereux en production !

Méthode sécurisée (recommandée) :

# 1. Télécharger le script
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh -o /tmp/trivy-install.sh

# 2. Inspecter le contenu
less /tmp/trivy-install.sh

# 3. Exécuter aprÚs vérification
sudo sh /tmp/trivy-install.sh -b /usr/local/bin

Installation Trivy : Alternatives

Si vous n’avez pas les droits sudo :

# Via Docker (aucune installation nécessaire)
docker container run --rm \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v $HOME/.cache/trivy:/root/.cache \
  aquasec/trivy:0.54.1 image python:3.11
Pin la version (:0.54.1) et persist le cache (-v $HOME/.cache) pour performances

Autres systĂšmes :

  • macOS : brew install trivy

  • Debian/Ubuntu stable : Via apt (voir documentation)

Exemple de résultat Trivy

trivy image python:3.11

Total: 145 vulnerabilities (52 HIGH, 93 MEDIUM)
┌──────────┬──────────────┬──────────┬─────────┐
│ Library  │ Vulnerability│ Severity │ Version │
│ openssl  │ CVE-2023-XXX │ HIGH     │ 1.1.1n  │
│ curl     │ CVE-2023-YYY │ MEDIUM   │ 7.68.0  │
└──────────┮──────────────┮──────────┮─────────┘

Action : Mettre Ă  jour, changer d’image de base, appliquer des patches

Utilisateurs non-root

ProblĂšme : Par dĂ©faut, les processus s’exĂ©cutent en tant que root

FROM python:3.11
COPY app.py /app/
CMD ["python", "/app/app.py"]  # Root !
Si compromis, l’attaquant a les privilĂšges root !

Solution : Utilisateur dédié

FROM python:3.11-slim

RUN groupadd -r appuser && useradd -r -g appuser appuser
WORKDIR /app
COPY app.py /app/
RUN chown -R appuser:appuser /app

USER appuser
CMD ["python", "/app/app.py"]

Bonnes pratiques utilisateur

  • Toujours crĂ©er un utilisateur dĂ©diĂ©

  • UID > 1000 (Ă©viter conflits systĂšme)

  • Ne jamais revenir Ă  root aprĂšs USER

  • VĂ©rifier : docker container exec <container> id

Comprendre les UID Linux

Linux numérote les utilisateurs (User ID) :

  • UID 0 : root (super-utilisateur)

  • UID 1-999 : comptes systĂšme (www-data, postgres, redis…​)

  • UID ≄ 1000 : utilisateurs normaux et applications

Avec volumes montés, UID < 1000 peut créer des conflits de sécurité !

UID : Exemple pratique

# RISQUÉ : UID fixe < 1000
RUN useradd -u 33 appuser
# Conflit possible si l'hĂŽte a www-data (UID 33) !

# SÛR : UID > 1000
RUN useradd -u 1001 appuser

# OU laisser le systĂšme choisir (sera ≄ 1000)
RUN useradd appuser

Vérifier : docker container exec <container> id

Gestion des secrets

Mauvaise pratique :

environment:
  - POSTGRES_PASSWORD=super_secret_123  # NON !
Visible dans docker container inspect, logs, historique !

Bonne pratique : Fichiers .env

Étape 1 : CrĂ©er le fichier .env

# .env (Ă  ne JAMAIS commiter)
DB_PASSWORD=super_secret_123

Étape 2 : ProtĂ©ger avec .gitignore

# .gitignore
.env
secrets/

Étape 3 : Utiliser dans docker-compose.yml

environment:
  POSTGRES_PASSWORD: ${DB_PASSWORD}
  API_KEY: ${API_KEY:-default_key}     # Valeur par défaut si absent
  DB_HOST: ${DB_HOST?required}          # Erreur si non défini
Docker Compose charge automatiquement le fichier .env

Syntaxes avancées :

  • ${VAR:-default} : Utilise default si VAR n’est pas dĂ©finie

  • ${VAR?error message} : Échoue avec message si VAR manquante

  • Utile pour sĂ©parer config obligatoire vs optionnelle

Alternative 1 : Docker Secrets

Gestion native dans Docker Swarm

  • Secrets cryptĂ©s dans le cluster Swarm

  • MontĂ©s en RAM dans /run/secrets/

  • AccĂšs contrĂŽlĂ© par service

echo "secret123" | docker secret create db_password -
docker service create --secret db_password postgres

Alternative 2 : HashiCorp Vault

Solution d’entreprise pour secrets

  • Serveur centralisĂ© avec API REST

  • Rotation automatique des credentials

  • Audit complet et secrets dynamiques

# Application interroge Vault via API
vault kv get -field=password secret/database/prod
Nécessite infrastructure dédiée (serveur Vault)

Alternative 3 : Cloud Providers

Services managés AWS / Azure / GCP

  • AWS Secrets Manager : intĂ©gration RDS/Aurora

  • Azure Key Vault : intĂ©gration Azure AD, HSM

  • GCP Secret Manager : intĂ©gration Cloud Run/GKE

✅ EntiĂšrement managĂ©, haute disponibilitĂ© ❌ Vendor lock-in, coĂ»t Ă  l’usage

Alternative 4 : Variables shell

Passage direct via environnement

  • Simple pour dev et CI/CD

  • Secrets injectĂ©s au runtime

# En une ligne
DB_PASSWORD="secret123" docker compose up -d

# Ou via export
export DB_PASSWORD="secret123"
docker compose up -d
Visible dans ps aux et historique shell !

Comparaison des solutions

SolutionComplexitĂ©CoĂ»tCas d’usage

Fichiers .env

âšȘ Faible

💰 Gratuit

Dev, petits projets

Docker Secrets

🟡 Moyenne

💰 Gratuit

Docker Swarm prod

Vault

🔮 ÉlevĂ©e

💰💰 Variable

Grandes organisations

Cloud Secrets

🟡 Moyenne

💰💰 Payant

Déjà sur cloud

Variables shell

âšȘ Faible

💰 Gratuit

Dev local, CI/CD

Le point commun des cinq solutions

À la fin, le secret est dans le conteneur :

  • en variable d’environnement (.env, variables shell)

  • en fichier sous /run/secrets/ (Docker Secrets)

  • ou en mĂ©moire, lu par l’application via une API (Vault, cloud)

  • dans tous les cas, le programme a la valeur en clair

Et si le programme ne recevait jamais le secret ?

Docker Sandboxes : le proxy d’identifiants

Proxy d’identifiants hors de la microVM
État au 2026-09-24, sbx v0.45.1. Outil rĂ©cent : les commandes peuvent changer.

Démo : ce que voit la sandbox

Sur l’hĂŽte, un secret factice liĂ© Ă  un seul domaine :

sbx policy init balanced
sbx secret set-custom --host httpbin.org --env DEMO_TOKEN --value valeur-reelle-576
sbx create shell --name demo .
sbx policy allow network httpbin.org

Dans la sandbox :

sbx exec demo sh -c 'echo $DEMO_TOKEN'
# sbx-cs-oQlw2y1oLoOzfg5D
sbx exec demo sh -c 'curl -sS -H "Authorization: Bearer $DEMO_TOKEN" https://httpbin.org/headers'
# "Authorization": "Bearer valeur-reelle-576"

Ce que le proxy ne protĂšge pas

  • Le programme peut toujours utiliser le secret vers l’hĂŽte dĂ©clarĂ©

  • Un hĂŽte qui renvoie l’en-tĂȘte le rĂ©vĂšle : httpbin.org vient de le faire !

  • Le proxy dĂ©chiffre le HTTPS : il installe sa propre autoritĂ© de certification dans la sandbox

  • Le secret rĂ©el reste sur l’hĂŽte : sans trousseau, dans un fichier

Le proxy garde la valeur hors de la sandbox, tant que l’hĂŽte dĂ©clarĂ© ne la renvoie pas. Il n’empĂȘche jamais de l’utiliser.

Phase 2 : Sécurisation (1h15)

Transformer SecureVote en application sécurisée

  • Images sĂ»res et lĂ©gĂšres

  • Scanner les vulnĂ©rabilitĂ©s avec Trivy

  • Utilisateurs non-root

  • ProtĂ©ger les secrets

  • Isoler les rĂ©seaux

Exercice 1 : Images sécurisées (20 min)

Mission :

  1. Scanner les images avec Docker Scout ou Trivy

  2. Identifier les vulnérabilités critiques

  3. Remplacer par des images -slim ou -alpine

  4. Re-scanner et comparer

docker scout cves securevote-backend:latest
# Modifier Dockerfile → python:3.11-slim
docker compose build backend
docker scout cves securevote-backend:latest

Exercice 2 : Utilisateurs non-root (25 min)

Mission :

Modifier tous les Dockerfiles pour des utilisateurs non-root

  1. Backend Python : créer flaskuser

  2. Frontend React : créer reactuser

  3. Nginx : utiliser nginx-unprivileged

FROM python:3.11-slim
RUN groupadd -r flaskuser && useradd -r -g flaskuser flaskuser
USER flaskuser

Vérification utilisateurs

docker compose exec backend ps aux
# Le processus principal (PID 1) doit tourner en tant que flaskuser

docker compose exec frontend ps aux
# Le processus principal (PID 1) doit tourner en tant que reactuser
Si vous voyez UID 0 ou root pour le processus principal, c’est Ă  corriger !

Exercice 3 : Sécuriser les secrets (30 min)

Mission :

  1. Identifier les secrets en clair dans docker-compose.yml

  2. Créer .env (sans le commiter !)

  3. Ajouter .env au .gitignore

  4. Utiliser les variables : ${DB_PASSWORD}

  5. Créer .env.example comme template

Checkpoint Phase 2

Vérification collective :

  • Scans : moins de vulnĂ©rabilitĂ©s ?

  • Aucun conteneur en root ?

  • Aucun secret en clair versionnĂ© ?

  • Application fonctionne ?

docker compose ps
docker compose exec backend ps aux
trivy image securevote-backend:latest --severity HIGH,CRITICAL

Configuration Production

Développement :

  • Ressources illimitĂ©es

  • Logs verbeux

  • RedĂ©marrages manuels

Production :

  • Ressources limitĂ©es

  • Logs structurĂ©s

  • Auto-healing

Limites de ressources

Pourquoi limiter ?

  • EmpĂȘcher monopolisation du serveur

  • Garantir stabilitĂ©

  • PrĂ©voir capacitĂ©

  • DĂ©tecter fuites mĂ©moire

Sans limites → 100% CPU/RAM !

Définir les limites

services:
  backend:
    deploy:
      resources:
        limits:
          cpus: '0.5'      # Max 50% CPU
          memory: 512M     # Max 512 Mo RAM
        reservations:
          cpus: '0.25'     # Min garanti
          memory: 256M

Choisir les bonnes valeurs

  1. Démarrer sans limites

  2. Observer : docker container stats

  3. Ajouter 20-30% de marge

  4. Tester sous charge

  5. Ajuster

Politiques de redémarrage

services:
  backend:
    restart: no              # Jamais
    restart: always          # Toujours
    restart: on-failure      # Si erreur
    restart: unless-stopped  # Sauf arrĂȘt manuel

Production : Privilégier unless-stopped ou on-failure

Restart avec limite

services:
  backend:
    restart: on-failure
Pour limiter le nombre de tentatives en Docker Swarm, utilisez deploy.restart_policy.max_attempts: 5

Évite les boucles infinies de redĂ©marrage

Health checks

services:
  backend:
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:5000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

Endpoint de santé

@app.route('/health')
def health():
    try:
        db.ping()
        return jsonify({"status": "healthy"}), 200
    except:
        return jsonify({"status": "unhealthy"}), 503

Dépendances entre services

services:
  backend:
    depends_on:
      database:
        condition: service_healthy
      cache:
        condition: service_started
  • service_started : DĂ©marrĂ© (pas forcĂ©ment prĂȘt)

  • service_healthy : Healthcheck validĂ©

  • service_completed_successfully : TerminĂ© avec succĂšs

Monitoring et logs

Observabilité :

  • Logs : Que s’est-il passĂ© ?

  • MĂ©triques : Combien de CPU/RAM/requĂȘtes ?

  • Traces : Quel chemin a pris une requĂȘte ?

TP13 a déjà couvert ELK

Driver de logs

services:
  backend:
    logging:
      driver: "json-file"
      options:
        max-size: "10m"      # Max 10 Mo
        max-file: "3"        # Garder 3 fichiers
Sans limites, les logs remplissent le disque !

Logs structurés

Mauvais : Server started on port 5000

Bon :

{"timestamp":"2025-01-15T10:30:00Z","level":"INFO","service":"backend","message":"Server started","port":5000}

Facilement parsables par ELK, Loki, etc.

Phase 3 : Production (1h)

Optimiser SecureVote pour la production

  • Limites de ressources

  • Politiques de redĂ©marrage

  • Health checks

  • Optimiser les logs

  • Tester la rĂ©silience

Exercice 4 : Limites de ressources (20 min)

Mission :

  1. Observer avec docker container stats

  2. Définir limites appropriées

  3. Tester sous charge (script fourni)

  4. Ajuster

docker container stats --no-stream
./load_test.sh
docker container stats

Exercice 5 : Restart et Health (25 min)

Mission :

  1. Ajouter politiques de redémarrage

  2. Créer endpoint /health dans backend

  3. Configurer healthchecks

  4. Tester en tuant un conteneur

docker compose kill backend
docker compose ps
docker compose logs backend

Exercice 6 : Dépendances (15 min)

Mission :

  1. Configurer depends_on avec conditions

  2. Tester ordre de démarrage

  3. Simuler indisponibilité DB

  4. Vérifier backend ne démarre pas sans DB

Checkpoint Phase 3

Application production-ready :

  • Limites de ressources ✅

  • Auto-healing ✅

  • Health checks ✅

  • DĂ©pendances ordonnĂ©es ✅

  • Logs optimisĂ©s ✅

docker compose config --services
docker compose ps
docker container stats --no-stream

Registres et Distribution

  • Serveur de stockage d’images Docker

  • Équivalent de npm pour Node, PyPI pour Python

  • Public (Docker Hub) ou privĂ© (self-hosted)

  • Gestion des versions (tags)

Docker Hub

docker image pull nginx:latest
# Équivalent à :
docker image pull docker.io/library/nginx:latest
  • Gratuit pour images publiques

  • Limites de rate (pull anonyme)

Registres privés

Pourquoi ?

  • Images propriĂ©taires

  • ContrĂŽle d’accĂšs

  • Compliance et sĂ©curitĂ©

  • Pas de limite de rate

Solutions de registres

  • Docker Registry (open source)

  • Harbor (CNCF, avec scan)

  • AWS ECR, Azure ACR, GCP GCR

  • GitLab / GitHub Container Registry

Démarrer un registre local

docker container run -d -p 5000:5000 --name registry registry:2
docker image tag securevote-backend:latest localhost:5000/securevote-backend:latest
docker image push localhost:5000/securevote-backend:latest

Tags et versions

# Bonne pratique : versionner
docker image tag myapp:latest myregistry.com/myapp:1.2.3
docker image tag myapp:latest myregistry.com/myapp:1.2
docker image tag myapp:latest myregistry.com/myapp:latest
Plusieurs tags → mĂȘme image

Exercice 7 : Registre local (Bonus)

Si le temps le permet :

  1. Démarrer registre local (port 5000)

  2. Tagger images SecureVote

  3. Pousser vers registre

  4. Modifier docker-compose.yml

  5. Re-déployer

Récapitulatif et bonnes pratiques

Checklist sécurité

  • ✅ Images officielles et Ă  jour

  • ✅ Scan rĂ©gulier des vulnĂ©rabilitĂ©s

  • ✅ Utilisateurs non-root systĂ©matiques

  • ✅ Secrets jamais en clair

  • ✅ RĂ©seau isolĂ© par dĂ©faut

Checklist production

  • ✅ Limites de ressources CPU/RAM

  • ✅ Politiques de redĂ©marrage

  • ✅ Health checks implĂ©mentĂ©s

  • ✅ DĂ©pendances ordonnĂ©es

  • ✅ Logs structurĂ©s et rotation

Ce qu’on a appris

  • SĂ©curiser une application Docker de bout en bout

  • Scanner et corriger les vulnĂ©rabilitĂ©s

  • GĂ©rer les secrets correctement

  • Configurer pour la production

  • Distribuer via registres

SecureVote : Avant/AprĂšs

Avant :

  • Images non scannĂ©es

  • Root partout

  • Secrets en clair

  • Pas de limites

AprĂšs :

  • Images slim, scannĂ©es

  • Utilisateurs dĂ©diĂ©s

  • Secrets protĂ©gĂ©s

  • Production-ready ✅

🔧 Troubleshooting Production

Conteneurs qui ne démarrent pas

Étape 1 : VĂ©rifier les logs

docker container logs nom-conteneur

# OU pour suivre en temps réel
docker container logs -f nom-conteneur

Étape 2 : Inspecter le conteneur

docker container inspect nom-conteneur

# Voir seulement l'état
docker container inspect --format='{{.State.Status}}' nom-conteneur

# Voir le code de sortie
docker container inspect --format='{{.State.ExitCode}}' nom-conteneur

ProblĂšmes courants :

SymptĂŽmeCause probableSolution

host not found in upstream

NGINX : résolution DNS statique

Utiliser variables : set $backend "app:8080" + proxy_pass http://$backend

cannot assign requested address

Port déjà utilisé

Changer port ou arrĂȘter service conflictuel

no such file or directory

Volume mal configuré

Vérifier chemin absolu dans volumes

permission denied

ProblĂšme permissions fichier

Vérifier propriétaire et chmod

Health Checks qui échouent

Étape 1 : VĂ©rifier le status des services

docker compose ps

# Voir seulement les unhealthy
docker compose ps | grep unhealthy

Étape 2 : Inspecter le health check

docker container inspect nom-conteneur | grep -A 20 Health

# Voir uniquement le status
docker container inspect --format='{{.State.Health.Status}}' nom-conteneur

# Voir les derniers logs du health check
docker container inspect --format='{{json .State.Health.Log}}' nom-conteneur | jq

Codes de sortie importants :

  • 0 : Health check rĂ©ussi ✅

  • 1 : Health check Ă©chouĂ© (erreur applicative)

  • 127 : Commande non trouvĂ©e (outil manquant dans l’image)

  • 137 : TuĂ© par signal (timeout)

ProblĂšmes courants :

ExitCode: 127
Output: "/bin/sh: curl: not found"
→ Solution : Installer curl dans Dockerfile
ExitCode: 1
Output: "Connection refused"
→ Solution : VĂ©rifier que l'app Ă©coute sur le bon port

Debugging avec Docker Events

Surveiller tous les événements Docker en temps réel :

# Voir tous les événements
docker events

# Filtrer par conteneur
docker events --filter container=nom-conteneur

# Filtrer par type d'événement
docker events --filter event=start
docker events --filter event=die
docker events --filter event=health_status

# Combiner plusieurs filtres
docker events --filter container=backend --filter event=health_status

Exemple de sortie :

2026-01-12T10:30:15 container health_status: unhealthy (name=backend)
2026-01-12T10:30:20 container die (name=backend, exitCode=137)
2026-01-12T10:30:25 container start (name=backend)

Utilisation pratique :

# Dans un terminal, monitorer les événements
docker events --filter event=health_status

# Dans un autre terminal, démarrer les services
docker compose up -d

# Observer en temps réel les changements de santé

📊 Checklist de Debugging

Quand un problĂšme survient en production :

  1. ✅ docker compose ps → Identifier les services en erreur

  2. ✅ docker container logs <service> → Lire les logs d’erreur

  3. ✅ docker container inspect <service> → VĂ©rifier la configuration

  4. ✅ Si unhealthy : docker container inspect pour voir Health.Log

  5. ✅ docker events en parallĂšle pour monitoring temps rĂ©el

Commande magique de debugging :

docker compose ps && \
docker compose logs --tail=50 && \
docker events --filter type=container

Affiche : statuts, logs récents, et écoute les événements !

VĂ©rifier la signature d’une image

Cosign (projet Sigstore) vĂ©rifie qui a signĂ© une image, pas seulement qu’elle est signĂ©e :

docker container run --rm ghcr.io/sigstore/cosign/cosign:v3.1.3 verify \
  --certificate-identity=krel-trust@k8s-releng-prod.iam.gserviceaccount.com \
  --certificate-oidc-issuer=https://accounts.google.com \
  registry.k8s.io/pause:3.10
Verification for registry.k8s.io/pause:3.10 --
The following checks were performed on each of these signatures:
  - The cosign claims were validated
  - Existence of the claims in the transparency log was verified offline
  - The code-signing certificate was verified using trusted certificate authority certificates

Avec --certificate-identity=someone@example.com, la commande échoue (code de sortie 12) : no matching signatures.

Aller plus loin

  • Orchestration : Kubernetes, Docker Swarm

  • Secrets Management : Vault, Sealed Secrets

  • Image Signing : Cosign (Sigstore), Notation

  • Runtime Security : Falco, Aqua Security

Ressources utiles

Fin de la session

Bravo, vous avez transformé SecureVote en application sécurisée et production-ready !

Questions ?

Bonus

Les petits plus

902050214 image2
902050214 image2
902050214 image2
902050214 image2
902050214 image2
902050214 image2

"Il en remet une couche"

docker image 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 image 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 --no-cache \
     patch \
     tar \
     mercurial \
     git \
     ruby \
     ruby-dev \
     ruby-bundler \
     make \
     g++ \
     zlib-dev \
     libxml2-dev \
     docker \
     nodejs \
     npm \
 && mkdir -p /var/lib/docker-builder \
 && mkdir -p /etc/docker-builder

Ne pas charger inutilement !

 && apk del make g++ zlib-dev libxml2-dev ruby-dev \
 && rm -f previously-downloaded.tar.gz \
 && 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.

Avec apk add --no-cache, l’index des paquets n’est jamais Ă©crit dans l’image : il n’y a pas de cache Ă  vider ensuite.

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 container 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 container inspect
docker container cp
docker image history
docker container 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 sh -c 'seq 1 100'

Alpine n’a pas bash, seulement le sh de BusyBox. Beaucoup de scripts copiĂ©s depuis Internet Ă©chouent sur les images minimales pour cette raison : executable file not found.

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-XXXXXXXXXXXXXXXXXXXX

🚀 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 container 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 container exec

rappel : docker container exec permet de lancer une commande dans un container

docker container exec <containerID> echo "hello"
docker container exec -it <containerID> bash

Debugging : docker container inspect

rappel : docker container inspect et docker image inspect listent toutes les caractĂ©ristiques d’un conteneur ou d’une image.

On peut filtrer le retour de la commande avec jq ou l’option --format.

Debugging : docker container cp

Cette commande permet d’Ă©changer des fichiers entre un conteneur et la machine hĂŽte.

docker container cp --help
Usage:  docker container 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

Aliases:
  docker container cp, docker cp

Options:
  -a, --archive       Archive mode (copy all uid/gid information)
  -L, --follow-link   Always follow symlinks in SRC_PATH
  -q, --quiet         Suppress progress output during copy

Debugging : docker image history

Cette commande permet d’afficher la concatĂ©nation de tous les Dockerfiles qui ont abouti Ă  cette image.

docker image 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 container stats

Cette commande permet d’avoir les stats en temps rĂ©el d’un conteneur.

docker container 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 image load est la premiĂšre Ă©tape de la formule magique pour percer les secrets de son Ɠuvre.

À vos claviers ! đŸ§™â€â™‚ïžâœš

✅ Solution travaux pratiques #14đŸ•”ïžâ€â™‚ïžđŸ”

docker image load est la premiÚre étape de la formule magique

Okay…​

đŸ§™â€â™‚ïžâœš

docker image 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 image 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 image 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 image inspect).

✅ Solution travaux pratiques #14đŸ•”ïžâ€â™‚ïžđŸ”

docker image 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 image 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 image 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

# Installer nginx sans garder l'index des paquets
RUN apk add --no-cache 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 container 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 container 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

# Installe Nginx sans conserver l'index des paquets dans la couche.
# Nginx est un serveur web populaire qui peut Ă©galement ĂȘtre utilisĂ© comme proxy inverse, Ă©quilibreur de charge et cache HTTP.
RUN apk add --no-cache nginx

# 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