Observabilité¶
Cette page ne couvre que la supervision : collecte des métriques, collecte des journaux,
tableaux de bord, accès aux interfaces d'exploitation. Les alertes sont traitées à la page
Exploitation : sur l'instance de référence, trente-quatre règles et le routage Alertmanager
vivent dans observability/prometheus-values.yaml, et les courriels partent par le SMTP du
client. Cette page ne couvre pas non plus la recette de conformité après installation, qui vit à
la page Validation.
Deux piles de supervision, une seule à déployer¶
C'est le piège principal pour un installateur tiers.
| Stack | Origine | Rôle dans une installation neuve |
|---|---|---|
monitoring |
charts Helm, appliqués à la main, hors du kustomize racine | c'est celle que l'on déploie et que l'on consomme |
observability |
manifests observability/*.yaml, tirés par le kustomize racine |
héritage. Un installateur ne la déploie pas |
Un opérateur qui applique kubectl kustomize . (l'étape 3 du Pas-à-pas)
obtient la seconde stack, dans le namespace observability : un Prometheus, un Grafana et un
Kibana qui démarrent sur leur configuration par défaut, sans ConfigMap, sans argument
--config.file, sans datasource provisionnée. Elle tourne, elle répond, et elle ne reçoit
aucune métrique ni aucun journal de la plateforme. Un opérateur qui ouvre ce Grafana sans
connaitre cette page conclut à tort que la supervision est en panne, alors qu'il regarde le
mauvais Grafana : celui qui porte réellement les données, les deux tableaux de bord versionnés
et l'authentification prévue est celui de la stack monitoring.
Décision pour toute installation cliente : déployer et exploiter uniquement la stack
monitoring. La stack observability posée par le kustomize racine peut être supprimée sans
risque une fois l'installation terminée (kubectl delete namespace observability) ou laissée
inerte ; dans les deux cas, ne jamais s'y fier et ne jamais y importer les tableaux de bord de
cette page. Kibana relève du même arbitrage : son Deployment ne porte aucune variable
ELASTICSEARCH_HOSTS, donc rien ne le raccorde à Elasticsearch. Le chemin de journaux
réellement utilisé passe par Alloy et Loki, décrit plus bas.
Installer les quatre releases Helm de la stack monitoring
Aucune de ces releases ne vient du kustomize racine. Chacune a son fichier de valeurs dans
observability/, complet et daté ; helm upgrade remplace le jeu de valeurs fourni, donc
ces fichiers sont la source de vérité, pas helm get values. Alertmanager n'est pas une
cinquième release : c'est un sous-chart de Prometheus, et le Service
prometheus-alertmanager vient de là.
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo add grafana https://grafana.github.io/helm-charts
helm repo update
kubectl apply -f observability/alertmanager-smtp.yaml
helm upgrade --install prometheus prometheus-community/prometheus -n monitoring --create-namespace \
--version 29.14.0 -f observability/prometheus-values.yaml
helm upgrade --install grafana grafana/grafana -n monitoring \
--version 10.5.15 -f observability/grafana-values.yaml
helm upgrade --install loki grafana/loki -n monitoring \
--version 7.0.0 -f observability/loki-values.yaml
helm upgrade --install alloy grafana/alloy -n monitoring \
--version 1.10.0 -f observability/alloy-values.yaml
kubectl apply -f observability/scrape/services-metriques.yaml
| Release | Chart | Ce que le fichier de valeurs porte |
|---|---|---|
prometheus |
prometheus-community/prometheus 29.14.0 |
les trente-quatre règles d'alerte, le routage Alertmanager, node-exporter du chart désactivé |
grafana |
grafana/grafana 10.5.15 |
source de données, provisionnement des tableaux de bord |
loki |
grafana/loki 7.0.0 |
mode SingleBinary, bornes memcached, labels |
alloy |
grafana/alloy 1.10.0 |
collecte des journaux vers Loki |
observability/alertmanager-smtp.yaml vient AVANT la release prometheus, et pas par
habitude : le fichier de valeurs monte le Secret alertmanager-smtp dans le pod Alertmanager,
qui y lit son mot de passe SMTP (smtp_auth_password_file). Si le Secret n'existe pas, le pod
reste en ContainerCreating sur un volume introuvable, et aucune alerte ne part. Le chemin
Vault est prod/alertmanager, clé smtp_password, écrit à l'étape 11 du pas-à-pas ; le
serveur, l'expéditeur et le destinataire sont dans le fichier de valeurs, en clair, car rien
n'y est secret. Le serveur est TV_SMTP_HOST, que render-config.sh substitue là aussi.
L'identifiant (smtp_auth_username) et l'expéditeur (smtp_from) restent ceux de l'instance
de référence : les remplacer à la main par ceux du client avant le helm upgrade,
l'expéditeur sur le domaine que le relais a vérifié. Le destinataire (to: sous
default-receiver) est une boîte de l'instance de référence dont render-config.sh ne
réécrit que le domaine : y mettre ${TV_ACME_EMAIL} à la main, dans le même passage.
Vérifier le chemin complet une fois la release posée,
sans attendre une vraie panne :
kubectl -n monitoring exec sts/prometheus-alertmanager -- \
amtool alert add TestCourriel severity=warning --alertmanager.url=http://localhost:9093
[FIRING:1] TestCourriel arrive sous une minute (group_wait: 30s). S'il
n'arrive pas, le journal du même pod porte la cause, notify retry avec le code SMTP, et
rien d'autre ne la portera. L'alerte de test expire d'elle-même au bout de cinq minutes.
observability/scrape/services-metriques.yaml ajoute les cibles que Prometheus ne découvre
pas seul : external-secrets, Longhorn, la base Postgres de Keycloak, cloudflared et Vault. Sans lui, les
groupes secrets, stockage, coffre, base-keycloak et exposition des règles n'ont aucune série à lire
et se taisent.
Composants et versions¶
| Composant | Espace de noms | Manifeste ou chart | Rôle |
|---|---|---|---|
| Prometheus | monitoring |
chart prometheus-29.14.0, observability/prometheus-values.yaml ; Service prometheus-server:80 |
métriques et règles d'alerte |
| Alertmanager | monitoring |
sous-chart de Prometheus, même fichier de valeurs ; Service prometheus-alertmanager:9093 |
routage des trente-quatre règles |
| Grafana | monitoring |
chart grafana-10.5.15, observability/grafana-values.yaml ; Service grafana:80 |
restitution |
| Loki | monitoring |
chart loki-7.0.0, observability/loki-values.yaml, mode SingleBinary |
journaux |
| Alloy | monitoring |
chart grafana/alloy, observability/alloy-values.yaml, DaemonSet |
lecture des journaux de pods |
| node-exporter | node-exporter |
core/monitoring/node-exporter.yaml, node-exporter:v1.11.1, port 9100 ; hors de la couche de base, posé par l'étape 10 du Pas-à-pas |
métriques hôte |
| metrics-server | kube-system |
core/metrics-server/metrics-server.yaml, v0.7.2, --metric-resolution=15s |
kubectl top et HPA, pas une source de supervision |
metrics-server alimente kubectl top et les HPA, jamais une alerte ni un tableau de bord : sa
latence de 15 à 30 secondes le rend aveugle aux défaillances rapides.
Traefik expose ses propres métriques Prometheus sur son entrypoint d'administration : cette seule source couvre toute la couche HTTP de la plateforme, les trois portails, Keycloak et les sept APIs métier, sans instrumentation applicative. C'est la source du tableau de bord RED décrit plus bas.
Le fichier prometheus.yaml versionné dans observability/ (image prom/prometheus:v2.54.1,
PVC prometheus-data de 50Gi) appartient à la stack observability, pas à la stack
monitoring : ce n'est pas un dimensionnement de référence pour l'instance réellement
exploitée, seulement la configuration par défaut d'un Prometheus qui ne reçoit rien.
Paramètres à régler par client¶
Les deux paramètres Loki ci-dessous ne se règlent qu'une fois la release loki créée : voir la
question ouverte plus haut.
| Paramètre | Valeur de référence | À faire pour un nouveau client |
|---|---|---|
| Rétention Prometheus, taille du PVC | non fixée dans ce dépôt | question ouverte, voir l'encart ci-dessus : ces valeurs se règlent au moment où la stack monitoring est installée, à convenir avec le client selon la durée de rétention contractuelle et le stockage disponible |
| Bornes des caches memcached de Loki | chunksCache.allocatedMemory: 1024, resultsCache.allocatedMemory: 512 (Mo), dans observability/loki-values.yaml |
ces valeurs ont été calibrées sur la mémoire des workers de l'instance de référence, après qu'un défaut de chart à 8192 Mo a monopolisé 91 % de la mémoire d'un nœud ; les recaler à la baisse ou à la hausse selon la mémoire réelle des workers du client avant le premier helm upgrade, pas après |
| Endpoint de push Loki | http://loki-gateway.monitoring.svc.cluster.local/loki/api/v1/push |
fixe pour tout client : c'est une adresse de Service interne au cluster, indépendante de ${TV_DOMAIN} |
Le fichier de valeurs Loki doit toujours porter l'intégralité des valeurs voulues :
helm upgrade remplace le jeu fourni, un fichier partiel réinitialise tout le reste aux
défauts du chart, dont le cache à 8192 Mo déjà responsable d'un incident. Il en va de même pour
observability/alloy-values.yaml.
Labels Loki, et pourquoi ils doivent rester stables¶
Les métadonnées Kubernetes arrivent dans Alloy sous forme de labels internes préfixés __, qui
ne sont jamais persistés s'ils ne sont pas explicitement recopiés. Sans cette recopie, Loki ne
reçoit que le label pod, qui contient le hash du ReplicaSet et change à chaque recréation : les
journaux d'un service décrochent alors à chaque déploiement, et il faut re-sélectionner le nom du
nouveau pod à la main.
observability/alloy-values.yaml recopie donc quatre labels stables, par une étape
discovery.relabel qui ne se retire pas :
| Label | Source | Rôle |
|---|---|---|
namespace |
__meta_kubernetes_namespace |
cloisonnement |
app |
label du pod app.kubernetes.io/name, sinon app |
sélection du service, invariante par déploiement |
container |
__meta_kubernetes_pod_container_name |
tri des conteneurs d'un même pod |
pod |
__meta_kubernetes_pod_name |
isoler une réplique précise |
La requête de référence porte sur le service, jamais sur le pod :
La config Alloy est gérée par Helm : un kubectl edit ou un kubectl apply direct sur sa
ConfigMap est écrasé au prochain helm upgrade. La seule source de vérité est
observability/alloy-values.yaml.
Importer les tableaux de bord¶
Grafana ne charge aucun tableau de bord au démarrage sur cette instance : aucun sidecar ne va
chercher de fichiers, le chemin /etc/grafana/provisioning n'est monté par rien. L'import est
manuel, pour chacun des deux fichiers versionnés dans le dépôt (red-par-service.json, UID
tradeval-red ; ressources.json, UID tradeval-ressources) :
Dashboards -> New -> Import -> Upload JSON file, puis choisir la datasource Prometheus.
Les deux fichiers déclarent un bloc __inputs : la boîte de dialogue réclame donc la
datasource à chaque import, rien n'est codé en dur. Le tableau tradeval-red répond au débit,
au taux d'erreur et à la latence par service, sur la seule source Traefik. Le tableau
tradeval-ressources répond à la question de savoir si le cluster est le facteur limitant,
avec une sélection par namespace. Les fichiers du dépôt font foi : une modification faite dans
l'interface Grafana n'existe que dans sa base SQLite et disparait au prochain import.
Vérifier que la pile reçoit réellement des données¶
L'import réussit à l'identique que la datasource choisie reçoive des métriques ou non : Grafana
affiche « Dashboard imported » aussi bien si la pile monitoring fonctionne, si elle n'existe
pas encore (voir la question ouverte plus haut) ou si la datasource retenue par erreur est celle
de la stack observability, qui ne reçoit rien. Un import réussi ne prouve donc rien à lui
seul : les trois contrôles suivants vérifient que les données arrivent, pas seulement que le
geste a été exécuté.
Les deux datasources s'appellent l'une comme l'autre « Prometheus » dans la boîte d'import de
Grafana ; seule leur URL les distingue. La bonne cible le Service prometheus-server dans le
namespace monitoring ; celle de la stack héritée cible le Service prometheus dans le
namespace observability. C'est ce nom de Service, dans l'URL de la datasource, qui tranche.
| Vérification | Symptôme si échec |
|---|---|
Les cibles Prometheus sont up, pas seulement déclarées : kubectl -n monitoring port-forward svc/prometheus-server 9090:80 & puis curl -sS 'http://localhost:9090/api/v1/query?query=up' renvoie au moins deux séries à la valeur 1, dont une pour Traefik |
Aucune série, ou commande qui échoue : le port-forward vise la mauvaise stack (observability n'a pas de Service prometheus-server), ou Traefik ne porte pas encore ses annotations de scrape |
Une requête LogQL de contrôle renvoie des lignes, après avoir provoqué du trafic sur le service interrogé : kubectl -n api rollout restart deploy/trade-classification-svc, puis kubectl -n monitoring port-forward svc/loki-gateway 3100:80 & et curl -sS -G http://localhost:3100/loki/api/v1/query_range --data-urlencode 'query={namespace="api", app="trade-classification-svc"}' |
Réponse vide : la release alloy n'existe pas (voir la question ouverte plus haut), les labels ne sont pas recopiés, ou le port-forward vise un Loki qui n'existe pas |
Le tableau tradeval-red affiche un débit non nul sur trade-classification-svc, après avoir envoyé une vingtaine de requêtes qui traversent réellement Traefik : for i in $(seq 1 20); do curl -sS -o /dev/null https://classification.${TV_DOMAIN}/; done, depuis un poste qui résout ce host vers la VIP, ou depuis un pod du cluster. Un redémarrage de pod ne suffit pas : ce tableau ne lit que des métriques Traefik, jamais produites par un simple rollout restart |
Débit qui reste à zéro malgré les requêtes envoyées, ou « No data » : la datasource retenue à l'import porte l'URL du Service prometheus dans le namespace observability (la pile héritée), pas celle du Service prometheus-server dans monitoring (la seule à choisir) ; ou les requêtes n'ont pas atteint Traefik, host mal résolu ou TLSStore pas encore prêt |
Accès aux interfaces et basic-auth¶
core/platform-access/ contient cinq Ingress Traefik : les trois de la stack monitoring,
Longhorn, et l'UI de Vault. L'étape 10 du
Pas-à-pas n'en pose que
deux, Longhorn et Vault, qu'elle applique par fichiers nommés : les trois autres visent le
namespace monitoring, qu'aucun manifeste du dépôt ne crée, et ne s'appliquent qu'une fois la
stack monitoring installée, ce que ce guide ne couvre pas. Tous portent les mêmes annotations
d'entrypoints et de TLS, sans spec.tls propre : le certificat vient du TLSStore par défaut qui
sert le wildcard *.${TV_DOMAIN}. Cette section ne couvre que les quatre premières ; l'UI Vault,
déjà présentée au Pas-à-pas, a sa propre authentification par jeton et un profil de risque
distinct.
| Host | Backend | Authentification |
|---|---|---|
grafana.${TV_DOMAIN} |
monitoring:grafana:80 |
login Grafana intégré |
prometheus.${TV_DOMAIN} |
monitoring:prometheus-server:80 |
basic-auth Traefik, core/platform-access/auth.yaml |
alertmanager.${TV_DOMAIN} |
monitoring:prometheus-alertmanager:9093 |
idem |
longhorn.${TV_DOMAIN} |
longhorn-system:longhorn-frontend:80 |
idem |
La basic-auth est posée par core/platform-access/auth.yaml
depuis le 2026-09-15 : un ExternalSecret et un Middleware dashboard-auth par namespace
concerné, monitoring et longhorn-system, et l'annotation
traefik.ingress.kubernetes.io/router.middlewares: <namespace>-dashboard-auth@kubernetescrd
sur les trois Ingress. Le fichier htpasswd vit dans Vault, prod/platform-access, clé users,
une ligne par compte au format bcrypt. Il n'y a rien à créer à la main : poser la clé dans
Vault, appliquer le fichier.
htpasswd -nbB admin '<mot de passe>' | vault kv patch -mount=secrets prod/platform-access users=-
kubectl apply -f core/platform-access/auth.yaml
Sans htpasswd sur le poste, n'importe quel conteneur httpd:2.4-alpine en porte un. Traefik
accepte bcrypt, MD5-APR1 et SHA1 ; openssl passwd -6 produit du SHA-512, qu'il ne lit pas.
Deux autres options existent, à la place de la basic-auth : restreindre par IP source avec un
Middleware ipAllowList, ou passer par un SSO (Traefik forward-auth vers Keycloak) pour une
protection homogène avec le reste de la plateforme.
Ne jamais publier d'enregistrement DNS public pour ces quatre hosts
La basic-auth protège d'un clic malheureux sur le réseau interne, pas d'Internet. La console Longhorn permet de supprimer des volumes. La protection réelle de ces hosts reste de n'être joignables que par le réseau interne ou le VPN : aucun enregistrement public, aucune route dans le tunnel Cloudflare.
Sans metrics-server, les HPA restent inertes ; ne pas rejouer l'incident historique sur eux
Les dix HPA de ce dépôt restent inertes tant que metrics-server n'est pas installé, sans
le signaler autrement que par une valeur <unknown> dans kubectl get hpa. Sur l'instance
de référence, l'installation de metrics-server a un jour réveillé d'un coup des HPA réglés
sur une cible mémoire et un maxReplicas de 50, saturant le CPU du cluster en quelques
minutes. Cette configuration a été corrigée depuis : les HPA versionnés aujourd'hui ne
portent plus de métrique mémoire et plafonnent à 3 répliques, 6 pour hub-portail. Ne pas
supposer pour autant qu'installer metrics-server est sans risque sur un cluster de
dimensionnement différent, ou après l'ajout d'un nouveau service : confronter les valeurs de
requests et les bornes maxReplicas à un usage réel avant d'installer metrics-server,
plutôt que d'hériter des chiffres déjà présents dans le dépôt sans les revérifier.