Aller au contenu
Outils medium

Maintenant : superviser Docker sans exposer le socket

15 min de lecture

logo Maintenant

Vous faites tourner une pile Docker et vous voulez savoir en un coup d'oeil ce qui est en vie, sans installer une usine à gaz ? Ce guide déploie Maintenant, un moniteur Docker et Kubernetes en un seul binaire Go, puis le branche sur votre hôte sans jamais monter le socket Docker dans le conteneur. Vous obtiendrez la découverte automatique des conteneurs, une sonde HTTP déclarée en label, une page de statut, et surtout une installation durcie qui ne transforme pas votre supervision en porte d'entrée. Public visé : administrateurs et DevOps à l'aise avec Docker Compose.

  • Déployer Maintenant avec Docker Compose derrière un socket proxy
  • Vérifier que la découverte des conteneurs fonctionne
  • Surveiller un service HTTP avec une sonde déclarée en label
  • Suivre une tâche planifiée avec un heartbeat
  • Publier une page de statut
  • Sécuriser l'accès, sachant que l'outil n'a pas d'authentification interne

Maintenant est un moniteur self-hosted écrit par Benjamin Touchard (kOlapsis). Il tient dans un binaire Go unique avec son interface Vue embarquée, stocke tout dans SQLite et consomme une vingtaine de méga-octets de mémoire. Son principe directeur le distingue des moniteurs classiques : au lieu de déclarer chaque cible à la main, il découvre automatiquement les conteneurs Docker ou les charges Kubernetes, et lit ses sondes dans les labels. La supervision suit donc le cycle de vie des services au lieu de vivre à côté.

Il couvre les besoins courants d'un petit parc : disponibilité HTTP et TCP, heartbeats pour les tâches planifiées, expiration des certificats TLS, métriques CPU et mémoire, et une page de statut publique. C'est un projet open-core sous licence AGPL-3.0 : l'édition Community est complète pour un usage mono-hôte, l'édition Pro ajoute le multi-hôte et l'escalade d'alertes.

Les deux outils publient une page de statut et surveillent des services, mais ils ne visent pas le même usage. Le tableau suivant aide à choisir sans se tromper.

CritèreMaintenantUptime Kuma
Déclaration des sondesLabels Docker, automatiqueÀ la main dans l'interface
Cible privilégiéeDocker, KubernetesTout service (HTTP, TCP, DNS, VPS)
Découverte des conteneursOui, nativeNon
Canaux d'alerteWebhook natif, reste en ProPlus de 90, tous gratuits
MaturitéJeune, un mainteneurÉtabli, large communauté

En pratique : si votre parc est surtout conteneurisé, Maintenant réclame beaucoup moins de configuration. Pour un mélange de conteneurs, de VPS et d'API externes, Uptime Kuma reste plus polyvalent. Les deux peuvent cohabiter.

  • Un hôte Linux avec Docker Engine et le plugin Compose, voir installer Docker
  • Un accès sudo sur cet hôte
  • De quoi joindre l'interface en local (port sur la loopback), ou un reverse proxy Traefik devant si vous l'exposez

Déployer Maintenant sans exposer le socket Docker

Section intitulée « Déployer Maintenant sans exposer le socket Docker »

