Docker multi-hôtes sans Kubernetes : options réelles et limites strictes

Cet article vous a aidé ?

Vous avez plus d’un serveur. Vous avez des conteneurs. Vous ne voulez pas Kubernetes — peut-être parce que votre équipe est petite,
vos charges sont ennuyeuses (dans le bon sens), ou vous avez déjà suffisamment de composants qui vous réveillent à 03:00.
Mais vous voulez toujours du « multi-hôtes » : ordonnancement, découverte de services, basculement et mises à jour sans
roulette SSH.

Le piège : « Docker sur plusieurs hôtes » n’est pas une seule fonctionnalité. C’est une pile de décisions concernant le réseau, l’identité,
le stockage et la sémantique des défaillances. Vous pouvez absolument le faire sans Kubernetes. Il faut juste accepter
les limites dès le départ — surtout autour du stockage stateful et du réseau — et choisir une approche qui correspond à votre
maturité opérationnelle.

Ce que « Docker multi-hôtes » signifie vraiment

Quand on parle de « Docker multi-hôtes », on entend généralement une ou plusieurs de ces capacités :

  • Ordonnancement : choisir un hôte pour un conteneur et le replacer après une défaillance.
  • Découverte de services : trouver « la chose » par nom, pas par IP.
  • Réseau : les conteneurs communiquent entre hôtes avec des noms stables et des politiques raisonnables.
  • Mises à jour : avancer, revenir en arrière, ne pas faire fondre la production.
  • Gestion de l’état : données persistantes qui ne sont pas « recréées » dans l’oubli.
  • Modèle de santé : ce que « sain » signifie et qui décide de redémarrer quoi.

Kubernetes regroupe ces choix dans un système cohérent (quoique complexe). Sans Kubernetes, vous assemblez le vôtre.
Cela peut être un avantage : moins d’abstractions, moins de contrôleurs « magiques », modèle mental plus simple. Ou un piège :
vous réinventez les 20 % les plus difficiles (état, identité, réseau) en vous sentant productif sur les 80 % les plus simples.

La question utile n’est pas « Comment faire du multi-hôtes sans Kubernetes ? » mais : Quel sous-ensemble ai-je vraiment besoin ?
Si vous exécutez des API sans état derrière un load balancer, vous pouvez être satisfait d’une orchestration minimale.
Si vous faites tourner des bases de données sur plusieurs hôtes avec « des conteneurs partout », soit vous faites de l’ingénierie de stockage sérieuse,
soit vous jouez à la fiabilité.

Faits et histoire qui comptent encore en 2026

  • L’histoire multi-hôtes initiale de Docker n’était pas Swarm : les premiers « clustering Docker » s’appuyaient sur des systèmes externes (Mesos, expériences basées sur etcd) avant que Swarm ne mûrisse.
  • Swarm mode (2016) a livré Raft intégré : le quorum des managers est un vrai système distribué ; traitez-le comme tel.
  • « Docker Machine » a eu son heure : il automatisait le provisionnement des nœuds à l’ère pré-IaC ; la plupart l’ont remplacé par Terraform/Ansible et n’y sont jamais revenus.
  • Kubernetes a gagné en partie en standardisant les attentes : découverte de services, déploiements progressifs et état désiré déclaratif sont devenus monnaie courante.
  • Les réseaux overlay existaient bien avant les conteneurs : VXLAN est plus ancien que la plupart des plateformes conteneurisées ; c’est toujours l’outil de travail pour un réseau L2-ish multi-hôtes.
  • Les runtimes conteneurs se sont séparés de Docker : containerd et runc sont devenus des composants distincts ; « Docker » est souvent la couche UX au-dessus.
  • Les conteneurs stateful ont toujours été controversés : l’argument « pets vs cattle » n’a pas vieilli ; il a juste migré dans les PV, drivers CSI et classes de stockage.
  • La découverte de services a connu des phases : des configs statiques, à ZooKeeper/etcd/Consul, jusqu’à « l’orchestrateur est la source de vérité ». Sans K8s, vous choisissez une phase.
  • iptables vs nftables est toujours d’actualité : le réseau des conteneurs se prend encore les pieds dans la sémantique des pare-feux hôtes et les versions du noyau.

Une idée paraphrasée qui mérite d’être collée sur votre écran, attribuée à Werner Vogels : Tout finit par tomber en panne ; concevez et exploitez en supposant que la défaillance est normale.
Si votre plan multi-hôtes nécessite des réseaux parfaits et des disques immortels, ce n’est pas un plan. C’est de l’espoir.

Options réalistes (et à qui elles s’adressent)

1) Docker Swarm mode

Swarm est la réponse la plus directe si vous voulez « Docker, mais sur plusieurs hôtes » avec un minimum d’outillage supplémentaire.
Il vous apporte l’ordonnancement, la découverte de services, les mises à jour progressives, les secrets et un réseau overlay. L’intégration est serrée.
L’écosystème est plus calme qu’avec Kubernetes, mais calme n’est pas synonyme de mort. Dans beaucoup d’entreprises, la discrétion est une qualité.

Choisissez Swarm si : vous voulez une plateforme cohésive, vos services sont majoritairement sans état, et vous pouvez vivre avec moins d’intégrations d’écosystème.
Évitez Swarm si : vous avez besoin de contrôles de politique sophistiqués, d’isolation multi-tenant, ou si vous comptez embaucher des personnes qui ne connaissent que Kubernetes.

2) HashiCorp Nomad (avec Docker)

Nomad est un ordonnanceur plus simple à appréhender que Kubernetes, tout en restant une véritable orchestration.
Il s’intègre bien avec Consul et Vault, et accepte les conteneurs Docker. Il peut aussi ordonnancer des workloads non conteneurisés,
ce qui importe parfois dans des environnements brownfield.

Choisissez Nomad si : vous voulez de l’ordonnancement et des checks de santé sans l’ampleur de Kubernetes, et que vous êtes à l’aise avec l’écosystème HashiCorp.
Évitez Nomad si : vous avez besoin du « marketplace Kubernetes » d’outils et d’opérateurs pour chaque système niche.

