Lab 3 — Configuration du collecteur
Le collecteur OpenTelemetry est le cœur de la chaîne : il reçoit (receivers), transforme (processors) et exporte (exporters) la télémétrie. Dans ce lab, vous allez lire sa configuration réelle, puis l’enrichir pour collecter des métriques système (hostmetrics) et des métriques produit (PostgreSQL) — sans toucher aux applications.
💡 Dans la démo, le collecteur est déployé par Helm : modifier sa configuration = modifier les values du chart +
helm upgrade. Pas de rebuild, pas de redéploiement manuel.
Prérequis
- Labs 1 et 2 terminés (stack +
review-servicedéployés). - Les accès ouverts (
./scripts/open-ui.sh) : UIs, Prometheus,review-serviceet zPages du collecteur. - Les variables de la formation chargées dans votre shell :
Sur le serveur partagé, gardez
$PF_HOSTdans les URLs comme dans les commandes ci-dessous : chaque participant a son propre nom (student3→localhost3), ce qui permet à tous d’utiliser les mêmes ports sans se marcher dessus.
Étapes
⏱️ Ce lab est dense — voici comment le parcourir.
En séance : l’étape 1a (où vit la configuration), puis les étapes 2 à 6 — c’est là que vous modifiez le collecteur et voyez le résultat.
À lire ensuite, à froid : les étapes 1c, 1d et 1e, la lecture commentée de la configuration réelle. Elles expliquent pourquoi elle est écrite ainsi — l’ordre des processors, le receiver déclaré qui ne tourne pas, ce qui se passe quand
memory_limitermord. Rien dans la suite du lab n’en dépend, mais c’est ce que vous relirez le jour où une configuration vous résistera.
1. Lire la configuration réelle du collecteur
a. Où vit cette configuration ?
Le collecteur est un DaemonSet (un pod par nœud) et sa configuration n’est qu’un fichier YAML ; tout ce que fait Helm, c’est le générer. Affichez-le :
💡
-o yamlrenverrait l’objet ConfigMap complet, avec la configuration noyée dans une longue chaîne YAML échappée.-o jsonpath='{.data.relay}'extrait la seule clé utile : son contenu est exactement le fichier/conf/relay.yamllu par le collecteur.
⚠️ Ne cherchez pas de logique dans l’ordre des blocs. Ce fichier n’a pas été écrit à la main : il est généré automatiquement par Helm. Vous y verrez ainsi la sortie (
exporters) avant l’entrée (receivers). Aucune importance : le collecteur lit un fichier YAML, où l’ordre des blocs ne change rien. Un seul endroit décrit le flux réel de la donnée —service.pipelines, tout en bas du fichier. C’est par lui qu’il faut commencer la lecture, puis remonter voir la définition de chaque composant qu’il cite.
b. Le vérifier depuis l’intérieur du pod (bonus)
L’image du collecteur est distroless : ni shell ni cat, donc kubectl exec est inutilisable. On lui adjoint un conteneur éphémère qui partage ses namespaces :
Puis, dans le shell obtenu :
--profile=sysadminpartage le namespace PID du pod cible : le processus1est le collecteur, et/proc/1/rootdonne accès à son système de fichiers depuis busybox. Vous lisez le fichier tel qu’il est monté — la preuve que ConfigMap et configuration effective coïncident.
MY_POD_IPest injecté par le chart via la downward API ; c’est la variable que la configuration référence sous la forme${env:MY_POD_IP}. Le collecteur écoute donc sur l’IP du pod, pas sur0.0.0.0.
Sortez avec exit. Le conteneur éphémère ne redémarre pas, mais reste attachable tant que le pod vit (kubectl attach ... -c <nom-généré> -i -t) ; il disparaît avec le pod.
🚫 N’utilisez pas
kubectl debug --copy-to=...ici. Cette variante crée un pod indépendant, copie du collecteur, qui réclame les mêmeshostPort(4317, 4318, 14250, 14268, 9411). Il restePendingtant que le DaemonSet tourne, donc invisible — mais au prochainhelm upgradeil s’empare des ports libérés et le nouveau pod du collecteur ne peut plus être planifié. Si vous en avez créé un :kubectl delete pod <nom> -n otel-demo.
c. Les sections qui comptent
Ce fichier a six sections : receivers (par où la donnée entre), processors (ce qui lui est appliqué au passage), connectors (la sortie d’un pipeline qui devient l’entrée d’un autre), exporters (par où elle sort), extensions (des services rendus hors du flux) et service (ce qui est réellement actif).
🔑 La règle d’or : un composant déclaré dans
receivers,processors,exportersouextensionsn’est qu’une définition. Il ne s’exécute que s’il est cité dansservice— dans un pipeline pour les trois premiers, dansservice.extensionspour les extensions. C’est l’oubli n°1 quand on configure un collecteur, et vous ferez les deux à l’étape 3 : brancher vos receivers dans le pipelinemetrics, et activerzpagesdansservice.extensions.
Voici les trois pipelines de la démo, tels qu’ils sont déclarés — c’est le bloc à avoir en tête pour l’étape 3 :
📖 La configuration complète, commentée section par section, est dans le Lab 3 bonus — avec cinq questions pour la parcourir : par où entrent vos traces, quel composant est à la fois exporter et receiver, quel receiver est déclaré mais ne tourne pas, dans quel ordre s’appliquent les processors. Lisez-la maintenant si le temps le permet, sinon après la séance : la suite du lab ne l’exige pas.
2. Constater ce qui manque dans Prometheus
Ouvrez l’URL affichée (http://localhost:9090 sur un poste individuel) et cherchez dans l’autocomplétion :
| Recherche | Résultat | Pourquoi |
|---|---|---|
kafka_ | des métriques ✔ | le receiver kafkametrics du collecteur les collecte |
system_ | des métriques ✔ — mais pas là où vous croyez | voir le piège ci-dessous |
system_cpu_load_average_15m | rien ✘ | seul le scraper load de hostmetrics produit la charge de la machine |
postgresql_ | rien ✘ | aucun receiver n’interroge la base |
⚠️ Le piège du préfixe.
system_*n’est pas vide, alors quehostmetricsn’est pas configuré : ces séries sont émises par trois applications Python de la boutique, dont le SDK embarque une instrumentation « system metrics ». Elles mesurent donc bel et bien la machine — mais chacune dans son coin, et sous son propre nom. Le nom d’une métrique ne suffit donc pas — regardez qui l’envoie :Vous n’obtenez que des applications (
otel-demo/load-generator…) : dans Prometheus,jobidentifie le service émetteur — c’estservice.namespace/service.name, les attributs que l’application déclare. Trois applications publient donc trois copies de la même mesure, étiquetées à leur nom : rien ne dit au lecteur qu’il s’agit du nœud, et une quatrième application ajouterait une quatrième copie. Comparezsum by (job) (system_cpu_time_seconds_total{state="idle"}): les totaux sont identiques à 0,01 % près, c’est bien la même machine mesurée plusieurs fois.Cette mesure de rencontre est en plus incomplète : le SDK Python publie 4 états CPU (
idle,irq,system,user) là oùhostmetricsen publie 8 (nice,steal,wait… s’y ajoutent). Chaque série étant une paire (cœur, état), chaque application publienb_cœurs × 4séries là où le collecteur en publienb_cœurs × 8: le compte exact dépend de la machine, le rapport du simple au double, non. Et surtout, cette mesure ignore la charge : c’est pourquoi le critère du lab estsystem_cpu_load_average_15m, qu’aucune de ces applications ne produit.🧮 Lire un
count bysans se tromper. Deux pièges, et c’est la requête la plus utile du lab.D’abord,
countcompte des séries, pas des secondes de CPU : il répond « combien de courbes portent ce nom de métrique », jamais « combien de temps processeur ». Pour additionner les valeurs, c’estsum. Lancez les deux sur la même métrique et l’écart saute aux yeux :countrenvoie quelques centaines,sumdes millions — des secondes de CPU cumulées depuis le démarrage de la machine. Deux questions différentes ; ici, on dénombre les émetteurs, donccount.Ensuite, les groupes affichés sont disjoints, et
{}n’est pas un total : c’est le groupe des séries dépourvues du label. Après l’étape 4, vous lirez donc une ligne{}pour le collecteur et une ligne par application, sans que l’une contienne les autres —count(system_cpu_time_seconds_total)sansbydoit valoir la somme des quatre.
3. Écrire le fichier de values qui ajoute les deux receivers
Créez manifests/30-otel-collector-values.yaml. C’est un fichier de values Helm, pas une configuration de collecteur brute : la configuration du collecteur y est rangée sous la section opentelemetry-collector.config, celle que Helm fusionne avec la configuration de la démo pour produire la ConfigMap de l’étape 1. Le fichier commence donc par ces deux lignes, et tout le reste s’indente dessous :
Il doit ajouter au collecteur :
- un receiver
hostmetrics(scraperscpu,memory,load,disk,network) — documentation ; - un receiver
postgresqlpointé sur la base de la boutique (servicepostgresql:5432, userroot, mot de passeotel) — celle-là même qu’utilise votrereview-service— documentation ; - l’extension
zpages(pages de debug du collecteur) ; - et il doit brancher les deux receivers dans le pipeline
metrics.
💡 D’où vient
zpages? C’est un composant déjà embarqué dans le binaire du collecteur (imageotel/opentelemetry-collector-contrib), simplement inactif dans la configuration de la démo : il n’y a rien à installer, seulement à déclarer — commehostmetricsetpostgresql, eux aussi fournis par l’image. Et comme toute extension,zpagesne participe pas au traitement de la télémétrie : il expose un serveur HTTP sur le collecteur lui-même, dont les pages affichent les composants réellement chargés. Vous l’ouvrirez à l’étape 5. Sa documentation officielle : README de l’extensionzpages.
⚠️ En YAML Helm, une liste redéfinie remplace la liste d’origine : le pipeline
metricsdoit re-lister tous les receivers que vous voulez conserver, pas seulement les nouveaux. (kubeletstatsetk8s_clusterfont exception : les presets du chart les ré-ajoutent après votre liste.)
4. Appliquer la nouvelle configuration
helm upgraderégénère la ConfigMap et redémarre le collecteur. Les values s’empilent : le fichier de la formation, puis le vôtre. Gardezmanifests/30-otel-collector-values.yaml: les labs 6, 7 et 8 le rempileront à chaquehelm upgrade, en plus de leur propre fichier.
Relisez la ConfigMap comme à l’étape 1 pour voir le résultat de votre travail :
5. Observer le pipeline dans zPages
Le collecteur vient d’être redéployé, donc son pod a changé — l’accès aux zPages a été rouvert tout seul par
open-ui.sh. Comptez-lui quelques secondes : si la page ne répond pas du premier coup, attendez et rechargez, ne changez rien.
Ouvrez l’URL affichée : vos deux receivers doivent apparaître dans le pipeline metrics.
zPages affiche les composants réellement chargés par le collecteur, là où la ConfigMap ne montre que ce qu’on lui a demandé. Le pipeline
metricsliste donc aussikubeletstatsetk8s_cluster, ajoutés par les presets du chart — c’est normal, vous ne les avez pas perdus.
6. Vérifier dans Prometheus
Reprenez le tableau de l’étape 2, avec les mêmes recherches — sur l’onglet Prometheus rechargé, une trentaine de secondes après la fin du rollout status de l’étape 4 : les deux receivers scrutent dès le démarrage du collecteur, mais celui-ci doit d’abord être redéployé.
| Recherche | Avant | Après |
|---|---|---|
system_cpu_load_average_15m | rien ✘ | présent ✔ — la charge du nœud |
postgresql_ | rien ✘ | présent ✔ |
count by (job) (system_cpu_time_seconds_total) | uniquement des applications | une ligne de plus, mais sans nom de job — c’est le collecteur |
⚠️ Pourquoi cette ligne est-elle vide ? Prometheus l’affiche
{}, sans étiquette. Rappelez-vous l’étape 2 :jobvautservice.namespace/service.name, deux attributs que l’application émettrice déclare. Orhostmetricsne mesure aucune application — il mesure une machine — et le collecteur ne lui prête pas son propre nom. Ces séries n’ont donc pas dejobdu tout, seulement unhost_name: celui du pod collecteur. Pour les isoler :(en PromQL, un label absent se sélectionne avec la chaîne vide). C’est vrai aussi de vos métriques PostgreSQL : elles n’ont pas de
job, leur repère d’origine estinstance="postgresql:5432".
Générez ensuite quelques avis : c’est votre review-service du Lab 2 qui écrit dans cette base.
Ouvrez l’URL affichée et postez une dizaine d’avis en cliquant sur Post review. Chaque clic est un INSERT dans la table reviews ; le compteur en haut de la liste vous dit combien elle en contient.
Une trentaine de secondes plus tard, dans Prometheus :
La valeur a augmenté de 10 : ce sont vos avis, comptés cette fois par le collecteur et non par l’application. Le compteur d’écritures de la même table dit la même chose, ins par ins :
⚠️ Ne guettez pas
postgresql_commits_totalpour y voir vos avis : la boutique écrit en base sans discontinuer — le load generator passe des commandes,accountingles enregistre — etrate(postgresql_commits_total[1m])tourne autour de 3 commits/s. Vos dix écritures s’y noient. Un compteur global ne dit jamais qui écrit : c’est le labelpostgresql_table_namequi vous ramène à votre service. Attention à ne pas confondre les deux tables d’avis de la base :public.reviewsest celle de votrereview-service,reviews.productreviewsest celle du serviceproduct-reviewslivré avec la démo.
Livrable
Dans Prometheus, onglet Graph, une courbe pour chacun de vos deux receivers :
La première prouve que hostmetrics tourne : c’est la charge du nœud, qu’aucune application de la démo ne publie. La seconde prouve que postgresql tourne, et elle a la bonne tête pour une capture — un escalier, une marche par avis posté à l’étape 6.