Aller au contenu

Semaine 8 — Les commandes de base de Docker

Durée estimée : 45 min · Prérequis : Docker : images et conteneurs

Objectifs d'apprentissage

  • Lancer un conteneur avec docker run, et choisir ses options principales.
  • Suivre un conteneur dans son cycle de vie : créé, en marche, arrêté, supprimé.
  • Rendre un service web d'un conteneur accessible depuis votre poste.
  • Lire les journaux d'un conteneur et y lancer une commande.

1. La forme d'une commande Docker

Toutes les commandes Docker ont la même forme :

Texte
docker  run  -d --name web -p 8080:80  nginx
│       │    └──────── options ──────┘ └── image
│       └── l'action
└── le programme

Les options de Docker se placent avant le nom de l'image. Tout ce qui suit le nom de l'image est transmis au programme du conteneur, et non à Docker : docker run nginx -d passe l'argument -d à nginx, et le conteneur ne s'exécute pas en arrière-plan.

Astuce

Chaque commande accepte --help : docker run --help, docker ps --help. Les options utilisées dans ce cours sont résumées à la section 6.

2. Le cycle de vie d'un conteneur

Un conteneur passe par quelques états. docker ps -a les affiche dans la colonne STATUS.

stateDiagram-v2
    direction LR
    [*] --> Created: docker create
    Created --> Up: docker start
    [*] --> Up: docker run
    Up --> Exited: docker stop, ou fin du programme
    Exited --> Up: docker start
    Exited --> [*]: docker rm
    Up --> [*]: docker rm -f
État affiché Ce que ça veut dire
Created Le conteneur existe, mais son programme n'a jamais été lancé.
Up 3 minutes Le programme tourne depuis trois minutes.
Exited (0) 2 seconds ago Le programme s'est terminé normalement. Le 0 est son code de sortie : zéro veut dire « tout s'est bien passé ».
Exited (1) ... ou un autre nombre Le programme s'est terminé sur une erreur. Lisez ses journaux avec docker logs.