3) systemd + Docker Compose + un load balancer

C’est l’approche du « script bash adulte ». Vous exécutez Compose par hôte, gérez le déploiement via CI (rsync, artifacts, images),
redémarrez via systemd, et le placez derrière un vrai load balancer.
Ce n’est pas glamour. Ça marche. Cela vous oblige aussi à résoudre la découverte, le déploiement et la réponse aux pannes.

Choisissez-la si : vous avez une poignée de nœuds, des charges stables, et que vous privilégiez la transparence aux fonctionnalités.
Évitez-la si : vous voulez un rescheduling automatique après perte d’un hôte, ou si vous avez des déploiements et des événements de scaling fréquents.

4) Ordonnancement DIY (s’il vous plaît, ne le faites pas) + découverte de services

Des gens essaient encore : un « ordonnanceur » maison qui choisit un hôte selon le CPU, puis lance docker run via SSH, puis s’enregistre dans Consul.
Ça peut marcher… jusqu’à la deuxième mode de défaillance. La troisième mode de défaillance est souvent un frein de carrière.

Blague #1 : L’orchestration maison, c’est comme écrire votre propre base de données : c’est éducatif, coûteux, et vous le ferez deux fois.

Docker Swarm : l’option « orchestration suffisante »

Swarm mode est intégré au Docker Engine. Vous initialisez un manager, joignez des workers, et définissez des services.
Vous obtenez un plan de contrôle avec consensus Raft. Cela signifie : les managers conservent l’état, et vous avez besoin d’un quorum.
Perdre le quorum et votre cluster devient une pièce de musée figée.

Forces de Swarm

  • Simplicité opérationnelle : un binaire, un modèle mental, peu de dépendances.
  • Modèle de service : réplicas, mises à jour progressives, checks de santé et routing mesh.
  • Secrets : distribution intégrée aux tâches, chiffrés en transit et au repos (dans le log Raft).
  • Bonnes valeurs par défaut : beaucoup d’équipes réussissent parce que Swarm a moins de réglages à mal configurer.

Limites de Swarm (celles qui mordent)

  • Les services stateful sont votre problème : l’ordonnanceur peut déplacer une tâche ; vos données ne peuvent pas téléporter.
  • Le réseau est « suffisant » jusqu’à ce qu’il ne le soit plus : les problèmes d’overlay se transforment en chasses multi-couches : noyau, MTU, conntrack, iptables, VXLAN.
  • Écosystème : moins d’intégrations tierces, moins d’« opérateurs », moins de patterns préconstruits.
  • Scaling day-2 : vous voudrez un jour des contrôles de politique et du RBAC que Swarm n’insiste pas à fournir.

Tâches pratiques (avec commandes, sorties et décisions)

Task 1: Validate swarm status and node roles

cr0x@server:~$ docker info | sed -n '/Swarm:/,/Runtimes:/p'
Swarm: active
 NodeID: 8q2m0h9v6q6f0m8c7xqkqz0vv
 Is Manager: true
 ClusterID: k1l2m3n4o5p6q7r8s9t0u1v2w
 Managers: 3
 Nodes: 9
 Default Address Pool: 10.0.0.0/8
 SubnetSize: 24
 Data Path Port: 4789
 Orchestration:
  Task History Retention Limit: 5
 Raft:
  Snapshot Interval: 10000
  Number of Old Snapshots to Retain: 0
  Heartbeat Tick: 1
  Election Tick: 10
 Dispatcher:
  Heartbeat Period: 5 seconds
 CA Configuration:
  Expiry Duration: 3 months
 Autolock Managers: false

Ce que cela signifie : Swarm est actif, ce nœud est manager, et vous avez 3 managers (le quorum nécessite 2).
Décision : Si vous n’avez qu’un manager, corrigez cela avant de faire confiance à Swarm pour autre chose qu’un labo. Pour 3 managers, assurez-vous qu’ils sont séparés par domaine de défaillance.

Task 2: Check node health and availability

cr0x@server:~$ docker node ls
ID                            HOSTNAME   STATUS  AVAILABILITY  MANAGER STATUS  ENGINE VERSION
8q2m0h9v6q6f*                 mgr-a      Ready   Active        Leader          25.0.3
3k4l5m6n7o8p9q0r1s2t3u4v5w6x   mgr-b      Ready   Active        Reachable      25.0.3
7y8z9a0b1c2d3e4f5g6h7i8j9k0l   mgr-c      Ready   Active        Reachable      25.0.3
1a2b3c4d5e6f7g8h9i0j1k2l3m4n   wrk-a      Ready   Active                        25.0.3

Ce que cela signifie : Les managers sont joignables ; les workers sont prêts.
Décision : Si un manager est « Unreachable », considérez-le comme un risque de quorum et enquêtez avant de déployer.

Task 3: Inspect service rollout health

cr0x@server:~$ docker service ps api --no-trunc
ID                          NAME         IMAGE                   NODE   DESIRED STATE  CURRENT STATE           ERROR  PORTS
u1v2w3x4y5z6                api.1        registry/app:1.9.2      wrk-a  Running        Running 2 hours ago
a7b8c9d0e1f2                api.2        registry/app:1.9.2      wrk-b  Running        Running 2 hours ago
g3h4i5j6k7l8                api.3        registry/app:1.9.2      wrk-c  Running        Running 2 hours ago

Ce que cela signifie : Toutes les réplicas tournent ; pas de boucle de redémarrage.
Décision : Si vous voyez « Rejected » ou « Failed », arrêtez le déploiement et inspectez logs/events avant de chasser des fantômes dans le load balancer.

Task 4: Confirm published ports and routing mesh behavior

cr0x@server:~$ docker service inspect api --format '{{json .Endpoint.Ports}}'
[{"Protocol":"tcp","TargetPort":8080,"PublishedPort":80,"PublishMode":"ingress"}]