Pour découvrir les conteneurs, Maintenant a besoin de lire l'API Docker. L'installation naïve consiste à lui monter /var/run/docker.sock. C'est une mauvaise idée, même en lecture seule : parler au socket équivaut à être root sur l'hôte, et le drapeau :ro ne protège que le fichier, pas l'API. Nous plaçons donc un docker-socket-proxy entre les deux, qui rejette toute écriture avec un code 403. Maintenant respecte la variable DOCKER_HOST, ce montage n'est donc pas nécessaire.

  1. Créer le fichier compose.yaml avec le proxy en amont et Maintenant qui pointe dessus. Les images sont épinglées par empreinte.

    services:
    # Composant root-equivalent : il porte le vrai socket, reste sur un
    # réseau interne et ne publie aucun port sur l'hôte.
    socketproxy:
    image: tecnativa/docker-socket-proxy@sha256:1f3a6f303320723d199d2316a3e82b2e2685d86c275d5e3deeaf182573b47476 # v0.4.2
    environment:
    CONTAINERS: 1 # découverte, inspect, stats, logs
    INFO: 1 # détection du runtime
    NETWORKS: 1 # métadonnées réseau
    # EVENTS, PING et VERSION sont déjà actifs par défaut.
    # POST reste à 0 : toute écriture renvoie 403.
    volumes:
    - /var/run/docker.sock:/var/run/docker.sock:ro
    networks: [dockerapi]
    read_only: true
    tmpfs: [/run, /tmp] # le proxy écrit sa conf haproxy dans /tmp
    security_opt:
    - no-new-privileges:true
    restart: unless-stopped
    maintenant:
    image: ghcr.io/kolapsis/maintenant@sha256:0f524deeef333735f34b59fa29013a3f876d09721ecbccfcbc1a143f29d9a627 # 1.3.5
    environment:
    DOCKER_HOST: tcp://socketproxy:2375 # parle au proxy, jamais au vrai socket
    MAINTENANT_ADDR: "0.0.0.0:8080"
    MAINTENANT_DB: /data/maintenant.db
    MAINTENANT_DISABLE_TELEMETRY: "1"
    volumes:
    - /proc:/host/proc:ro
    - maintenant-data:/data
    ports:
    - "127.0.0.1:8899:8080" # loopback uniquement, voir la section Sécurité
    networks: [dockerapi, web]
    depends_on: [socketproxy]
    read_only: true
    security_opt:
    - no-new-privileges:true
    tmpfs:
    - /tmp:noexec,nosuid,size=64m
    restart: unless-stopped
    networks:
    dockerapi:
    internal: true # le proxy n'est joignable que depuis ce réseau
    web: {}
    volumes:
    maintenant-data: {}
  2. Démarrer la pile.

    Fenêtre de terminal
    docker compose up -d
  3. Attendre que Maintenant se connecte au proxy. Au premier démarrage il peut afficher un mode dégradé le temps que le proxy réponde, puis se reconnecte seul.

    Fenêtre de terminal
    curl -s http://127.0.0.1:8899/api/v1/health

    La sortie doit indiquer le runtime connecté et confirmer la version :

    {"runtime":{"connected":true,"name":"docker"},"status":"ok","version":"1.3.5"}

Une installation qui démarre n'est pas une installation qui voit vos conteneurs. La commande suivante confirme que la découverte a bien traversé le proxy.

Fenêtre de terminal
curl -s http://127.0.0.1:8899/api/v1/containers | \
python3 -c "import sys,json;print(json.load(sys.stdin)['total'], 'conteneurs découverts')"

Le nombre renvoyé doit correspondre aux conteneurs actifs de l'hôte. Ouvrez ensuite http://127.0.0.1:8899 dans un navigateur : le tableau de bord liste les conteneurs, leur état, et remonte déjà des constats de sécurité (ports exposés, conteneurs privilégiés) sans aucune configuration.

Le trait qui distingue Maintenant : une sonde n'est pas un formulaire à remplir, c'est un label posé sur le conteneur à surveiller. Elle vit et meurt avec le service. Ajoutez ces labels sur un conteneur applicatif, par exemple un serveur web nommé demo-web :

services:
demo-web:
image: nginx@sha256:5616878291a2eed594aee8db4dade5878cf7edcb475e59193904b198d9b830de
networks: [web]
labels:
maintenant.endpoint.http: "http://demo-web:80/"
maintenant.endpoint.interval: "10s"
maintenant.endpoint.failure-threshold: "2"

Après un docker compose up -d, la sonde apparaît sans autre geste :

Fenêtre de terminal
curl -s http://127.0.0.1:8899/api/v1/endpoints | \
python3 -c "import sys,json
for e in json.load(sys.stdin)['endpoints']:
print(e['container_name'], e['status'], e['last_http_status'])"

Une sonde saine affiche up et 200. Pointez volontairement une URL inexistante (/health sur un nginx nu) pour observer la bascule en down après le nombre d'échecs configuré : c'est la meilleure façon de vérifier que l'alerte se déclenchera le jour venu.

Un heartbeat surveille l'inverse d'un service web : non pas « répond-il ? » mais « s'est-il exécuté à l'heure ? ». Idéal pour une sauvegarde nocturne ou un cron. On crée le moniteur, on récupère une URL, et la tâche la contacte à la fin de son travail.

Fenêtre de terminal
curl -s -X POST http://127.0.0.1:8899/api/v1/heartbeats \
-H 'Content-Type: application/json' \
-d '{"name":"sauvegarde-nuit","interval_seconds":3600,"grace_seconds":300}'

La réponse contient un identifiant. La tâche planifiée ajoute alors un simple appel en fin d'exécution :

Fenêtre de terminal
# à la fin du script de sauvegarde
curl -fsS -o /dev/null "http://127.0.0.1:8899/ping/<identifiant>"

Tant que le ping arrive dans la fenêtre (intervalle plus délai de grâce), la sonde reste au vert. Passé ce délai sans signe de vie, Maintenant lève une alerte deadline_missed. L'URL de ping vaut mot de passe : traitez l'identifiant comme un secret et ne le commitez jamais.