docker run enchaîne docker pull (si l'image est absente), docker create et docker start. C'est la commande utilisée en pratique pour créer un conteneur.

Un conteneur arrêté (Exited) conserve sa couche inscriptible et ses paramètres. docker start le relance dans cet état. docker rm le supprime définitivement, avec sa couche inscriptible.

3. Lancer un conteneur : docker run

Au premier plan ou en arrière-plan

Par défaut, docker run attache le terminal au conteneur : la sortie du programme s'affiche, et l'invite revient quand il se termine. Pour un serveur, qui ne se termine pas, le terminal reste occupé.

L'option -d (detached) exécute le conteneur en arrière-plan. Docker affiche l'identifiant du conteneur créé et rend l'invite immédiatement.

Bash
docker run -d nginx
Texte
197ac5e25f5707d6480d75b442ad24eeaf82907122013aad2498c5e7fa1bf6c2

Donner un nom

Sans l'option --name, Docker génère un nom aléatoire, comme angry_tesla. Nommer le conteneur permet de le désigner de façon prévisible dans les commandes suivantes :

Bash
docker run -d --name web nginx

Un nom est unique sur l'hôte. Toutes les commandes acceptent le nom ou l'identifiant ; pour l'identifiant, les premiers caractères suffisent s'ils ne sont pas ambigus : docker stop 197a.

Une session interactive

L'option -i garde l'entrée standard ouverte, et -t alloue un pseudo-terminal. Combinées en -it, elles permettent d'utiliser un programme interactif, comme un shell, dans le conteneur :

Bash
docker run -it alpine sh

Le shell s'exécute dans un nouveau conteneur Alpine. exit termine le shell, qui est le programme principal : le conteneur s'arrête.

Suppression automatique

L'option --rm supprime le conteneur dès qu'il s'arrête. Elle convient aux conteneurs lancés pour une seule commande :

Bash
docker run --rm alpine cat /etc/os-release

Passer une variable d'environnement

L'option -e définit une variable d'environnement pour le programme du conteneur. C'est le moyen habituel de paramétrer une image sans la modifier : mot de passe, adresse d'une base de données, langue.

Bash
docker run --rm -e PRENOM=Camille alpine sh -c 'echo "Bonjour $PRENOM"'
Texte
Bonjour Camille

À la semaine 7, -e DB_HOST=10.70.8E.10 indiquait ainsi au site d'Aria l'adresse de sa base de données.

4. Publier un port : -p

Le réseau des conteneurs

À son installation, Docker crée dans l'hôte un bridge nommé docker0, sur le réseau privé 172.17.0.0/16. C'est le même principe que le bridge vmbr1 de la semaine 3 : un commutateur logiciel interne. Chaque conteneur reçoit une interface branchée sur ce bridge et une adresse dans ce réseau, par exemple 172.17.0.2. L'hôte sert de passerelle et fait la traduction d'adresses (NAT) vers l'extérieur.

Les conteneurs peuvent donc sortir vers Internet, mais aucune machine extérieure ne peut joindre le réseau 172.17.0.0/16. nginx écoute sur le port 80 de son conteneur, et ce port n'est pas accessible depuis votre poste.

La publication

L'option -p publie un port : Docker redirige vers un port du conteneur les connexions reçues sur un port de l'hôte.

Bash
docker run -d --name web -p 8080:80 nginx
Texte
-p 8080:80
   │    └── le port du conteneur, où nginx écoute
   └── le port de l'hôte, où arrivent les connexions

L'ordre est toujours hôte:conteneur.

flowchart LR
    P["Poste de travail"]
    subgraph VM["VM docker · 10.70.#.31"]
        PV(["port 8080"])
        B["bridge docker0<br/>172.17.0.1"]
        subgraph C["conteneur web · 172.17.0.2"]
            N["nginx, port 80"]
        end
    end
    P -- "http://10.70.#.31:8080" --> PV
    PV -- "redirection" --> B
    B --> N

Le port du conteneur est déterminé par l'application : 80 pour nginx, 3306 pour MySQL. Le port de l'hôte est libre. Plusieurs conteneurs issus de la même image peuvent donc coexister, à condition que chacun soit publié sur un port différent de l'hôte :

Bash
docker run -d --name site1 -p 8081:80 nginx
docker run -d --name site2 -p 8082:80 nginx
docker run -d --name site3 -p 8083:80 nginx

Les trois nginx écoutent sur le port 80 sans conflit, puisque chacun a sa propre interface et sa propre adresse sur docker0. Seul le port de l'hôte doit être unique.

5. Observer et gérer les conteneurs

Lister : docker ps

Bash
docker ps
Texte
CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS         PORTS                                   NAMES
197ac5e25f57   nginx     "/docker-entrypoint.…"   2 minutes ago   Up 2 minutes   0.0.0.0:8080->80/tcp, :::8080->80/tcp   web

docker ps n'affiche que les conteneurs en cours d'exécution. L'option -a (all) ajoute les conteneurs arrêtés. Un conteneur qui vient d'être lancé et qui n'apparaît pas dans docker ps s'est généralement arrêté : docker ps -a indique son état et son code de sortie.

La colonne PORTS reprend la publication : 0.0.0.0:8080->80/tcp signifie que le port 8080, sur toutes les adresses IPv4 de l'hôte, est redirigé vers le port 80 du conteneur. Sans publication, la colonne n'indique que le port déclaré par l'image, sans flèche : 80/tcp.

Lire les journaux : docker logs

Docker conserve tout ce que le programme principal écrit sur sa sortie standard et sa sortie d'erreur. docker logs l'affiche :

Bash
docker logs web

Avec -f (follow), l'affichage continue au fil des nouvelles lignes, comme tail -f. Ctrl+C interrompt l'affichage, pas le conteneur.

docker logs est la première commande de diagnostic : un conteneur qui s'arrête de lui-même y écrit presque toujours la cause de son arrêt.

Arrêter, redémarrer, supprimer

Bash
docker stop web      # arrête le programme ; le conteneur passe à Exited
docker start web     # le relance, avec son contenu
docker rm web        # le supprime ; il doit être arrêté
docker rm -f web     # l'arrête et le supprime d'un coup

Lancer une commande dans un conteneur en marche : docker exec

docker exec lance un processus supplémentaire dans un conteneur en cours d'exécution, avec les mêmes namespaces que son programme principal :

Bash
docker exec web ls /usr/share/nginx/html

Avec -it et un shell, on obtient une session interactive dans le conteneur :

Bash
docker exec -it web bash

exit termine ce shell, mais le conteneur continue de s'exécuter : son programme principal, nginx, n'est pas concerné.

docker run crée, docker exec s'ajoute

docker run -it nginx bash crée un nouveau conteneur dont le programme principal est bash à la place de nginx : aucun serveur web n'y est lancé. Pour agir dans un conteneur existant, la commande est docker exec.

Gérer les images

Bash
docker images        # liste les images sur le disque de la VM
docker pull alpine   # télécharge une image sans lancer de conteneur
docker rmi alpine    # supprime une image (aucun conteneur ne doit en dépendre)

6. Aide-mémoire

Action Commande
Lancer un conteneur en arrière-plan docker run -d --name NOM IMAGE
Lancer un conteneur avec un shell interactif docker run -it IMAGE sh
Exécuter une commande ponctuelle, sans conserver le conteneur docker run --rm IMAGE COMMANDE
Publier un port docker run -d -p PORT_HÔTE:PORT_CONTENEUR IMAGE
Définir une variable d'environnement docker run -e VARIABLE=valeur IMAGE
Lister les conteneurs en cours d'exécution docker ps
Lister tous les conteneurs docker ps -a
Lire les journaux docker logs NOM (ou -f pour suivre)
Ouvrir un shell dans un conteneur en cours d'exécution docker exec -it NOM sh
Arrêter / relancer docker stop NOM / docker start NOM
Supprimer docker rm NOM (ou rm -f s'il tourne)
Voir les images docker images
Supprimer une image docker rmi IMAGE
Supprimer les conteneurs arrêtés / les images inutilisées docker container prune / docker image prune -a

Pièges fréquents

  • Placer une option après l'image. docker run nginx -p 8080:80 ne publie aucun port : l'argument est transmis à nginx.
  • Inverser les ports. -p 80:8080 redirige le port 80 de l'hôte vers le port 8080 du conteneur, où rien n'écoute. L'ordre est hôte:conteneur.
  • Supposer qu'un docker run en échec ne laisse rien. Lorsque docker run échoue sur port is already allocated, le conteneur a déjà été créé : il reste à l'état Created et son nom est réservé. Il faut le supprimer avec docker rm avant de relancer la commande.
  • Utiliser docker run pour redémarrer un conteneur. docker run crée toujours un nouveau conteneur. docker start relance un conteneur existant.
  • Laisser s'accumuler les conteneurs arrêtés. Ils réservent leur nom et occupent de l'espace disque. docker ps -a les liste, docker container prune les supprime.

Vérification des acquis

Question 1

Vous voulez que le site d'un conteneur nginx soit visible à l'adresse http://10.70.4.31:9000. Quelle option utilisez-vous ?

  • a) -p 80:9000
  • b) -p 9000:80
  • c) -p 9000