Ce que cela signifie : Le port 80 est publié via ingress (routing mesh). N’importe quel nœud peut accepter des connexions et les rediriger vers les tâches.
Décision : Si vous déboguez des timeouts intermittents, envisagez de passer en mode publication host et d’utiliser un load balancer externe pour des chemins de trafic plus clairs.

Task 5: Check overlay networks and peers

cr0x@server:~$ docker network ls
NETWORK ID     NAME              DRIVER    SCOPE
1c2d3e4f5g6h   ingress           overlay   swarm
7h8i9j0k1l2m   backend           overlay   swarm
a1b2c3d4e5f6   bridge            bridge    local
f6e5d4c3b2a1   host              host      local

Ce que cela signifie : Vous avez le réseau ingress par défaut et un overlay personnalisé.
Décision : Si « ingress » manque ou est corrompu, le réseau des services se comportera comme une maison hantée. Réparez le réseau du cluster avant de blâmer l’application.

Task 6: Verify gossip/control-plane ports are reachable

cr0x@server:~$ ss -lntup | egrep ':(2377|7946|4789)\b'
tcp   LISTEN 0      4096          0.0.0.0:2377      0.0.0.0:*    users:(("dockerd",pid=1123,fd=41))
tcp   LISTEN 0      4096          0.0.0.0:7946      0.0.0.0:*    users:(("dockerd",pid=1123,fd=54))
udp   UNCONN 0      0             0.0.0.0:7946      0.0.0.0:*    users:(("dockerd",pid=1123,fd=55))
udp   UNCONN 0      0             0.0.0.0:4789      0.0.0.0:*    users:(("dockerd",pid=1123,fd=56))

Ce que cela signifie : Le port manager Swarm (2377), gossip (7946 tcp/udp) et VXLAN (4789/udp) sont en écoute.
Décision : Si ceux-ci ne sont pas présents ou sont bloqués par des pare-feu hôtes, le réseau overlay et l’adhésion des nœuds échoueront de façon non triviale.

Nomad : ordonnancement sensé sans le coût complet de Kubernetes

Le pitch de Nomad est simple : un ordonnanceur unique, clustering facile, jobspecs clairs et moins de pièces mobiles.
En pratique, le côté « moins de pièces mobiles » est vrai jusqu’à ce que vous ajoutiez Consul pour la découverte et Vault pour les secrets,
moment où vous êtes toujours plus simple que Kubernetes mais pas exactement en camping avec un silex.

Où Nomad s’intègre le mieux

  • Workloads mixtes (VMs, raw exec, conteneurs Docker) dans un seul ordonnanceur.
  • Équipes qui veulent des jobs et des allocations explicites plutôt qu’un univers de contrôleurs.
  • Environnements déjà équipés de Consul/Vault.

Limites de Nomad (pratiques, pas idéologiques)

  • Intégrations de stockage : il vous faut toujours une stratégie de volumes qui survive à la perte d’un nœud.
  • Réseau : vous pouvez le faire proprement, mais il faut décider : host networking, bridge, CNI, service mesh, etc.
  • Attentes de l’écosystème applicatif : beaucoup de fournisseurs supposent des objets Kubernetes, pas des jobs Nomad.

Tâches pratiques

Task 7: Check Nomad cluster health quickly

cr0x@server:~$ nomad server members
Name     Address          Port  Status  Leader  Raft Version  Build  Datacenter  Region
nomad-1  10.20.0.11       4648  alive   true    3             1.7.5  dc1         global
nomad-2  10.20.0.12       4648  alive   false   3             1.7.5  dc1         global
nomad-3  10.20.0.13       4648  alive   false   3             1.7.5  dc1         global

Ce que cela signifie : 3 serveurs, un leader, le raft est sain.
Décision : S’il n’y a pas de leader, arrêtez les déploiements ; corrigez d’abord le quorum/le réseau.

Task 8: Inspect allocations for a job and read failure clues

cr0x@server:~$ nomad job status api
ID            = api
Name          = api
Type          = service
Priority      = 50
Status        = running
Datacenters   = dc1
Task Groups   = web (3 running)

Latest Deployment
ID          = 3c1b2a9d
Status      = successful
Description = Deployment completed successfully

Allocations
ID        Node ID   Task Group  Version  Desired  Status   Created   Modified
a1b2c3d4  n-11      web         17       run      running  2h ago    2h ago
e5f6g7h8  n-12      web         17       run      running  2h ago    2h ago
i9j0k1l2  n-13      web         17       run      running  2h ago    2h ago

Ce que cela signifie : Le déploiement a réussi ; les allocations sont stables.
Décision : Si des allocations sont « failed », exécutez nomad alloc status et vérifiez les erreurs du driver (pull d’image, conflits de ports, contraintes CPU/mémoire).

Compose + systemd + SSH : outils tranchants, arêtes vives

Cette approche est courante dans les structures petites à moyennes et dans les environnements réglementés où « nouvelle plateforme » est une expression taboue.
On la trouve aussi dans des entreprises où il existe des équipes plateforme mais vous n’y avez pas accès.

Le modèle : chaque hôte exécute un ensemble connu de conteneurs. Vous déployez via CI (rsync, artifacts, images),
redémarrez via systemd, et utilisez un load balancer ou DNS pour répartir le trafic. Le basculement est géré soit par le LB (stateless)
soit par des humains (stateful). Oui, c’est moins « cloud-native ». C’est aussi compréhensible à 02:00.

Tâches pratiques

Task 9: Confirm the real state of containers on a host

cr0x@server:~$ docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}'
NAMES        IMAGE                    STATUS                  PORTS
api          registry/app:1.9.2       Up 2 hours              0.0.0.0:8080->8080/tcp
worker       registry/worker:1.9.2    Up 2 hours
postgres     postgres:16              Up 14 days              0.0.0.0:5432->5432/tcp

Ce que cela signifie : Cet hôte héberge une base de données et des conteneurs applicatifs ; la BD a une longue uptime.
Décision : Si « postgres » vit sur chaque hôte « pour la commodité », arrêtez-vous et concevez un placement d’état approprié. La commodité est la mère des pannes.