Maintenant expose une page de statut publique à l'adresse /status, alimentée par les sondes déjà en place. Elle est prévue pour être accessible sans authentification, contrairement au tableau de bord. Vérifiez qu'elle répond :

Fenêtre de terminal
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8899/status/api

Un code 200 confirme que l'API publique de la page fonctionne. La composition des composants affichés et le nom de l'organisation se règlent dans le tableau de bord, section statut.

C'est le point le plus important de ce guide, et il mérite d'être dit sans détour. Maintenant n'a pas d'authentification interne, par conception. Comme Prometheus ou Dozzle, il délègue cette responsabilité à un reverse proxy. La conséquence est directe : quiconque atteint le port de l'interface a un accès complet à l'API d'administration, peut lire les journaux des conteneurs (donc d'éventuels secrets qui s'y trouvent) et créer des webhooks. C'est pourquoi le compose.yaml de ce guide publie le port sur 127.0.0.1 uniquement.

Trois mesures forment le socle d'un déploiement sain :

  • Ne jamais publier le port sur toutes les interfaces. Gardez 127.0.0.1:8899:8080 en local, ou une IP de LAN précise, jamais 8899:8080 seul. Maintenant lui-même signale d'ailleurs un port exposé comme un risque critique, y compris sur son propre conteneur.
  • Mettre un reverse proxy authentifié devant pour tout accès distant : Traefik couplé à Authelia, par exemple. Les routes /api/v1/ et / exigent l'authentification ; seules /ping, /status et le manifeste restent publiques.
  • Filtrer l'API Docker par un socket proxy, comme fait plus haut. Le détail de ce mécanisme et la démonstration de pourquoi :ro ne suffit pas sont dans le guide dédié.

Enfin, la télémétrie est active par défaut et envoie chaque heure des compteurs anonymes vers metrics.kolapsis.com. Elle est minimale et sans identifiant d'hôte, mais pour un usage orienté souveraineté, le MAINTENANT_DISABLE_TELEMETRY: "1" de notre compose la coupe dès le départ.

Maintenant est sous licence AGPL-3.0, une licence libre reconnue par l'OSI qui impose de publier les modifications même en usage réseau. Le modèle est open-core : l'édition Community couvre la découverte, les sondes, les heartbeats, les certificats et la page de statut ; l'édition Pro (29 euros par mois) débloque le multi-hôte, l'escalade d'alertes, les canaux Slack/Teams et l'enrichissement CVE. Pour un mono-hôte, la version gratuite est autonome. Le multi-hôte, lui, est justement le besoin d'un prestataire qui gère plusieurs clients : c'est un arbitrage à intégrer avant d'adopter l'outil à grande échelle.

SymptômeCause probableSolution
health renvoie "connected":falseLe proxy n'est pas encore joignableAttendre la reconnexion, vérifier docker compose logs socketproxy
Le proxy boucle en Restartingread_only avec une version 0.3.0Épingler l'empreinte de la v0.4.2 indiquée
Aucun conteneur découvertDOCKER_HOST absent ou mauvais réseauVérifier que Maintenant est sur le réseau dockerapi
Une sonde reste down sans raisonURL du label injoignable depuis le conteneurTester l'URL, vérifier que les deux services partagent un réseau
Interface accessible depuis le LAN sans mot de passePort publié trop largementRepasser en 127.0.0.1:8899:8080 et ajouter un reverse proxy

Trois points méritent d'être connus avant d'adopter l'outil. D'abord, le bus-factor : le projet repose sur un seul mainteneur et reste jeune, ce qui est un risque pour une brique de supervision destinée à durer. Ensuite, l'absence de tag de version sur le registre d'images oblige à épingler par empreinte, ce que fait ce guide. Enfin, quelques valeurs par défaut documentées ne correspondent pas au comportement observé sur les sondes : fiez- vous à ce que renvoie l'API plutôt qu'à la table de documentation.

  • Maintenant est un moniteur Docker/Kubernetes en un binaire Go, avec découverte automatique et sondes en labels.
  • Ne jamais monter le socket Docker dans le conteneur : un socket proxy filtre l'API en lecture seule, et DOCKER_HOST suffit.
  • Les sondes HTTP se déclarent en labels, pas dans l'interface, et suivent le cycle de vie du conteneur.
  • Les heartbeats surveillent les tâches planifiées via une URL de ping à traiter comme un secret.
  • Aucune authentification interne : garder le port en loopback et mettre un reverse proxy authentifié devant tout accès distant.
  • AGPL-3.0, open-core : le mono-hôte est gratuit, le multi-hôte est en édition Pro. Couper la télémétrie pour un usage souverain.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn