Outil indispensable à la gestion de vos stack Docker, Docker Compose est très fortement utilisé par la communauté. Je l'utilise moi même au quotidien, mais avec un nombre toujours croissant de serveurs à gérer, la commande commence à trouver ses limites !

Car aujourd'hui peu de solutions s'offrent à nous afin de déployer facilement des ensembles de services sur des hôtes distants. Quelles solutions avons-nous pour déployer des applications on remote ?

Ansible ? Puppet ? Une pipeline CI/CD ? Ces outils sont formidables pour automatiser des déploiements connus et maîtrisés. Mais de façon plus pragmatique, ils ajoutent également une couche de complexité dont je n'ai pas forcément besoin afin de réaliser mes démos ou de livrer facilement en production une nouvelle stack oneshot.

Initialement prévu pour s'exécuter sur un moteur Docker présent localement, la commande Docker Compose ne répond pas encore à ce besoin. Mais la commande commence à évoluer et la prochaine version 1.26, qui est actuellement en release candidate apporte quelques réponses !! Qu'apporte cette version afin de répondre à ce besoin ?

La copie de fichiers ...

Avant d'aborder les nouveautés, un constat sur la méthodologie utilisée par beaucoup d'entre nous pour déployer des services conteneurisés sur des hôtes distants.

Une utilisation courante de Compose consiste à copier la source du projet, avec votre fichier docker-compose.yml, sur la machine distante et d'y installer la commande.

Que vous utilisiez scp , rsync ou git, peu importe. La manipulation fonctionne sans aucun soucis, exemple ici :

$ ssh user@remotehost
$ git clone myrepo
$ sudo curl -L "https://github.com/docker/compose/releases/download/1.25.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose
$ cd myrepo
$ docker-compose up -d

Mais dans certains cas cette façon de procéder peut devenir problématique. Effectivement, il va falloir installer docker-compose sur toutes vos machines et maintenir l'exécutable à jour si vous souhaitez utiliser les dernières fonctionnalités.

Et je ne vais pas aborder la question de la sécurité de vos projets. Généralement les fichiers contiennent des éléments sensibles, mot de passe de base de données, clé API, etc ... Vous les effacez de vos serveurs après le déploiement ?

À noter également que cette manipulation peut parfois créer quelques soucis de versionning dans vos fichiers docker-compose.yml. Est-ce que j'ai bien récupéré le dernier fichier modifié directement sur la production !? ( Oui oui on a tous déjà fait une manipulation de ce type 😂 )

Pour des environnements ou le nombre de serveurs à manipuler est peu conséquent, cette utilisation est simple et efficace. Mais si votre parc est bien plus important... Cette méthodologie va vite devenir un casse-tête. D'autres solutions ?!

Docker Compose 1.26

Alors qu'ajoute cette version de Compose qui me pousse à vous écrire un article rien qu'à ce sujet ?Et bien cette version ajoute notamment :  

  • Add v3.8 schema support

Mais nous aurons sûrement l'occasion de revenir là-dessus plus tard 😀

Pour le moment, ce qui m'intéresse :

  • Add docker context support

Aucun commentaire :

J'ai pu évoquer l'utilisation des context Docker très récemment sur le blog. Prenez le temps de lire l'article présent ici, car vous allez très rapidement comprendre tout l'intérêt de cette notion :

Docker : context
Vous gérez plusieurs nodes Docker, des clusters Swarm ou même encore des clusters Kubernetes et la gestion de vos conteneurs sans une interface commune devient complexe. Avez-vous pensé à utiliser les contexts de Docker afin de centraliser votre CLI ?

Moi je pars installer cette nouvelle version immédiatement !

sudo curl -L "https://github.com/docker/compose/releases/download/1.26.0-rc3/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

Et on rend tout ça exécutable :

sudo chmod +x /usr/local/bin/docker-compose

Vérifiez immédiatement votre version :

docker-compose --version
docker-compose version 1.26.0-rc3, build 46118bc5

Les context

Rappelons tout de même rapidement :

Un context Docker est un mécanisme qui permet de fournir des noms aux endpoints de l'API Docker et de stocker ces informations pour une utilisation ultérieure. Les contexts Docker peuvent être facilement gérés avec la CLI Docker.

Assurez-vous bien évidement d'avoir mis en place des clés SSH sur votre machine distante. Il est également nécessaire que l'utilisateur cible est accès au groupe Docker.

Dans un premier temps, il va donc falloir créer un context afin de pouvoir utiliser ce endpoint ultérieurement :

docker context create dev --docker host=ssh://debian@X.X.X.X --default-stack-orchestrator=swarm

🚩 Même si votre serveur n'est pas un cluster Swarm, à date, la commande ne fonctionne pas si je ne précise pas l'orchestrateur. Aucune incidence pour un node Docker simple. 🚩

Vous pouvez vérifier la création de votre context avec la commande suivante :

docker context ls

Je vais maintenant créer un fichier docker-compose.yml d'exemple :

version: "3.7"
services:
  frontend:
    image: traefik:2.2
    command: --providers.docker --entrypoints.web.address=:80 --providers.docker.exposedbydefault=false
    ports:
      # The HTTP port
      - "80:80"
    volumes:
      # So that Traefik can listen to the Docker events
      - /var/run/docker.sock:/var/run/docker.sock
    depends_on:
      - backend
      
  backend:
    image: containous/whoami
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.rule=Path(`/`)"
      - "traefik.http.services.whoami.loadbalancer.server.port=80"

Oui oui, par hasard j'utilise une image Traefik et whoami 😄

Il me reste plus qu'à déployer cette stack dans mon context. J'ai deux possibilités pour réaliser ça :

docker-compose --context dev up -d

🚩 Sans le flag -d, vous allez avoir un retour d'erreur. La commande ne permet pas de garder la main sur le conteneur lancé. Logique mais aussi frustrant parfois ! 🚩

Je vais vérifier que tout est lancé :

docker --context dev ps

Et aussi :

curl http://X.X.X.X/
Hostname: 205f75ae1962
IP: 127.0.0.1
IP: 172.19.0.2
RemoteAddr: 172.19.0.3:46222
GET / HTTP/1.1
Host: X.X.X.X
User-Agent: curl/7.68.0
Accept: */*
Accept-Encoding: gzip
X-Forwarded-For: X.X.X.X
X-Forwarded-Host: X.X.X.X
X-Forwarded-Port: 80
X-Forwarded-Proto: http
X-Forwarded-Server: c3c5cf6e4f02
X-Real-Ip: X.X.X.X

Nous pouvons arrêter la stack :

docker-compose --context dev down

De mon côté, j'ai une erreur pour le moment lors de l'arrêt de la stack mais tout semble fonctionner correctement :

ERROR: for lfache_frontend_1  ChannelException(2, 'Connect failed')
Traceback (most recent call last):
  File "bin/docker-compose", line 6, in <module>
  File "compose/cli/main.py", line 72, in main
  File "compose/cli/main.py", line 128, in perform_command
  File "compose/cli/main.py", line 424, in down
  File "compose/project.py", line 340, in down
  File "compose/project.py", line 328, in remove_stopped
  File "compose/parallel.py", line 341, in parallel_remove
  File "compose/parallel.py", line 335, in parallel_operation
  File "compose/parallel.py", line 112, in parallel_execute
  File "compose/parallel.py", line 210, in producer
  File "compose/container.py", line 259, in remove
  File "site-packages/docker/utils/decorators.py", line 19, in wrapped
  File "site-packages/docker/api/container.py", line 994, in remove_container
  File "site-packages/docker/utils/decorators.py", line 46, in inner
  File "site-packages/docker/api/client.py", line 238, in _delete
  File "site-packages/requests/sessions.py", line 615, in delete
  File "site-packages/requests/sessions.py", line 533, in request
  File "site-packages/requests/sessions.py", line 646, in send
  File "site-packages/requests/adapters.py", line 449, in send
  File "site-packages/urllib3/connectionpool.py", line 672, in urlopen
  File "site-packages/urllib3/connectionpool.py", line 387, in _make_request
  File "http/client.py", line 1252, in request
  File "http/client.py", line 1298, in _send_request
  File "http/client.py", line 1247, in endheaders
  File "http/client.py", line 1026, in _send_output
  File "http/client.py", line 966, in send
  File "site-packages/docker/transport/sshconn.py", line 32, in connect
  File "site-packages/paramiko/transport.py", line 879, in open_session
  File "site-packages/paramiko/transport.py", line 1017, in open_channel
paramiko.ssh_exception.ChannelException: ChannelException(2, 'Connect failed')
[20604] Failed to execute script docker-compose

docker --context dev ps ne renvoie aucun conteneur actif. Le down semble donc fonctionner correctement.

J'ai parlé de deux méthodes, car il n'est pas forcément nécessaire de préciser votre context systématiquement.

Vous pouvez changer celui par défaut afin que docker-compose l'utilise. Rien de plus simple :

docker context use dev

Dans ces conditions, la commande docker-compose fonctionne comme d'habitude !


Vous avez pu voir au cours de l'article comment déployer vos stacks sur des serveurs distants, sans aucune copie de fichier avec l'utilisation de la nouvelle version de la commande Docker Compose.

Je trouve que la combinaison de docker context et docker-compose --context est vraiment formidable et va faciliter le déploiment d'instance et de conteneur dans bien des cas !

Cet article vous a t-il donné envie d'essayer cette nouveauté ? En tout cas de mon côté, je n'en ai pas fini avec les context. Prochain cas d'utilisation avec ... Docker Swarm ! Nous verrons ça ensemble au prochain épisode 😀

En tout cas  n'hésitez pas à m'apporter des remarques ou des commentaires sur Twitter ! C'est toujours un plaisir d'avoir des retours ! 😇