Task 10: Use systemd to make Compose behave like a service

cr0x@server:~$ systemctl status app-stack
● app-stack.service - Docker Compose App Stack
     Loaded: loaded (/etc/systemd/system/app-stack.service; enabled; preset: enabled)
     Active: active (running) since Sat 2026-01-03 09:12:44 UTC; 2h 3min ago
   Main PID: 1459 (docker)
      Tasks: 18 (limit: 18952)
     Memory: 612.4M
        CPU: 9min 12.180s
     CGroup: /system.slice/app-stack.service
             ├─1467 /usr/bin/docker compose up
             └─... containers ...

Ce que cela signifie : Votre stack est ancrée à l’init ; les redémarrages ne « oublieront » pas silencieusement de la démarrer.
Décision : Si vous n’avez pas cette sorte de scaffolding ennuyeux, vous finirez par déboguer une « panne aléatoire » qui est en réalité un « hôte rebooté ».

Réseau multi-hôtes : où la confiance meurt

Le réseau multi-hôtes n’est jamais juste « ouvrir quelques ports ». C’est le MTU, l’encapsulation, les tables conntrack, le routage asymétrique,
des règles de pare-feu oubliées, et cette mise à jour du noyau qui a changé le comportement de nftables.

Si vous utilisez des overlays Swarm (VXLAN), vous créez un réseau encapsulé au-dessus de votre réseau.
Cela peut parfaitement fonctionner — jusqu’à ce que votre underlay ait un MTU plus petit que prévu, ou que votre équipe sécurité
bloque UDP 4789 parce que « nous n’utilisons pas ça ». Spoiler : vous l’utilisez maintenant.

Tâches pratiques

Task 11: Detect MTU mismatch symptoms quickly

cr0x@server:~$ ip link show dev eth0 | sed -n '1,2p'
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 52:54:00:12:34:56 brd ff:ff:ff:ff:ff:ff

Ce que cela signifie : Le MTU est 1450 (commun sur les réseaux cloud avec VXLAN/GRE déjà en place).
Décision : Si votre overlay suppose 1500 et que votre underlay est 1450, vous verrez des timeouts étranges et des réponses partielles. Alignez les MTU ou configurez le MTU de l’overlay en conséquence.

Task 12: Check conntrack exhaustion (classic “it works until it doesn’t”)

cr0x@server:~$ sudo sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max
net.netfilter.nf_conntrack_count = 248901
net.netfilter.nf_conntrack_max = 262144

Ce que cela signifie : Vous êtes proche du maximum de conntrack.
Décision : Si le compte approche la limite pendant des pics de trafic, vous aurez des connexions abandonnées et des pannes « aléatoires ». Augmentez le max (en tenant compte de la mémoire) et/ou corrigez les motifs de trafic.

Task 13: Confirm overlay VXLAN traffic is flowing

cr0x@server:~$ sudo tcpdump -ni eth0 udp port 4789 -c 5
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
10:12:01.123456 IP 10.20.0.11.53422 > 10.20.0.12.4789: UDP, length 98
10:12:01.223455 IP 10.20.0.12.4789 > 10.20.0.11.53422: UDP, length 98
10:12:01.323441 IP 10.20.0.13.48722 > 10.20.0.11.4789: UDP, length 98
5 packets captured

Ce que cela signifie : Des paquets VXLAN sont présents sur le réseau.
Décision : Si vous ne voyez rien alors que les services tentent de communiquer, vous avez probablement des blocs pare-feu/security group ou un routage incorrect entre nœuds.

Blague #2 : Le réseau overlay est un excellent moyen d’apprendre l’analyse de paquets — principalement parce que vous n’aurez pas le choix.

Stockage pour containers multi-hôtes : réalité, pas illusions

Les charges sans état sont faciles à répartir. Les charges stateful sont là où les plateformes méritent leur place.
Sans Kubernetes, vous n’avez pas les abstractions CSI ni un cycle de vie PV standard. Vous pouvez quand même gérer correctement les systèmes stateful.
Mais il faut choisir un modèle de stockage délibérément.

Trois patterns de stockage raisonnables

Pattern A: Stockage local, placement épinglé (simple, honnête)

Vous exécutez des services stateful sur des hôtes spécifiques avec des disques locaux (LVM, ZFS, ext4). Vous épinglez le placement (contraintes Swarm, contraintes Nomad, ou « cet hôte exécute la BD »).
Le basculement est une procédure, pas une illusion.

Avantages : Rapide, simple, moins de dépendances. Inconvénients : la défaillance d’un hôte nécessite une récupération manuelle à moins d’ajouter la réplication côté application.

Pattern B: Stockage fichier réseau (NFS)

NFS est le cafard de l’infrastructure : il survit à tout, y compris aux opinions fortes.
Pour beaucoup de charges — uploads partagés, artifacts, contenu lecture-lourd — c’est acceptable. Pour des bases écriture-lourde, c’est souvent misère.

Avantages : Simple, largement supporté. Inconvénients : latence, sémantiques de verrouillage et problèmes de voisin bruyant ; « c’est lent » devient un mode de vie.

Pattern C: Bloc distribué (Ceph RBD) ou système de fichiers en cluster

Ceph peut vous fournir des blocs répliqués entre hôtes. C’est puissant, et cela vient avec du poids opérationnel.
Si vous n’avez pas de personnes pour l’exploiter, vous apprendrez le stockage à la dure. Si vous en avez, cela peut être excellent.

Avantages : véritables possibilités de basculement, sémantiques de bloc cohérentes. Inconvénients : complexité opérationnelle, tuning des performances, et une longue liste de « dépendances ».

Tâches pratiques

Task 14: Inspect Docker volume usage and driver choice

cr0x@server:~$ docker volume ls
DRIVER    VOLUME NAME
local     pgdata
local     uploads

Ce que cela signifie : Ce sont des volumes locaux. Ils résident sur l’hôte.
Décision : Si vous attendez qu’un conteneur se réordonne sur un autre hôte et conserve ses données, les volumes locaux ne le feront pas. Épinglez le placement ou utilisez un stockage partagé/distribué.

