Docker-compose
Référence de la syntaxe en version 3
Installer docker et docker-compose
<source>
- Enlever les vieilles versions
sudo apt-get remove docker docker-engine docker.io containerd runc
- Préalable
sudo apt-get update sudo apt-get install apt-transport-https ca-certificates curl gnupg lsb-release
- Ajouter la clef GPG
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
- Ajouter le repo
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
- Installer Docker
apt update apt install docker-ce docker-ce-cli containerd.io
- Installer docker-compose
apt install docker-compose </source>
Explication des versions de docker-compose
Doc sur les versions Il existe à l'heure actuelle (Octobre 2021) trois versions de Docker-compose: Les versions 2 et 3 sont valides, la 1 est dépréciée. Il est recommandé d'utiliser la dernière version, mais nous y reviendrons.
Il faut spécifier la version au début de chaque fichier docker-compose. Pour cela: <source> version :2
- Correspond à 2.0
version: 2.1
- Ou
version: 3 </source> Si on ne mets pas cette ligne, le fichier est considéré comme étant en version 1.
Les différences notables:
- Version 1 : Ne prend pas en compte la notion de networking, ni celle de volumes. Le seul moyen de lier des conteneurs est par l'usage de links.
Exemple: <source lang="yaml"> web:
build: . ports: - "5000:5000" volumes: - .:/code links: - redis
redis:
image: redis
</source>
- Version 2 : On peut déclarer des volumes et des réseaux. Par défaut, les conteneurs sont dans le même réseau et leur hostname est leur nom; mais on peut faire ses propres réseaux. Les versions mineures ont apporté de nouveaux paramètres, voir ladoc en lien ci-dessus.
Exemple un peu "chiadé": <source lang="yaml"> version: "2.4" services:
web: build: . ports: - "5000:5000" volumes: - .:/code networks: - front-tier - back-tier redis: image: redis volumes: - redis-data:/var/lib/redis networks: - back-tier
volumes:
redis-data: driver: local
networks:
front-tier: driver: bridge back-tier: driver: bridge
</source>
- Version 3 : La version 3 a été conçue pour être cross-compatible avec le mode Swarm de docker-compose. De nombreuses options ont disparu :( La fonctionnalité deploy a été ajoutée, j'y reviendrait plus bas.
Un exemple simple : freshrss
Un fichier docker-compose doit **toujours** s'appeller docker-compose.yml. En général, je fais un dossier pour chaque projet dans /opt.
Le docker-compose suivant fait tourner un conteneur pour FreshRSS (un très bon lecteur de flux en mode web). <source lang="yaml"> --- version: "3" services:
freshrss: image: ghcr.io/linuxserver/freshrss container_name: freshrss environment: - PUID=1000 - PGID=1000 - TZ=Europe/London volumes: - ./config:/config ports: - 3100:80 restart: unless-stopped
</source> Explications:
- --- : marque le début d'un fichier yaml.
- services : on va déclarer nos services.
- freshrss : nom de mon service
- image : nom de l'image dans le docker hub
- container_name : le nom de mon conteneur
- environment: Sert à déclarer des variables d'environnement dans le conteneur. Celles-ci sont utilisées par mon application.
volumes
- volumes : on déclare des volumes.
Un volume est un dossier partagé entre le conteneur et la machine hôte. Ils servent surtout à y mettre les données importantes, pour ne pas les perdres entre deux démarrages. L'utilisation de volumes est pratique car elle permet de ranger toute notre application dans un dossier avec son docker-compose, de la déplacer, la lancer ailleurs, etc.
Mon docker-compose.yml est dans /opt/freshrss.
Ici, la mention ./config:/config permet de binder (à gauche) le dossier de ma machine hôte /opt/freshrss/config avec (à droite) le dossier /config dans mon conteneur.
ports
- ports: on binde les ports.
Ici, il s'agit de binder des ports, sur le même principe que pour le volume. Le port 3100 de mon hôte mènera ainsi au port 80 du conteneur. Par défaut, c'est en tcp, mais on peut préciser (et même binder des ports de protocole différents): 3100/tcp:87/udp
Quand redémarre le conteneur ?
- restart : sert à définir les conditions de redémarrage du conteneur.
Peut être à no, always-on, unless-stopped, on-failure. Il s'agit ici d'une syntaxe de version 2, qui est compatible comme ça mais pas en mode swarm. La version swamr donnerait plutôt: <source lang="yaml"> version: "3.9" services:
redis: image: redis:alpine deploy: restart_policy: condition: on-failure delay: 5s max_attempts: 3 window: 120s
</source>
Je ne rentre pas dans les détails sur la section deploy, je vais le faire juste après.
De façon (un peu) plus poussée
La section deploy, mode de compatibilité
Cette section, apparue en version 3, contient toutes les directives utiles à un déploiement sur swarm. Cependant, elle peut aussi contenir des choses utiles sans swarm, comme une limite de ram ou une restart-policy. Les deux choses qui m'intéressent sont surtout:
- La limite de mémoire
- La restart policy
...car ce sont des choses que l'on peut utiliser en version 3, sans swarm. En effet, par défaut, un lancement sans swarm ignore les directives dans deploy !
Pour pouvoir lancer un fichier en version 3, sans swarm, avec une section deploy et en récupérant ce qui est récupérable, il faut lancer son fichier avec l'option (non documentée) --compatibility. On va voir un exemple un peu plus parlant.
Un exemple avec des réseaux, des limites mémoire, 2 conteneurs
<source lang="yaml"> version: "3"
networks:
gitea: external: false db: external: false ipam: config: - subnet: 172.19.0.0/24
services:
database: image: mariadb:latest container_name: mariadb_gitea volumes: - ./gitea_db/:/var/lib/mysql environment: - MYSQL_ROOT_PASSWORD=ithaibai0C - MYSQL_DATABASE=gitea - MYSQL_USER=gitea - MYSQL_PASSWORD=ithaibai0C networks: db: ipv4_address: 172.19.0.10 deploy: resources: limits: memory: 200M reservations: memory: 100M
server: image: gitea/gitea:latest container_name: gitea environment: - USER_UID=1000 - USER_GID=1000 restart: always networks: gitea: db: ipv4_address: 172.19.0.20 volumes: - ./gitea:/data - /etc/timezone:/etc/timezone:ro - /etc/localtime:/etc/localtime:ro ports: - "3000:3000" deploy: resources: limits: memory: 200M reservations: memory: 100M
</source>
section network
<source lang="yaml"> networks:
gitea: external: false db: external: false ipam: config: - subnet: 172.19.0.0/24
</source> Il n'est pas obligatoire de déclarer des IPs pour s'assurer de la connectivité des conteneurs; voir Docker-compose#En_utilisant_les_hostnames Ici, je déclare mes réseaux. J'en ai 2 ; gitea et db. L'option external si elle est à True sert à rejoindre un réseau déjà existant (ce qui n'est pas mon cas ici). Si elle est activée, docker-compose va chercher un réseau du nom de mon réseau et tenter de s'y connecter.
Par défaut, un réseau utilise une adresse privée aléatoire. Je peux définir le sous-réseau que je veux donner à mon réseau avec les options "ipam" et suivantes.
Ensuite, pour chacun de mes conteneurs, je peux leur dire quelle adresse ils prendront si j'ai définit un subnet dans la partie réseau (je ne peux pas leur définir d'adresse sans avoir déclaré de subnet dans réseau bien sûr, et par défaut les conteneurs ont une adresse aléatoire): <source lang="yaml">
networks: db: ipv4_address: 172.19.0.10
</source>
En utilisant les hostnames
Par défaut, deux conteneurs situés dans le même réseau peuvent résoudre les noms de conteneurs comme des noms dns. Dans l'exemple ci-dessous, le conteneur 'alice' peut parler à 'bob' directement via une requête sur http://bob/stufftograb et ça se passera bien. Donc, configurer les réseaux est facultatif. <source lang="yaml"> version: '3.3'
networks:
monreseau:
services:
alice: restart: always environment: - MASTER_URL=http://bob/stufftograb networks: - monreseau bob: restart: always networks: - monreseau
</source>
section deploy : limites mémoire
En version 3 sans swarm, avec le mode de compatibilité, je peux limiter la ram allouée aux conteneurs (mais pas le CPU). Autrement, ils se servent allègrement jusqu'à ce qu'il n'y en ai plus (et ils redémarrent).
Cela se fait par la section suivante: <source lang="yaml">
deploy: resources: limits: memory: 200M reservations: memory: 100M
</source> Ici, je limite à 200 Mo et je réserve 100Mo.
Choisir quel utilisateur lance les conteneurs
C'est utile notamment pour savoir à quel utiisateur doivent appartenir les dossiers qui me servent de volumes. Cela se fait par la directive user: "UID:GID": <source lang="yaml"> service:
database: container_name: mydb user: "1001:1001"
</source>
Choisir les serveurs DNS de mon conteneur
Cela se fait simplement via une directive appellée DNS: <source lang="yaml">
blackbox-exporter: dns: - 9.9.9.9 - 8.8.8.8 - 1.1.1.1
</source>
Ici, mon conteneur blackbox ira résoudre ses adresses auprès de quad9 (puis de Google, puis de Cloudflare).
Voir les stats de mes conteneurs (mémoire, cpu, etc)
Cela se fait via la commande "docker stats": <source> docker stats CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS 29448d6f6660 freshrss 0.01% 19.97MiB / 200MiB 9.98% 7.36kB / 6.41kB 221kB / 10.3MB 14 5700f28b120e mariadb_gitea 0.03% 91.88MiB / 200MiB 45.94% 113kB / 269kB 17.8MB / 8.19kB 8 90751ba098af gitea 0.00% 126.3MiB / 200MiB 63.13% 346kB / 1.44MB 745kB / 36.9kB 12 </source>
Lancement, arrêt
À effectuer en se trouvant dans le dossier de mon docker-compose.
<source>
- Lancement normal
docker-compose up
- Lancement avec mise en arrière plan
docker-compose up -d
- Lancement avec mode de compatibilité
docker-compose --compatibility up -d
- Arrêt
docker-compose down </source>
Mise à jour d'un conteneur en latest
docker compose pull docker compose up -d