On repart de l'exemple donné dans Try Docker Compose de la doc officielle.
Créer votre propre projet et préparez-le.
- Démo sur les différentes options de
compose
- YAML
- up
- Développement : Watch des sources sans volume
- Variables d'environnement
- Secrets
- Utiliser les profiles (activation/désactivation de services)
- Rediriger les logs
- Override les variables d'environnement de Docker Compose
- Gestion des environnements : un seul ou plusieurs fichiers ? Démo d'une méthode basée sur le merge
- Docker compose et l'orchestration
Utiliser un linter pour détecter les erreurs, par exemple yamllint
yamllint compose.yml
docker compose up
docker compose ps
docker volume ls
#Voir quel volume est attaché à un conteneur
docker inspect <id conteneur> | grep Mounts -A 10
Regarder les conteneurs (le nom par défaut : nom du projet-nom du service-d'un identifiant incrémenté
)
Si pas de nom de projet, nom du repertoire courant
On voit qu'un volume anonyme a été crée. Il a été crée par redis
(Dockerfile de son image contient instruction VOLUME
), aller voir le code source de son image (Dockerfile
) pour vous en convaincre.
---
services:
web:
build: .
ports:
- "8000:5000"
#Plus besoin de ce hack ici
# volumes: #volume anonyme
# - .:/code
develop:
watch:
- path: ./
action: sync
target: /code
environment:
FLASK_DEBUG: "true"
redis:
image: "redis:alpine"
#Lancer le projet
docker compose up -d
#Lancer le watch
docker compose watch
Puis modifier les sources, et requêter le serveur web pour tester le reload.
Placer les variables d'environnement dans un fichier externe : plus simple à maintenir, allège le fichier compose, centralise les valeurs.
Créer un fichier d'env .env
Le fichier
.env
est chargé automatiquement par Docker Compose (comportement par défaut, override dans variable d'envCOMPOSE_ENV_FILES
)
MA_VARIABLE=FOO
FLASK_DEBUG=false
web:
build: .
ports:
- "8000:5000"
# volumes: #volume anonyme
# - .:/code
environment:
FLASK_DEBUG: ${FLASK_DEBUG}
MA_VARIABLE: ${MA_VARIABLE}
Inspecter variable d'env sur le conteneur :
docker compose up
docker exec <nom> env
docker compose up
recrée un conteneur si sa config (compose) change. Si la variable d'environnement interpolée change dans le .env
, docker compose
le détecte et recrée aussi le conteneur ! Cool.
Modifier le fichier d’environnement .env
:
#FLASK_DEBUG=false
FLASK_DEBUG=true
puis relancer le projet :
docker compose up
Observer la reconstruction automatique du conteneur au changement d'une variable d'environnement.
La variable d'env du
.env
n'est injectée que si elle est utilisée dans le compose ou par le conteneur.
Sur un dépôt distant (remote), on créera un fichier
.env.dist
avec des configs par défaut. Chaque personne quipull
le dépôt fait une copie localecp .env.dist .env
et définit ses propres variables d’environnement en fonction de ses besoins. Si pousser des modifs : les mettre dans le.env.dist.
(partager avec le reste de l'équipe). Sinon, ignorer le.env
(local a la machine, au dev) en le mettant dans le.gitignore
.
Utiliser docker compose config
pour checker l'interpolation correcte des variables d'environnement.
Créer un fichier password
avec my-secret-password
, la valeur à protéger le plus possible :
echo 'my-secret-password' > secret
~~~yaml
services:
web:
build: .
ports:
- "8000:5000"
environment:
FLASK_DEBUG: ${FLASK_DEBUG}
MA_VARIABLE: ${MA_VARIABLE}
secrets:
- my_secret
redis:
image: "redis:alpine"
#Nouvelle section
secrets:
my_secret:
file: ./password
Modifier le code source :
import time
import os
import redis
from flask import Flask
app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379)
def get_hit_count():
retries = 5
while True:
try:
return cache.incr('hits')
except redis.exceptions.ConnectionError as exc:
if retries == 0:
raise exc
retries -= 1
time.sleep(0.5)
@app.route('/')
def hello():
count = get_hit_count()
# Chemin vers le fichier secret
secret_file_path = "/run/secrets/my_secret" # Ce chemin peut varier selon le système d'exploitation
# Vérifier si le fichier secret existe
if os.path.exists(secret_file_path):
# Lire le contenu du fichier secret
with open(secret_file_path, 'r') as secret_file:
secret_value = secret_file.read().strip()
else:
secret_value = 'not found, sorry'
return 'Coucou ! I have seen this {} times. Voici le secret : {}\n'.format(count, secret_value)
Reconstruire l'image :
docker compose build web
docker compose up -d
#Test
curl localhost:8000
Modifier le fichier compose.yaml
:
---
services:
web:
build: .
ports:
- "8000:5000"
# volumes: #volume anonyme
# - .:/code
environment:
FLASK_DEBUG: ${FLASK_DEBUG}
MA_VARIABLE: ${MA_VARIABLE}
# deploy:
# mode: replicated
# replicas: 3
secrets:
- my_secret
profiles: [frontend]
develop:
watch:
- path: ./
action: sync
target: /code
redis:
image: "redis:alpine"
Tester :
#Les 2 s'activent (web et redis)
docker compose --profile frontend up
#Que redis
docker compose up
#Rediriger les logs des events dans un fichier en tache de fond
docker compose events > dengine.log &
Voir les variables
Dans un fichier .env
a la racine du projet :
COMPOSE_PROFILES=frontend
Nous utilisons la gestion des variables d'environnement basée sur le merge.
Créer un fichier compose.dev.yaml
et compose.prod.yaml
en plus du fichier de base compose.yaml
.
#compose.dev.yaml
---
services:
web:
environment:
MA_VARIABLE: ${MA_VARIABLE_DEV}
#compose.prod.yaml
---
services:
web:
environment:
MA_VARIABLE: ${MA_VARIABLE_PROD}
Et toujours un de base compose.yaml
(config par défaut, a ne jamais utiliser seul)
#compose.yaml
---
services:
web:
build: .
ports:
- "8000:5000"
environment:
FLASK_DEBUG: ${FLASK_DEBUG}
MA_VARIABLE: ${MA_VARIABLE_PROD}
secrets:
- my_secret
profiles: [frontend]
develop:
watch:
- path: ./
action: sync
target: /code
redis:
image: "redis:alpine"
profiles: [debug]
secrets:
my_secret:
file: ./password
Remarque : ici je n'ai qu'un fichier d'environnement (pour les besoins de la démo), mais il en faudrait deux :
prod.env
,dev.env
, et chaque fichiercompose
ferait référence à son propre fichier d’environnement sous l'attributenv_file
.
#Lancer en env de prod
docker compose -f compose.yml -f compose.prod.yml up -d
docker exec -it composetest-web-1 env
#Lancer en env de dev
docker compose -f compose.yml -f compose.dev.yml up -d
docker exec -it composetest-web-1 env
Attention, l'ordre est important :
docker compose -f compose.prod.yml -f compose.yml
est différent dedocker compose -f compose.yml -f compose.prod.yml
Le mieux c'est d'essayer vous-même les différentes méthodes (héritage, include, merge, combinaison des mécanismes,...) :
- Faites un mini projet minimal pour chaque méthode (avec 2 ou 3 services par exemple)
- Simuler la variation d'un paramètre d'environnement. Faites les modifs nécessaires (port, path, etc.)
- Regarder ce que vous devez modifier et quels fichiers vous allez modifier
- Une fois terminés, regardez ce que vous allez push comme changements sur un depot (utiliser git, ou un outil de diff pour vous assister)
- Regarder ce que vous allez pull comme changements depuis le dépôt;
- Regardez les erreurs que vous pouvez rencontrer, commettre;
- Regarder les commandes
docker compose up
que vous devez lancer, si vous devez les modifier
Choisir la méthode qui fait le plus sens pour vous et votre organisation. C'est une décision à débattre et trouver un compromis (chacun·e ses préférences).
Au delà de docker compose up
qui est capable de détecter n'importe quel rebuild à faire en fonction des changements des sources, compose
offre également les options deploy
, replica
et restart
:
services:
web:
build: .
ports:
- "8000:5000"
# volumes: #volume anonyme
# - .:/code
environment:
FLASK_DEBUG: "true"
deploy:
mode: replicated
replicas: 3
docker compose up
docker compose ps
Si on combine deploy
avec restart
, on peut créer un pool de conteneurs issus d'un service dont le nombre est déterminé avec certitude à l'execution et dans le temps (disponibilité renforcée)
services:
web:
build: .
ports:
- "8000:5000"
# volumes: #volume anonyme
# - .:/code
environment:
FLASK_DEBUG: "true"
deploy:
mode: replicated
replicas: 3
restart: always #par exemple
Kill un conteneur du service web
et observez. Vous devriez toujours en avoir 3 (un autre de crée). Regardez les stats ou les events pour mieux voir ce qu'il se passe.