Task 15: Find where local Docker volumes actually reside

cr0x@server:~$ docker volume inspect pgdata --format '{{.Mountpoint}}'
/var/lib/docker/volumes/pgdata/_data

Ce que cela signifie : Les données sont sous le répertoire data de Docker sur cet hôte.
Décision : Sauvegardez-les comme données hôtes, pas comme « données du conteneur ». Si vous réinstallez l’hôte ou déplacez le répertoire root de Docker, planifiez les migrations.

Task 16: Validate disk health and saturation (host-level truth)

cr0x@server:~$ iostat -xz 1 3
Linux 6.5.0 (server)  01/03/2026  _x86_64_  (8 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          12.31    0.00    4.22    8.77    0.00   74.70

Device            r/s     w/s   rkB/s   wkB/s  await  aqu-sz  %util
nvme0n1         45.2   210.1  1824.3  9312.7  18.40    2.31  96.8

Ce que cela signifie : Le disque est proche de la saturation (%util ~97%) avec des temps d’attente significatifs.
Décision : Si votre « problème de cluster » est en réalité un disque surchargé, corrigez le stockage d’abord : média plus rapide, meilleur caching, moins d’écriture amplifiée, ou déplacez les workloads bruyants hors du nœud.

Task 17: If using NFS, confirm mount options and latency risk

cr0x@server:~$ mount | grep nfs
10.30.0.50:/exports/uploads on /mnt/uploads type nfs4 (rw,relatime,vers=4.2,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,sec=sys,clientaddr=10.20.0.21)

Ce que cela signifie : NFSv4.2 avec de grands rsize/wsize et des montages hard (bon pour la correction, pas toujours pour la latence tail).
Décision : Si les applis se bloquent pendant des ennuis du serveur NFS, c’est attendu avec hard. Décidez si vous préférez la correction (généralement) ou la réactivité (rarement).

Task 18: Check container-level filesystem pressure

cr0x@server:~$ docker system df
TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          42        18        27.4GB    10.2GB (37%)
Containers      51        9         1.8GB     1.1GB (61%)
Local Volumes   12        7         814.3GB   120.5GB (14%)
Build Cache     0         0         0B        0B

Ce que cela signifie : Les volumes occupent la majeure partie du disque ; le pruning d’images ne vous sauvera pas.
Décision : Si les disques se remplissent, vous avez besoin d’une gestion du cycle de vie des volumes et de sauvegardes, pas d’un rituel docker image prune.

Sécurité, secrets et identité entre hôtes

Le multi-hôtes signifie plus de frontières de confiance. Docker mono-hôte peut se permettre un « bon, c’est sur la même machine ».
Une fois que vous avez plusieurs nœuds, vous transportez des secrets, ouvrez des ports, et laissez des ordonnanceurs agir en votre nom.

Que faire (et quoi éviter)

  • Faire : utiliser les secrets Swarm ou un gestionnaire de secrets externe. Ne pas intégrer les secrets dans les images.
  • Faire : verrouiller la socket API Docker ; c’est root avec meilleur marketing.
  • Faire : faire tourner/rotater les certificats du cluster (Swarm le fait ; surveillez quand même les expirations).
  • Éviter : exposer Docker Engine TCP sans mTLS et sans politiques réseau strictes.

Tâches pratiques

Task 19: Check Docker daemon exposure risk

cr0x@server:~$ ss -lntp | grep dockerd
LISTEN 0      4096       127.0.0.1:2375      0.0.0.0:*    users:(("dockerd",pid=1123,fd=7))

Ce que cela signifie : L’API Docker est sur localhost:2375 (non chiffrée). C’est risqué mais pas immédiatement catastrophique.
Décision : Si c’est lié à 0.0.0.0, corrigez-le immédiatement. Si vous avez besoin d’un contrôle distant, utilisez un tunnel SSH ou mTLS sur 2376 avec allowlists firewall.

Task 20: Validate Swarm secret usage in a service

cr0x@server:~$ docker service inspect api --format '{{json .Spec.TaskTemplate.ContainerSpec.Secrets}}'
[{"File":{"Name":"db_password","UID":"0","GID":"0","Mode":292},"SecretID":"p4s5w0rds3cr3t","SecretName":"db_password"}]

Ce que cela signifie : Le service consomme un secret Swarm comme fichier avec mode 0444 (292).
Décision : Si les secrets sont passés en variables d’environnement, supposez qu’ils fuiront dans les logs et dumps de crash tôt ou tard. Préférez les secrets sous forme de fichiers quand possible.

Observabilité et exploitation : logs, métriques, traces

Multi-hôtes sans Kubernetes ne signifie pas « sans observabilité ». Cela signifie que vous ne pouvez pas compter sur les outils natifs Kubernetes pour masquer les trous.
Vous devez standardiser les logs, métriques et la télémétrie hôte de base — parce que vous déboguerez des problèmes cross-host, et vous voudrez des faits.

Tâches pratiques

Task 21: Check container restart storms and correlate with host pressure

cr0x@server:~$ docker inspect api --format 'RestartCount={{.RestartCount}} OOMKilled={{.State.OOMKilled}} ExitCode={{.State.ExitCode}}'
RestartCount=7 OOMKilled=true ExitCode=137

Ce que cela signifie : Le conteneur a été tué par OOM et redémarré ; le code de sortie 137 le confirme.
Décision : Augmentez la limite mémoire, corrigez la fuite mémoire, ou réduisez la concurrence. Ne « résolvez » pas cela en ajoutant des réplicas si chaque replica OOM sous charge.

Task 22: Read node-level memory pressure (Linux doesn’t lie)

cr0x@server:~$ free -h
               total        used        free      shared  buff/cache   available
Mem:            31Gi        27Gi       1.2Gi       512Mi       3.0Gi       1.8Gi
Swap:          4.0Gi       3.8Gi       256Mi

Ce que cela signifie : Peu de mémoire disponible et swap presque plein : vous êtes en zone dangereuse.
Décision : Si vous swappez beaucoup, la latence va exploser. Ajoutez de la RAM, serrez les limites des conteneurs, ou déplacez des workloads. « Ça va » n’est pas une stratégie mémoire.

Task 23: Spot Docker daemon errors around networking and iptables

cr0x@server:~$ journalctl -u docker --since "1 hour ago" | tail -n 10
Jan 03 10:44:11 server dockerd[1123]: time="2026-01-03T10:44:11.112233Z" level=warning msg="could not delete iptables rule" error="iptables: No chain/target/match by that name."
Jan 03 10:44:15 server dockerd[1123]: time="2026-01-03T10:44:15.334455Z" level=error msg="failed to allocate gateway (10.0.2.1): Address already in use"
Jan 03 10:44:15 server dockerd[1123]: time="2026-01-03T10:44:15.334499Z" level=error msg="Error initializing network controller"

Ce que cela signifie : Le contrôleur réseau de Docker est mécontent ; chaînes iptables manquantes et conflits de gateway.
Décision : Arrêtez de flamber sur les conteneurs. Corrigez l’état réseau de l’hôte (règles obsolètes, bridges en conflit, ou backend firewall incohérent), puis redémarrez Docker proprement.

Playbook de diagnostic rapide

Les pannes multi-hôtes sont habituellement l’une des quatre choses : plan de contrôle, underlay/overlay réseau, latence de stockage, ou pression sur les ressources.
L’astuce est de trouver laquelle en minutes, pas en heures.

Premier : confirmer que le plan de contrôle est sain

  • Swarm : docker node ls montre des managers joignables et des workers prêts.
  • Nomad : nomad server members montre un leader et des pairs vivants.
  • Si le plan de contrôle est dégradé, arrêtez les déploiements et les « redémarrages progressifs ». Vous ne ferez que faire tourner l’état.

Second : vérifier la pression sur les ressources des nœuds affectés

  • Mémoire : OOM kills, usage du swap, free -h, compteurs de redémarrage des conteneurs.
  • CPU : file d’attente et saturation (utilisez top ou pidstat si disponible).
  • Disque : iostat -xz et remplissage des systèmes de fichiers.
  • Si un nœud est « hot », drain it (Swarm) ou stop scheduling there (Nomad) avant de déboguer le code applicatif.

Troisième : valider les fondamentaux du réseau

  • Ports en écoute : 2377/7946/4789 pour les overlays Swarm.
  • MTU : confirmer le MTU de l’underlay et les attentes de l’overlay.
  • Conntrack : assurez-vous de ne pas atteindre le plafond.
  • Si le trafic overlay est absent (tcpdump ne montre rien), vous regardez un blocage pare-feu, routage ou policy du security group.

Quatrième : isoler le stockage comme goulot d’étranglement (surtout pour des timeouts « aléatoires »)

  • Utilisation disque et await : iostat -xz.
  • Comportement NFS : montages, réactivité du serveur, et si les applis se bloquent sur des montages hard.
  • Croissance des volumes : docker system df et usage des systèmes de fichiers.
  • Si le stockage est lent, tout au-dessus semble cassé. Réparez la couche de base d’abord.

Erreurs courantes : symptôme → cause racine → correctif

1) « Les conteneurs ne peuvent pas se joindre entre hôtes »

