Sommaire
Plop,
Solution simple à un problème qui m'a souvent cassé les gonades et dont je suis resté longtemps sans réponse : comment redéployer une stack docker swarm ?
Contexte
Docker est un truc muche permettant de faire tourner des applications dans des containers.
Docker Swarm permet de faire des groupes afin de facilement répartir les containers sur plusieurs serveurs.
Une stack est un groupe cohérent de containers destinés à travailler ensemble.
Exemple
Exemple de stack :
services:
webapp:
image: "webapp"
networks:
- traefik_public
- redis
- pg
deploy:
labels:
- "traefik.enable=true"
- "traefik.http.routers.monservice.rule=Host(`monservice.exemple.org`)"
- "traefik.http.routers.monservice.tls.certresolver=leresolver"
- "traefik.http.services.monservice.loadbalancer.server.port=80"
pg:
image: "postgres:18"
networks:
- pg
volumes:
- pg_data:/var/lib/postgresql
redis:
image: "valkey:latest"
networks:
- redis
networks:
traefik_public:
external: true. Et là c'est le drame.
pg:
redis:
volumes:
pg_data:
puis un petit coup de
$ docker stack deploy -c compose.yml
pis pouf, vous avez 3 containers qui tournent dans votre grappe : une web app, une base de données postgres et un serveur de clé valeur valkey (fork de redis). Y'a des sous réseaux afin d'isoléer tout ce petit monde (valkey ne peut parler qu'à la webapp. pareil pour postgres. valkey et postgres ne peuvent pas se parler entre eux). la db a un volume afin que ses données soient persistantes. La web app est automatiquement géré par le frontal web traefik (avec certificat tls lets encrypt)
Tout est géré automatiquement, y'a rien a faire c'est simple, c'est beau, c'est propre. j'aime. ça a le Jojo seal of approval®, une récompense rare.
Apparté, comment déployer un cluster docker swarm ?
Sur un des serveurs : docker swarm init
Sur les autres : docker swarm join <params donnés par docker swarm init>
Tout est géré automatiquement, y'a rien a faire c'est simple, c'est beau, c'est propre. j'aime. ça a le Jojo seal of approval®, une récompense rare.
Pour les petites infras, docker swarm c'est la vie, mangez en.
Le drame de la CI/CD
Maintenant, imaginez que vous êtes le développeur de webapp.
Vous développez une nouvelle fonctionnalité, vous la pushé, l'intégration continue exécute les tests, construit l'image docker et la publie sur votre registre.
Jusque là pas de problème.
Maintenant, vous voulez redéployer votre stack docker swarm afin de relancer les containers avec des images à jour. Et là c'est le drame.
Parce que voyez-vous, la doc est formelle. le seul moyen de (re-)déployer une stack, c'est via la commande docker stack deploy [-c compose_file]. Commande à exécuter sur un des serveurs de la grappe swarm. C'est à dire pas votre serveur actuel parce que là vous êtes sur celui de la CI.
Arf.
J'vais quand même pas demander à ma CI de se connecter en ssh à ma grappe, télécharger le compose.yaml depuis le dépot et exécuter le docker stack deploy. C'est ignoble et ça a l'air un peu relou à implémenter pour que ça soit suffisamment robuste.
Pendant longtemps ma solution a été d'utiliser un webhook portainer : la CI faisait une requête HTTP sur une URL secrète, et pouf portainer redeploy la stack.
C'était insatisfaisant parce fallait d'abord déclarer la stack dans portainer pour obtenir l'url du webhook à enregister dans la CI. Moi je suis une sale feignasse, je veux que tout marche seul automagiquement:(
Les contextes à la rescousse
Les contextes sont un moyen de gérer plusieurs démons docker depuis un client docker.
Par défault, votre client docker ne connait qu'un seul contexte, default (votre docker local).
jtremesay@nemo ~> docker context ls
NAME DESCRIPTION DOCKER ENDPOINT ERROR
default * Current DOCKER_HOST based configuration unix:///var/run/docker.sock
Toutes les commandes docker que vous tapez sont envoyées à ce contexte, comme docker run, docker ls, ou cas qui nous intéresse, docker stack deploy.
Et oh joie, vous pouvez déclarer des contextes distants :
$ docker context create --docker "host=ssh://alpha.exemple.org" alpha
Exemple d'utilisation :
# Commande exécuté localement :
$ docker ps
# Changement de contexte :
$ docker context use alpha
# Commande exécuté sur alpha :
$ docker ps
# Restaurer le contexte par défaut :
$ docker context use default
# Utiliser un contexte pour un one-shot:
$ docker --context alpha ps
$ DOCKER_CONTEXT=alpha docker ps
Contextes et CI
Préparation à effectuer sur le nœud docker :
# Création d'un utilisateun dédié à la CI
$ sudo useradd -m -G docker ci
# Se connecte en tant que CI,
# crée une clé SSH, et configure SSH afin d'accepter la
# clé et de n'autoriser que la command
# `docker system dial-stdio`
sudo -u ci -s
ssh-keygen
echo 'command="docker system dial-stdio"' $(cat $HOME/.ssh/id_ed25519.pub) >> $HOME/.ssh/authorized_keys
Exemple avec Github Actions :
jobs:
deploy:
runs-on: ubuntu-latest
needs: build-prod
if: github.ref == 'refs/heads/main'
steps:
- name: Création de la clé privée SSH depuis les secrets
shell: bash
run: |
mkdir -p ~/.ssh
echo "${{ secrets.ssh_private_key }}" > ~/.ssh/private.key
chmod 600 ~/.ssh/private.key
cat > ~/.ssh/config <<EOF
Host docker
HostName alpha.exemple.org
IdentityFile ~/.ssh/private.key
StrictHostKeyChecking no
EOF
- name: Creation du contexte Docker
shell: bash
run: |
docker context create docker --docker "host=ssh://docker"
docker context use docker
- name: Connection à Docker hub (nécessaire si votre image est dans un dépot privé)
uses: docker/login-action@v3
with:
username: ${{ secrets.docker_username }}
password: ${{ secrets.docker_password }}
- name: Redéployement
shell: bash
run: |
docker stack deploy \
--detach=false \
--with-registry-auth \
--prune \
my_stack
Pour les gens intéressés, j'en ai fait une action dédié. En terme de support, y'a pas de support, donc je vous enjoins à forker joyeusement ou copier directement le code dans votre workflow :)
En travail préliminaire, y'a juste à renseigner la clé SSH dans les secrets de la CI. À vous de voir si vous préférez le faire qu'une fois au niveau global et accessible par tous vos dépots afin de ne plus jamais avoir à y toucher et que ça juste marche ou carrément créer un nouvel user (ou juste clé) par repo pour sécuriser et cloisonner.
Ça n'a pas le jojo seal of approval® parce que j'aime pas trop balader comme ça des clés ssh, mais ça reste simple et efficace, la CI va automatiquement crée la stack au premier push et la redeploy automatiquement lors des pushs suivant, y'a pas d'autres dépendances que docker et ssh.

