Relais et régions
Un collecteur à côté de vos agents, et le lien entre régions franchi avec de nouvelles tentatives.
Ce qu’est un relais
mcpd relay fait tourner le même binaire comme un collecteur
qui ne stocke rien lui-même. Il accepte toutes les requêtes d’ingestion HTTP
qu’accepte un daemon, écrit la requête dans un spool sur disque, répond à
l’émetteur, puis expédie la requête aux daemons que nomme
--upstream. Il n’ouvre aucun stockage.
Faites-en tourner un dans chaque région où tournent vos agents. Un émetteur parle alors à un collecteur situé à quelques millisecondes, et le lien entre régions est franchi par le relais, avec de nouvelles tentatives, plutôt que par chaque agent de son côté.
un relais en périphérie avec deux upstreams
mcpd relay \
--ingest-key $INGEST_KEY \
--upstream http://mcpd.eu.internal:4317 \
--upstream http://mcpd.us.internal:4317 $ MCPD_UPSTREAMS=http://mcpd.eu.internal:4317,http://mcpd.us.internal:4317 mcpd --data /data/mcpd.db config | grep ^relayrelay 2 upstream(s) in order, http://mcpd.eu.internal:4317, http://mcpd.us.internal:4317; each sender's key passed on; spool at /data/mcpd.spool/relay, 1024 MiB$ curl -s -D - -o /dev/null localhost:4317/v1/ingest -H "content-type: application/json" -d @batch.json | grep -e ^HTTP -e mcpd-relayHTTP/1.1 202 Acceptedmcpd-relay: spooled$ curl -s localhost:4317/v1/ingest -H "content-type: application/json" -d @batch.json{"accepted":0,"duplicate":0,"rejected":0} Ce qu’il répond
Tout POST, sur tout chemin autre que les sondes, est accepté. Les chemins
OTLP /v1/traces, /v1/logs et
/v1/metrics répondent 200 avec un partialSuccess
OTLP, ce qu’attend un exporter OpenTelemetry. Tous les autres chemins
répondent 202 avec l’en-tête mcpd-relay: spooled et le corps
{"accepted":0,"duplicate":0,"rejected":0} : la
requête est conservée et sera livrée, et les vrais décomptes reviennent à
l’upstream, qui ne l’a pas encore vue. Tous les SDK lisent n’importe quel
2xx comme un succès.
Le relais vérifie --ingest-key lui-même, avant d’écrire quoi
que ce soit, et applique --max-body-bytes et
--request-timeout-seconds comme le fait un daemon. La requête
est dans le spool, <spool-dir>/relay, avant l’envoi de la
réponse.
Livraison et bascule
Quatre workers vident le spool. Chaque requête part vers le premier
upstream, dans l’ordre de --upstream, qui n’est pas en pause.
Un échec qui peut être réessayé met cet upstream en pause, avec backoff et
jitter ou pour la durée qu’indique son Retry-After, et la
requête part aussitôt vers l’upstream suivant. À la fin de la pause, le
primaire est de nouveau essayé en premier : le trafic y revient de
lui-même.
Un refus définitif, comme un 400, n’est pas envoyé à l’upstream suivant :
celui-ci refuserait les mêmes octets. La requête est déplacée dans
dead/ du répertoire de spool et comptée.
Pas de double comptage
La livraison se fait au moins une fois, et un renvoi n’est stocké qu’une
fois. Chaque requête porte un en-tête Idempotency-Key : celui
de l’émetteur s’il en a envoyé un, sinon relay- suivi d’un
condensé du chemin, de l’encodage du contenu et du corps. Chaque porte
d’ingestion du daemon respecte cet en-tête, et une clé présente dans le
corps d’un lot natif l’emporte sur lui. Une requête que le relais renvoie
après un délai dépassé, ou qu’il envoie au secondaire après que le primaire
l’a prise et que la réponse s’est perdue, est reconnue et signalée comme
doublon.
Clés
Avec --upstream-key, le relais présente cette clé à chaque
upstream en jeton Bearer. Sans elle, l’en-tête Authorization ou
x-mcpd-key de l’émetteur est gardé avec la requête mise en
spool, dans un fichier que seul l’utilisateur du relais peut lire, puis
transmis. La seconde forme conserve le projet et la portée de chaque
émetteur tels que l’upstream les voit.
Contre-pression et disponibilité
Le spool est borné par --spool-max-bytes, 1 GiB pour le relais
sauf réglage contraire. Quand il est plein, une requête est refusée avec un
503 et Retry-After: 5. Tous les SDK et les exporters
OpenTelemetry réessaient dans ce cas : c’est l’émetteur qui garde les
données, au lieu que le relais les jette.
/healthz répond toujours 200. /readyz répond :
- 200
oktant qu’un upstream est joignable ; -
200
degraded, avec undetail, quand aucun upstream n’a répondu à son/readyzni pris une livraison dans les 30 dernières secondes. Les upstreams sont sondés toutes les 5 secondes ; - 503 quand le spool est plein, ou quand aucun upstream n’est joignable et que le spool est plein à 80 % au moins.
Un load balancer continue donc d’envoyer vers un relais qui met en tampon, et détourne le trafic d’un relais qui ne peut plus en prendre.
Métriques du relais
| Métrique | Type | Signification |
|---|---|---|
mcpd_relay_accepted_total | compteur | Requêtes écrites dans le spool |
mcpd_relay_refused_full_total | compteur | Requêtes refusées avec un 503 parce que le spool était plein |
mcpd_relay_failovers_total | compteur | Requêtes livrées à un autre upstream que le premier |
mcpd_relay_dead_letter_total | compteur | Requêtes refusées pour de bon par un upstream, déplacées dans dead/ |
mcpd_relay_spool_entries | jauge | Requêtes en attente dans le spool |
mcpd_relay_spool_bytes | jauge | Octets contenus dans le spool |
mcpd_relay_spool_oldest_seconds | jauge | Âge de la plus ancienne requête en attente, ou 0 |
mcpd_relay_delivered_total | compteur | Requêtes acceptées par un upstream, avec les labels upstream et order |
mcpd_relay_failed_attempts_total | compteur | Tentatives qu’un upstream n’a pas acceptées, avec les mêmes labels |
mcpd_relay_upstream_up | jauge | 1 quand l’upstream a répondu dans les 30 dernières secondes, 0 sinon |
mcpd_uptime_seconds | jauge | Secondes écoulées depuis le démarrage de ce processus |
Un mcpd_relay_failovers_total qui monte signifie que le
primaire échoue et que le secondaire porte son trafic.
mcpd_relay_spool_oldest_seconds mesure le retard de la
livraison.
Plusieurs régions
Faites tourner plusieurs réplicas du daemon, dans une région ou plus, sur
un même stockage Postgres ou ClickHouse, avec les exécutions dans Postgres
(--runs-data postgres://...) ou désactivées : un stockage
d’exécutions embarqué appartient à un seul processus. Le /readyz de chaque réplica indique déjà s’il peut
joindre le stockage. Placez devant eux un relais dans chaque région, avec
un --upstream qui liste d’abord le daemon le plus proche, puis
un daemon d’une autre région.
Quatre réglages se comportent autrement dès qu’il y a plus d’un réplica :
- Les clés d’idempotence sont conservées dans le stockage partagé : un renvoi qui arrive sur un autre réplica est donc toujours reconnu. Postgres enregistre la clé dans la même transaction que le lot.
- Les plafonds de quota par minute sont comptés par réplica. Divisez le plafond voulu par le nombre de réplicas.
- La notation en direct, l’échantillonnage en queue et les alertes des moniteurs tournent sur un seul réplica à la fois. Les réplicas se relaient grâce à un bail dans le stockage partagé : son détenteur le renouvelle à chaque passe, le libère à l’arrêt, et un autre réplica prend le relais en trois passes au plus s’il s’arrête. Chaque réplica évalue ses moniteurs et ses objectifs, donc chacun répond à l’API des moniteurs, et seul le détenteur poste le webhook. Configurez-les de la même façon sur chaque réplica.
- L’analyse d’aspects tourne sur chaque réplica qui l’a configurée. Configurez-la sur un seul réplica.
Exemples de déploiement
deploy/docker/docker-compose.multi-region.yml démarre Postgres,
deux daemons et un relais sur une seule machine : toute la topologie, sous
une forme que vous pouvez essayer.
Le chart Helm accepte mode: relay. Trois fichiers de valeurs
sous deploy/helm/examples/ décrivent chacun une région :
region-core.yaml, region-standby.yaml et
region-edge-relay.yaml, ce dernier pour une région qui ne fait
tourner qu’un relais.
La suite
- Copier les données vers d’autres éditeurs : Transfert.
- Les sondes, les métriques et les stockages : Exploitation.
- Toutes les options : Configuration.