Symptôme : appels inter-services en timeout ; les noms DNS se résolvent mais les connexions stagnent.

Cause racine : VXLAN (UDP 4789) bloqué ou MTU mismatch provoquant des pertes par fragmentation.

Correctif : autoriser UDP 4789 de bout en bout ; aligner les MTU ; valider avec tcpdump et un test de petit payload ; envisager le mode host publish + LB externe pour plus de clarté.

2) « Le manager Swarm est devenu en lecture seule / bloqué »

Symptôme : les déploiements restent bloqués, les services ne convergent pas, le statut des managers oscille.

Cause racine : perte de quorum ou connectivité manager instable ; parfois latence disque sur les logs Raft du manager.

Correctif : exécutez 3 ou 5 managers ; séparez-les par domaines de défaillance ; assurez-vous que les disques des managers sont fiables ; ne colocalisez pas les managers sur les nœuds de stockage les plus bruyants.

3) « Nous avons monté les réplicas, mais c’est devenu plus lent »

Symptôme : latence plus élevée après ajout de réplicas ; plus de 5xx.

Cause racine : goulot partagé : base de données, NFS, conntrack, ou limites du LB ; aussi des stampedes de cache.

Correctif : mesurez d’abord la couche goulot ; configurez des pools de connexions ; rate-limitez ; scalerez la dépendance, pas seulement la couche sans état.

4) « Réinitialisations de connexion aléatoires lors de pics de trafic »

Symptôme : pannes intermittentes qui disparaissent quand vous regardez.

Cause racine : table conntrack pleine, épuisement des ports éphémères, ou churn d’état du pare-feu hôte.

Correctif : vérifiez l’usage nf_conntrack ; augmentez les limites ; réduisez le churn de connexions avec keepalives ; tunez le comportement du LB ; assurez l’uniformité des timeouts.

5) « Le conteneur stateful a été reschedulé et perdu des données »

Symptôme : le service redémarre sur un autre hôte et revient « vierge ».

Cause racine : volume local sur l’ancien hôte ; l’ordonnanceur a fait exactement ce que vous avez demandé, pas ce que vous vouliez.

Correctif : épinglez les workloads stateful aux nœuds ; utilisez une vraie réplication (replication Postgres streaming, etc.) ; ou implémentez un stockage partagé/distribué de manière délibérée.

6) « Les upgrades cassent le réseau après reboot »

Symptôme : après des mises à jour du kernel/pare-feu, les réseaux Docker ne s’initialisent plus.

Cause racine : mismatch iptables vs nftables, règles obsolètes, changement de valeurs par défaut.

Correctif : standardisez les builds OS ; validez le backend firewall ; testez le comportement au reboot ; gardez un plan de rollback pour les composants réseau hôte.

Checklists / plan étape par étape

