1. « Conteneur lancé » ≠ « service opérationnel »
Un conteneur dans docker ps, ce n'est pas encore la preuve que le service fonctionne vraiment. Un conteneur, c'est juste une « boîte » autour d'un processus. Il peut être lancé, et l'application à l'intérieur est encore en train de démarrer, a planté lors de l'initialisation du contexte, écoute sur le mauvais port ou a tout simplement gelé au démarrage. On introduit donc une règle simple : le succès n'est pas docker run, le succès c'est une réponse HTTP + des logs de démarrage clairs.
Fixons un modèle de vérification minimal pour qu'il devienne non pas de la magie ponctuelle, mais un réflexe.
Au niveau du premier lancement, le service est considéré « vivant » quand deux conditions sont remplies. Premièrement, l'endpoint de service répond — c'est un indicateur rapide que l'application s'est levée et qu'au moins l'infrastructure de base de Spring Boot fonctionne. Deuxièmement, l'endpoint applicatif répond aussi — c'est la confirmation que non seulement la couche HTTP s'est levée, mais que ton contrôleur et ton code fonctionnent vraiment, et pas juste un « stub de santé ». Et en parallèle on lit les logs pour comprendre ce qui s'est passé au démarrage, sans avoir à deviner aux symptômes.
Voici à quoi ça ressemble sous forme de petit schéma :
flowchart TD
%% On vérifie d'abord le fait de la réponse HTTP, puis on approfondit les détails
A[Conteneur lancé] --> B{"HTTP répond ?"}
B -->|Non| C[On regarde docker ps + ports + docker logs]
B -->|Oui| D{"Health OK ? /actuator/health"}
D -->|Non| E[On lit les logs : qu'est-ce qui n'a pas démarré]
D -->|Oui| F{"Endpoint métier OK ? /api/catalog/items"}
F -->|Non| G[Logs + vérification de la bonne URL]
%% Si on est arrivés ici — on considère le service vivant pour cette étape
F -->|Oui| H[Le service est considéré vivant pour cette étape]
Remarque une chose psychologique importante : ce schéma est intentionnellement construit pour que tu ne tâtonnes pas avec des commandes aléatoires. D'abord on observe, ensuite on vérifie, ensuite on lit les symptômes.
2. Smoke-check n° 1 : on vérifie /actuator/health
Le premier smoke-check doit être rapide, prévisible et ne pas dépendre de données de démo ou d'une logique métier complexe. On commence donc par l'endpoint Actuator /actuator/health. Dans notre service d'apprentissage, il est déjà présent comme partie de la baseline opérationnelle, et sa tâche est simple : dire « l'application est en vie » dans un format JSON compréhensible. Ce n'est pas une question de beauté, c'est de la diagnostique.
Vérifions d'abord que le conteneur est bien lancé et qu'il a bien le nom avec lequel on l'a démarré : catalog-service.
# On vérifie que le conteneur est vraiment lancé et que le port est mappé vers l'extérieur
docker ps
# CONTAINER ID NAMES STATUS PORTS
# 4c2a... catalog-service Up 15 seconds 0.0.0.0:8080->8080/tcp
Maintenant on fait une requête sur le health. Si tu as curl, on l'utilise :
# Smoke-check : on vérifie qu'Actuator répond et que l'application s'est levée
curl http://localhost:8080/actuator/health
# {"status":"UP"}
Si tu es sous Windows et que curl se comporte de façon inattendue parce que PowerShell substitue parfois son alias, tu peux utiliser Invoke-RestMethod :
# Pour PowerShell, c'est un moyen «natif» pratique de faire une requête HTTP
Invoke-RestMethod http://localhost:8080/actuator/health
# status
# ------
# UP
Le sens de la réponse pour aujourd'hui est très concret. On ne s'intéresse pas à « est-ce que tout est parfait » — ça viendra plus tard. Ce qui nous intéresse : y a-t-il une réponse du tout et y a-t-il UP dedans ?
Élargissons légèrement la vérification : parfois il est utile de voir le statut HTTP immédiatement. Pour ça on peut ajouter le flag -i qui affichera les en-têtes :
# -i affiche le statut et les en-têtes — pratique pour voir 200/401/503 etc. immédiatement
curl -i http://localhost:8080/actuator/health
# HTTP/1.1 200
# Content-Type: application/vnd.spring-boot.actuator.v3+json
#
# {"status":"UP"}
Si tu as reçu 200 et UP, ça signifie que l'application a levé la couche web et que l'infrastructure de base de Spring Boot à l'intérieur du conteneur est en vie. Si tu as reçu Connection refused, ce n'est pas « Actuator est cassé », c'est un signal que ton ordinateur n'a pas pu se connecter au port : soit le conteneur n'est pas lancé, soit le port n'est pas publié, soit l'application n'a pas encore démarré, soit tu vérifies le mauvais port.
Et oui, il y a une situation tout à fait normale au démarrage : le conteneur est déjà lancé, mais l'application est encore en train de charger. Alors curl peut ne pas réussir du premier coup. Ce n'est pas une raison de réinstaller Docker ou de partir en philosophie. Attends quelques secondes et refais la requête. Spring Boot est vif, mais pas télépathe : il lui faut du temps pour déployer le contexte.
3. Smoke-check n° 2 : on vérifie /api/catalog/items
Après le health, on fait une deuxième vérification — l'endpoint applicatif. Dans notre projet, c'est GET /api/catalog/items. Il est bien parce qu'il vérifie plusieurs choses à la fois : le routage fonctionne, le contrôleur a démarré, la couche service ne plante pas au démarrage et la réponse se sérialise en JSON. Pour un premier lancement, c'est largement suffisant : on ne construit pas une pyramide de tests, on fait les premiers secours sur place.
Faisons la requête :
# Smoke-check applicatif : on vérifie que ton contrôleur et la sérialisation de réponse fonctionnent
curl http://localhost:8080/api/catalog/items
# [{"id":1,"sku":"SKU-001","title":"Demo item","price":10.00,"status":"ACTIVE"}]
Le JSON exact et le nombre d'éléments peuvent varier selon comment le starter repo est préparé, mais la logique est la même. Ce qui t'importe, c'est de voir deux choses : la réponse arrive et elle ressemble à du JSON — pas à du HTML, pas à une « whitelabel error page » et pas au silence.
Si tu veux vérifier plus soigneusement et t'habituer à penser en codes de statut, tu peux faire comme ça :
curl -i http://localhost:8080/api/catalog/items
# HTTP/1.1 200
# Content-Type: application/json
#
# [...]
Maintenant décortiquons les variantes typiques de « ce qui a mal tourné ».
Si à la place du JSON tu vois un 404, ça signifie souvent quelque chose de banal : tu as fait une faute dans l'URL ou l'application a démarré mais l'endpoint n'est pas enregistré, par exemple si tu appelles accidentellement /api/catalog/item au lieu de /api/catalog/items. Et là il est important de ne pas se transformer en « mage qui lit le destin dans les 404 ». On ne devine pas — on ouvre les logs et on regarde si le contrôleur s'est bien levé et si l'application est allée jusqu'au bout du démarrage.
Si tu vois un 500, le conteneur et l'application sont probablement en vie, puisqu'une réponse est quand même arrivée. Mais une erreur s'est produite lors du traitement de la requête. À ce stade on n'entre pas profondément dans la correction de la logique métier, mais on doit savoir voir cette erreur dans les logs, sinon Docker va vite devenir pour toi une « boîte noire ».
À cette étape devrait apparaître une sensation utile : le health, c'est « le pouls est là », et /api/catalog/items c'est « le patient peut déjà dire son nom et sa date de naissance ». L'analogie médicale du siècle, non, mais pour un premier lancement elle fait le job.
4. On lit les logs de démarrage
Les logs, c'est ta principale « caméra de surveillance » pour voir ce qui se passe vraiment à l'intérieur du conteneur. Les débutants traitent souvent docker logs comme un outil « seulement quand tout est cassé ». Mais l'approche ingénieur est différente : les logs se lisent à chaque démarrage, même quand tout semble normal. C'est comme se regarder dans le miroir avant de sortir : on peut ne pas regarder, mais on va regretter quand on réalisera que sur la tête on avait « le projet s'est construit mais n'a pas démarré ».
La commande la plus simple, et la plus utile au démarrage, ressemble à ça :
docker logs catalog-service
Mais en pratique tu remarqueras vite : il peut y avoir beaucoup de logs, et le plus souvent c'est justement la « queue » du démarrage qu'il faut. Donc il est pratique de limiter le résultat :
# On prend la «queue» des logs pour voir rapidement le démarrage, une erreur ou la ligne Started ...
docker logs --tail 80 catalog-service
# ... Starting CatalogApplication ...
# ... Tomcat started on port 8080 (http) ...
# ... Started CatalogApplication in 2.4 seconds ...
Qu'est-ce qu'on cherche précisément dans les logs de démarrage ? Pas besoin de lire chaque ligne comme un roman — Spring Boot n'écrit pas toujours de façon narrative. On a besoin de quelques signaux.
Premièrement, dans les logs, il doit être visible que l'application démarre vraiment, et ne se termine pas instantanément. C'est habituellement une ligne du type Starting ....
Deuxièmement, doit apparaître un signal que le serveur web s'est levé et écoute un port. Dans le cas classique c'est quelque chose comme Tomcat started on port 8080 (http).
Troisièmement, on cherche la dernière ligne, ou une proche par le sens, indiquant que l'application a terminé son démarrage. C'est généralement Started ... in ... seconds. C'est l'une des lignes les plus précieuses à ce stade : elle transforme le démarrage de « il semble que ça fonctionne » en « l'application est allée jusqu'au bout de l'initialisation ».
Si tu as besoin d'observer le démarrage en temps réel, tu peux ouvrir un second terminal et activer un « abonnement » aux logs :
# -f = follow, les logs arriveront en streaming (pour arrêter le résultat — Ctrl+C)
docker logs -f catalog-service
Maintenant un point important : les logs et la vérification HTTP doivent coexister dans ta tête. Par exemple, la situation : curl /actuator/health donne Connection refused. Que fais-tu ? Tu ne réécris pas le Dockerfile dans la panique. Tu ouvres docker logs et tu regardes : l'application démarre-t-elle seulement ? Peut-être qu'elle a planté au démarrage. Et si oui, les logs contiendront la raison concrète, et non le sentiment que « l'univers est contre toi ».
Pour rendre ça encore plus mécanique et dépendre moins de l'humeur, tu peux garder chez toi ce mini-ensemble de commandes, littéralement comme une checklist :
docker ps
curl -i http://localhost:8080/actuator/health
curl -i http://localhost:8080/api/catalog/items
docker logs --tail 80 catalog-service
Ce n'est pas un « script », c'est une habitude. Quand cette habitude est là, Docker cesse d'être effrayant : il devient prévisible.
5. On assemble le tableau : symptômes HTTP + logs = diagnostic rapide
Le problème le plus fréquent du débutant dans Docker ressemble à : « Ça ne marche pas ». C'est une mauvaise formulation non pas parce que tu es une mauvaise personne, mais parce qu'elle n'aide pas le cerveau à choisir la prochaine étape. Il faut apprendre à transformer « ça ne marche pas » en tableau observable : ce que montre curl, ce que montre docker ps, ce que montrent les logs.
Pour ce premier conteneur, il y a un rituel utile, qu'il vaut mieux parcourir en entier et non se souvenir par morceaux :
- ./gradlew bootJar — on construit l'artefact.
- java -jar build/libs/docker-java-catalog-service-0.0.1-SNAPSHOT.jar — on vérifie une fois que le jar lui-même fonctionne sans Docker.
- docker build -t docker-java-catalog-service:day3 . — on construit l'image depuis la racine du projet.
- docker run --name catalog-service -p 8080:8080 docker-java-catalog-service:day3 — on lance le conteneur.
- curl http://localhost:8080/actuator/health — on vérifie le signe de vie de base.
- curl http://localhost:8080/api/catalog/items — on vérifie que le chemin applicatif est aussi vivant.
- docker logs --tail 80 catalog-service — on lit le tableau de démarrage, sans deviner sur un seul symptôme.
Si après docker build et docker run le tableau reste flou, ne traite pas tout comme un « pur problème Docker ». Reviens au java -jar local : cette étape sépare rapidement un artefact cassé d'une conteneurisation cassée.
Quand ce minimum se répète sans surprises, le Dockerfile cesse d'être un fichier magique et devient un moyen ordinaire de gérer le lancement.
Voici un petit tableau-antisèche qui aide à ne pas s'affoler. Ce n'est pas une encyclopédie, c'est un « traducteur de symptômes » minimal pour le premier lancement.
| Ce que tu vois dans curl | Ce que ça signifie le plus souvent | Quoi vérifier en premier |
|---|---|---|
| Failed to connect / Connection refused | Personne n'écoute sur le port : conteneur non lancé, port non publié, application n'a pas levé le serveur web | docker ps, section PORTS, puis docker logs --tail 80 |
| HTTP 200 sur /actuator/health, mais 404 sur /api/catalog/items | L'application s'est levée, mais tu appelles le mauvais URL ou l'endpoint est vraiment absent | Vérifier le chemin, puis docker logs pour des erreurs de mapping/démarrage |
| HTTP 500 sur /api/catalog/items | L'endpoint est trouvé, mais le traitement de la requête plante à l'intérieur de l'application | docker logs (la stack trace / la raison s'y trouvera) |
| Reste suspendu sans réponse (timeout) | Le conteneur est en vie, mais l'application peut être gelée au démarrage ou bloquée au traitement | docker logs -f, voir si le démarrage est allé jusqu'à Started ... |
Remarque : presque dans chaque ligne du tableau remontent les logs. C'est normal. Dans le monde conteneurisé, les logs sont la « boîte noire » qui devient soudain transparente parce que tu sais utiliser docker logs.
Encore une technique pratique : quand tu vois un problème, essaie de le formuler via l'une de deux questions. Soit « le processus est-il en vie ? » — c'est visible dans docker ps et les logs, — soit « HTTP répond-il ? » — c'est visible dans curl. Quand tu apprends à répondre honnêtement à ces questions, la moitié des problèmes typiques cesse d'être un mystère.
Quand l'image est déjà construite et le conteneur lancé, une vérification runtime encore plus courte sous forme de texte aide — tu peux même la sauvegarder dans tes notes :
1) Il y a un conteneur dans docker ps ?
2) Il y a un port 8080 publié ?
3) /actuator/health répond-il ?
4) /api/catalog/items répond-il ?
5) Si quelque chose manque — ouvrir docker logs et chercher le moment où tout a mal tourné
Oui, c'est presque une notice de montage de meuble. Mais le meuble, au moins, on peut le monter en silence. Et Spring Boot, si on ne l'écoute pas, commence à « se venger » avec des stack traces.
6. Erreurs classiques lors du smoke-check et de la lecture des logs
Erreur n° 1 : s'arrêter à docker ps et proclamer la victoire.
On en a très envie : le conteneur est dans la liste — donc tout va bien. Mais le conteneur peut être « Up » et l'application à l'intérieur être encore en train de démarrer ou déjà tombée et redémarrée — dans des scénarios plus complexes. Même à cette étape précoce, la discipline est simple : après le lancement du conteneur, on fait au moins une requête HTTP et on regarde les logs de démarrage. Ce n'est pas de la paranoïa, c'est de l'hygiène.
Erreur n° 2 : vérifier seulement /api/catalog/items et ignorer /actuator/health.
Si l'endpoint applicatif ne répond pas, tu tombes immédiatement dans le monde des « trop de raisons possibles ». L'endpoint health est nécessaire comme filtre rapide : l'application s'est-elle seulement levée, ou est-ce que tu frappes dans le vide ? Ça économise des minutes, voire des heures, et aide à ne pas engueuler les contrôleurs quand le problème est en fait dans le démarrage de l'application.
Erreur n° 3 : confondre Connection refused et 404, en appelant les deux « ça ne marche pas ».
Connection refused signifie que la connexion ne s'est pas établie : personne n'écoute sur ce port ou tu te connectes au mauvais endroit. 404 signifie que tu t'es connecté à l'application, mais qu'elle n'a pas trouvé la route. Ce sont deux univers différents. Dans le premier, tu regardes le conteneur, les ports et le démarrage. Dans le second — la correction de l'URL, les mappings et, bien sûr, les logs.
Erreur n° 4 : lire une seule dernière ligne des logs et essayer de tout comprendre à partir d'elle.
Spring Boot sait planter de façon élégante, mais la cause se trouve presque toujours plus haut dans le log. Si l'application n'a pas démarré, dans les logs il y a habituellement un moment « c'est là que tout a mal tourné » : une exception, un message sur un port, une configuration manquante. C'est pourquoi au démarrage il vaut mieux regarder au moins les 50–100 dernières lignes. --tail 80 est un bon minimum. Cherche un bloc cohérent : de Starting jusqu'à Started ou jusqu'à l'erreur explicite.
Erreur n° 5 : chercher les logs par un container ID aléatoire parce qu'on a lancé le conteneur sans nom.
Techniquement ça se vit, mais c'est douloureux. Quand le conteneur a un nom catalog-service, tu écris simplement docker logs catalog-service et tu ne dépenses pas de ressources cérébrales à copier-coller des ID. Dans le développement réel, c'est un détail qui économise des dizaines de micro-stress par jour. Et les micro-stress, comme chacun sait, s'accumulent parfaitement pour produire l'état « pourquoi je suis fatigué alors que je n'ai rien fait ».
GO TO FULL VERSION