# Joli
Posté par flagos . Évalué à 5 (+3/-0). Dernière modification le 05 mars 2026 à 17:26.
Pour mon "homelab" sur un VPS, j'utilise docker compose et non swarm (vu qu'il n'y a qu'une seule machine), mais j'ai eu un peu le meme sujet que toi.
Pour ma part, j'ai considéré que le coup de la clé SSH avec droits root sur Github était rédhibitoire, meme pour un homelab. Je suis donc parti en quete d'une version webhook. J’étais tombe notamment sur Gantry qui est specifique a swarm et qui pourrait t’intéresser: Il marche sur un webhook (avec une API key) et docker pull depuis la registry les containers qui ont un certain label. Donc pas besoin de le "configurer" qui est le reproche que tu fais a portainer si j'ai bien compris. Il suffit que tu mettes les labels sur tes containers au déploiement.
Bon moi pour ma part j'ai fait plus simple, j'ai fait un Dockerfile avec le package
webhookavec une API key, et qui fait simplement git pull + docker compose up --build. Je deploie tout ca avec un playbook ansible, j'ai juste une liste de services a déclarer, j'utilise une variable ansible pour ca. Dans l'esprit homelab, c'est bien suffisant, mais j'aurais bien aimer avoir une solution a base de labels.[^] # Re: Joli
Posté par jtremesay (site web personnel) . Évalué à 4 (+2/-0).
à l'époque où j'avais monté ce serveur, docker compose était présenté comme un truc à utilisé uniquement pour les développements. Et vu la simplicité de Swarm (même avec un seul node), j'ai pas eu à changer mes habitudes.
Ça me faisait déjà chier d'avoir une dépendance aussi lourde juste pour gérer le redeploy. Mais ouais, en plus le fait de devoir pré-créer la stack dans portainer m'a incité à chercher une solution plus simple
Merci !
tu noteras quand même que dans ma config la clé SSH ne permet que de se connecter à un simple utilisateur qui n'a accès qu'à un sous ensemble de docker.
mais ouais, comme j'ai pas encore configuré docker ou podman en rootless; si quelqu'un choppe la clé, il peut très bien faire
docker --context monserveur run -it -v /:/mnt ubuntuet là je suis mort :D# Je le croyais mort... mais non !
Posté par bbo . Évalué à 6 (+4/-0).
J'étais arrêté au fait que Swarm était mort des années de cela.
Je n'ai suivi les actus Docker que de très loin, la vie m'ayant emmené plutôt côté Podman avec lequel je ne fais franchement rien de compliqué ou de fou fou.
Au risque d'exposer mon cruel manque de culture, j'étais donc un peu resté sur docker/podman-compose d'un côté et Kubernetes/Nomad de l'autre (peut-être entretenu par les outils comme Rancher Desktop ou Podman Desktop). Avec des trucs comme dokku au milieu.
Enfin bref, tout ça pour commenter que je ne savais pas que Docker Swarm existait encore et que celui-qui-est-mort s'appelle finalement Docker Classic Swarm.
Ce que la doc précise bien :
Donc : je te remercie de m'avoir mis à jour !
Envoyer un commentaire
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.