Étape par étape : choisir la bonne approche non-Kubernetes

  1. Inventaire des types de workloads : APIs sans état, jobs en arrière-plan, bases de données stateful, stockage fichier partagé.
  2. Décidez votre contrat de défaillance : rescheduling automatique vs basculement manuel pour les parties stateful.
  3. Choisissez le niveau d’orchestration :
    • Besoin de rescheduling et découverte de services : Swarm ou Nomad.
    • Besoin de « exécuter la même chose sur ces hôtes » : Compose + systemd.
  4. Concevez le réseau : overlay vs host networking ; définissez les ports ; confirmez MTU et politiques firewall.
  5. Concevez le stockage : local épinglé, NFS, ou bloc distribué ; notez RPO/RTO et testez les restaurations.
  6. Modèle de sécurité : distribution des secrets, TLS, contrôles d’adhésion des nœuds, moindre privilège.
  7. Base d’observabilité : logs centralisés, métriques, alertes sur la pression des nœuds, et trace des déploiements.
  8. Exécutez des drills de défaillance : tuez un nœud, coupez un lien, remplissez un disque, et confirmez que le comportement correspond aux attentes.

Checklist opérationnelle : avant d’aller multi-hôtes

  • 3 nœuds manager (si Swarm), placés sur des domaines de défaillance distincts.
  • Synchronisation temporelle (chrony/ntpd) sur tous les nœuds ; la dérive provoque des comportements étranges dans TLS et systèmes de consensus.
  • Règles firewall autorisant explicitement les ports requis entre nœuds.
  • Versions OS/noyau cohérentes entre nœuds (ou au moins des combinaisons testées).
  • Topologie de stockage documentée : où résident les données, comment elles sont sauvegardées et restaurées.
  • Comportement du load balancer documenté : checks de santé, timeouts, réutilisation de connexions.
  • Runbooks pour drain de nœud, rollback, et récupération de quorum du cluster.

Tâches pratiques : drain et contrôles de rollback

Task 24: Drain a problematic Swarm node safely

cr0x@server:~$ docker node update --availability drain wrk-b
wrk-b

Ce que cela signifie : Swarm déplacera les tâches hors de wrk-b (sauf services global).
Décision : Utilisez ceci quand un nœud a des erreurs disque, des problèmes de kernel, ou du thrash de ressources. Ne le laissez pas « Active » en espérant qu’il se comporte.

Task 25: Pause a bad Swarm rollout

cr0x@server:~$ docker service update --update-parallelism 0 api
api

Ce que cela signifie : Mettre la parallelism à 0 arrête effectivement la progression d’une mise à jour.
Décision : Utilisez ceci quand de nouvelles tâches échouent et que vous devez arrêter l’hémorragie pendant que vous inspectez logs et contraintes.

Task 26: Roll back a Swarm service to the previous spec

cr0x@server:~$ docker service update --rollback api
api

Ce que cela signifie : Swarm rétablit le service à sa configuration/image précédente.
Décision : Si votre dernier changement coïncide avec des erreurs, rollbackez tôt. Déboguez plus tard. La fierté n’est pas un SLO.

Trois mini-histoires tirées de la vie en entreprise

Mini-histoire 1 : L’incident causé par une mauvaise hypothèse

Une entreprise SaaS de taille moyenne utilisait Docker Swarm pour des services sans état et a décidé de « containeriser tout » pour standardiser les déploiements.
L’équipe base de données a accepté, avec une condition : « Nous garderons les données sur un volume. »
Ils ont créé un volume Docker local et ont avancé.

Un mois plus tard, un worker est mort brutalement — alimentation, pas un arrêt propre. Swarm a reschedulé la tâche de la base de données sur un autre nœud.
Le conteneur a démarré proprement. Le check de santé est passé. L’application a commencé à écrire dans une base toute neuve.
En quelques minutes, les clients ont constaté des données manquantes. Le support a escaladé. Tout le monde a eu la même pensée terrible : « Attendez, avons-nous fragmenté la réalité ? »

La mauvaise hypothèse était subtile : ils pensaient que « volume » signifiait « portable ». Dans Docker, un volume local est local.
Swarm a fait son travail et a déplacé la tâche. Le stockage a fait son travail et est resté là — sur les disques du serveur mort.

La récupération a impliqué de remettre le nœud sous alimentation sur une table de bench assez longtemps pour extraire les données, puis restaurer dans la nouvelle instance de la base.
Ils ont ensuite reconstruit l’architecture en utilisant la réplication côté application et en épinglant les tâches stateful à des nœuds spécifiques avec des contraintes explicites.
La phrase finale du postmortem était brève : « Nous avons traité l’état local comme une ressource de cluster. »

Mini-histoire 2 : L’optimisation qui s’est retournée contre eux

Une équipe plateforme interne voulait des déploiements plus rapides. Leur cluster Swarm tirait des images depuis un registry qui ralentissait parfois aux heures de pointe.
Quelqu’un a suggéré : « Pré-tirons les images sur chaque nœud en job nocturne, ainsi les déploiements ne dépendront jamais du réseau. »
Ça sonnait sensé et paraissait très DevOps.

Le job nocturne exécutait docker pull pour une douzaine d’images volumineuses sur chaque nœud. Ça a marché — les déploiements se sont accélérés.
Puis un nouveau symptôme est apparu : vers 01:00, les API internes ont commencé à timeout, et la file de messages a pris du retard.
Ce n’était pas une panne totale. C’était pire : un système lent et instable qui provoquait des disputes entre ingénieurs.

Le coupable n’était pas le CPU. C’était la saturation disque et réseau. Le pull des couches a écrasé le stockage, rempli le cache de pages avec des données peu utiles,
et créé des egress bursty qui ont heurté les backups. Sous pression, le noyau a commencé à recollecter la mémoire agressivement.
La latence a bondi. Les retries se sont multipliés. Le système est entré dans une boucle de rétroaction.

