Cours Ubuntu 26.04 / Exécuter une application Docker avec volumes et ports limités

Une application conteneurisée doit gérer séparément son image, ses données persistantes et ses ports publiés. Ce laboratoire installe Docker Engine et Compose sur une VM Ubuntu 26.04 neuve, publie Nginx sur la boucle locale et vérifie qu’un volume nommé conserve un fichier après recréation du conteneur.

Choisir l’environnement et comprendre les privilèges

Une image contient le système de fichiers et les paramètres servant à créer un conteneur. Le conteneur ajoute un état d’exécution et une couche d’écriture ; sa suppression peut perdre les modifications de cette couche. Un volume monté est un stockage distinct, dont le cycle de vie peut survivre au conteneur.

Utilisez une VM dédiée avec accès Internet, espace disque disponible et console. Sur un serveur qui héberge déjà Docker, containerd, Podman ou Kubernetes, inventoriez d’abord les paquets, services, conteneurs, volumes et dépôts. Ne supprimez pas les paquets de cette machine en copiant une commande de nettoyage destinée à une installation neuve.

La documentation d’installation Docker pour Ubuntu inclut Ubuntu 26.04 LTS et les architectures prises en charge. Elle énumère aussi les paquets concurrents. Vérifiez que votre VM correspond à ces prérequis avant de choisir cette méthode ; ne mélangez pas sans plan les paquets Ubuntu docker.io, le dépôt Docker et une installation Snap.

L’accès au socket Docker et l’appartenance au groupe docker confèrent des privilèges très élevés sur l’hôte, comme le rappelle le guide Docker après installation. Les commandes de ce cours utilisent sudo. Ne rendez pas le socket accessible à tous et ne montez pas ce socket dans une application sans avoir étudié ses droits.

Installer Engine et Compose depuis le dépôt officiel

Sur la VM neuve, confirmez qu’aucune installation ni source Docker n’existe, y compris dans un fichier APT portant un autre nom. Le script ci-dessous vérifie la version Ubuntu et les deux chemins qu’il crée ; il ne remplace pas cet inventaire. Enregistrez tout le bloc dans ~/install-docker-lab.sh, puis lancez bash ~/install-docker-lab.sh :

set -eu
. /etc/os-release
if [ "$ID" != ubuntu ] || [ "$VERSION_ID" != 26.04 ]; then
    printf "This lab requires Ubuntu 26.04.\n" >&2
    exit 1
fi
if [ -e /etc/apt/sources.list.d/docker.sources ] || [ -L /etc/apt/sources.list.d/docker.sources ] || [ -e /etc/apt/keyrings/docker.asc ] || [ -L /etc/apt/keyrings/docker.asc ]; then
    printf "Inspect the existing Docker repository first.\n" >&2
    exit 1
fi
sudo apt update
sudo apt install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
sudo tee /etc/apt/sources.list.d/docker.sources >/dev/null <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: ${UBUNTU_CODENAME:-$VERSION_CODENAME}
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo docker version
sudo docker compose version
sudo docker run --rm hello-world

Signed-By limite l’emploi de la clé à cette source, selon le principe de gestion des dépôts et clés APT. L’installation peut démarrer le moteur ; hello-world télécharge puis exécute une image. Relisez les demandes du gestionnaire de paquets et arrêtez-vous à toute erreur de signature ou de dépendance.

Attendez l’affichage des versions client et serveur, de Compose et du message de réussite de hello-world. Notez les versions installées. Si le script s’arrête après création de la clé ou du dépôt, inspectez ces fichiers avant de le relancer : il refuse leur présence pour éviter un remplacement silencieux. Retirer seulement le dépôt ne désinstalle pas le moteur et n’efface pas ses données.

Définir un projet neuf, son volume et son port

Le laboratoire Nginx précédent garde son port 8080 ; ce projet utilise 8081 sur l’hôte et 80 dans le conteneur. Le mot hôte désigne ici la VM Ubuntu qui exécute Docker, pas l’ordinateur Windows hébergeant la VM. Consultez les écoutes et les projets existants :

sudo ss -lntp
sudo docker compose ls --all
sudo docker ps -a --filter label=com.docker.compose.project=ubuntu-course-container-lab
sudo docker volume ls --filter label=com.docker.compose.project=ubuntu-course-container-lab
sudo docker volume ls

Le projet ubuntu-course-container-lab et le volume ubuntu-course-container-lab_lab-html doivent être nouveaux. Même un volume conservé après down compte comme un objet existant : ne le réutilisez pas avec des données importantes. Si le port ou les noms sont occupés, choisissez-en d’autres et adaptez l’ensemble du cours de manière cohérente.

Créez ~/compose-lab seulement s’il n’existe pas, puis enregistrez le fichier complet compose.yaml dans ce nouveau dossier :

mkdir "$HOME/compose-lab"
name: ubuntu-course-container-lab
services:
  web:
    image: nginx:stable-alpine
    ports:
      - "127.0.0.1:8081:80"
    volumes:
      - lab-html:/usr/share/nginx/html
    restart: unless-stopped
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"
volumes:
  lab-html:

Le nom explicite du projet rend son identité visible ; assurez-vous aussi que des variables COMPOSE_PROJECT_NAME ou des options -p provenant d’un autre environnement ne la remplacent pas. Toutes les commandes suivantes reviennent dans le même dossier et utilisent le même fichier. Une modification de nom de projet peut créer un nouveau volume vide au lieu de retrouver les données attendues.

