# Docker ![Schema Docker Swarm](images/docker-wave-whale.svg "Schema Docker Swarm") **Les conteneurs - Utilisation avancée** Maxime Poullain • Christian Tritten ## Docker run Permet d'instancier un conteneur à partir d'une image ### Docker run - options Option | Description - | - `--detach`, `-d` | Mode _détaché_ : lance le conteneur à l'arrière plan `--name NAME` | Donne un nom au conteneur `--interactive`, `-i` | Conserve STDIN ouvert `--tty`, `-t` | Alloue un pseudo-TTY ### Docker run - restart policies On utilise l'option `--restart` avec ces paramètres : Policy | Result -------------------------|--------------------------------------- `no` | Par défaut. Ne redémarre pas `on-failure[:max-retries]` | Redémarre seulement avec un _non-zero exit status_ `always` | Redémarre tout le temps et indéfiniment. Démarre aussi en même temps que le démon Docker `unless-stopped` | Redémarre tout le temps sauf si il a été coupé ### Contraintes sur les ressources * Par défaut une application conteneurisée peut consommer l'intégralité de la mémoire disponible de l'hôte. * Lorsqu'il n'y a plus de mémoire disponible, le kernel Linux : 1. lance une _Out Of Memory Exception_ (OOME), 2. commence à [tuer des processus](https://www.kernel.org/doc/gorman/html/understand/understand016.html) pour libérer de la mémoire. Il est possible de limiter les ressources allouées à un conteneur grâce à la technologie des _control groups_. Option | Description -------------------------|--------------------------------------- `--memory` | Limite mémoire, minimum 4M `--memory-swap` | Limite mémoire totale, (memoire + swap) `--cpu` | Limite sur l'utilisation du cpu https://docs.docker.com/config/containers/resource_constraints/ Sur un système en production il est recommandé d'appliquer systématiquement des limitations sur l'utilisation de la mémoire. ## Docker exec Permet d'éxécuter des commandes dans le conteneur en plus du processus de base. Notamment utile pour débugger. ### Docker exec - options Option | Shorthand | Default | Description ----------------|-------------|---------|------------------------------ `--detach` | `-d` | False | Detached mode: run command in the background `--interactive` | `-i` | False | Keep STDIN open even if not attached `--tty` | `-t` | False | Allocate a pseudo-TTY `--user` | `-u` | False | Username or UID ### Docker exec - exemple * Ouvrir une console Bash dans le conteneur : `$ docker exec -it mon_conteneur /bin/bash` * Créer un fichier à l'intérieur du conteneur : `$ docker exec mon_conteneur touch /tmp/test` ## Docker cp Permet de copier des fichiers/dossiers depuis et vers un conteneur en cours d'exécution. * Copie un fichier de l'hôte vers le conteneur : `$ docker cp fichier nom-du-conteneur:/chemin/vers/fichier` * Créer un fichier du conteneur vers l'hôte : `$ docker cp nom-du-conteneur:/chemin/vers/fichier fichier` ## Docker diff Affiche les modifications effectuées sur le système de fichiers du conteneur. ``` $ docker diff mon-conteneur C /var C /var/lib C /var/lib/apt C /var/lib/apt/lists A /var/lib/apt/lists/lock A /var/lib/apt/lists/auxfiles A /var/lib/apt/lists/deb.debian.org_debian_dists_bullseye_InRelease A /var/lib/apt/lists/partial ``` ## Monitoring simple des conteneurs ### Docker Stats Docker embarque un équivalent de la commande `top` permettant d'afficher les métriques des conteneurs en cours d'exécution. `docker stats` ```none CONTAINER CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS 5098617c3654 0.05% 0B / 0B 0.00% 648B / 0B 0B / 0B 2 b2d0efcb5151 1.00% 0B / 0B 0.00% 648B / 0B 0B / 0B 3 cbdb1069b241 0.10% 0B / 0B 0.00% 648B / 0B 0B / 0B 5 ``` ### ctop Métriques des conteneurs dans une interface à la `top`. ![ctop](images/ctop.gif) Source : [https://ctop.sh/](https://ctop.sh/) ## Configuration d'une application ### Variables d'environnement - Il est possible d'injecter des variables d'environnement dans le conteneur au moment de sa création. - L'option `--env` permet de spécifier une variable et sa valeur. - L'option `--env-file` permet de passer un fichier de variables. - Une fois injectées dans le conteneur ces variables sont accessibles par le premier processus lancé. #### Exemple - L'image de _MariaDB_ est conçue pour être configurée à l'exécution à l'aide de variables d'environnement. - Ces variables vont permettre d'initialiser une base avec un utilisateur et mot de passe personnalisés. ```none $ docker run --name mariadb \ --env MYSQL_ROOT_PASSWORD=docker \ --env MYSQL_DATABASE=docker \ --env MYSQL_USER=docker \ --env MYSQL_PASSWORD=docker \ mariadb:10.7.1 ``` #### Exemple avec --env-file `mariadb-vars` ```none MYSQL_ROOT_PASSWORD=docker MYSQL_DATABASE=docker MYSQL_USER=docker MYSQL_PASSWORD=docker ``` ```none $ docker run --name mariadb \ --env-file=mariadb-vars \ mariadb:10.7.1 ``` #### Mini TP : variables d'environnement ![Travaux pratiques](images/tp.gif) Créer un conteneur basé sur l'image `xian310/who-is-there:21` avec la configuration suivante : - Lancer le conteneur au premier plan. - Publier le port d'écoute dans le conteneur (8080) vers le port de votre choix sur l'hôte. Accéder à l'application dans le navigateur web en précisant le chemin `/env` dans l'url. Créer le conteneur : ```none $ docker run --rm --publish 8080:8080 xian310/who-is-there:21 ``` Accéder à l'application : ```none $ firefox http://127.0.0.1:8080/env ``` - Supprimer le conteneur. - Créer un nouveau conteneur en lui passant au moins 2 variables d'environnement de votre choix. - Recharger la page web. Créer un nouveau conteneur en lui passant des variables d'environnement : ```none $ docker run --rm --publish 8080:8080 \ --env MA_VARIABLE=hello --env MON_AUTRE_VARIABLE="au revoir" \ xian310/who-is-there:21 ``` - On peut configurer la couleur de fond de la page `/env` en passant la variable `COLOR` au conteneur. - Pour une liste des couleur possibles : https://fr.wikipedia.org/wiki/Couleur_du_Web Changer la couleur de fond de la page `/env` : ```none $ docker run --rm --publish 8080:8080 \ --env COLOR=lightblue \ xian310/who-is-there:21 ``` ```none $ docker run --rm --publish 8080:8080 \ --env COLOR=pink \ xian310/who-is-there:21 ``` ## Gestion des données Persistance des données avec Docker ### 3 mécanismes différents Docker offre trois mécanismes permettant de monter des données de l'hôte dans un conteneur : - volumes - bind mounts - tmpfs volumes Dans le doute les _volumes_ sont presque toujours le bon choix. ![Type de montage](images/types-of-mounts.png) Source : documentation officielle Docker Quel que soit le type de montage utilisé, les données seront présentées de la même façon au conteneur, c'est à dire sous la forme d'un dossier ou d'un fichier unique. ### Volumes ![Type de montage](images/types-of-mounts-volume.png) - Les _volumes_ sont stockés sur le système de fichiers de l'hôte dans le dossier `/var/lib/docker/volumes/`. - Un volume est créé et _managé_ par Docker. - Les processus autres que Docker ne devraient donc pas y modifier directement les données. - Un volume peut être monté simultanément dans plusieurs conteneurs * en lecture/écriture ou lecture seule, * très utile pour partager des données entre conteneurs. - Un volume est indépendant des conteneurs. - Le contenu persiste même si aucun conteneur n'utilise le volume. - Un volume doit être explicitement supprimé. - un volume peut être nommé ou anonyme. (Un volume anonyme se voit attribuer un identifiant unique sur l'hôte) - Les volumes supportent la notion de _volume drivers_. (pour stocker les données sur des hôtes distant ou dans le cloud par exemple) - un volume peut être sauvegardé, restoré ou migré facilement d'un hôte Docker à l'autre. (Stopper les conteneurs utilisant le volume, sauvegarder le dossier `/var/lib/docker/volumes//`). 1. Créer un volume `$ docker volume create VOLUME` 2. Lister les volumes `$ docker volume ls` 3. Inspecter un volume `$ docker volume inspect VOLUME` 4. Supprimer un volume `$ docker volume rm VOLUME` 5. Supprimer tous les volumes non-utilisés `$ docker volume prune` ```none $ docker volume create mon-volume ``` ```none $ docker volume inspect mon-volume ``` ```json [ { "CreatedAt": "2018-01-16T10:15:51+01:00", "Driver": "local", "Labels": {}, "Mountpoint": "/var/lib/docker/volumes/mon-volume/_data", "Name": "mon-volume", "Options": {}, "Scope": "local" } ] ``` * `docker volume inspect` permet de connaître le chemin réel du volume sur le système hôte. ### Bind mounts ![Type de montage](images/types-of-mounts-bind.png) - Les _bind mounts_ permettent de "monter" n'importe quel fichier ou dossier du système hôte à l'intérieur du conteneur. - Exemples d'utilisation de Bind mount : * partager des fichiers de configuration entre l'hôte et des conteneurs. * partager du code source entre l'hôte et un conteneur (sur un poste de développement). - Le fichier ou le répertoire source peut ne pas exister au préalable sur l'hôte (il sera créé le cas échéant). - __Attention !__ Si un dossier ou fichier critique pour le système hôte est monté dans le conteneur, ce dernier peut y accéder sans restriction. ### tmpfs mounts ![Type de montage](images/types-of-mounts-tmpfs.png) - Les _tmpfs mounts_ sont stockés directement dans la mémoire de l'hôte. - Les données sont donc volatiles et détruites en même temps que le conteneur qui les utilise (ou en cas de reboot du système). - Les _tmpfs mounts_ ne peuvent pas être partagés entre plusieurs conteneurs. ### Exemples de montages 1. Monte un volume nommé ```none $ docker run --detach \ --name apache \ --publish 8080:80 \ --mount source=mon-volume,target=/var/www/mon-app \ httpd:2.4 ``` 2. Monte un répertoire local (bind mount) ```none $ docker run --detach \ --name apache \ --publish 8080:80 \ --mount type=bind,source=/home/debian/mon-app,destination=/var/www/mon-app \ httpd:2.4 ``` 3. Monte un répertoire local (bind mount) en read-only ```none $ docker run --detach \ --name apache \ --publish 8080:80 \ --mount type=bind,source=/home/debian/mon-app,destination=/var/www/mon-app,readonly \ httpd:2.4 ``` ### -v ou --mount ? - A l'origine , les options `-v` et `--volume` étaient utilisées pour les conteneurs _standalone_ et l'option `--mount` pour les _services_ Swarm. - On peut utiliser les 2 syntaxes. ### Volumes et droits d'accès Attention : les accès sur les fichiers restent soumis aux droits Linux. ### Travaux pratiques ![Travaux pratiques](images/tp.gif) [TP Docker Volumes](../travaux-pratiques/slides/docker/tp-volumes.html) ### Accéder à la racine du système de fichiers du conteneur ```none # docker inspect -f '{{.State.Pid}}' mon-conteneur 11384 # cd /proc/11384/root/ total 52 drwxr-xr-x 19 root root 4096 déc. 18 17:26 ./ drwxr-xr-x 19 root root 4096 déc. 18 17:26 ../ drwxr-xr-x 2 root root 12288 mars 9 2017 bin/ drwxr-xr-x 5 root root 360 déc. 18 17:08 dev/ -rwxr-xr-x 1 root root 0 déc. 18 17:08 .dockerenv* drwxr-xr-x 2 root root 4096 déc. 18 17:08 etc/ drwxr-xr-x 2 99 99 4096 mars 9 2017 home/ dr-xr-xr-x 263 root root 0 déc. 18 17:08 proc/ drwxr-xr-x 2 root root 4096 déc. 18 17:23 root/ dr-xr-xr-x 13 root root 0 déc. 18 17:11 sys/ drwxrwxrwt 2 root root 4096 mars 9 2017 tmp/ drwxr-xr-x 3 root root 4096 mars 9 2017 usr/ drwxr-xr-x 4 root root 4096 mars 9 2017 var/ ``` Spécification RunC : [https://github.com/opencontainers/runc/blob/master/libcontainer/SPEC.md](https://github.com/opencontainers/runc/blob/master/libcontainer/SPEC.md) ### Connaître l'espace disque utilisé par Docker `docker system df` ```none $ docker system df TYPE TOTAL ACTIVE SIZE RECLAIMABLE Images 23 2 3.871GB 3.794GB (98%) Containers 11 0 2.989GB 2.989GB (100%) Local Volumes 4 0 94.22MB 94.22MB (100%) ``` ### Nettoyer le système `docker system prune` * Supprime les conteneurs arrêtés * Supprimes les images qui n'ont plus de tag associé * avec `-a` supprime aussi les images non utilisées * Supprime les volumes orphelins * Supprime les réseaux vides ## Travaux pratiques ![Travaux pratiques](images/tp.gif) [TP application complète Web + DB](../travaux-pratiques/slides/docker/tp-web-db.html) ## Docker Compose Application multi-conteneurs - _Compose_ est un outil permettant de configurer et déployer une application composée de plusieurs conteneurs. - _Compose_ n'est pas inclus dans Docker il faut donc l'installer en plus. [https://docs.docker.com/compose/](https://docs.docker.com/compose/) - L'application (mono-conteneur ou multi-conteneurs) est décrite dans un fichier texte au format _yml_. - Chaque service (conteneur) possède sa propre section de configuration. - Par défaut un réseau personnalisé de type Bridge est automatiquement créé pour que l'ensemble des conteneurs de l'application : * puissent communiquer entre eux, * être isolés des autres conteneurs. ### docker-compose.yml ```yaml version: '3' services: web: build: . # ici l'image sera construite à partir d'un Dockerfile ports: - "5000:5000" volumes: - .:/code - logvolume01:/var/log healthcheck: test: ["CMD", "curl", "-f", "http://localhost"] interval: 1m30s timeout: 10s retries: 3 redis: image: redis # ici on reprend une image déjà existante volumes: logvolume01: {} ``` Directive | Description -------------------------|--------------------------------------- `image` | Spécifie l'image Docker à utiliser `build` | Spécifie le chemin pour build l'image `ports` | Configure la publication de ports `depends_on` | Exprime la dépendance entre plusieurs services et ainsi l'ordre de lancement `restart` | Spécifie le configuration de redémarrage du conteneur `healthcheck` | Configure une vérification de la santé du service `...` | https://docs.docker.com/compose/compose-file/ Derrière un proxy : ```yaml version: "3" services: my-service: build: context: my-folder/ args: - http_proxy=http:///my.proxy.url:3128 - https_proxy=https:///my.proxy.url:3128 ``` Pour vérifier la syntaxe du fichier docker-compose.yml : `$ docker-compose config` Pour déployer la stack applicative en une seule commande : `$ docker-compose up -d` Pour arrêter la stack sans supprimer les conteneurs : `$ docker-compose stop` Pour supprimer complètement la stack : `$ docker-compose down` Cela entraîne la suppression des conteneurs et des réseaux associés. Pour afficher les logs de tous les conteneurs de la stack : `$ docker-compose logs` ### Le fichier `.env` * Permet de déclarer des variables dont la valeur sera automatiquement substituée lors du traitement du fichier _docker-compose.yml_. * Est localisé dans le même dossier que _docker-compose.yml_. * `docker-compose config` permet de visualiser le fichier final. `.env` ```bash MA_VARIABLE=hello-world MON_AUTRE_VARIABLE=bonjour ``` `docker-compose.yml` ```yaml version: '3' services: hello: image: ${MA_VARIABLE} environment: - env_var_name=${MON_AUTRE_VARIABLE} ``` ### Travaux pratiques ![Travaux pratiques](images/tp.gif) [TP Docker Compose](../travaux-pratiques/slides/docker/tp-compose.html) ## Docker CMD --format Permet d'utiliser des templates Go pour personnaliser l'affichage ```none $ docker images --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}" ``` ```none IMAGE ID REPOSITORY TAG 1afc387274f5 cheatsheet latest 258031686918 node 8.1.2 a2ff708b7413 debian latest 00f017a8c2a6 busybox latest 00f017a8c2a6 10.6.30.30:5000/mpoullain/busybox latest ``` [https://docs.docker.com/engine/admin/formatting/](https://docs.docker.com/engine/admin/formatting/) ## API du démon Docker * Le démon Docker fourni une API qui permet de : - récupérer des informations sur le démon et les objets Docker - lancer des commandes * Documentation : [https://docs.docker.com/develop/sdk/](https://docs.docker.com/develop/sdk/) Lister les conteneurs : ```none $ curl --unix-socket /var/run/docker.sock http://1.24/containers/json | jq . ``` ```json [ { "Id": "4ab7c5898ac9a594b42406c0afc2250b1b7da36e8966348c09e32fde84515608", "Names": [ "/determined_bohr" ], "Image": "debian:stretch", "ImageID": "sha256:8626492fecd368469e92258dfcafe055f636c21a5865a98a0a6...", "Command": "bash" } ... ] ``` Démarrer un conteneur : ```none $ curl --unix-socket /var/run/docker.sock -X POST \ http://1.24/containers/4ab7c5898ac9/start ``` Afficher les logs d'un conteneur : ```none $ curl --unix-socket /var/run/docker.sock \ http://1.24/containers/4ab7c5898ac9/logs?stdout=1 ``` ## Mode debug Il est possible d'activer le mode debug du démon Docker : ```none root@node1: # sudo cat /etc/docker/daemon.json {"debug":true} ``` ```none root@node1: # systemctl reload docker ``` ```none root@node1: # tail -f /var/log/syslog c'est beaucoup plus verbeux !! ```