La solution a été banale : arrêter de pré-puller sur tous les nœuds en même temps, étaler l’opération, limiter la bande passante, et empêcher l’« optimisation de déploiement » de concurrencer le trafic de production.
Ils ont aussi mesuré les tailles d’images et les ont réduites — car le gain de performance le plus simple est de ne pas transférer des octets inutiles.

Mini-histoire 3 : La pratique ennuyeuse mais correcte qui a sauvé la mise

Une équipe finance exploitait Nomad avec Docker pour des services internes. Rien de spectaculaire : trois serveurs Nomad, une flotte de clients, Consul pour la découverte.
L’équipe était irréalement stricte sur deux choses : runbooks documentés et tests de restauration routiniers pour les composants stateful.
On se moquait d’eux. En silence, tout le monde comptait sur eux.

Un vendredi, un contrôleur de tableau de stockage a commencé à osciller. La latence n’a pas explosé immédiatement ; elle a vacillé.
Les applications ont commencé à timeout « aléatoirement ». L’ingénieur on-call a exécuté la routine de diagnostic rapide : plan de contrôle sain, CPU ok, mémoire ok, puis iostat.
L’attente disque montait. Les logs montraient des retries dans plusieurs services. C’était le stockage, pas le code.

Ils ont exécuté le runbook : drainer les clients les plus affectés, basculer le service stateful vers une réplique qui utilisait un backing storage différent,
et réduire l’écriture amplifiée en arrêtant un job batch. Pendant ce temps, ils ont restauré une sauvegarde récente dans un environnement propre pour valider l’intégrité.
Cette dernière étape a pris des efforts, mais elle a calmé la panique : ils savaient qu’ils avaient une copie sûre.

À la fin de l’incident, le postmortem était presque décevant de calme. La plus grande leçon a été que les pratiques ennuyeuses fonctionnent.
Les drills de restauration ne sont pas glamour, mais ils transforment les désastres en après-midis pénibles.

FAQ

1) Puis-je exécuter Docker Compose sur plusieurs serveurs ?

Pas en tant que « cluster » cohérent avec ordonnancement. Compose est par hôte. Vous pouvez déployer le même fichier Compose sur plusieurs hôtes,
mais vous devez gérer vous-même le load balancing, la découverte et le basculement.

2) Docker Swarm est-il « mort » ?

Swarm n’est pas tendance, et c’est différent de mort. Il est stable, intégré et encore utilisé. Le risque est la dynamique d’écosystème :
moins de patterns tiers, moins de candidats ayant de l’expérience directe, et moins d’intégrations éditeur.

3) Quelle est la plus grande limite du multi-hôtes Docker sans Kubernetes ?

Le cycle de vie des workloads stateful et la portabilité du stockage. Ordonnancer un processus est facile. Ordonnancer des données en toute sécurité est la partie difficile.
Si vous ne concevez pas explicitement le stockage, vous finirez par perdre des données ou de la disponibilité.

4) Dois-je utiliser des réseaux overlay ou host networking ?

Pour la simplicité et la performance, le host networking plus un vrai load balancer est souvent plus facile à exploiter.
Les overlays aident pour la connectivité service-à-service et la portabilité, mais ajoutent des modes de défaillance (MTU, blocages UDP, conntrack).
Choisissez selon l’appétence de votre équipe au débogage.

5) Comment faire la découverte de services sans Kubernetes ?

Swarm offre du DNS de service intégré sur les réseaux overlay. Avec Nomad, Consul est un choix courant.
Dans le monde Compose+systemd, vous pouvez utiliser des upstreams statiques dans un load balancer, des enregistrements DNS, ou un système de discovery comme Consul — conservez juste la cohérence.

6) Qu’en est-il de la gestion des secrets ?

Les secrets Swarm conviennent pour beaucoup de cas. Pour des environnements plus larges ou soumis à conformité, utilisez un gestionnaire de secrets dédié.
Évitez les secrets en variables d’environnement quand c’est possible ; ils fuient trop facilement.

7) Puis-je exécuter des bases de données en containers sur plusieurs hôtes en toute sécurité ?

Oui, mais vous devez choisir un modèle de réplication/basculement qui correspond à la base de données et à votre maturité opérationnelle.
« Mettez-le en container et laissez l’ordonnanceur le déplacer » est la voie vers le regret.

8) Combien de nœuds manager dois-je avoir dans Swarm ?

Utilisez 3 ou 5 managers. Un manager est un point unique de défaillance. Deux managers ne tolèrent pas une défaillance sans perdre le quorum.
Trois managers tolèrent une défaillance et continuent d’avancer.

9) Quel est un bon baseline non-Kubernetes pour une petite équipe ?

Pour des services majoritairement sans état : Swarm avec 3 managers, load balancer externe, et une histoire de stockage claire pour les quelques composants stateful.
Pour un placement majoritairement fixe : Compose + systemd + load balancer, plus runbooks documentés.

Prochaines étapes pratiques

  1. Rédigez votre contrat de défaillance : que se passe-t-il quand un nœud meurt, et qui/quoi déplace les workloads.
  2. Choisissez un niveau d’orchestration et engagez-vous pour un an : Swarm, Nomad, ou Compose+systemd. Les mélanger, c’est comment la complexité s’invite déguisée.
  3. Concevez d’abord le stockage pour les services stateful : local épinglé + réplication, NFS pour les bons workloads, ou bloc distribué si vous pouvez l’exploiter.
  4. Renforcez le réseau : confirmez MTU, capacité conntrack et ports requis. Testez avant que la production ne vous y force.
  5. Opérationnalisez : procédures de drain de nœud, commandes de rollback, drills de sauvegarde/restauration, et une checklist de diagnostic rapide réellement utilisée.

Docker multi-hôtes sans Kubernetes est absolument faisable. La bonne décision n’est pas de prétendre éviter la complexité.
La bonne décision est de choisir quelle complexité vous paierez — et la payer délibérément, en plein jour, avec du monitoring.

← Précédent
Stratégie de hot-swap ZFS : comment remplacer des disques sans panique
Suivant →
ZFS Special Small Blocks : comment turbocharger les charges de petits fichiers

Laisser un commentaire