El kit del labo — https://github.com/hrhouma2/aiopsatlas-observabilite-labo-fr — clonado en
lab3en la lección 03. Todos los comandos de esta lección se escriben desde esa carpeta. Grafana se describe en el puerto 3000; si elegisteGRAFANA_PORT=3001, reemplaza 3000 por 3001 en todas partes.
Iniciar el labo es girar la llave: el motor (Docker) lanza los diez servicios, los indicadores se encienden uno por uno, y al cabo de un minuto el tablero está vivo. Las nueve interfaces web son los nueve relojes de ese tablero: cada una muestra el mismo labo desde un ángulo distinto. Prometheus es el indicador bruto; Grafana es el parabrisas, la vista cómoda sobre las mismas cifras; Alertmanager y el webhook son los testigos luminosos; la API es la ruta que el automóvil está recorriendo.
demarrer encadena cuatro fases. Descarga las seis imágenes públicas (Prometheus, Alertmanager, Grafana, Alloy, node-exporter, cAdvisor: alrededor de 1,5 GB la primera vez, nada después); construye las cuatro imágenes locales (api, charge, webhook a partir de python:3.13-slim, y loki para agregarle un binario busybox que sirve para la prueba de salud); crea una red Docker (labo-observabilite_labo) y tres volúmenes (grafana-data, prometheus-data, loki-data); y luego inicia los diez contenedores y espera a que cada uno responda a su prueba de salud (healthcheck). Algunos servicios dependen de otros: charge solo arranca una vez que la API está sana, grafana y alloy esperan a Prometheus y Loki.
Los tres volúmenes son la memoria del labo. arreter detiene los contenedores pero conserva los volúmenes: las series de Prometheus, los logs de Loki y los ajustes de Grafana sobreviven, y demarrer retoma donde estabas. reinitialiser elimina los volúmenes: labo nuevo. Documentación: Docker Compose — Volumes y Docker Compose — Healthchecks y depends_on.
| Comando | Lo que hace | Cuándo usarlo |
|---|---|---|
prerequis | Verifica Docker, Compose, la memoria, los procesadores, los nueve puertos | Antes del primer demarrer (lección 03) |
demarrer | Descarga, construye, crea red y volúmenes, inicia los 10 servicios, espera a que estén listos | Al inicio de cada sesión |
etat | Tabla de los contenedores, destinos Prometheus, series, alertas, estado de cada interfaz | Todo el tiempo: es tu primer reflejo |
journal <service> | Las 100 últimas líneas de log de un servicio | Cuando un servicio no responde, o para leer los logs de la API |
casser <scenario> | Provoca una falla: api, erreurs, lenteur, disque | Práctica de este módulo y módulos siguientes |
reparer | Anula todas las fallas | Después de cada casser |
arreter | Detiene los contenedores, conserva los datos | Al final de la sesión |
reinitialiser | Elimina contenedores y volúmenes, previa confirmación | Para empezar de cero |
Las nueve interfaces, y qué se mira en ellas:
| Interfaz | URL | Lo que miras allí |
|---|---|---|
| Prometheus | http://localhost:9090 | La página Query para escribir una consulta, Alerts para las reglas, Status → Target health para los 8 destinos |
| Alertmanager | http://localhost:9093 | Las alertas recibidas, agrupadas por alertname y service; los silencios |
| Grafana | http://localhost:3000 | Los tres tableros de control aprovisionados, Explore, las tres fuentes de datos |
| API de catálogo | http://localhost:8000 | /cours, /cours/C0001, /sante, /metrics: el servicio observado, visto desde afuera |
| webhook | http://localhost:8090 | La lista de las alertas recibidas de Alertmanager, refrescada cada 10 s |
| Loki | http://localhost:3100/ready | Responde ready; Loki no tiene interfaz, se consulta a través de Grafana |
| Alloy | http://localhost:12345 | Su interfaz de diagnóstico: los componentes de config.alloy y su estado |
| node-exporter | http://localhost:9100/metrics | La página bruta de las métricas de la máquina anfitriona |
| cAdvisor | http://localhost:8080 | Las métricas por contenedor; /metrics para la página bruta |
Iniciar. Desde lab3:
.\labo.ps1 demarrer./labo.sh demarrerEn la máquina del curso, en el primer arranque (las líneas de descarga y de construcción están recortadas):
== Téléchargement des images (long la première fois : ~1,5 Go) ==
Image labo-webhook:1.0.0 Skipped Image can be built
Image labo-api:1.0.0 Skipped Image can be built
Image labo-charge:1.0.0 Skipped Image can be built
Image labo-loki:3.7.7 Skipped Image can be built
…
== Construction des images du labo (api, charge, webhook, loki) et démarrage ==
…
Network labo-observabilite_labo Created
Volume labo-observabilite_grafana-data Created
Volume labo-observabilite_prometheus-data Created
Volume labo-observabilite_loki-data Created
…
Container labo-prometheus Healthy
Container labo-api Healthy
Container labo-charge Starting
Container labo-charge Started
Container labo-loki Healthy
Container labo-alloy Started
Container labo-grafana Started
== Attente que chaque service soit prêt ==
prometheus prêt (0 s)
alertmanager prêt (0 s)
loki prêt (0 s)
alloy .. prêt (6 s)
node-exporter prêt (0 s)
cadvisor prêt (0 s)
api prêt (0 s)
webhook prêt (0 s)
charge prêt (0 s)
grafana ... prêt (9 s)
Le labo est prêt.
Grafana http://localhost:3000 (utilisateur admin · mot de passe aiopsatlas2026)
Prometheus http://localhost:9090 (Status → Target health, puis onglet Graph)
Alertmanager http://localhost:9093
API catalogue http://localhost:8000/cours · http://localhost:8000/metrics
Webhook http://localhost:8090 (les alertes reçues)
Loki http://localhost:3100/ready · Alloy http://localhost:12345
node-exporter http://localhost:9100/metrics · cAdvisor http://localhost:8080
Étape suivante : .\labo.ps1 etat (laissez tourner 2 minutes pour avoir des courbes)Lo que hay que ver: los cuatro Skipped Image can be built (las imágenes locales no se descargan, se construyen), los tres volúmenes creados, labo-charge Starting solo después de labo-api Healthy, y luego los diez prêt. En la máquina del curso, demarrer tomó 44 segundos una vez las imágenes en caché. La última línea te dice que esperes dos minutos: Prometheus necesita algunos scrapes para dibujar una curva.
Leer etat.
.\labo.ps1 etat
== Conteneurs ==
NAME SERVICE STATUS
labo-alertmanager alertmanager Up About a minute (healthy)
labo-alloy alloy Up 48 seconds (healthy)
labo-api api Up About a minute (healthy)
labo-cadvisor cadvisor Up About a minute (healthy)
labo-charge charge Up About a minute (healthy)
labo-grafana grafana Up 48 seconds (healthy)
labo-loki loki Up About a minute (healthy)
labo-node-exporter node-exporter Up About a minute (healthy)
labo-prometheus prometheus Up About a minute (healthy)
labo-webhook webhook Up About a minute (healthy)
== Supervision ==
✔ Prometheus répond — cibles up : 8/8
séries en mémoire : 9038
alertes : 0 active(s), 0 en attente (pending)
✔ Alertmanager répond (http://localhost:9093)
✔ Grafana répond (http://localhost:3000)
✔ Loki répond (http://localhost:3100)
✔ API catalogue répond — version 1.0.0, 64 cours
✔ Webhook répond — 0 alerte(s) reçue(s) (http://localhost:8090)
Labo : 10/10 services, 8/8 cibles up, 0 alertes actives.Lo que hay que ver: dos bloques. Conteneurs (contenedores): diez líneas, todas Up … (healthy). Supervision (supervisión): Prometheus lee sus 8 destinos, almacena 9038 series (este número sube durante los primeros minutos, luego se estabiliza hacia 12 000 a 15 000 en la máquina del curso), ninguna alerta; la API conoce 64 cursos; el webhook no recibió nada. La última línea, Labo : 10/10 services, 8/8 cibles up, 0 alertes actives., es la que debes volver a encontrar al final de cada práctica.
Prometheus: Query, Alerts, Status. Abre http://localhost:9090. El menú superior tiene tres entradas: Query, Alerts, Status. En Status, el submenú propone Target health, Rule health, Service discovery, Runtime & build information, TSDB status, Command-line flags, Configuration, Alertmanager discovery. Ve a Target health: ocho bloques, uno por job, cada uno con 1 / 1 up, la URL leída (Endpoint), las etiquetas, la fecha del último scrape y el estado UP. En la máquina del curso:
api
1 / 1 up
Endpoint Labels Last scrape State
http://api:8000/metrics instance="api:8000" job="api" service="api" 6.014s ago UPVuelve a Query, escribe up en el campo de entrada y haz clic en Execute (o Enter). Debajo del campo, tres pestañas: Table, Graph, Explain. En Table, ocho líneas de la forma up{instance="localhost:9090", job="prometheus"} 1, todas en 1. Debajo de las pestañas, una línea del tipo Load time: 40ms Result series: 8 te dice cuántas series respondieron. Pasa a Graph: ocho líneas planas en 1. El módulo 2 pasará el resto del tiempo en esta página.
Grafana: tres tableros de control listos. Abre http://localhost:3000, usuario admin, contraseña aiopsatlas2026. La página de inicio lista Recent dashboards: los tres tableros de control aprovisionados por el kit, en la carpeta Labo observabilite. El menú principal (ícono arriba a la izquierda) da Dashboards, Explore, Alerting, Connections → Data sources. En Connections → Data sources, tres fuentes: Prometheus (por defecto), Loki, Alertmanager. La API de Grafana confirma la misma lista:
uid=api-catalogue titre=« API catalogue — signaux dorés » panneaux=23 dossier=Labo observabilite
uid=hote-conteneurs titre=« Hôte et conteneurs » panneaux=16 dossier=Labo observabilite
uid=journaux-api titre=« Journaux de l'API » panneaux=6 dossier=Labo observabilite
source Prometheus type=prometheus uid=prometheus default=True
source Loki type=loki uid=loki default=False
source Alertmanager type=alertmanager uid=alertmanager default=FalseAbre API catalogue — signaux dorés (API de catálogo — señales de oro): las cuatro señales de oro (tráfico, errores, latencia, saturación) de la API, más dos métricas de negocio (inscripciones, cursos consultados). Las curvas están planas al principio: hacen falta dos minutos de carga. Lo que hay que ver: nada de lo que ves aquí está almacenado en Grafana. Cada panel envía una consulta PromQL a Prometheus en el momento en que miras. Grafana es el parabrisas, no el motor.
Loki se lee en el mismo lugar. Abre Explore en el menú, elige la fuente Loki en el selector de arriba, escribe {service="api"} en el campo de consulta y lanza con Run query: las líneas JSON de la API desfilan, las más recientes arriba, cada una con sus etiquetas. Loki solo conoce cuatro en este labo, obtenidas con http://localhost:3100/loki/api/v1/labels: code, conteneur, niveau, service. service y conteneur vienen de Docker, niveau y code son extraídas del JSON por Alloy. Cambia el selector a Prometheus, escribe up, Run query: las ocho series del paso 3 aparecen, en tabla y en curva. Explore es el lugar donde se prueba una consulta antes de convertirla en un panel (lección 07).
La API de catálogo, desde afuera. Abre las cuatro URL siguientes en el navegador, o con Invoke-RestMethod / curl:
http://localhost:8000/sante → {"etat":"ok","version":"1.0.0","cours":64}
http://localhost:8000/cours/C0001 → {"id":"C0001","titre":"Introduction à Python","categorie":"programmation","niveau":"debutant","prix":89,"duree_heures":6,"professeur":"Karim Haddad","tags":["code","algorithmes"],"note":4.8,"inscrits":2319}
http://localhost:8000/cours/C9999 → 404 {"detail":"cours C9999 introuvable"}
http://localhost:8000/metrics → 271 lignes de texte, les métriques de la leçon 02Lo que hay que ver: /sante es la ruta que Docker llama para el healthcheck; /cours/C0001 es uno de los 64 cursos; /cours/C9999 responde un 404 limpio, que la API cuenta en http_requetes_total{code="404",route="/cours/{id}"} y escribe en un log WARNING; /metrics es lo que Prometheus lee. Cada respuesta lleva un encabezado x-id-requete (por ejemplo c5c49525ea53), el mismo identificador que en el log JSON de esa solicitud.
Alertmanager y el webhook: los testigos, apagados. Abre http://localhost:9093: el menú propone Alerts, Silences, Status, Settings, y un botón New Silence. En funcionamiento normal, la página Alerts está vacía. Abre http://localhost:8090: una página «Alertes reçues d'Alertmanager» (alertas recibidas de Alertmanager) con una tabla de seis columnas (Reçue à, Alerte, Sévérité, État, Service, Résumé) y la línea Aucune alerte reçue pour l'instant.; la página se refresca cada 10 segundos. El formato bruto está en http://localhost:8090/alertes.json ([]) y el estado del servicio en http://localhost:8090/sante:
{"etat":"ok","alertes_en_memoire":0,"notifications":0,"alertes":0}Lo que hay que ver: dos páginas vacías, y eso es lo que se quiere. La práctica de este módulo las llena con casser api.
Loki, Alloy, node-exporter, cAdvisor: detrás de escena. Cuatro páginas sin interfaz cómoda:
http://localhost:3100/ready → ready
http://localhost:12345/-/ready → Alloy is ready.
http://localhost:9100/metrics → 1578 lignes (node_cpu_seconds_total, node_memory_MemTotal_bytes…)
http://localhost:8080/metrics → 3444 lignes (container_memory_working_set_bytes{name="labo-api"}…)Lo que hay que ver: Loki y Alloy responden con una palabra; trabajan para Grafana, no para ti directamente. Los dos exporters exponen la misma forma de página que la API (# HELP, # TYPE, una línea por serie), con muchas más líneas. Abre http://localhost:8080 sin /metrics: cAdvisor redirige a /containers/, una pequeña página que muestra la máquina completa (Usage Overview, CPU Total Usage, Memory Total Usage, Network Throughput) y un enlace Docker Containers hacia el detalle de cada contenedor.
Leer un log.
.\labo.ps1 journal apilabo-api | {"horodatage": "2026-09-15T19:33:25.839+00:00", "niveau": "INFO", "id_requete": "46bb533b33e9", "methode": "GET", "route": "/cours/{id}", "code": 200, "duree_ms": 13.9, "message": "GET /cours/C0038 -> 200"}
labo-api | {"horodatage": "2026-09-15T19:33:25.843+00:00", "niveau": "INFO", "id_requete": "5c0d4e124807", "methode": "GET", "route": "/cours", "code": 200, "duree_ms": 30.4, "message": "GET /cours -> 200"}
labo-api | {"horodatage": "2026-09-15T19:33:25.916+00:00", "niveau": "INFO", "id_requete": "5ccaee4d1a2a", "methode": "GET", "route": "/sante", "code": 200, "duree_ms": 1.0, "message": "GET /sante -> 200"}Luego journal charge:
labo-charge | {"horodatage": "2026-09-15T19:33:22.848+00:00", "niveau": "INFO", "message": "résumé des 30 dernières secondes", "requetes": {"201": 28, "total": 264, "200": 210, "404": 21, "422": 2, "500": 3}}Lo que hay que ver: la API escribe una línea por solicitud; el generador de carga escribe un resumen cada 30 segundos, alrededor de 260 solicitudes (casi 9 por segundo), de las cuales unos veinte 404 y algunos 500 deliberados. Esas cifras son las que volverás a encontrar en Prometheus con rate() en el módulo 2.
Detener, y luego reiniciar sin perder nada.
.\labo.ps1 arreter Container labo-webhook Stopping
Container labo-alertmanager Stopping
…
Container labo-api Stopped
Container labo-prometheus Stopped
✔ conteneurs arrêtés — vos données sont conservées ; .\labo.ps1 demarrer pour reprendredocker compose ps -a muestra entonces diez líneas Exited; etat termina con Labo : 0/10 services, ?/? cibles up, ? alertes actives. (los ? porque Prometheus ya no responde). Relanza .\labo.ps1 demarrer: esta vez, sin descarga, sin construcción, unos treinta segundos. En Prometheus, escribe up y pasa a Graph con un rango de una hora: la curva se interrumpe durante la detención y se reanuda, con el historial anterior intacto. Es el volumen prometheus-data el que conservó las series.
demarrer se detiene en un servicio. El mensaje es explícito: Un service n'a pas démarré. Regardez son journal : .\labo.ps1 journal <service>. Hazlo. El caso más frecuente es un puerto ocupado entre prerequis y demarrer (otro programa lanzado entretanto): Docker se niega entonces a publicar el puerto, y el log del contenedor está vacío porque nunca arrancó. Relanza prerequis.
journal sin nombre de servicio. .\labo.ps1 journal solo muestra:
usage : .\labo.ps1 journal <prometheus|alertmanager|loki|alloy|node-exporter|cadvisor|api|webhook|charge|grafana>Los diez nombres válidos están ahí; son los nombres de los servicios en docker-compose.yml, no los de los contenedores (api, no labo-api). Del mismo modo, .\labo.ps1 casser tout responde usage : .\labo.ps1 casser <api|erreurs|lenteur|disque>.
Grafana responde Unauthorized. Si llamas a la API de Grafana sin estar conectado, por ejemplo http://localhost:3000/api/search en una pestaña privada, obtienes:
{"extra":null,"message":"Unauthorized","messageId":"auth.unauthorized","statusCode":401,"traceID":""}Es normal: las páginas de API piden una sesión o una contraseña. Conéctate primero en http://localhost:3000 (admin / aiopsatlas2026), luego recarga.
Los tableros de control Grafana muestran «No data». Dos causas, en este orden: el labo lleva menos de dos minutos corriendo (espera); o el rango de tiempo arriba a la derecha apunta a un período en que el labo estaba detenido (vuelve a poner «Last 15 minutes» o «Last 1 hour»).
etat muestra Labo : 0/10 services, ?/? cibles up, ? alertes actives. El labo está detenido. .\labo.ps1 demarrer.
En PowerShell, demarrer muestra líneas rojas NativeCommandError alrededor de Container labo-api Stopping. Son los mensajes de progreso de Docker Compose, escritos en la salida de error; cuando labo.ps1 se lanza a su vez desde otro script o con 2>&1, PowerShell 5.1 los viste de error. El labo arrancó correctamente: verifica con etat.
demarrer descarga seis imágenes, construye cuatro imágenes locales, crea una red y tres volúmenes, luego lanza diez contenedores y espera sus pruebas de salud; 44 segundos en la máquina del curso una vez las imágenes en caché. etat tiene dos bloques: los contenedores (diez Up … (healthy)) y la supervisión (8/8 destinos, el número de series, las alertas, el estado de cada interfaz); su última línea nominal es Labo : 10/10 services, 8/8 cibles up, 0 alertes actives.. Prometheus (9090) tiene tres páginas: Query con sus pestañas Table, Graph, Explain; Alerts; Status → Target health con los 8 destinos UP. Grafana (3000, admin / aiopsatlas2026) tiene tres tableros de control aprovisionados en la carpeta Labo observabilite y tres fuentes de datos, Prometheus por defecto; no almacena ninguna medida por sí mismo. La API (8000) responde en /sante, /cours/C0001, /metrics, y un 404 limpio para un curso desconocido, con un encabezado x-id-requete en cada respuesta. Alertmanager (9093) y el webhook (8090) están vacíos en funcionamiento normal. journal <service> toma el nombre del servicio, no del contenedor. arreter conserva los volúmenes: las series sobreviven, la curva up se interrumpe y luego se reanuda.
grafana/provisioning/. Por eso la carpeta Labo observabilite está marcada «managed by classic file provisioning» en la API.docker compose ps, logs, stop: los comandos que labo.ps1 y labo.sh encadenan por ti; journal api es docker compose logs --tail 100 api.etat, en Target health, en Alertmanager y en el webhook, antes de reparar.