Le volume lab-html est monté à la place du répertoire de documents Nginx. Pour un volume vide, Docker copie normalement les fichiers déjà présents à cet emplacement dans l’image ; un volume non vide masque ces fichiers. Les règles des volumes Docker expliquent cette initialisation. Ce mécanisme n’est pas une synchronisation continue avec l’image.

Le tag stable-alpine peut désigner une image différente lors d’un prochain téléchargement. Pour reproduire un déploiement, relevez et testez son digest, puis gérez sa mise à jour ; figer indéfiniment une ancienne image n’est pas une politique de maintenance. Les limites de journaux sont des exemples à adapter, et ne plafonnent ni le volume de données ni tout le stockage Docker.

Démarrer puis vérifier la réponse et le stockage

(
set -eu
cd "$HOME/compose-lab"
sudo docker compose config
sudo docker compose pull
sudo docker compose up -d
sudo docker compose ps
sudo docker compose logs --tail 30 web
)

config vérifie et affiche le modèle Compose résolu ; il ne prouve pas que le port est libre ni que le service répond. Relisez l’adresse publiée, le volume et l’image avant up. Si le téléchargement ou le démarrage échoue, corrigez l’erreur et examinez les journaux avant d’écrire dans le volume.

Dans ce volume neuf réservé au cours, remplacez la page initiale de Nginx par le marqueur du laboratoire. Ce remplacement est volontaire et ne doit pas viser un volume de production :

(
set -eu
cd "$HOME/compose-lab"
sudo docker compose exec -T web sh -c 'printf "volume-lab\n" > /usr/share/nginx/html/index.html'
response=$(curl --noproxy '*' --fail --show-error --retry 10 --retry-connrefused --retry-delay 1 http://127.0.0.1:8081/)
printf '%s\n' "$response"
test "$response" = volume-lab
container_id=$(sudo docker compose ps -q web)
test -n "$container_id"
sudo docker inspect "$container_id" --format '{{json .Mounts}}'
sudo docker image inspect nginx:stable-alpine --format '{{json .RepoDigests}}'
)

Attendez volume-lab et une entrée de montage de type volume sur /usr/share/nginx/html. Relevez le nom réel de la source. Les reprises curl tolèrent un court délai de démarrage mais une réponse différente reste un échec ; un conteneur indiqué running n’est pas nécessairement prêt à répondre.

Recréer le conteneur et contrôler l’exposition réseau

(
set -eu
cd "$HOME/compose-lab"
before=$(sudo docker compose ps -q web)
test -n "$before"
sudo docker compose up -d --force-recreate
after=$(sudo docker compose ps -q web)
test -n "$after"
printf 'Avant : %s\nAprès : %s\n' "$before" "$after"
test "$before" != "$after"
response=$(curl --noproxy '*' --fail --show-error --retry 10 --retry-connrefused --retry-delay 1 http://127.0.0.1:8081/)
printf '%s\n' "$response"
test "$response" = volume-lab
sudo docker compose ps
sudo docker inspect "$after" --format '{{json .NetworkSettings.Ports}}'
sudo ss -lntp
)

Les identifiants doivent différer et le contenu rester volume-lab. compose up --force-recreate remplace le conteneur tout en conservant les volumes montés. Cette observation établit la persistance du fichier de ce laboratoire ; elle ne prouve ni une sauvegarde ni l’intégrité complète d’une base de données.

L’association 127.0.0.1:8081:80 limite ce port publié à la boucle locale du moteur dans le mode de réseau utilisé ici. Elle ne coupe pas les communications entre conteneurs du même réseau. La documentation de publication des ports signale aussi une exception des moteurs antérieurs à 28.0.0, où des voisins du même segment réseau pouvaient atteindre des ports publiés sur localhost. Utilisez une version maintenue et vérifiez la configuration réseau réelle.

Depuis une autre machine, tentez une nouvelle connexion vers l’adresse LAN de la VM sur 8081 : elle ne doit pas atteindre cette publication locale. Consultez à la fois inspect, compose ps et les règles réseau ; ss seul peut ne pas représenter tout le transfert effectué par Docker. --noproxy '*' empêche les tests curl locaux de passer par un mandataire défini dans l’environnement.

Si vous publiez ultérieurement un service sur une adresse externe, étudiez le filtrage Docker et le pare-feu du fournisseur. Les interactions entre Docker et les pare-feux empêchent de supposer que les seules règles UFW protègent les ports publiés. Ne désactivez pas globalement les règles Docker pour tenter de résoudre ce problème.

Arrêter sans effacer les données et préparer une sauvegarde

(
set -eu
cd "$HOME/compose-lab"
sudo docker compose down
sudo docker volume ls --filter label=com.docker.compose.project=ubuntu-course-container-lab
)

Par défaut, compose down retire les conteneurs et réseaux du projet, tout en conservant les volumes nommés. Pour confirmer leur réutilisation, relancez up -d dans le même dossier, puis reprenez le contrôle curl avec le marqueur attendu.

down -v, volume rm et certaines opérations prune peuvent supprimer des données : ce ne sont pas des variantes ordinaires d’arrêt. Avant un nettoyage, identifiez le volume exact, ses utilisateurs et une sauvegarde restaurable. Ne supprimez pas tout /var/lib/docker et ne manipulez pas directement les fichiers internes d’un volume en cours d’utilisation.

Pour une application réelle, coordonnez les écritures et utilisez une sauvegarde cohérente avec son fonctionnement, par exemple l’export propre d’une base de données. Conservez les copies sur un support indépendant et testez la restauration dans un autre projet. Un volume persistant reste exposé à la panne de l’hôte, à la suppression et aux erreurs de l’application.