swarmexec_ docs
Utiliser le client — commandes, options, config & la TUI.
swarmexec vous donne docker exec -it, les logs, la redirection
de ports et la gestion des volumes sur n'importe quel
conteneur d'un Docker Swarm, depuis un seul terminal. Un petit
agent par nœud (un service Swarm global) fait le travail sur
son nœud ; le client demande au manager du Swarm quel nœud
exécute votre cible, puis se connecte directement à l'agent de ce nœud via
mTLS. Cette page documente le client.
Sommaire
1. Fonctionnement
Deux binaires, un seul protocole réseau :
- agent — s'exécute comme un service Swarm
global(une tâche par nœud), monte le socket Docker de ce nœud et expose une API gRPC restreinte qui relaie exec / logs / redirection de port vers les conteneurs sur son propre nœud. Vous le déployez une fois avecswarmexec init. - client (cette CLI) — s'exécute sur votre poste de
travail. Il dialogue avec l'API du manager Swarm pour
déterminer quel nœud exécute votre cible et l'id du conteneur, puis se
connecte directement à l'agent de ce nœud sur le port
9443. Il n'y a pas de maillage agent-à-agent.
La connexion au manager utilise votre contexte Docker CLI (elle respecte donc
--context, les bastions ssh://, mTLS, etc.). La
connexion à l'agent est authentifiée par TLS mutuel, ou par un secret partagé
avec un agent auto-signé — voir Authentification.
2. Installation
Nécessite Docker Engine 19.03 ou plus récent (API 1.40) sur le manager et sur chaque nœud. swarmexec refuse un démon plus ancien dès la connexion, en nommant les deux versions, plutôt que de se connecter puis d'échouer vue après vue. Deux fonctionnalités demandent un peu plus au démon du nœud : l'utilisation des ressources en direct et la vue des images par nœud veulent Docker 23.0 (API 1.41/1.42) ; en dessous elles restent vides et le disent, au lieu d'échouer.
Le client est un unique binaire statique. Récupérez le build de la dernière version pour votre plateforme :
Builds : linux-amd64,
linux-arm64,
darwin-arm64,
darwin-amd64,
windows-amd64.
L'image de l'agent est publique sur Docker Hub sous
logleio/swarmexec-agent.
3. Démarrage rapide
Pointez votre contexte Docker sur un manager Swarm, puis provisionnez les
agents une seule fois. init crée un secret partagé, déploie
l'agent sur chaque nœud en mode auto-signé et écrit une configuration client
correspondante — de sorte que les autres commandes fonctionnent immédiatement.
Retirez à nouveau les agents avec swarmexec down (cela laisse
votre configuration client intacte).
4. Sélecteurs de cible
exec, logs et port-forward prennent une
cible comme premier argument. Elle est résolue dans cet ordre :
| Forme | Exemple | Signification |
|---|---|---|
service | web | La tâche en cours d'exécution du service. S'il a plus d'une réplique, c'est ambigu — voir ci-dessous. |
service.slot | web.2 | Un emplacement de réplique précis. Pendant une mise à jour progressive, la tâche la plus récente de l'emplacement l'emporte. |
task-id | xxh8k1… | Un ID de tâche Swarm. |
container-id | 3f9a2b… | Un préfixe d'ID de conteneur. Nécessite un nœud : passez --node, ou il est trouvé en parcourant les tâches en cours d'exécution. |
Ambiguïté. Un simple nom de service avec plusieurs répliques
ne peut pas être résolu vers une seule tâche. exec vous invite à
choisir dans une liste numérotée lorsqu'il est exécuté de façon interactive ;
logs et port-forward ne demandent jamais — ils
affichent la liste des candidats et se terminent. Levez l'ambiguïté avec un
emplacement (web.0, web.1, …).
5. Configuration
Les paramètres proviennent de quatre couches, chacune l'emportant sur la précédente :
Une option ne l'emporte que lorsque vous la passez effectivement, de sorte qu'une valeur du fichier ou de l'environnement subsiste sauf si elle est explicitement remplacée.
Fichier de configuration
Le chemin par défaut est le premier de ceux-ci qui est défini :
$SWARMEXEC_CONFIG$XDG_CONFIG_HOME/swarmexec/config.yaml~/.config/swarmexec/config.yaml
Remplacez-le avec --config <path>. Le fichier est en YAML ;
un fichier manquant ne pose pas de problème, un fichier mal formé est une
erreur. Il est écrit avec les droits 0600 (il peut contenir le
secret partagé). Le fichier de log se trouve à côté par défaut —
~/.config/swarmexec/swarmexec.log — sauf si vous définissez
--log-file. Clés :
| Clé | Type | Signification |
|---|---|---|
ca | string | certificat CA qui vérifie le certificat serveur de l'agent (mTLS) |
cert | string | certificat client — son CN est votre identité d'opérateur |
key | string | clé privée du client |
port | int | port de l'agent (par défaut 9443) |
addr_mode | string | hostname (par défaut) ou ip — comment joindre un nœud |
server_name | string | remplace le nom de serveur TLS utilisé pour vérifier l'agent |
agent_secret | string | secret partagé pour un agent auto-signé |
agent_secret_file | string | lit le secret depuis ce fichier (prioritaire sur agent_secret) |
insecure | bool | ignore la vérification du certificat serveur de l'agent (agents auto-signés) |
legacy_secret | bool | envoie en plus le secret brut, pour les agents antérieurs à v1.17.3 — désactivé par défaut ; voir ci-dessous |
operator | string | identité d'audit lorsqu'aucun certificat client n'est utilisé (par défaut : nom d'utilisateur du système) |
logs.format | string | format de log par défaut pour logs et la TUI : classic | json | logfmt | gelf | raw (vide = classic) |
logs.min_level | string | niveau minimum par défaut : trace..fatal (omettez pour aucun filtre de niveau) |
ui.dim | float | à quel point l'arrière-plan derrière un overlay ouvert est assombri, une fraction 0–1 (par défaut 0.6 ; 0 = aucun assombrissement) |
La section logs: définit les valeurs par défaut de l'analyse et
du filtrage des logs tenant compte du format ; les options
--log-format / --min-level de la commande
logs les remplacent :
Inspectez la configuration effective et fusionnée (secret masqué) avec :
addr-mode
hostname (par défaut) joint le nom d'hôte signalé par le nœud ;
ip joint son adresse annoncée. Utilisez ip lorsque
les noms d'hôte des nœuds ne sont pas résolvables depuis votre poste de
travail — init écrit addr_mode: ip dans la
configuration générée précisément pour cette raison. Le leader du swarm
signale 0.0.0.0 pour lui-même ; le client récupère
automatiquement sa véritable adresse depuis la liste des pairs raft.
Variables d'environnement
| Variable | Définit |
|---|---|
SWARMEXEC_CONFIG | chemin du fichier de configuration |
SWARMEXEC_CA / _CERT / _KEY | matériel mTLS |
SWARMEXEC_PORT | port de l'agent |
SWARMEXEC_ADDR_MODE | hostname / ip |
SWARMEXEC_SERVER_NAME | nom de serveur TLS |
SWARMEXEC_AGENT_SECRET / _FILE | secret partagé / fichier de secret |
SWARMEXEC_INSECURE | ignore la vérification (1/true/yes/on) |
SWARMEXEC_OPERATOR | identité d'audit |
SWARMEXEC_UI_DIM | assombrissement du fond de l'overlay (ui.dim) |
SWARMEXEC_KEYS | chemin du fichier de raccourcis de la TUI (par défaut keys.yaml à côté de la configuration) |
SWARMEXEC_SSH_MULTIPLEX | 0/off/false/no désactive le partage des connexions ssh (voir Partage des connexions ssh) |
DOCKER_CONTEXT | contexte Docker pour l'API du manager |
XDG_CONFIG_HOME | base du chemin de configuration par défaut |
6. Authentification
Le client s'authentifie auprès de l'agent selon l'un de deux modes.
Mode A — TLS mutuel (par défaut)
Définissez ca, cert et key (les trois
requis). Le client vérifie l'agent par rapport à votre CA et présente son
certificat ; l'agent vous autorise et vous audite par le CN
du certificat. C'est le mode par défaut et la posture recommandée.
Mode B — agent auto-signé + secret partagé
Plus simple à exploiter (un secret, pas de PKI) — c'est ce que met en place
init. Définissez agent_secret (ou
agent_secret_file), et soit un ca pour vérifier
l'agent soit insecure: true pour ignorer la
vérification. Un certificat client est facultatif (mais cert et
key doivent être définis ensemble ou tous deux vides). Votre
identité d'audit est la valeur operator (par défaut : votre nom
d'utilisateur système).
ca sur tout réseau auquel vous ne
faites pas confiance, et gardez le secret hors de l'historique de votre shell
(utilisez agent_secret_file ou le fichier de configuration).
Passage à v1.17.3 — mettez d'abord les agents à jour
invalid or missing agent secret alors même que votre secret est
correct. Mettez-les à jour d'abord :
legacy_secret: true dans la configuration
du client envoie aussi le secret brut, avec l'exposition décrite ci-dessus.
Retirez-le dès que les agents sont à jour, et ajoutez
-allow-legacy-secret=false à l'agent pour qu'aucun client ne puisse
mettre l'identifiant sur le câble par accident.
7. Contexte Docker & SSH
--context sélectionne le contexte Docker CLI utilisé pour l'API
du manager. Ordre de résolution : --context →
$DOCKER_CONTEXT → $DOCKER_HOST → le contexte actif
dans ~/.docker/config.json → le socket local
unix:///var/run/docker.sock.
docker — il intègre le SDK Go de Docker et dialogue
directement avec l'API du manager, lisant lui-même toute métadonnée de
contexte depuis les fichiers. Tout ce dont il a besoin est un point d'accès
manager Swarm joignable (les appels de résolution de nœud ne
fonctionnent que contre un manager) :
$DOCKER_HOSTsur un managertcp://distant (mTLS) — dans ce cas aucun Docker n'est installé sur votre poste de travail ;- un contexte
ssh://— nécessite le clientssh(pas docker) ; le trafic du manager et de l'agent est tunnelisé au travers ; - le socket local
unix:///var/run/docker.sock— uniquement le repli par défaut, et la seule option qui implique un démon local.
docker elle-même n'est nécessaire que pour
créer des contextes nommés (docker context create) — ou
créez-les avec swarmexec context create <name> --docker-host …,
de sorte que la CLI docker n'est plus nécessaire même pour cela ; utilisez
$DOCKER_HOST pour éviter entièrement les contextes nommés.
(init ne lit les identifiants du docker login local
que pour une image d'agent privée — pas pour l'image publique par
défaut.)
Bastion SSH. Si l'hôte du contexte est un point d'accès
ssh://, l'API du manager est tunnelisée par SSH — tout comme la
connexion à l'agent : comme les points d'accès node:9443 des
nœuds ne sont généralement pas routables depuis votre poste de travail, le
client tunnelise automatiquement le trafic gRPC de l'agent par le même hôte
SSH. Aucune option supplémentaire nécessaire.
8. Options globales
Ces options persistantes s'appliquent à chaque commande :
| Option | Défaut | Description |
|---|---|---|
--config | — | chemin du fichier de configuration (par défaut ~/.config/swarmexec/config.yaml) |
--context | — | contexte docker pour l'API du manager ; prend en charge ssh:// (aussi $DOCKER_CONTEXT) |
--port | 9443 | port de l'agent |
--addr-mode | hostname | adresse de connexion au nœud : hostname | ip |
--ca | — | certificat CA pour vérifier l'agent (mTLS) |
--cert | — | certificat client (mTLS ; le CN est l'identité d'opérateur) |
--key | — | clé privée du client (mTLS) |
--server-name | — | remplace le nom de serveur TLS pour la vérification de l'agent |
--agent-secret | — | secret partagé pour un agent auto-signé |
--agent-secret-file | — | fichier depuis lequel lire le secret partagé |
--insecure | false | ignore la vérification du certificat serveur de l'agent |
--operator | nom d'utilisateur du système | identité d'opérateur signalée pour l'audit |
--log-level | info | verbosité des logs : debug | info | warn | error | off (off désactive entièrement la journalisation) |
--log-file | — | chemin du fichier de log (par défaut swarmexec.log à côté du fichier de configuration) |
--info | — | affiche la version, la licence et les informations de contact |
--version | — | affiche la version du client et du protocole |
slog, format texte) dans le fichier de log et dans
un tampon circulaire en mémoire. Dans la TUI, les logs ne sont jamais écrits
dans le terminal (cela corromprait l'écran) — le tampon circulaire alimente à
la place un visualiseur en direct dans l'application (voir
La TUI). --log-level off désactive
entièrement la journalisation ; un fichier de log qui ne peut pas être ouvert
n'est pas fatal (il se rabat uniquement sur le tampon circulaire).
9. Commandes
init — provisionner les agents
Déploie l'agent comme service Swarm global via l'API du manager : crée un secret Docker de secret partagé, exécute l'agent en mode auto-signé sur chaque nœud (port hôte 9443) et écrit la configuration client correspondante. À exécuter une fois par swarm.
| Option | Défaut | Description |
|---|---|---|
--image | docker.io/logleio/swarmexec-agent:latest | image de l'agent à déployer |
--secret | aléatoire | secret partagé à utiliser (par défaut : en générer un aléatoire) |
--service-name | swarmexec_agent | nom du service de l'agent |
--port | 9443 | port hôte que l'agent publie |
--force | false | met à jour le service s'il existe déjà |
--save-config | true | écrit la configuration client |
--registry-auth | true | transmet les identifiants de registre locaux pour que les nœuds puissent récupérer une image privée |
--wait | true | attend que les agents démarrent et signale la progression |
--rollout-timeout | 90s | durée d'attente du démarrage des agents |
--force pour
déployer une nouvelle version de l'agent — voir
redéployer les agents sans changer le secret.
down — retirer les agents
L'inverse de init : retire le service d'agent global et, par
défaut, le secret Docker de secret partagé. Il ne touche pas
à votre configuration client.
| Option | Défaut | Description |
|---|---|---|
--service-name | swarmexec_agent | nom du service d'agent à retirer |
--keep-secret | false | ne pas retirer le secret Docker de secret partagé |
-y, --yes | false | ne pas demander de confirmation |
doctor — diagnostiquer le swarm
Vérifie la connexion au manager, si le service d'agent est déployé, et sonde
l'agent de chaque nœud prêt pour vérifier sa joignabilité et l'écart de
version. Affiche un tableau par nœud
(NODE AGENT VERSION PROTO) ; se
termine avec un code non nul si le manager est injoignable ou si un agent est
en mauvaise santé. Un nœud signalé too old (init --force) exécute un
agent plus ancien que votre client — voir
redéployer les agents sans changer le secret.
| Option | Défaut | Description |
|---|---|---|
--connect-timeout | 10s | délai de connexion par nœud |
--json | false | affiche du JSON au lieu d'un tableau |
security report — un rapport Markdown sur tout le cluster
La surcouche des risques de sécurité analyse les spécifications
des services et affiche le résultat à l'écran. Cette commande écrit la même analyse
— plus les contrôles qui relèvent du cluster et d'aucun service en
particulier — au format Markdown, pour la relire loin du terminal, la joindre à un
ticket, ou la committer à côté de vos fichiers de stack et la comparer d'une
version à l'autre. La sortie va sur stdout sauf si
-o est donné : elle se redirige aussi facilement qu'elle s'enregistre.
Quatre contrôles s'exécutent au niveau du cluster, et aucun ne peut être répondu par la spécification d'un service :
| Contrôle | Gravité | Ce qu'il signale |
|---|---|---|
network-unencrypted — « le trafic overlay n'est pas chiffré » | medium | un réseau overlay transportant du trafic de services sans chiffrement du plan de données. Swarm tunnellise le trafic entre nœuds sur VXLAN en clair à moins que le réseau n'ait été créé avec --opt encrypted, et le service en fonctionnement n'a pas l'air différent dans un cas ou dans l'autre. Les réseaux sans rien d'attaché ne sont pas signalés — ils ne transportent rien — ni ingress, qui ne peut pas être chiffré du tout : un constat le concernant ne pourrait jamais être levé |
autolock-disabled — « les managers ne sont pas autolockés » | medium | le magasin raft des managers n'est pas chiffré au repos. Il contient chaque secret, chaque config et la clé de l'autorité de certification du cluster, et sa clé se trouve sur le même disque : qui emporte le disque d'un manager emporte le tout. Activez-le avec docker swarm update --autolock=true et conservez la clé de déverrouillage : un manager redémarré la demandera. Si la configuration du swarm est illisible, le rapport indique autolock-unknown plutôt que de deviner — supposer « désactivé » inventerait un constat, et supposer « activé » serait un faux tout-va-bien |
agent-proto-mismatch / agent-version-skew / agent-too-old / agent-unreachable | high / medium / low | l'agent est ce qui applique l'autorisation à chaque exec, chaque log et chaque port-forward : un agent plus ancien que votre client peut ne pas appliquer une règle que le client tient pour acquise. Un désaccord de protocole est high et prime sur un écart de version : les deux extrémités ne s'accordent pas sur le contrat lui-même, et pas seulement sur le build qui l'implémente. Un agent qui n'a pas répondu est signalé comme une lacune, pas comme un succès. Aucun écart n'est signalé pour un client non publié (dev), qui diffère par construction de tout agent publié — la même exception que fait doctor |
unused-secret / unused-config | low | un secret ou une config qu'aucun service ne référence. Il continue d'être distribué par le magasin raft et reste lisible par tout ce qui atteint un manager — le plus souvent un identifiant que quelqu'un a fait tourner sans jamais le supprimer, si bien que l'ancienne valeur est toujours vivante dans le cluster longtemps après que tout le monde la croit partie |
Ce que le rapport dit de lui-même. Il est lu loin du terminal, par quelqu'un qui n'était pas là quand il a tourné : il porte donc son propre contexte — quel cluster, quand, avec quel build. Il comporte aussi une section Not covered — agents injoignables, configuration du swarm illisible, liste de nœuds vide — parce que le silence dans un rapport de sécurité se lit comme un tout-va-bien sur un terrain qu'il n'a jamais foulé. L'ordre ne dépend que des constats : deux rapports d'un cluster inchangé ne diffèrent que par l'horodatage et peuvent être comparés l'un à l'autre — ce qui ne vaut quelque chose que parce qu'un constat absent signifie non trouvé et jamais non cherché.
Le fichier est écrit en 0600, les répertoires parents
créés en 0700. Il nomme chaque service, chaque réseau et chaque secret
du cluster avec chaque faiblesse trouvée : c'est une carte des points d'attaque, et
elle n'a rien à faire en lecture pour tous les comptes de la machine.
| Option | Défaut | Description |
|---|---|---|
-o, --output | stdout | écrire dans ce fichier au lieu de stdout |
--connect-timeout | 10s | délai de connexion par nœud lors de la vérification des agents |
--skip-agents | false | ne pas contacter les agents des nœuds. Plus rapide, et l'écart des agents figure alors dans Not covered au lieu d'être omis en silence |
Le même rapport s'écrit depuis la TUI : appuyez sur w dans la surcouche des risques de sécurité. C'est une collecte neuve à l'échelle du cluster, pas un vidage de ce que la surcouche affiche, et elle tourne hors de la goroutine de l'interface — celle-ci reste réactive pendant qu'elle interroge chaque nœud.
stack export / stack diff — une stack déployée sous forme de fichier
stack export relit une stack déployée depuis le cluster et l'écrit en
YAML de forme compose. stack diff compare un fichier de stack à ce
qui tourne réellement et affiche un diff unifié : une ligne
+ est ce que déployer le fichier ajouterait, une
- ce qu'il retirerait. Comme
git diff --exit-code, la commande sort en 1 dès
qu'il y a une différence : utilisable en CI.
Dans la TUI, E sur l'onglet Stacks/Services propose les deux pour la stack sous le curseur — la ligne de la stack, un de ses services, ou un de ses conteneurs.
Pourquoi pas une comparaison de texte. Une stack déployée porte
ce que le fichier n'a jamais eu : l'empreinte de l'image que le démon a résolue au
déploiement, ses propres valeurs par défaut de politique de redémarrage et de mise
à jour, et l'espace de noms de la stack préfixé à chaque réseau, secret et volume.
Comparer les deux comme du texte signale des dizaines de différences pour une
stack pourtant exactement à jour — ce qui est pire que pas d'outil du tout,
puisque cela apprend à ignorer la sortie. À la place, le fichier passe par
le chargeur et le convertisseur compose de docker eux-mêmes, le
code qu'utilise docker stack deploy, puis les deux côtés sont réduits
par la même fonction. La question à laquelle il est répondu est
« déployer ce fichier changerait-il quelque chose ? », pas « ces deux
fichiers sont-ils écrits pareil ? ». Les valeurs par défaut du démon sont
retirées des deux côtés ; une valeur qui n'est pas la valeur par
défaut s'affiche toujours, donc rien de réel n'est masqué.
Un export est une description, pas une sauvegarde. La valeur d'un
secret est en écriture seule dans l'API du moteur — elle ne peut
jamais être relue — et le contenu d'un volume vit sur les nœuds. Les deux sont
donc déclarés external, et le fichier exporté le dit dans son propre
en-tête : recréer la stack ailleurs suppose de créer d'abord les secrets. Les
champs de spécification que ce rendu ne porte pas (tty,
ulimits, réglages seccomp et AppArmor, et quelques autres) sont
nommés au même endroit, car l'échec dangereux d'un outil de comparaison n'est pas
une réponse fausse mais un silence assuré : « aucune différence » arrive
toujours avec ses réserves.
Les variables sont interpolées depuis votre environnement, exactement comme le
fait docker stack deploy. Un fichier contenant ${TAG}
décrit donc une stack différente pour un TAG différent, et le diff
dépend de l'environnement où il tourne — ce qui est correct : prétendre le
contraire signalerait « aucun changement » pour un déploiement qui changerait
l'image.
| Commande | Description |
|---|---|
stack ls | les stacks déployées sur le cluster |
stack export <stack> | écrit la stack en YAML compose ; avec -o dans un fichier, sinon sur stdout |
stack diff <fichier> [stack] | compare un fichier à la stack déployée. Le nom de la stack vaut par défaut celui du fichier sans extension ; --stack ou un second argument le remplace. Sort en 1 à la moindre différence |
stack deploy — appliquer un fichier de stack, après vérification
Charge un fichier de stack, exécute les contrôles de sécurité
sur ce qu'il déploierait réellement, puis l'applique. Ce qui en fait l'intérêt
face à docker stack deploy, c'est le garde-fou : le
fichier est vérifié avant que quoi que ce soit ne soit créé, et si
quelque chose au-dessus de l'informatif est trouvé, le déploiement
s'arrête et demande.
Dans la TUI, E → Deploy a file fait de même : d'abord les constats, d poursuit, Esc abandonne. À ce moment rien n'a été créé, ce qui est précisément l'objet de cette pause.
Les contrôles ne sont pas un second jeu de règles. Le fichier est
converti exactement en les valeurs ServiceSpec que le déploiement
soumettrait, et les huit analyseurs existants tournent dessus — les
mêmes contrôles qui posent le bouclier dans l'arbre et remplissent la surcouche
! ainsi que le rapport de sécurité. Deux jeux dériveraient : une règle
durcie d'un côté et pas de l'autre signifie que le garde-fou laisse passer un
fichier que l'arbre signale dès qu'il tourne — et à qui on a dit « rien
trouvé », on a dit quelque chose de faux. Cela fait aussi que les contrôles
voient ce que le cluster fera, pas ce que le fichier
dit ; les valeurs par défaut et raccourcis de compose sont entre
les deux.
--yes ne veut pas dire « ignore les constats ». Cela
veut dire « ne demande pas », et une exécution qui trouve quelque chose au-dessus
de l'informatif refuse tout de même et sort avec un code non nul.
Autrement, le garde-fou deviendrait une formalité dès la première mise en CI. Pour
déployer malgré les constats il faut écrire --force : autre chose à
taper, autre chose à expliquer ensuite. Une exécution non interactive sans réponse
sur stdin compte comme non.
| Option | Description |
|---|---|
--check | vérifie et s'arrête. Sort en 1 si quelque chose au-dessus de l'informatif a été trouvé : utilisable comme garde-fou de pipeline |
-y, --yes | ne pas demander. Déploie si les contrôles sont propres ; refuse sinon |
--force | déployer malgré les constats, sans demander |
--prune | supprime les services de la stack que le fichier ne déclare plus. Désactivé par défaut, comme dans docker : un fichier qui n'est qu'une partie de la stack est bien plus souvent un oubli qu'un ordre de suppression |
Ce que fait un déploiement, dans cet ordre : réseaux, puis secrets, puis configs, puis services — un service qui référence quelque chose qui n'existe pas encore échoue, et la stack reste à moitié appliquée. Les réseaux externes sont vérifiés en premier, car un réseau inexistant est la façon la plus courante pour un déploiement de s'arrêter à mi-chemin. Un secret existant n'est jamais écrasé : sa valeur est immuable dans swarm, le changer signifie en créer un nouveau sous un autre nom. Si un déploiement échoue malgré tout en cours de route, ce qui a déjà été appliqué est rapporté avec l'erreur plutôt que laissé à deviner.
Redéployer les agents sans changer le secret
Tôt ou tard, vous devrez redéployer les agents sur un swarm déjà provisionné :
un agent se bloque, doctor affiche un nœud en
too old (init --force), une commande échoue avec « agent is
older than this client (missing RPC) — update it with swarmexec init
--force », ou un nœud a été réinstallé et son agent n'est jamais
revenu. La solution consiste à relancer init avec
--force, dont c'est exactement le rôle : mettre à jour le
service s'il existe déjà. Sans cette option, lancer init sur un
service existant est une erreur d'utilisation qui vous demande d'ajouter
--force ; rien n'est modifié.
L'agent est alors redéployé sur chaque nœud et le secret partagé
reste inchangé. Les secrets Docker sont immuables :
init retrouve le swarmexec_agent_secret existant, le
signale comme reusing existing et conserve la valeur déjà présente
dans le cluster. Tout client qui fonctionnait avant continue de fonctionner — il
n'y a rien à redistribuer.
--secret lors d'un redéploiement. Si
le secret existe déjà, il ne peut pas être écrasé : votre valeur n'est
pas appliquée au cluster, mais elle est tout de
même écrite dans votre configuration client. À moins qu'elle ne
corresponde à la valeur réelle du secret existant, les agents vous rejetteront à
partir de ce moment, et la panne n'apparaît que plus tard, sous les traits d'un
agent cassé plutôt que d'une configuration locale erronée. init
avertit précisément dans ce cas ; prenez cet avertissement au sérieux. Pour
changer réellement le secret du cluster, vous devez d'abord supprimer le secret
(aucun service ne doit le référencer) puis relancer init.
--save-config vaut true par défaut : un redéploiement
réécrit donc aussi ~/.config/swarmexec/config.yaml. Passez
--save-config=false dès que la configuration client locale doit
rester intacte — par exemple lorsque vous redéployez les agents depuis une machine
dont la configuration est déjà correcte, ou depuis la CI.
Vérifiez ensuite. doctor indique un état par nœud ;
ok sur tous les nœuds signifie que client, agent et secret sont de
nouveau cohérents.
ps — lister les tâches
Liste les tâches/conteneurs candidats et le nœud sur lequel chacun s'exécute.
Prend un filtre de service facultatif. Colonnes :
SERVICE SLOT CONTAINER NODE IP UPTIME.
Cela dialogue uniquement avec l'API du manager — cela fonctionne même avant
que les agents soient joignables.
| Option | Défaut | Description |
|---|---|---|
--json | false | affiche du JSON au lieu d'un tableau |
exec — exécuter une commande / ouvrir un shell
Exécute dans un conteneur situé n'importe où dans le swarm. Sans commande, il
ouvre /bin/sh. Un TTY est alloué automatiquement lorsque stdin
est un terminal et que vous n'avez donné aucune commande ; forcez-le avec
-t. Le code de sortie propre de la commande distante est propagé
tel quel.
| Option | Défaut | Description |
|---|---|---|
-i, --stdin | true | garde stdin ouvert |
-t, --tty | auto | alloue un TTY (auto : vrai si et seulement si stdin est un terminal et sans commande) |
-u, --user | — | nom d'utilisateur ou UID (p. ex. 1000:1000) |
-w, --workdir | — | répertoire de travail dans le conteneur |
-e, --env | — | définit des variables d'environnement (KEY=VALUE, répétable) |
--node | — | indice/remplacement de nœud pour les cibles container-id |
--connect-timeout | 10s | délai de connexion à l'agent |
logs — diffuser les logs
Diffuse les logs d'un conteneur situé n'importe où dans le swarm.
| Option | Défaut | Description |
|---|---|---|
-f, --follow | false | continue à diffuser les nouvelles lignes de log |
--tail | 0 | lignes depuis la fin pour commencer (0 = toutes) |
-t, --timestamps | false | préfixe chaque ligne d'un horodatage |
--since | 0 | uniquement les logs plus récents que ceci (p. ex. 10m, 1h) |
--log-format | classic | analyse les lignes comme classic | json | logfmt | gelf | raw (par défaut depuis la config, sinon classic) |
--min-level | — | n'affiche que ce niveau et au-dessus : trace | debug | info | warn | error | fatal |
--grep | — | n'affiche que les lignes dont le message (analysé) correspond à cette regexp Go |
--node | — | indice/remplacement de nœud pour les cibles container-id |
--connect-timeout | 10s | délai de connexion à l'agent |
Analyse & filtrage tenant compte du format.
--log-format indique au client comment lire chaque ligne pour
qu'il puisse en extraire un niveau et un message :
classic extrait un niveau d'une ligne de texte brut,
json analyse du JSON de style logstash (champs
level/message), logfmt analyse le style
clé=valeur utilisé par de nombreuses applications Go, le démon Docker
et les outils HashiCorp (msg/message,
level/lvl/severity, ts/time),
gelf analyse du JSON
GELF Graylog (niveau syslog numérique), et raw laisse passer les
lignes inchangées. --min-level écarte alors tout ce qui est en
dessous du niveau choisi, et --grep ne conserve que les lignes
dont le message analysé correspond à la regexp.
--min-level, de sorte que les traces de pile multilignes ne sont
pas perdues. Les mêmes valeurs par défaut peuvent être fixées une fois sous la
section de config logs: (les options
l'emportent).
Suivi au travers du remplacement de conteneur. Avec
-f sur une cible service ou service.slot,
logs continue de suivre lorsque le conteneur qu'il diffuse est
remplacé par une mise à jour progressive, un redémarrage ou une
replanification : il ré-résout le conteneur en cours d'exécution du service
(le même emplacement, ou le même nœud pour un service global),
se reconnecte automatiquement — comme docker service logs -f —
et affiche une ligne d'avis atténuée
(container replaced; reconnected to <id> on <node>).
Il attend jusqu'à ~30 s qu'un remplaçant soit planifié avant d'abandonner, et
s'arrête proprement si le service est retiré. Une cible container-id
simple n'a pas de successeur, elle s'arrête donc simplement comme avant.
port-forward (alias pf) — rediriger un port local
Lie un port TCP local et le redirige vers un port à l'intérieur d'un
conteneur, sans publier ce port sur le cluster. Le port local prend par
défaut la valeur du port distant. Cibler un service redirige vers exactement
une de ses tâches (celle vers laquelle la cible se résout),
pas à travers les répliques. Lié à 127.0.0.1 par défaut. Appuyez
sur Ctrl-C pour arrêter.
| Option | Défaut | Description |
|---|---|---|
--address | 127.0.0.1 | adresse locale à lier (le loopback garde le port hors de votre réseau) |
--node | — | indice/remplacement de nœud pour les cibles container-id |
--connect-timeout | 10s | délai de connexion à l'agent |
volume ls — lister les volumes
Liste les volumes sur tous les nœuds et quels nœuds détiennent chacun. Les
volumes Swarm sont locaux au nœud, le client interroge donc chaque nœud et
agrège. Filtre de nom par sous-chaîne facultatif. Colonnes :
VOLUME DRIVER NODES USED BY AGE
(plus SIZE avec --size).
| Option | Défaut | Description |
|---|---|---|
--size | false | calcule aussi la taille sur disque de chaque volume (plus lent : du par volume) |
--sort | name | trie par : name | nodes | used | age | size (size implique --size) |
--reverse | false | inverse le sens du tri |
--connect-timeout | 10s | délai de connexion par nœud |
--json | false | affiche du JSON au lieu d'un tableau |
volume rm — supprimer un volume
Supprime un volume sur chaque nœud qui le détient (--all) ou sur
des nœuds précis (--node, répétable). L'un des deux est requis.
| Option | Défaut | Description |
|---|---|---|
--all | false | supprime sur chaque nœud qui détient le volume |
--node | — | supprime uniquement sur ces nœuds (répétable) |
--force | false | passe l'option force de docker |
-y, --yes | false | ne pas demander de confirmation |
--connect-timeout | 10s | délai de connexion par nœud |
ui — TUI interactive
Une vue interactive des conteneurs et des volumes, avec exec, logs et redirection de ports intégrés. Nécessite un terminal interactif. Voir La TUI pour les touches.
| Option | Défaut | Description |
|---|---|---|
--connect-timeout | 10s | délai de connexion à un agent |
config show — inspecter la config
Affiche la configuration client effective et fusionnée avec le secret masqué.
context (alias ctx) — gérer les contextes Docker
Crée et gère les contextes Docker que --context (et
$DOCKER_CONTEXT) résolvent pour l'API du manager. swarmexec écrit
dans le magasin sur disque propre à docker, donc les contextes créés ici sont
interchangeables avec la CLI docker — et vous n'avez plus besoin
que docker soit installé, même pour en créer un. Le contexte
default intégré ne peut pas être retiré.
context create <name> — crée un contexte pointant sur un hôte manager (prend exactement un argument de nom) :
| Option | Défaut | Description |
|---|---|---|
--docker-host | — | requis — point d'accès du démon docker : ssh:// | tcp:// | unix:// | npipe:// |
--description | — | description facultative |
--ssh-jump | — | hôte(s) de rebond SSH pour un contexte ssh://, séparés par des virgules (ProxyJump / -J multi-sauts) ; injectés à la fois dans la connexion à l'API Docker et dans le tunnel vers l'agent |
--use | false | en fait aussi le contexte courant |
context ls (alias list) — liste les contextes ; colonnes NAME CURRENT DOCKER ENDPOINT (l'actif marqué *) :
context use <name> — définit le contexte courant :
context rm <name> [name...] (alias remove) — retire un ou plusieurs contextes :
| Option | Défaut | Description |
|---|---|---|
-f, --force | false | requis pour retirer le contexte courant (sa sélection est réinitialisée à default) |
Partage des connexions ssh
Avec un contexte ssh://, swarmexec atteint le cluster deux fois par
ssh : l'API du manager Docker via l'assistant de connexion d'ssh, et chaque
agent de nœud via son propre tunnel vers le même hôte. Chaque exec, chaque flux
de logs, chaque redirection de port, chaque relevé de statistiques et chaque
cycle de rafraîchissement ouvrait jusqu'ici une connexion ssh neuve — une
poignée de main TCP, un échange de clés et une authentification par appel, et
autant par hôte de rebond — vers un bastion qui était connecté un instant plus
tôt.
swarmexec leur fait désormais partager un seul transport, à l'aide du
ControlMaster d'OpenSSH : la première connexion vers une
destination l'ouvre, chacune des suivantes devient un canal dessus. Mesuré sur un
cluster de trois nœuds derrière un hôte de rebond, swarmexec doctor
est passé de 8 authentifications et 3,6 s à
2 et 1,0 s. Le trafic lui-même ne change pas, et le gain
croît avec ce que l'on fait dans une session.
-
Les sockets de contrôle se trouvent dans
$XDG_RUNTIME_DIR/swarmexec/ssh/(à défaut, le répertoire de cache), créés en0700— un socket de contrôle est une session authentifiée bien vivante : il reste dans votre propre arborescence et jamais dans/tmp. - Un socket par destination et route : deux contextes atteignant le même manager par des hôtes de rebond différents ne partagent rien, car ce n'est pas la même connexion.
- La connexion partagée survit à la commande pendant 60 s, ce qui rend la suivante peu coûteuse, puis se termine d'elle-même. Ensuite, plus rien de swarmexec ne retient de connexion.
- Un socket laissé par un maître tué brutalement est sans danger : ssh constate qu'il est mort et ouvre une connexion ordinaire à la place.
-
Indisponible sous Windows, dont OpenSSH n'implémente pas le partage de
connexions. Partout ailleurs,
SWARMEXEC_SSH_MULTIPLEX=0le désactive — à savoir si vous configurez vous-mêmeControlPath, car le réglage de swarmexec passe par la ligne de commande ssh et prime donc sur~/.ssh/config.
10. La TUI
swarmexec ui possède sept onglets — Stacks/Services (1),
Volumes (2), Forwards (3),
Networks (4), Secrets (5),
Nodes (6), Configs (7) — une
barre latérale des contextes sur le bord droit, et un pied de
page sur deux lignes : les
rappels de touches par onglet en haut, puis une ligne d'état avec le contexte docker actif
(ctx <name>, pour qu'il soit toujours clair sur quel cluster vous
êtes), un résumé en direct du cluster, le nombre de redirections et — sur l'onglet Volumes —
combien de volumes vous avez sélectionnés. Ces touches fonctionnent sur chaque onglet :
La barre d'onglets est adaptative : sur un terminal plus étroit elle bascule sur des libellés courts — St/Sv, Vol, Fwd, Net, Sec, Node, Cfg — pour que les sept onglets restent visibles au lieu que les derniers soient tronqués. (Cfg, c'est Configs.) Les chiffres de raccourci et les zones cliquables à la souris ne changent pas.
| Touche | Action |
|---|---|
| ? | ouvre la surcouche des raccourcis — la référence complète et vivante des touches (elle est générée à partir de votre keymap, donc les touches réaffectées s'affichent correctement) ; le pied de page d'une seule ligne n'a de place que pour les touches les plus utilisées |
| Tab | passe à l'onglet suivant |
| 1–8 | saute à Stacks/Services / Volumes / Forwards / Networks / Secrets / Contexts / Nodes / Configs |
| j k | descend / monte (aussi ↓ ↑) |
| r | rafraîchit l'onglet actif et le résumé du cluster |
| y | copie la liste courante dans le presse-papiers (OSC52) |
| m | bascule la capture de la souris (off = la sélection/copie propre de votre terminal) |
| ` | ouvre / ferme le visualiseur de logs en direct (voir ci-dessous) |
| q | quitte |
Onglet Stacks/Services
L'onglet 1, autrefois intitulé Containers. C'est un seul arbre, stack → service → conteneur, et c'est là que vivent exec, les logs, la redirection de ports, l'inspection et les éditeurs de service.
Vérification de version :latest. Swarm épingle
:latest à un digest au moment du déploiement, donc un service
étiqueté :latest exécute en réalité une image fixe. swarmexec
résout cela auprès du registre (en utilisant vos identifiants docker locaux)
et annote la ligne du service juste après l'URI de l'image :
la véritable version entre parenthèses — lue depuis le label
org.opencontainers.image.version de l'image — et une
flèche vers le haut ↑ lorsque le :latest
actuel du registre est un digest plus récent que celui sur lequel le service
est épinglé (p. ex. nginx:latest (1.4.0) ↑). Au mieux et mis en
cache : les erreurs de registre laissent simplement la ligne non annotée. La
section IMAGE de la surcouche d'inspection affiche la même
version et, lorsqu'une image plus récente existe, une ligne sélectionnable
nouvelle version disponible — déplacez-vous dessus et
appuyez sur u (ou Enter) pour mettre à jour le
service.
Définir une version avec u — pas seulement quand une mise à jour
existe. u ne dépend plus qu'une mise à niveau soit
proposée. La touche est disponible dans toute inspection de service
dont swarmexec a pu lire les tags d'image auprès du registre — aussi bien pour un
service épinglé à une version que pour un service sur :latest — et
depuis n'importe quelle ligne de la surcouche, si bien que vous n'avez jamais à
chercher l'indication. Redéfinir la même version, épingler ce
qui tourne actuellement ou revenir à un tag plus ancien sont des
usages tout à fait normaux ; inutile d'attendre qu'une mise à jour existe. Le
libellé du pied de page vous dit dans quelle situation vous êtes :
u update version lorsqu'une version plus récente a été trouvée
(la ligne nouvelle version disponible est alors là aussi, avec une
cible concrète), et u set version lorsqu'il n'y en a pas.
Ce que u ouvre dépend de ce que connaît le registre. Sur un service
épinglé à une version pour lequel une version plus
récente existe, c'est le sélecteur de version : un champ dont
l'autocomplétion propose les tags plus récents de la même famille, le
plus élevé en premier et prérempli avec celui-ci. Sur un service déjà sur le tag
le plus récent, c'est le même sélecteur, à ceci près que les suggestions se
rabattent sur tous les tags que liste le dépôt (prérempli avec le tag en
cours d'exécution) — c'est ainsi que l'on épingle ou que l'on revient en arrière. Un
service sur :latest obtient lui aussi le sélecteur, avec tous les tags
connus : y choisir une version concrète est la façon de sortir un service
:latest du tag flottant pour l'épingler — précisément ce que
réclame le constat de risque unpinned-image (voir la surcouche des risques
de sécurité plus bas). La seule exception est un service :latest dont une
mise à jour de digest est en attente : celui-là reste la
simple confirmation sur le nouveau digest du registre qu'il a toujours
été, car un sélecteur inviterait à y saisir latest, qui se résout au tag nu
et abandonnerait silencieusement l'épinglage par digest que la mise à jour est
justement là pour rafraîchir.
Dans le sélecteur, la liste de suggestions est plafonnée à 25 entrées
(un dépôt très actif peut en lister des centaines) et le champ reste en texte
libre : vous pouvez saisir n'importe quel tag existant, y compris un
plus ancien pour l'épingler ou revenir en arrière vers une version éprouvée. Un
tag saisi est validé contre les tags du dépôt (un tag qu'il ne liste pas est rejeté), et
choisir un tag plus ancien affiche un avertissement de rétrogradation
avant de l'appliquer. Dans tous les cas, la modification est un seul
ServiceUpdate / mise à jour progressive, derrière une confirmation. Si le
registre n'a renvoyé aucun tag et qu'il n'y a pas non plus de cible
concrète — registre privé ou injoignable, ou service épinglé par digest — vous obtenez un
avis explicatif et rien n'est modifié.
| Touche | Action |
|---|---|
| / | ouvre la barre de recherche (filtre par service / conteneur / nœud) |
| h l | replie / déplie sur les trois niveaux. h replie la ligne sous le curseur — un stack replie tout le groupe, un service déplié replie ses conteneurs — et là où il n'y a plus rien à replier, elle remonte vers le parent, si bien que des appuis successifs parcourent conteneur → service → stack. l déplie la ligne sous le curseur, ou descend vers son premier enfant si elle est déjà ouverte |
| Enter | sur un conteneur : ouvre le menu d'actions ; sur un service ou un stack : le replie / déplie |
| s | bascule le regroupement par stack — arbre groupé ⟷ liste plate de services (voir ci-dessous) |
| L | logs (L majuscule) — sur un conteneur ses propres logs, sur un service les logs agrégés de toutes ses tâches |
| ! | ouvre la surcouche des risques de sécurité pour toute la liste des services (voir ci-dessous) |
| p | redirige le port de la tâche sous le curseur |
| i | inspecte le nœud sous le curseur — une surcouche navigable avec un résumé tabulaire ; déplacez la sélection avec ↑/↓ ou j/k, y/Enter copie la ligne sélectionnée, une barre d'onglets en haut nomme les trois vues et 1/2/3 sélectionnent directement tableau / stats / JSON brut du démon, tandis que t les fait défiler (Esc/q/i ferme). Sur un service la surcouche l'édite aussi : s mise à l'échelle, f mise à jour forcée, p ports, l labels, e env, n réseaux, S secrets, v montages, A alias ; D diagnostique pourquoi il ne s'exécute pas partout ; X le supprime |
| X | supprime un service directement depuis l'arbre (X majuscule, c.-à-d. Shift+x) — sans détour par la surcouche d'inspection. Ce sur quoi elle agit dépend de la ligne sous le curseur : sur une ligne de service elle supprime ce service ; sur une ligne de conteneur elle supprime le service propriétaire du conteneur, car une tâche seule ne peut pas être supprimée — Swarm la replanifierait aussitôt ; sur une ligne de stack elle ne supprime rien et signale brièvement dans le pied de page que les services d'un stack se suppriment individuellement, ou avec docker stack rm <stack>. Elle passe par la même confirmation que le X de l'inspection ci-dessous — elle supprime définitivement le service et arrête toutes ses tâches ; irréversible. Touche majuscule fixe, pas une action de keymap réaffectable ; le pied de page l'indique en rouge comme X remove et la surcouche ? la liste aussi. Le X à l'intérieur de la surcouche d'inspection est inchangé |
Chaque service est affiché (même un mis à l'échelle à zéro), chaque ligne
rendue comme une ligne docker service ls — nom, mode, nombre en
cours d'exécution/souhaité, image et ports publiés — et colorée selon son état
réel : le nombre en cours d'exécution/souhaité donne la couleur de base
(aqua = toutes les tâches actives, orange = partiel, rouge = arrêté, gris =
mis à l'échelle à zéro), et un healthcheck en échec la remplace
(voir plus bas). Un marqueur ▸/▾ indique si
un service est replié ou déplié ; ses conteneurs en cours d'exécution
s'imbriquent en dessous. Un service en cours de mise à jour
progressive porte un badge coloré sur sa ligne d'arbre —
⟳ updating ou
↺ rolling back — et le même statut apparaît dans
l'overlay d'inspection du service ; il disparaît dès que la mise à jour est terminée.
Colorée par le healthcheck, et pas seulement par le nombre de
réplicas. La couleur d'une ligne de service venait du seul rapport en cours
d'exécution / souhaité : un service dont tous les conteneurs échouaient à
leur healthcheck s'affichait donc toujours en un paisible 3/3
aqua. Le compte était juste et la ligne trompeuse. L'état de tâche de Swarm n'aide
pas davantage : une tâche est running pendant que son conteneur rate
chaque sonde — le verdict doit donc venir du nœud. Une sonde en échec est désormais
traitée comme une dégradation de même nature qu'un réplica manquant :
- tous les conteneurs contrôlés unhealthy → la ligne passe au rouge, ce qui est aussi grave qu'aucun en cours d'exécution ;
- certains unhealthy → orange, comme un déploiement à moitié fait ;
- tous healthy → la couleur que le nombre de réplicas donnait déjà à la ligne, inchangée ;
- des conteneurs sans healthcheck déclaré → la ligne n'est pas recolorée du tout. On ne sait rien d'eux, et deviner serait la même erreur dans l'autre sens.
Les marqueurs. Une ligne de service porte
✖ N unhealthy en rouge, ou
◌ N starting en jaune tant que les
sondes ne sont pas encore passées ; une feuille de conteneur porte
✖ unhealthy /
◌ starting. Unhealthy prime sur starting :
c'est celui sur lequel il faut agir. Une ligne de stack cumule le même marqueur sur tout
ce qu'elle contient (voir Regroupé par stack plus bas). Un conteneur
sain n'obtient
aucun marqueur, ni d'ailleurs un conteneur sans
healthcheck — ainsi le marqueur continue de vouloir dire quelque chose. Ils
côtoient les marqueurs de ressources cpu/mem ci-dessous, et la
santé vient en premier dans la ligne : c'est elle qui contredit le
compte juste à côté.
Up 3 days (healthy)), la chaîne même qu'imprime
docker ps. L'analyse est volontairement stricte : tout
ce qu'elle ne reconnaît pas devient pas de verdict plutôt qu'une
supposition, de sorte que si le démon reformule un jour cette ligne, swarmexec
cesse de prétendre savoir au lieu d'annoncer sain un conteneur en échec. Même
prérequis que pour les chiffres d'utilisation : il faut des agents de cette version
ou plus récents, un cluster qui n'a pas été redéployé n'affiche donc aucun marqueur
de santé — déployez-les avec
swarmexec init --force.
Marqueurs CPU et mémoire en direct. À côté de ces badges, une
ligne peut aussi porter ce qu'elle consomme réellement à l'instant :
jusqu'ici swarmexec ne pouvait montrer que ce que l'ordonnanceur avait
réservé (voir l'onglet Nœuds plus bas). Un conteneur — ou un service —
dont l'utilisation dépasse 70 % reçoit un marqueur orange, et
90 % un rouge, en fin de ligne : la ressource nommée en
toutes lettres (cpu pour le CPU, mem pour
la mémoire) suivie du pourcentage
(cpu 94 %,
mem 91 %). Si les deux chauffent, les deux
apparaissent. Des mots plutôt que des symboles, à largeur égale : un marqueur dont il
faut aller chercher le sens ne fait pas son travail, et des lettres ne peuvent pas
échouer à s'afficher dans un terminal. En dessous de 70 % rien n'est dessiné, pour
que les marqueurs restent
un signal et non du papier peint. Une ligne de service prend
la pire de ses réplicas, pas la moyenne — une moyenne masque
précisément le conteneur qui est sur le point de mourir, celui qu'il faut voir.
(La mémoire absolue d'une ligne de service est la somme sur les réplicas ; le
pourcentage est le pic.)
De quoi ces pourcentages sont-ils le pourcentage ? Chacun est mesuré par rapport à ce que ce conteneur a réellement le droit d'utiliser : sa propre limite lorsqu'il en a une, la capacité du nœud sinon. C'est là tout l'enjeu — 91 % d'une limite de 256 Mo signifie qu'un OOM kill est proche, 91 % d'un nœud de 64 Go est une tout autre conversation. La vue stats de la surcouche d'inspection affiche les deux côtés de cette division par conteneur et nomme la base sous le tableau.
Stats) ; chaque agent échantillonne ses propres conteneurs en
arrière-plan et répond depuis sa mémoire, si bien que le client l'interroge
simplement sur le cycle de rafraîchissement qu'il a déjà. Deux conséquences. Un
pourcentage de CPU est un delta entre deux relevés : il n'existe
donc pas tant que l'agent n'en a pas pris deux (quelques secondes) — pendant ce
temps les vues affichent …, jamais 0 % ; la mémoire n'a
besoin d'aucun delta et apparaît dès le premier relevé. Et un agent n'échantillonne
que tant que quelqu'un regarde : il s'arrête environ une minute
après la dernière requête et oublie ses relevés plutôt que d'en servir de périmés.
C'est pourquoi les tout premiers chiffres après l'ouverture de l'interface mettent
un instant à se remplir. C'est délibéré — un agent que personne ne regarde ne doit
rien coûter.
Stats — ne fournit tout simplement aucun
relevé. Les marqueurs restent éteints, le bloc du nœud reste absent et tout le
reste est intact ; le client met de plus ce nœud de côté un moment au lieu de le
sonder à chaque rafraîchissement. Sur un cluster qui n'a pas encore été redéployé,
il n'y a donc aucun chiffre d'utilisation ni aucun marqueur de santé
tant que les agents ne
sont pas à jour — déployez-les avec
swarmexec init --force. C'est de loin la raison la
plus probable de ne rien voir.
Regroupé par stack. docker stack deploy étiquette
chaque service qu'il crée avec com.docker.stack.namespace. swarmexec
lit ce label dans la spec du service que le manager a déjà renvoyée — Swarm n'a
aucun objet « stack », ce label est le seul lien — et imbrique l'arbre sur trois
niveaux : stack → service → conteneur. Une ligne de stack
affiche le nom du stack, combien de services en dépendent et le nombre de tâches
en cours d'exécution/souhaité cumulé
((3 svc · 7/8)), coloré selon exactement la même règle
qu'une ligne de service : le total en cours d'exécution / souhaité, avec le
même remplacement par le healthcheck par-dessus. Les compteurs de synthèse
la suivent lorsqu'ils ne sont pas
nuls : ⟳ n — les services de ce stack
en cours de mise à jour progressive —,
🛡 n — les services présentant un
constat de sécurité qui mérite une action, le même bouclier que
portent les lignes de service — puis le marqueur de santé,
✖ N unhealthy ou
◌ N starting, cumulé sur tous les
conteneurs de tous les services du stack. Même replié, un stack indique donc si
quelque chose à l'intérieur demande votre attention. Et c'est ici que la remontée de
la santé compte le plus : la ligne de stack est le niveau qu'un opérateur parcourt
en premier, un « tout est en ligne » trompeur y est donc pire qu'un étage
plus bas, et non moins grave.
Les services créés avec docker service create ne portent pas de label
de stack ; ils sont regroupés sous (no stack), qui est
toujours trié en dernier et ne repousse donc jamais les vrais stacks vers le bas.
Rien n'est masqué : chaque service apparaît toujours exactement une
fois. Les stacks sont triés par nom et démarrent dépliés, de sorte que l'arbre groupé
montre les mêmes services que l'arbre plat ; les replis que vous faites survivent au
rafraîchissement automatique.
Le regroupement est actif par défaut, mais il ne prend effet que
lorsqu'au moins un service du cluster porte réellement un label de stack — sur un
cluster sans stacks, l'arbre est exactement tel qu'il a toujours été, sans parent
(no stack) inutile. s bascule entre l'arbre groupé et la
liste plate de services (le pied de page l'indique comme s stacks) ; s'il
n'y a aucun label de stack, il le dit plutôt que de redessiner un arbre identique.
La touche est réaffectable via stack_group dans
keys.yaml.
La recherche (/) ne change pas : le filtre porte toujours sur le service, le conteneur et le nœud. Un stack que le filtre vide disparaît de l'arbre, et les compteurs de chaque ligne de stack décrivent ce qui s'y trouve réellement — ils suivent donc le filtre au lieu d'annoncer des services que vous avez filtrés.
Le menu d'actions propose Logs, Bash,
Sh, Shell as user… (demande un utilisateur/UID,
comme docker exec -u, pour les images dont l'utilisateur par défaut
n'a pas les outils ou les droits nécessaires) et
Port forward (les shells indisponibles
sont grisés après un sondage). Dans la barre de recherche, Enter
conserve le filtre et revient à la liste ; Esc efface le filtre et
ferme la barre.
Risques de sécurité (!). À chaque récupération de la liste des services, swarmexec exécute un ensemble de petits analyseurs statiques sur la spec de chaque service — la même spec que le manager a déjà renvoyée : il n'y a donc aucun appel d'API supplémentaire, l'agent n'intervient pas et rien n'est exécuté à l'intérieur de vos conteneurs. Un service porteur d'un constat qui mérite une action est marqué d'un 🛡 en tête de sa ligne dans l'arbre des services, dans un emplacement de largeur fixe devant le nom, de sorte que les boucliers forment une colonne verticale de lecture. Appuyez sur ! pour ouvrir la surcouche des risques de sécurité, et sur w à l'intérieur pour écrire un rapport Markdown sur tout le cluster — qui couvre davantage que la surcouche, puisqu'il ajoute les contrôles relevant du cluster et non d'un service.
Les huit contrôles livrés à ce jour. D'abord ceux qui peuvent marquer un service, puis, en fin de liste, les purement informatifs :
| Contrôle | Sévérité | Ce qu'il signale |
|---|---|---|
docker-socket — « Docker socket mounted in » | high | le constat le plus grave de tous. Un bind mount dont la source est la socket du démon Docker — /var/run/docker.sock, /run/docker.sock ou tout chemin source se terminant par /docker.sock. Tout ce qui tourne dans ce conteneur peut parler à la socket, et qui peut parler à la socket peut démarrer un conteneur privilégié sur ce nœud — c'est donc, de fait, root sur l'hôte, quel que soit l'utilisateur sous lequel tourne le conteneur lui-même. Le montage en lecture seule n'y change rien : la socket est une API, pas un fichier dont le contenu compte — le constat le précise lui-même lorsque le montage est en lecture seule. Il nomme le chemin cible où la socket est montée |
added-capability — « capability NOM added » | high / medium | une capability Linux que le service ajoute à son conteneur. Swarm n'a pas de --privileged : les capabilities sont donc la façon dont un service réclame davantage de pouvoir sur l'hôte — d'où l'intérêt de lire l'ensemble ajouté. Huit sont traitées comme high, chacune avec la raison que porte le constat : ALL (accorde toutes les capabilities), SYS_ADMIN (quasi root : contrôle des montages, des namespaces et des cgroups), SYS_MODULE (peut charger des modules noyau), SYS_PTRACE (peut inspecter et contrôler d'autres processus), SYS_RAWIO (accès d'E/S brut aux périphériques), DAC_READ_SEARCH (contourne les contrôles de permission en lecture), NET_ADMIN (contrôle total du réseau du nœud), NET_RAW (peut forger et capturer des paquets bruts). Toute autre capability ajoutée est medium — un privilège au-delà de l'ensemble par défaut. Un constat par capability ajoutée ; les deux écritures sont reconnues, quelle que soit la casse (CAP_SYS_ADMIN et SYS_ADMIN) |
host-network — « runs on the host network » | high | le service est rattaché au réseau nommé host : le conteneur partage la pile réseau du nœud, il n'y a donc plus d'isolation réseau et la publication de ports de swarm ne s'applique plus. Il atteint tout ce que le nœud atteint — y compris les services écoutant sur localhost, que l'on suppose d'ordinaire hors de portée d'un conteneur |
unconfined — « seccomp disabled » / « AppArmor disabled » | high | le bac à sable au niveau du noyau a été désactivé pour le conteneur : seccomp réglé sur unconfined — le conteneur peut effectuer n'importe quel appel système, ce qui supprime la principale barrière face aux exploits noyau — ou confinement AppArmor désactivé. Les deux sont signalés séparément : un service qui coupe les deux obtient deux constats |
root-user — « runs as root » | high | la spec fixe le conteneur sur root explicitement — User=root ou l'UID 0 (les formes numériques comme 00 ou +0 sont aussi détectées ; seule la partie utilisateur de user:group est examinée) |
secret-in-env — « secret in environment variable » | high | une clé d'environnement qui ressemble à un identifiant contient une valeur littérale dans la spec du service, lisible par quiconque peut lire la spec. Utilisez plutôt un secret Docker ou la convention *_FILE |
root-user — « no user set » | low | aucun utilisateur n'est défini : le conteneur s'exécute donc sous l'utilisateur par défaut de l'image, souvent root. Purement informatif, et ne marque pas le service : un utilisateur non défini est la valeur par défaut de Swarm sur presque tous les services, et savoir s'il s'agit réellement de root dépend du USER de l'image, que la spec du manager ne révèle pas |
no-resource-limits — « no resource limits » | low | la tâche ne fixe ni limite CPU ni limite mémoire : un conteneur qui s'emballe peut consommer le nœud entier et affamer tout ce qui s'y trouve. Purement informatif — voir ci-dessous |
unpinned-image — « image not pinned » | low | l'image est épinglée sur :latest, ou ne porte aucun tag (ce qui revient à :latest) — la version qui tourne peut changer sans modification de la spec, un déploiement n'est donc pas reproductible. Une image épinglée par digest (…@sha256:…) est exactement reproductible et n'est jamais signalée, et un deux-points appartenant au port de l'hôte du registre (registry:5000/img) n'est pas pris pour un tag. Purement informatif — voir ci-dessous |
Pourquoi les contrôles informatifs ne marquent jamais un service.
Seul un constat au-dessus de low — high ou medium — rend un service
actionnable et pose le 🛡 sur sa ligne dans l'arbre. Les constats
low (no-resource-limits, unpinned-image et le « no user
set » de root-user) sont vrais de presque tous les services d'un
cluster réel : les badger mettrait un bouclier sur presque chaque ligne et
détruirait précisément le signal en un coup d'œil pour lequel le marqueur existe. Ils
ne sont pas pour autant escamotés — dès qu'un service est marqué pour autre chose,
ses constats low sont listés avec les autres dans la surcouche, c'est-à-dire là où
ils valent vraiment la peine d'être lus.
secret-in-env regarde les variables d'environnement, et son constat
ne nomme que la clé de la variable
d'environnement ; sa valeur n'est jamais lue dans le constat, jamais rendue et
jamais copiée. Le contrôle est également conçu pour rester discret : il compare des
jetons entiers délimités par _ (PASSWORD,
PASSWD, PASS, PASSPHRASE,
SECRET, TOKEN, APIKEY,
CREDENTIAL(S), PRIVATEKEY, ainsi que les paires
API_KEY, ACCESS_KEY, PRIVATE_KEY,
SECRET_KEY, CLIENT_SECRET, AUTH_TOKEN), si
bien que COMPASS, PASSENGER_PORT ou
BYPASS_AUTH ne sont pas signalés. Les clés qui ne font que
référencer un identifiant sont exemptées (_FILE,
_PATH, _URL, _URI, _NAME,
_ID, _TYPE, _ENABLED,
_REQUIRED, _LENGTH, _TIMEOUT,
_TTL, _EXPIRY, _ALGORITHM), tout comme les
valeurs qui ne peuvent manifestement pas être un identifiant : un chemin absolu, un
booléen, un nombre.
La surcouche liste chaque service marqué (« N of M service(s) flagged · 8 checks »), groupé par service, chaque constat sur sa propre ligne avec une pastille de sévérité — ● high, ● medium, ● low — un titre court et une explication d'une ligne. Les constats sont triés du plus grave au moins grave. Dès qu'un service est marqué pour quelque chose d'actionnable, ses constats informatifs de niveau low sont listés eux aussi. Si le service sous le curseur en fait partie, il est présélectionné et amené à l'écran. Quand rien n'est marqué, la surcouche le dit explicitement et nomme chaque contrôle qui a tourné (« Checked: Docker socket mounted in, added Linux capabilities, host network, seccomp / AppArmor disabled, container user, secrets in environment variables, missing resource limits, unpinned image. »), de sorte que le feu vert dit aussi ce qu'il couvre ; si la liste des services n'est pas encore chargée, elle indique que rien n'a encore été analysé plutôt que de donner un faux feu vert. j/k font défiler, Esc (ou q, ou ! à nouveau) ferme.
security_risks dans keys.yaml.
i ouvre une surcouche d'inspection pour le nœud
sous le curseur — sur un service elle affiche
docker service inspect, et sur une feuille
conteneur l'inspection de tâche swarm (la
vue du manager de cette instance : état, emplacement, nœud, id du conteneur,
spécification du conteneur, ressources et historique d'état). Les deux
proviennent du manager du swarm. La surcouche s'ouvre sur un
résumé tabulaire, orienté opérateur avec des sections
ordonnées par pertinence opérationnelle — réseaux, labels, volumes/montages et
secrets (et configs) d'abord, puis ports, image, mode, env, ressources,
placement et politique de mise à jour (plus l'état, le nœud et l'id du
conteneur pour un conteneur/tâche), avec les ids et horodatages en dernier.
La première ligne de la surcouche, à l'intérieur de la bordure et fixée au-dessus du
contenu qui défile, est une barre d'onglets qui nomme ses
trois vues — table 1 stats 2 raw json 3
— la vue courante dans la couleur d'accentuation ; elle a la même forme que la barre
d'onglets de la fenêtre principale : un libellé suivi du chiffre qui le sélectionne.
1, 2 et 3 sautent directement au
tableau, aux stats et au JSON brut du démon, et t fait toujours défiler
tableau → stats → JSON brut puis revenir. L'indication du
pied de page se résume à 1-3/t view, et le titre de la bordure ne répète
plus le nom de la vue courante — il dit simplement
inspect service foo, puisque la barre indique déjà où vous êtes.
Notez que la vue tabulaire est la vue de tâche du manager, pas d'un
docker container inspect complet et local au nœud du conteneur en cours
d'exécution — une véritable inspection de conteneur via l'agent est prévue.
La vue stats. Celle du milieu montre l'utilisation des ressources en direct — les mêmes relevés que ceux qui marquent l'arbre : l'ouvrir ne coûte aucun appel supplémentaire et les chiffres continuent de se rafraîchir tant qu'elle est ouverte. C'est un tableau, une ligne par conteneur du service (ou l'unique conteneur d'une inspection de tâche) :
Un tableau, parce qu'un pourcentage nu n'est pas lisible si l'on ne sait pas déjà
comment l'outil mesure — 94 % de quoi ? Chaque cellule porte ses
propres unités : 0.07 / 4.00 cores n'a besoin d'aucune
légende, et le pourcentage se tient à côté du chiffre dont il est le pourcentage ; et
plusieurs réplicas se comparent en parcourant une colonne plutôt
qu'en confrontant des blocs empilés. La colonne HEALTH formule le
verdict en toutes lettres : healthy, unhealthy,
starting, ou none pour un conteneur qui ne déclare aucun
healthcheck. none et healthy sont délibérément
deux mots distincts, et non un mot et un vide : « aucun healthcheck
configuré » et « la sonde passe » sont deux faits différents, et un vide se lirait
comme le second. Un conteneur qu'aucun agent n'a rapporté — nœud injoignable, agent
trop ancien pour le RPC Stats, ou conteneur démarré depuis le dernier
relevé — affiche no reading dans les deux colonnes de ressources au lieu
d'un zéro, qui se lirait comme « au repos ».
La note sous le tableau nomme le dénominateur : la limite propre du conteneur lorsqu'il en a une, la capacité du nœud sinon. C'est cela qui donne un sens à un pourcentage — 85 % d'une limite de 8 Gio et 85 % d'un nœud de 64 Gio sont deux conversations différentes. La mémoire est en unités binaires (MiB/GiB), les mêmes que le détail de nœud et celles que docker rapporte lui-même : le même chiffre ne se lit jamais différemment à deux endroits. Avec plus d'un conteneur mesuré, une synthèse s'ajoute sous le tableau : CPU et mémoire de la pire réplica — pas une moyenne, qui masque justement le conteneur sur le point d'être tué par l'OOM — plus la mémoire totale sur l'ensemble. L'entrée t de la surcouche d'aide ? indique en conséquence cycle table / stats / raw JSON.
La surcouche est navigable et sélectionnable, pas seulement un simple défilement : les lignes de données sont individuellement sélectionnables et vous déplacez le curseur ligne par ligne avec ↑/↓ ou j/k (les en-têtes de section et les lignes vides sont ignorés). y copie toujours la ligne sélectionnée dans votre presse-papiers via OSC52 — le même mécanisme de copie utilisé ailleurs — pour que vous puissiez saisir un seul id, montage ou adresse sans sélectionner de texte à la main. Enter copie aussi, sauf sur une ligne NETWORKS repliable, où il déplie/replie plutôt cette ligne (voir ci-dessous). Les trois vues (le résumé tabulaire, la vue stats et le JSON brut, atteints par 1–3 ou en faisant défiler avec t) sont sélectionnables et copiables ligne par ligne. Un pied de page liste toujours les touches disponibles, de sorte que ce que vous pouvez faire est visible d'un coup d'œil ; pour un service, le pied de page affiche aussi les touches d'édition ci-dessous.
| Touche | Action |
|---|---|
| ↑ ↓ j k | déplace la sélection entre les lignes de données (en-têtes/vides ignorés) |
| y | copie la ligne sélectionnée dans le presse-papiers (OSC52) — toujours |
| Enter | sur une ligne NETWORKS repliable, déplie/replie ce niveau — les lignes s'imbriquent sur deux niveaux, un réseau et son détail N containers ; sur toute autre ligne, la copie (OSC52) |
| 1 2 3 | sélectionne une vue directement — 1 résumé tabulaire, 2 stats (CPU / mémoire en direct, voir ci-dessous), 3 JSON brut du démon ; ce sont les chiffres qu'affiche la barre d'onglets |
| t | fait défiler les trois vues — résumé tabulaire → stats (CPU / mémoire en direct, voir ci-dessous) → JSON brut du démon → retour ; les trois sont sélectionnables, et la barre d'onglets en haut marque celle où vous êtes |
| a | menu d'actions — tous les éditeurs et actions du service en une seule liste, sans Maj (services uniquement ; voir ci-dessous) |
| s f p l e n S v A | édite le service — mise à l'échelle / mise à jour forcée / ports / labels / env / réseaux / secrets / montages / alias (service uniquement ; S est en majuscule, Maj+s, pour le distinguer de s mise à l'échelle ; A est aussi en majuscule ; voir ci-dessous) |
| Esc q i | ferme la surcouche |
La section NETWORKS est repliable, et elle
répond à « à quelle adresse est-ce joint ? » avant même de rien déplier :
chaque réseau attaché apparaît comme une ligne repliée
+ <network> 🔒 vip 10.0.5.2/24 (2 dns names). Une
icône de cadenas 🔒 après le nom du réseau signale un réseau
overlay chiffré (chiffrement du plan de données,
--opt encrypted) ; son absence signifie non chiffré. Sur
l'inspection d'un service, l'adresse est étiquetée
vip — l'IP virtuelle du service sur ce réseau, l'adresse à
laquelle répond le répartiteur de charge du swarm, tirée du
ServiceInspect du manager (Endpoint.VirtualIPs). Sur
celle d'un conteneur/tâche, elle est étiquetée
addr : l'adresse propre de ce conteneur sur ce réseau. Un service
en mode d'endpoint dnsrr n'a aucune VIP — c'est voulu, pas une
donnée manquante — donc au lieu d'un blanc, le réseau déplié porte la ligne
no vip — dnsrr endpoint mode, the DNS name resolves to the containers.
Le réseau ingress est lui aussi listé ici, bien qu'aucune spec ne
le mentionne — le swarm y attache le service de lui-même (voir plus bas). Il est
ajouté après les réseaux que déclare la spec, jamais intercalé
entre eux, et son en-tête se lit
+ ingress vip 10.0.0.250/24 (routing mesh) : la mention
(routing mesh) prend la place d'un décompte de noms DNS qui ne
pourrait jamais dire que (0 dns names).
Déplacez-vous sur une ligne de réseau et appuyez sur Enter pour la
déplier ou la replier. Dépliée, elle liste les noms DNS qui se résolvent vers le
service/conteneur sur ce réseau — le nom du service,
tasks.<service> et tout alias personnalisé — pour que vous
puissiez voir sous quel nom DNS il est joint et quels alias il porte, par
réseau. En dessous se trouve une seconde ligne repliable,
+ 2 containers (au singulier, + 1 container) ;
Enter sur celle-ci ouvre le niveau le plus profond : une
ligne par conteneur, alignée en colonnes,
web.1 10.0.1.5/24 host-a — nom de la tâche, son adresse sur ce
réseau, et le nœud sur lequel elle tourne (les tâches répliquées sont nommées
<service>.<slot>, les globales
<service>.<node>, faute de slot).
Le filtre porte sur l'état souhaité (desired state) de la
tâche, pas sur son état courant : toute tâche que le manager veut encore voir
tourner est listée, y compris celles qui ne sont que preparing,
assigned ou starting — elles détiennent déjà leur
adresse, et pendant une mise à jour progressive c'est le cas de la plupart ;
filtrer sur l'état courant viderait donc le détail précisément quand il est le
plus intéressant. Une tâche que le manager a abandonnée — état souhaité
shutdown, c'est-à-dire remplacée par une mise à jour progressive,
supprimée par une mise à l'échelle ou en échec — n'est pas listée : son adresse
a été libérée et peut déjà appartenir à un autre conteneur, l'afficher serait
donc activement faux. Une tâche qui ne sert pas encore de trafic porte son état
entre parenthèses en fin de ligne,
web.1 10.0.1.5/24 host-a (starting) ; une tâche en cours
d'exécution n'a pas de suffixe. Les lignes viennent d'un TaskList
de l'API du manager filtré sur le service, au mieux (best-effort) : une erreur
d'API laisse simplement le détail vide. Les deux niveaux se replient
indépendamment ; replier le réseau masque avec lui le niveau des conteneurs.
La ligne ingress est celle qui ne figure dans aucune spec : le
swarm rattache un service au réseau ingress de lui-même dès qu'il publie un port
en mode ingress, et cet attachement n'apparaît nulle part dans la
spec du service — pourtant sa vip est l'adresse à laquelle le
routing mesh répond réellement, ce que l'on veut savoir en
premier quand un port publié se comporte mal. Elle obtient donc une ligne à elle,
ajoutée après les réseaux déclarés par la spec et de forme légèrement différente :
aucun nom DNS de service ne se résout sur ingress, aussi, là où un autre réseau
compte ses noms, cet en-tête porte la mention (routing mesh).
Dépliée, la ligne liste à la place les ports publiés via ingress
qui y ont mené le service — la raison d'être de la ligne —, un par ligne :
published 2222 -> 22/tcp. Les ports publiés en mode
host contournent le routing mesh et n'y sont délibérément pas
listés. En dessous, elle descend jusqu'aux conteneurs exactement comme toute autre
ligne de réseau : le même niveau + 1 container, avec l'adresse propre
de chaque conteneur sur le réseau ingress. Un service qui ne publie aucun port en
ingress n'y a pas de VIP — et donc pas de telle ligne du tout.
Un service GitLab publiant SSH sur 2222, les deux niveaux dépliés :
Copier dans NETWORKS donne l'adresse sans son
masque — 10.0.5.2, la forme que vous collez dans un
curl ou un ping : sur une ligne de réseau son
vip/addr, sur une ligne de conteneur l'adresse de ce conteneur. Une ligne de
réseau sans adresse (dnsrr) copie le nom du réseau, comme avant. Sur les deux
types de lignes repliables Enter bascule ; sur toute autre ligne il
copie (et y copie toujours).
Lorsque vous inspectez un service (pas une feuille
conteneur/tâche, dont la surcouche reste en lecture seule), le pied de page de la
surcouche expose aussi plusieurs touches d'édition, chacune un
ServiceUpdate de l'API du manager qui déclenche une mise à jour
progressive / réconciliation des tâches du service.
Commencez par a — le menu d'actions. Sur une inspection
de service (et là seulement), a ouvre une liste unique de tous les
éditeurs et actions que propose la surcouche, pour ne pas avoir à retenir ~14
touches sensibles à la casse (d/D, s/f,
p/l/e, n/S/v,
r/P, R, X). C'est le chemin
découvrable, sans Maj — et non un chemin réduit : choisir une
entrée ferme le menu et ouvre exactement ce qu'ouvre la touche directe ; les deux
sont donc équivalents, pas des variantes au comportement
différent. Les entrées, dans l'ordre :
Update image version… (Set image version… quand il n'y en a pas de
plus récente — épingler une version de votre choix ou y revenir est tout aussi valable),
Diff spec (previous → current), Roll back to the previous version,
Why — placement diagnosis, Scale, Force-update,
Edit ports, Edit labels, Edit env, Edit networks,
Edit secrets, Edit mounts, Edit resources,
Edit placement, Remove service
— la destructive, signalée en rouge — et Cancel. On se déplace avec
j/k (g/G sautent à la première/dernière
entrée), Enter sélectionne ; Esc, ou l'entrée Cancel,
ferme le menu sans rien faire. Le sélecteur de version d'image vient
en premier, parce que fixer une version est le changement de
service le plus ordinaire qui soit ; sa touche directe u fonctionne
toujours depuis n'importe quelle ligne de l'inspection. Si l'image est de celles
pour lesquelles aucune version ne peut être choisie — une référence sans tag, ou un
registre qui refuse de lister les tags du dépôt, typiquement un registre privé sans
identifiants —, l'entrée reste dans la liste et le dit au lieu de
disparaître en silence, et renvoie vers le docker service update --image
qui, lui, fonctionne. La touche directe de chaque entrée :
| Touche | Action |
|---|---|
| d | diff — affiche un diff unifié de la spécification actuelle du service par rapport à sa précédente (ce que la dernière mise à jour progressive a changé), en utilisant le PreviousSpec de Swarm. Les champs modifiés sont listés groupés par champ avec des lignes - supprimé / + ajouté (image, mode/répliques, env, labels, réseaux, alias, secrets, montages, ports, contraintes de placement, préférences de répartition, limites/réservations de ressources) ; les champs inchangés sont omis. Si le service n'a jamais été mis à jour (pas de spécification précédente) il l'indique. Surcouche en lecture seule — j/k défilent, Esc ferme |
| R | revient à la version précédente (R majuscule, c.-à-d. Maj+r, pour le distinguer de r limites de ressources) — la contrepartie du diff d ci-dessus : il annule la dernière mise à jour progressive. La boîte de confirmation affiche ce même diff inversé sous « This will undo: », car c'est bien ce que le retour arrière va changer — une ligne que la mise à jour a ajoutée apparaît comme - supprimé, et une qu'elle a supprimée revient comme + ajouté. L'aperçu est plafonné à 12 entrées, suivies d'une note « … and N more » qui renvoie à d pour le diff complet (si seules les métadonnées ont changé, il indique qu'il n'y a aucune différence au niveau des champs). Confirmer déclenche un retour arrière côté serveur — un seul ServiceUpdate de l'API du manager avec ServiceUpdateOptions{Rollback: "previous"}, pas une réapplication du PreviousSpec côté client — c'est donc le manager qui l'exécute, en respectant la configuration de retour arrière propre au service (parallélisme, délai, action en cas d'échec), et il rapporte la progression via l'état de mise à jour rollback_started : exactement le badge ↺ rolling back décrit plus haut, sur la ligne de l'arbre et dans cette surcouche. Un service qui n'a jamais été mis à jour n'a pas de spécification précédente — vous obtenez un message explicatif, pas une erreur. Également accessible depuis le menu d'actions a. Comme d, D et X, c'est une touche fixe de l'inspection, pas une action remappable du keymap |
| s | mise à l'échelle — demande un nouveau nombre de répliques et l'applique (services répliqués uniquement ; un service global signale qu'il ne peut pas être mis à l'échelle) |
| f | mise à jour forcée — redéploie le service sans changer sa spécification (l'équivalent de docker service update --force : incrémente TaskTemplate.ForceUpdate), après une confirmation. Chaque tâche est redémarrée / replanifiée, c'est ainsi que vous débloquez un service coincé dans un état incomplet (p. ex. 1/2 répliques). Un seul ServiceUpdate (mise à jour progressive) |
| X | supprime le service (X majuscule) — le supprime définitivement et arrête toutes ses tâches, derrière une confirmation ; irréversible. Un seul ServiceRemove de l'API du manager ; en cas de succès la surcouche d'inspection se ferme et l'arbre des services se rafraîchit |
| D | diagnostique le placement (D majuscule) — répond à pourquoi un service ne s'exécute pas partout où vous l'attendez en une seule vue, au lieu de le pourchasser à travers plusieurs commandes docker. Pour un service global il liste chaque nœud avec ✓ en cours d'exécution ou ✗ et la raison pour laquelle il est exclu (disponibilité drain/pause, un état down, une contrainte de placement non satisfaite, ou une plateforme non concordante) — c'est pourquoi un cluster de 3 nœuds peut légitimement afficher 2/2 (le troisième nœud n'est pas éligible). Pour un service répliqué il liste chaque tâche qui n'est pas en cours d'exécution avec le message propre au planificateur (contraintes, ressources insuffisantes, erreurs de récupération d'image, …). Les contraintes qui ne peuvent pas être vérifiées côté client (engine.labels.*) sont signalées, pas devinées. Surcouche en lecture seule |
| p | édite les ports publiés — ouvre un éditeur de liste étagé des ports du service, chacun sous la forme PUBLISHED:TARGET[/proto] (proto tcp|udp|sctp, par défaut tcp), p. ex. 8080:80/tcp |
| l | édite les labels — le même éditeur de liste étagé sur des entrées key=value |
| e | édite les variables d'environnement — le même éditeur de liste étagé sur l'env du ContainerSpec, chacune saisie comme KEY=VALUE (p. ex. LOG_LEVEL=debug) ; la clé est requise, la valeur peut être vide et peut elle-même contenir =. Comme ports/labels/montages, cet éditeur autorise l'édition e, pour que vous puissiez ajuster une valeur sur place. Contrairement aux autres éditeurs, e ouvre ici l'entrée dans une zone de texte multiligne défilable (plutôt qu'un champ sur une ligne) pour que les longues valeurs comme GITLAB_OMNIBUS_CONFIG soient confortables à éditer — tapez librement (Enter insère un saut de ligne), Ctrl-S enregistre, Esc annule. Appliquer remplace l'env du service en un seul ServiceUpdate (mise à jour progressive) |
| n | édite les réseaux — un éditeur de liste étagé sur les attachements réseau du service ; comme une entrée n'est qu'un nom c'est ajout/retrait seulement (pas d'édition e). Le champ d'ajout complète automatiquement les noms de réseau (suggérant les réseaux auxquels le service n'est pas déjà attaché, ou tapez-en un vous-même). Appliquer remplace les attachements en un seul ServiceUpdate. Les alias DNS s'éditent aussi d'ici : sélectionnez un réseau dans cet éditeur et appuyez sur A pour ouvrir une liste étagée des alias DNS du service sur ce réseau (a ajouter / e éditer / d supprimer / y copier / u annuler / w appliquer / Esc annuler) ; appliquer remplace les alias de ce réseau en un seul ServiceUpdate. Fonctionne même quand le service n'a pas encore d'alias. (Vous devez d'abord appliquer un réseau nouvellement ajouté avant de pouvoir définir ses alias.) |
| S | édite les secrets (S majuscule, c.-à-d. Maj+s, pour le distinguer de s mise à l'échelle) — le même éditeur de liste étagé que les réseaux sur les secrets que le service référence ; une entrée n'est qu'un nom, donc c'est ajout/retrait seulement (pas d'édition e). Le champ d'ajout complète automatiquement les noms de secret (suggérant les secrets que le service n'utilise pas déjà, ou tapez-en un vous-même). Appliquer remplace les références de secret du service en un seul ServiceUpdate (mise à jour progressive) ; chaque secret est monté dans /run/secrets/<name>. Fonctionne même quand le service n'a actuellement aucun secret |
| v | édite les montages — un éditeur de liste étagé sur les volumes et bind mounts du service. Ajouter (a) ou éditer (e) ouvre un petit formulaire au lieu d'un unique champ de texte : une case bind mount, une source (quand la case est décochée c'est un nom de volume avec autocomplétion des volumes existants du cluster ; quand elle est cochée c'est un chemin hôte), un chemin conteneur, et une case read-only. (En interne chaque entrée reste volume:NAME:TARGET[:ro] / bind:/host/path:TARGET[:ro].) Les sources bind et toutes les cibles doivent être des chemins absolus. Appliquer remplace les montages en un seul ServiceUpdate (mise à jour progressive). Garde-fou bind-mount (souple) : si la liste étagée contient des bind mounts, appliquer affiche d'abord un avertissement listant les nœuds sur lesquels le service pourrait être planifié (calculés à partir des contraintes de placement plus le rôle/labels/disponibilité des nœuds) et vous rappelle que chaque source bind doit déjà exister sur tous — swarmexec ne peut pas vérifier les chemins hôte (l'agent n'a pas d'accès au système de fichiers de l'hôte), donc les chemins bind ne sont ni autocomplétés ni vérifiés pour leur existence ; c'est purement indicatif |
| r | édite les limites de ressources — un petit formulaire pour définir, changer ou effacer les limites CPU et mémoire du service (et, en option, les réservations). Le CPU est donné en cœurs (p. ex. 0.5, 2) ; la mémoire comme une taille lisible (p. ex. 512m, 2g, 1.5GiB). Laisser un champ vide efface cette limite (Swarm la traite alors comme illimitée). Une réservation ne peut pas dépasser sa limite. Appliquer effectue un seul ServiceUpdate (mise à jour progressive) ; les limites de Pids existantes et les réservations device/génériques sont préservées |
| P | édite le placement (P majuscule, c.-à-d. Maj+p, pour le distinguer de p ports) — ouvre un petit menu avec deux éditeurs étagés : Contraintes et Préférences de répartition.
ServiceUpdate (mise à jour progressive) ; l'autre partie est préservée. |
Ces éditeurs sont étagés : a ajoute une entrée,
d supprime celle sous le curseur, y copie l'entrée
sélectionnée dans votre presse-papiers via OSC52 (pour que vous puissiez
saisir p. ex. une seule variable d'environnement en la consultant),
u annule le dernier changement étagé (ajout / édition / suppression)
tant que vous n'avez pas encore appliqué — appuyez-y de façon répétée pour
remonter dans l'historique étagé — et w applique tous les
changements d'un coup en un seul ServiceUpdate (donc ajouter,
disons, plusieurs volumes puis appuyer sur w déclenche une
mise à jour progressive, pas une par entrée), derrière une confirmation. Si
vous appuyez sur Esc alors que des changements sont encore étagés,
l'éditeur ne les abandonne pas en silence — il demande s'il faut
appliquer, abandonner, ou continuer
l'édition. Tant que les changements sont encore étagés, ils sont
mis en évidence pour que vous voyiez exactement ce qui va
changer : les entrées ajoutées ou éditées apparaissent en
vert (nouveau) et les
entrées supprimées subsistent comme des lignes atténuées
rouge « supprimé » — la mise en évidence
s'efface une fois que vous appliquez. Les éditeurs ports,
labels, montages, env et
alias (alias atteints depuis l'éditeur de réseaux via
A) ont aussi e pour éditer l'entrée sous le curseur ; les
éditeurs réseaux et secrets n'ont pas de
e — une entrée de réseau ou de secret n'est qu'un nom, donc c'est
ajout (a) / retrait (d) seulement. Le e de
l'éditeur env ouvre une zone de texte multiligne défilable
(Ctrl-S enregistrer / Esc annuler) pour les longues
valeurs ; les autres éditeurs conservent le champ sur une seule ligne avec
Enter pour confirmer.
L'arbre se rafraîchit automatiquement toutes les ~10 s, de sorte qu'un conteneur remplacé ou un service mis à l'échelle en arrière-plan apparaît de lui-même — le curseur et tout service déplié sont préservés. r force toujours un rafraîchissement immédiat.
Onglet Volumes
| Touche | Action |
|---|---|
| / | recherche — filtre la liste par nom de volume, driver ou nœud |
| n | crée un volume — un formulaire avec nom, driver (par défaut local), labels (k=v,k=v) et un nœud cible (autocomplète les noms de nœud ; laissez vide pour créer sur chaque nœud, car les volumes sont locaux au nœud). Le crée sur l'agent de chaque nœud cible et signale le succès/échec par nœud. Tab se déplace entre les champs, Esc annule |
| space | sélectionne / désélectionne le volume (marqué ▣) pour une suppression en masse |
| a | sélectionne / désélectionne tous les volumes actuellement affichés |
| d | supprime les volumes sélectionnés — ou celui sous le curseur — sur chaque nœud qui les détient, derrière une confirmation (avec une surcouche de progression ; les suppressions s'exécutent avec un parallélisme borné) |
| P | purge : supprime tout volume qu'aucun conteneur en cours d'exécution ne monte et qu'aucun service ne déclare, derrière une confirmation |
| Enter | affiche quels nœuds détiennent le volume ; les labels du volume sont listés en lecture seule au-dessus des nœuds (Docker n'a pas d'API de mise à jour de volume, donc les labels ne peuvent pas être édités après création — définissez-les à la création du volume) |
| i | affiche quels services/conteneurs l'utilisent |
| A | attache le volume à un service : choisissez un service (autocomplétion du nom) → saisissez le chemin cible du conteneur (absolu) → choisissez Attach ou Attach read-only. Ajoute le montage via un seul ServiceUpdate (mise à jour progressive) — la contrepartie volume des actions d'attachement réseau/secret ; l'inverse se trouve dans l'éditeur de montages v de l'inspection de service |
| s S | fait défiler le champ de tri (name → nodes → used → age → size) / inverse |
La suppression tient compte des nœuds : un volume est supprimé sur chaque nœud qui le détient. La purge épargne les volumes qu'un service déclare même si aucune tâche n'est en cours d'exécution, elle n'effacera donc pas les données d'une stack arrêtée. Pour un contrôle plus fin, Enter ouvre la liste par nœud où space sélectionne des nœuds, d supprime la copie du nœud en surbrillance et a supprime sur tous les nœuds — chacun derrière une confirmation.
Onglet Forwards
| Touche | Action |
|---|---|
| Enter | affiche le détail complet de la redirection (y compris toute erreur) |
| d | arrête la redirection sélectionnée |
| o | copie l'URL de la redirection (http://127.0.0.1:<port>) |
Un conteneur en cours d'exécution est annoté dans l'arbre par
local→remote (p. ex. 9090→8080). Les redirections
continuent de fonctionner quand leur surcouche se ferme et quand vous
changez de cluster ; elles s'arrêtent sur d ou quand vous quittez l'UI.
La colonne CLUSTER nomme le contexte auquel chacune appartient,
estompée pour celui que vous regardez — une redirection sur un autre cluster reste
la vôtre et continue d'écouter, mais ses colonnes CONTAINER et
NODE désignent des choses invisibles d'ici. L'annotation dans l'arbre
est par cluster pour la même raison : un identifiant de conteneur n'est unique
qu'au sein de son propre démon.
De ce fait, un port local que vous utilisez déjà est refusé d'emblée, en nommant la redirection qui le détient et le cluster auquel elle appartient — le message reste ouvert pour que vous en choisissiez un autre. Le « address already in use » du système reste pour le cas où il a raison : un port détenu par un autre programme, que swarmexec ne peut pas nommer.
Onglet Networks
Une liste des réseaux du swarm — nom, driver, portée, type
(ingress / internal / attachable), si le réseau est
chiffré et combien de services s'attachent à chacun. La
colonne TYPE affiche overlay ou le nom du driver
(p. ex. bridge) pour les réseaux ordinaires plutôt qu'un tiret. La
colonne ENC affiche 🔒 yes
quand le réseau overlay a le chiffrement du plan de données activé (créé avec
--opt encrypted), sinon un tiret. Le nom de chaque réseau et sa
cellule TYPE sont codés par couleur selon le genre (priorité la
plus haute d'abord) :
- attachable — vert
- internal — jaune
- ingress — gris
- autre overlay / à portée swarm — aqua
- local (
bridge,host, …) — gris atténué
| Touche | Action |
|---|---|
| n | crée un réseau — ouvre un formulaire (driver par défaut overlay) avec les options swarm courantes : attachable (laisser des conteneurs autonomes rejoindre), encrypted (chiffrement du plan de données overlay), internal (pas de routage externe), IPv6, un MTU facultatif, un subnet/gateway facultatif (IPAM) et des labels (k=v,k=v). Tab se déplace entre les champs, Enter sur Create confirme, Esc annule. Le crée via un seul NetworkCreate de l'API du manager, puis rafraîchit l'onglet |
| Enter / i | affiche les services attachés, chacun avec ses conteneurs en cours d'exécution imbriqués en dessous. Les labels propres au réseau sont listés en lecture seule en haut (Docker n'a pas d'API de mise à jour de réseau, donc les labels de réseau ne peuvent pas être édités après création — définissez-les à la création du réseau). Chaque en-tête de service affiche aussi combien d'alias DNS il a sur ce réseau (ou aucun alias) |
| Enter | dans la vue des membres : déplie / replie les alias DNS du service sélectionné (gardés repliés par défaut pour que la liste reste compacte) ; les alias apparaissent indentés sous l'en-tête du service |
| A | dans la vue des membres : ajoute / édite les alias DNS du service sélectionné sur ce réseau — ouvre le même éditeur d'alias étagé que la vue d'inspection (a ajouter / e éditer / d supprimer / w appliquer / Esc annuler) ; appliquer remplace les alias de ce réseau en un seul ServiceUpdate et rafraîchit la vue. Fonctionne même quand le service n'a pas encore d'alias. (A est en majuscule, Maj+a, pour le distinguer de a attacher ; les conteneurs autonomes hors swarm ne sont pas éditables — les alias sont un ServiceUpdate par service) |
| a | dans la vue des membres : attache un service à ce réseau — un champ d'autocomplétion (suggérant les services pas déjà attachés ; vous pouvez aussi taper n'importe quel nom), puis une confirmation de mise à jour progressive |
| d | dans la vue des membres : détache un service de ce réseau — un champ d'autocomplétion (suggérant les services actuellement attachés ; vous pouvez aussi taper n'importe quel nom), puis une confirmation de mise à jour progressive |
Attacher ou détacher effectue une mise à jour de service qui ajoute ou retire
le réseau dans la spécification du service, puis rafraîchit l'onglet Networks.
C'est une opération de l'API du manager (ServiceUpdate) — le même
canal que le client utilise pour la topologie — et elle déclenche une
mise à jour progressive qui redémarre les tâches du service.
Onglet Secrets
Une liste en lecture seule des secrets du swarm — nom, combien de services utilisent chacun, âge, dernière mise à jour et nombre de labels. Les valeurs des secrets ne sont jamais affichées : l'API Docker ne les expose pas.
| Touche | Action |
|---|---|
| Enter | affiche les métadonnées du secret et les services/conteneurs qui l'utilisent |
| a | dans la vue de détail : attache le secret à un service — un champ d'autocomplétion (suggérant les services qui ne l'utilisent pas déjà ; vous pouvez aussi taper n'importe quel nom), puis une confirmation de mise à jour progressive |
| d | dans la vue de détail : détache le secret d'un service — un champ d'autocomplétion (suggérant les services qui l'utilisent actuellement ; vous pouvez aussi taper n'importe quel nom), puis une confirmation de mise à jour progressive |
Attacher ajoute le secret au service, monté dans
/run/secrets/<name> (comme docker service update
--secret-add) ; détacher retire la référence de secret. Puis l'onglet
Secrets se rafraîchit. Comme l'attachement/détachement réseau, chacun est un
ServiceUpdate de l'API du manager qui déclenche une
mise à jour progressive redémarrant les tâches du service.
Barre latérale des contextes
Les clusters occupent une colonne sur le bord droit, pas un onglet. Changer de cluster est quelque chose que l'on fait depuis l'endroit où l'on est — et la liste des clusters est précisément ce qui indique où c'est. Elle a donc sa place à l'écran, pas sur une page où il faut se rendre.
c lui donne le clavier depuis n'importe quel onglet ; c à
nouveau ou Esc le rend à l'onglet où vous étiez. Le cluster actif est
marqué ▶ — le nom que le pied de page affiche. Un cluster déjà visité
pendant cette session porte un · vert : il est toujours connecté,
donc y basculer est instantané. Un cluster qui a refusé la connexion porte un
✗ rouge. Le contexte default intégré est protégé.
La barre latérale s'ajuste au nom de contexte le plus long et s'efface sur un terminal étroit : en dessous d'environ 92 colonnes elle se masque plutôt que de tronquer l'arbre, et revient tant que c la retient. Les points d'accès ne figurent pas dans la colonne — il n'y a pas la place, et i en montre le détail complet.
Changer de cluster conserve votre session. L'UI ne se reconstruit
pas : la position du curseur, les stacks et services dépliés et le filtre
/ sont mémorisés par cluster, si bien qu'en revenant vous
retrouvez exactement l'endroit que vous aviez quitté. Les redirections de
port continuent de tourner ; l'onglet Forwards gagne une colonne
CLUSTER pour voir d'un coup d'œil lesquelles appartiennent au cluster
affiché et lesquelles non.
Seul le cluster que vous regardez est interrogé : en basculant ailleurs, l'autre cesse de se rafraîchir et sa connexion ssh expire d'elle-même peu après. Le premier passage vers un cluster prend le temps qu'il faut pour l'atteindre, car la connexion est établie avant que quoi que ce soit change à l'écran — si elle échoue, on vous dit de quel cluster il s'agit et vous restez sur celui qui fonctionne. Chaque retour ultérieur est immédiat.
| Touche | Action |
|---|---|
| c | donner le clavier à la barre latérale depuis n'importe quel onglet ; c à nouveau ou Esc le rend |
| i | détails du contexte — point d'accès et hôtes de rebond, pour lesquels la colonne n'a pas la place |
| u / Enter | active le contexte sélectionné — en fait le contexte courant (y compris le contexte courant stocké par docker) et bascule l'UI vers ce cluster, en conservant votre position, vos filtres et vos redirections de port |
| n | crée un contexte — un formulaire guidé. Une case « Connect to Docker over SSH » décide du point d'accès : quand elle est cochée, vous renseignez utilisateur / hôte / port SSH et une seconde case « Use a jump host » révèle un champ jump host(s) (séparés par des virgules, ProxyJump multi-saut) — swarmexec les stocke sur le contexte et injecte -J dans les deux connexions ssh de l'API Docker et du tunnel de l'agent, pour que les bastions fonctionnent sans éditer ~/.ssh/config. Quand elle est décochée, vous saisissez un hôte tcp:// / unix:// simple. Le formulaire assemble l'hôte docker pour vous, et Test teste le point d'accès assemblé avant que vous n'enregistriez. Aussi en CLI : swarmexec context create <name> --docker-host ssh://ops@mgr --ssh-jump bastion1,bastion2 |
| d | retire le contexte sélectionné (derrière une confirmation) ; retirer le contexte courant réinitialise la sélection à default |
Onglet Nodes
Liste les nœuds du swarm avec des informations générales et quelques
agrégations — NODE (nom d'hôte ; managers en aqua, le leader
marqué ★), ROLE (manager / worker), AVAIL
(active / pause / drain, codé par couleur), STATE (ready / down),
version ENGINE, TASKS (tâches en cours d'exécution
planifiées sur le nœud), VOLS (volumes que le nœud détient —
locaux au nœud, donc cela se remplit un instant après le reste) et
LABELS (nombre).
| Touche | Action |
|---|---|
| Enter / i | détails du nœud — une surcouche en lecture seule avec nom d'hôte, id, rôle/leader, disponibilité, état, adresse, moteur, plateforme, CPU, mémoire, nombre de tâches en cours d'exécution, les blocs ressources réservées, utilisation réelle et images sur ce nœud (voir ci-dessous), nombre de volumes et la liste complète des labels |
| l | édite les labels du nœud sélectionné — un éditeur de liste étagé (a ajouter / e éditer / d supprimer / w appliquer / Esc annuler) sur des entrées key=value. Appliquer effectue un seul NodeUpdate — contrairement à un service, une mise à jour de nœud s'applique immédiatement (pas de mise à jour progressive). Les labels de nœud sont couramment utilisés comme cibles de contraintes de placement (node.labels.<k>) |
| a | définit la disponibilité du nœud — un menu Active / Pause / Drain qui signale l'état dans lequel le nœud se trouve actuellement. Active y planifie les tâches normalement ; Pause conserve les tâches en cours d'exécution et empêche seulement les nouveaux placements ; Drain déplace toutes les tâches hors du nœud. Active et Pause s'appliquent aussitôt ; Drain passe par une confirmation qui indique combien de tâches en cours d'exécution le swarm va arrêter et replanifier ailleurs (les services dont les tâches ne peuvent être placées nulle part ailleurs deviennent unschedulable). Comme un changement de labels, c'est un seul NodeUpdate (lecture-modification-écriture sur la spec du nœud) et cela s'applique immédiatement — les nœuds n'ont pas de mise à jour progressive. Remappable via node_availability |
| P | récupère de l'espace disque occupé par les images sur le nœud (P majuscule, c.-à-d. Shift+p — le même geste que celui de l'onglet Volumes pour son prune). Ouvre un petit menu avec les deux modes décrits ci-dessous, chacun derrière sa propre confirmation ; mieux vaut lire lequel est lequel avant de choisir. Le pied de page se termine par P reclaim images et la surcouche ? l'annonce comme reclaim image disk space on the node. Remappable via node_prune_images |
Ressources réservées. Les détails du nœud comportent un bloc
reserved by tasks (scheduler's view) : pour le CPU et la
mémoire, une barre plus réservé / capacité et ce qui
reste libre. La barre est verte, passe au jaune à partir de 75 % et au rouge à
100 % ou au-delà ; le surengagement est signalé explicitement, et « libre » ne
devient jamais négatif. Un nœud qui n'annonce aucune capacité le dit au lieu de
dessiner une barre. Le bloc ne coûte aucun appel d'API supplémentaire — la liste des
nœuds récupère déjà la liste des tâches.
Reservations déclarées par les tâches — exactement le calcul
que fait l'ordonnanceur du swarm lui-même lorsqu'il décide si une tâche tient sur un
nœud, et la raison habituelle pour laquelle un service reste unschedulable. La
consommation réelle de CPU/RAM est locale au nœud et n'est pas ce qui est
affiché ici : elle a son propre bloc, juste en dessous. Les tâches qui ne déclarent
aucune réservation sont
comptées et signalées séparément : elles ne réservent rien ici mais
peuvent tout de même consommer le nœud entier, donc un faible pourcentage ne
signifie pas que le nœud est inactif — et dans la plupart des clusters, peu de
services définissent des réservations. Une tâche conserve sa réservation depuis son
affectation jusqu'à ce qu'elle atteigne un état terminal : les tâches en preparing ou
starting comptent donc, tandis que shutdown / failed / complete / rejected ne
comptent pas (le swarm les garde dans la liste des tâches à titre d'historique).
Utilisé par les conteneurs. En dessous vient un second bloc — in use by containers (measured on the node) — dessiné avec les mêmes barres proportionnelles : ce que les conteneurs du nœud consomment réellement à cet instant, le CPU en cœurs et la mémoire, chacun face à la capacité du nœud. Les deux blocs sont délibérément séparés, car ils répondent à des questions différentes et sont régulièrement très éloignés l'un de l'autre : un nœud peut être entièrement réservé et inactif, ou à peine réservé et en feu. Les chiffres viennent de l'agent de ce nœud, la même source que les marqueurs d'utilisation de l'arbre des services — tant que le delta CPU n'est pas prêt, la ligne affiche measuring…, et un nœud dont l'agent est injoignable ou trop ancien n'obtient aucun bloc plutôt qu'une rangée de zéros qui se lirait comme « inactif ».
Images sur ce nœud. En dernier, les détails portent un bloc images on this node : ce que pèse le magasin de couches du nœud et en combien d'images, puis ce qui en est récupérable en restes non étiquetés (un nœud qui n'a rien de non étiqueté le dit, plutôt que de proposer un zéro). Lorsqu'il y en a, une ligne supplémentaire indique combien d'espace est en outre étiqueté mais inutilisé — formulé comme un coût, l'enlever oblige à retélécharger ces images, et non comme une offre. Ce bloc existe parce que les images sont la seule ressource locale au nœud que le côté cluster ne voit pas du tout : l'API du manager n'a aucune vue sur les images, contrairement aux volumes, dont elle sait au moins qu'ils sont montés. Les volumes se géraient déjà depuis swarmexec à l'échelle du cluster ; un nœud dont le disque se remplissait silencieusement de vieilles couches n'était même pas visible. Le bloc se charge de son côté, en parallèle de la liste des nœuds, et apparaît donc un instant après le reste de la surcouche.
Récupérer l'espace — P sur l'onglet Nodes. Le menu ouvert comporte deux entrées, délibérément pas une action avec une case à cocher, car ce sont deux actes différents et une case invite à tomber par accident sur le destructeur :
| Entrée | Ce qu'elle supprime |
|---|---|
| Untagged leftovers | les couches laissées derrière eux par les rebuilds, dont rien ne peut démarrer. Sans risque : cela ne peut ni arrêter un service ni forcer un téléchargement |
| Every unused image | supprime en plus les images étiquetées qu'aucun conteneur n'exécute à cet instant. Sur un nœud swarm, cela comprend tout service actuellement mis à zéro réplica et toute tâche entre deux redémarrages : chacune devra retélécharger son image |
Deux modes, deux autorisations. L'agent les autorise comme deux
actions distinctes — image.prune pour les restes non étiquetés et
image.prune.all pour le balayage — de sorte qu'une politique peut
autoriser la récupération des couches non étiquetées sans autoriser la
destructrice. Chaque prune est inscrit dans le journal d'audit de
l'agent avec le mode utilisé, l'espace récupéré et le nombre d'images parties.
docker system df tire ses rapports, et coïncident exactement avec lui —
vérifié sur un nœud réel à 56 images, 32,70 Go sur le disque, 22,99 Go
récupérables. En particulier, swarmexec n'additionne pas les
tailles des images prises une à une : la taille d'une image inclut toutes les
couches dont elle est bâtie, et les couches sont partagées — cette somme surestime
donc largement ; pendant le développement, elle annonçait 32,7 Go là où le
démon disait 22,2 Go. Une réserve qu'il faut énoncer : le chiffre
non étiquetés seulement est une borne inférieure — une
couche partagée par deux images non étiquetées n'appartient à la taille propre
d'aucune des deux — si bien qu'un prune des non étiquetées peut libérer un peu
plus qu'annoncé, jamais moins.
Même prérequis que les chiffres d'utilisation et de santé ci-dessus : il faut des
agents de cette version ou plus récents. Un nœud dont l'agent est
plus ancien n'affiche tout simplement aucun bloc images, et signale qu'il n'a pas
répondu si vous appuyez sur P — déployez-les avec
swarmexec init --force.
Onglet Configs
Onglet 8 — le jumeau de l'onglet Secrets pour l'autre objet que le
swarm distribue aux conteneurs, avec une différence décisive : le contenu d'un
config est lisible. L'onglet Secrets ne peut jamais montrer une valeur (l'API
Docker ne la renvoie pas) ; le détail d'un config affiche le contenu
réel, si bien que vous voyez ce qu'un service reçoit vraiment sans passer par
docker config inspect. C'est tout l'intérêt de cet onglet.
La liste est en lecture seule : CONFIG (nom), USED BY
(combien de services le montent), SIZE, AGE,
UPDATED et LABELS (nombre).
| Touche | Action |
|---|---|
| Enter / i | ouvre la vue de détail : nom, id, taille, création, mise à jour, les services qui le montent, ses labels — et le contenu du config |
| j k / g G | dans la vue de détail : faire défiler · aller au début / à la fin (Esc ferme) |
Le contenu est récupéré à la demande, à l'ouverture du détail — pas
avec la liste — car un config peut être un nginx.conf entier ; en
attendant, la vue affiche loading…, et un config vide indique
(empty). Ce qui est rendu est un aperçu, plafonné
à la fois à 64 Kio et à
500 lignes. Les deux plafonds sont nécessaires : ne borner que les
octets laisse encore un fichier fait de lignes très courtes devenir des dizaines de
milliers de lignes rendues — chacune indentée —, et c'est précisément ce qui fait
ramer la vue. Une troncature est toujours annoncée (« … truncated at
500 lines », ou à la limite d'octets), jamais une coupe silencieuse, et le
contenu binaire est signalé, pas déversé (« (binary content, N — not
shown) », détecté en cherchant des octets NUL en tête des données). Le config complet
est à un docker config inspect de là.
USED BY est dérivé exactement comme dans l'onglet Secrets : le swarm
ne tient aucun index inverse d'un config vers ses consommateurs, donc
swarmexec lit les spécifications des services (un ServiceList) et
reconnaît une référence par l'ID ou le nom du config — une spec peut
porter l'une ou l'autre forme, les deux sont donc consultées et fusionnées. Au mieux :
si la liste des services ne peut pas être lue, les configs s'affichent quand même,
simplement sans cette correspondance.
Shell & logs intégrés
Dans un shell intégré, Ctrl-] détache (sans terminer le processus).
La vue des logs — un seul conteneur ou les logs agrégés d'un service —
prend en charge la même analyse et le même filtrage tenant compte du format
que la commande logs :
| Touche | Action |
|---|---|
| f | bascule le suivi |
| F | fait défiler le format de log (classic → json → logfmt → gelf → raw) |
| l | fait défiler le filtre de niveau minimum (off → trace → … → fatal → off) |
| / | saisit une regexp grep sur le message (vide l'efface) |
| m | bascule la capture de la souris (off = la sélection/copie propre de votre terminal) |
| ↑ ↓ | défile |
| Esc / q | ferme |
La vue rerend les lignes mises en tampon en direct quand vous changez de
format, de niveau ou de grep, et la barre de titre affiche l'état actif
fmt:… lvl:… grep:….
Lors du suivi, la vue des logs se reconnecte au travers du remplacement de
conteneur de la même façon que la commande
logs — à la fois pour les logs d'un seul
conteneur et les logs agrégés d'un service (chaque réplique suivie par
emplacement). Les avis de reconnexion apparaissent en ligne dans la vue des
logs.
Visualiseur de logs
Appuyez sur ` (accent grave) depuis n'importe quel onglet pour basculer une surcouche de logs en direct. Elle affiche les enregistrements les plus récents du tampon circulaire en mémoire — les plus récents en bas — se rafraîchissant tant qu'elle est ouverte et codée par couleur selon le niveau (rouge = error, jaune = warn, gris = debug). ↑/↓ défilent ; Esc, q ou ` la ferment. C'est la fenêtre de la TUI sur les mêmes logs qui vont dans le fichier de log, puisque le terminal lui-même ne peut pas les afficher pendant que l'UI dessine.
Touches personnalisées
Les touches de raccourci sont remappables dans
~/.config/swarmexec/keys.yaml (à côté de
config.yaml ; remplacez le chemin avec
$SWARMEXEC_KEYS). Chaque valeur est une seule touche, ou le mot
space. Les touches structurelles — Enter,
Esc, Tab, les flèches, les touches de numéro d'onglet
1–8 et les alias vim
j/k/g/G — sont fixes. Le pied de
page affiche toujours vos touches réelles, et la surcouche ? les liste
toutes.
Le chargement n'échoue jamais : une touche invalide ou réservée, une action inconnue, ou deux actions liées à la même touche sur un onglet se rabattent chacune sur le défaut, et l'UI affiche les avertissements une fois au démarrage.
11. Codes de sortie
| Code | Signification |
|---|---|
0 | succès |
2 | erreur d'usage / d'option / de configuration, ou une sélection interactive abandonnée |
125 | échec de transport — manager ou agent injoignable, erreur de connexion/TLS, erreur de flux, confirmation abandonnée (reflète le 125 de Docker) |
N | pour exec, le code de sortie non nul propre à la commande distante, propagé tel quel |