Voir la réponse **b) `-p 9000:80`** Le port de l'hôte, où arrivent les connexions, est à gauche : `9000`. Le port du conteneur, où nginx écoute, est à droite : `80`. Le choix a) inverse les deux : il redirige le port 80 de l'hôte vers le port 9000 du conteneur, où nginx n'écoute pas.

Question 2

Vous tapez docker run -d --name test alpine, puis docker ps. Le conteneur test n'apparaît pas. Pourquoi ?

  • a) L'image alpine n'a pas été téléchargée.
  • b) Le programme par défaut d'Alpine est un shell. Sans entrée standard ouverte (-i), il n'a rien à lire et se termine aussitôt, ce qui arrête le conteneur.
  • c) -d masque les conteneurs dans la liste.
Voir la réponse **b) Le shell s'est terminé aussitôt.** Le programme principal de l'image `alpine` est `sh`. Sans `-i`, son entrée standard est fermée : il lit une fin de fichier et se termine avec le code 0. `docker ps -a` montre le conteneur à l'état `Exited (0)`. Le choix a) est faux : une image absente est téléchargée automatiquement, ou la commande échoue avec un message d'erreur.

Question 3

Vous avez lancé un conteneur web à partir de nginx. Vous voulez lister les fichiers de son site sans l'arrêter. Quelle commande utilisez-vous ?

  • a) docker run -it web ls /usr/share/nginx/html
  • b) docker exec web ls /usr/share/nginx/html
  • c) docker start web ls /usr/share/nginx/html
Voir la réponse **b) `docker exec web ls /usr/share/nginx/html`** `docker exec` lance un processus supplémentaire dans un conteneur en cours d'exécution, sans affecter son programme principal. Le choix a) cherche une **image** nommée `web` : `docker run` prend toujours une image en argument, jamais un conteneur.

Question 4

Vous arrêtez un conteneur avec docker stop, puis vous le relancez avec docker start. Les fichiers que vous aviez modifiés dans le conteneur sont-ils encore là ?

  • a) Oui : un conteneur arrêté garde son contenu jusqu'à ce qu'il soit supprimé.
  • b) Non : chaque démarrage repart de l'image.
Voir la réponse **a) Oui.** `docker stop` arrête le programme principal, mais le conteneur reste sur le disque avec sa couche inscriptible. `docker start` relance ce même conteneur. `docker rm` supprime la couche inscriptible. Un conteneur créé ensuite avec `docker run` part de l'image, sans les modifications. La conservation des données au-delà de la vie d'un conteneur, avec les **volumes**, est traitée à la semaine 10.

Pour aller plus loin