Relay e regioni
Un collector accanto ai tuoi agenti, e il collegamento fra le regioni attraversato con i tentativi.
Che cos’è un relay
mcpd relay fa girare lo stesso binario come collector che non
archivia niente. Accetta ogni richiesta HTTP di ingest che accetta un
daemon, scrive la richiesta in uno spool su disco, risponde al mittente e
poi spedisce la richiesta ai daemon indicati da --upstream.
Non apre nessuno storage.
Fanne girare uno in ogni regione in cui girano i tuoi agenti. Un mittente parla allora con un collector a pochi millisecondi di distanza, e il collegamento fra le regioni lo attraversa il relay, con i tentativi, invece di ogni agente per conto suo.
un relay di bordo con due upstream
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} Che cosa risponde
Qualunque POST, su qualunque percorso diverso dalle sonde, viene accettato.
I percorsi OTLP /v1/traces, /v1/logs e
/v1/metrics rispondono 200 con un partialSuccess
OTLP, che è quello che si aspetta un exporter OpenTelemetry. Ogni altro
percorso risponde 202 con l’header mcpd-relay: spooled e il
corpo {"accepted":0,"duplicate":0,"rejected":0}: la
richiesta è trattenuta e verrà consegnata, e i conteggi veri spettano
all’upstream, che non l’ha ancora vista. Ogni SDK legge qualunque 2xx come
successo.
Il relay controlla --ingest-key da sé, prima di scrivere
qualunque cosa, e applica --max-body-bytes e
--request-timeout-seconds come fa un daemon. La richiesta è
nello spool, <spool-dir>/relay, prima che parta la
risposta.
Consegna e failover
Quattro worker spediscono lo spool. Ogni richiesta va al primo upstream,
nell’ordine di --upstream, che non sta aspettando. Un errore
che si può ritentare mette in attesa quell’upstream, con backoff e jitter
o per tutto il tempo indicato dal suo Retry-After, e la
richiesta passa subito all’upstream successivo. Quando l’attesa finisce,
il primario torna a essere provato per primo, così il traffico ci ritorna
da solo.
Un rifiuto definitivo, come un 400, non viene mandato all’upstream
successivo: rifiuterebbe gli stessi byte. La richiesta viene spostata in
dead/ nella directory di spool e contata.
Nessun conteggio doppio
La consegna è almeno una volta, e un nuovo invio viene archiviato una volta
sola. Ogni richiesta porta un header Idempotency-Key: quello
del mittente quando ne ha mandato uno, altrimenti relay-
seguito da un digest del percorso, della codifica del contenuto e del
corpo. Ogni porta di ingest del daemon rispetta l’header, e una chiave nel
corpo di un batch nativo ha la precedenza su di esso. Una richiesta che il
relay rimanda dopo un timeout, o che manda al secondario dopo che il
primario l’ha presa e la risposta è andata persa, viene riconosciuta e
riportata come duplicato.
Chiavi
Con --upstream-key, il relay presenta quella chiave a ogni
upstream come token Bearer. Senza, l’header Authorization o
x-mcpd-key del mittente viene conservato con la richiesta
nello spool, in un file che solo l’utente del relay può leggere, e passato
avanti. La seconda forma mantiene il progetto e l’ambito di ogni mittente
così come li vede l’upstream.
Contropressione e prontezza
Lo spool è limitato da --spool-max-bytes, 1 GiB per il relay
se non lo imposti. Quando è pieno, una richiesta viene rifiutata con un 503
e Retry-After: 5. Ogni SDK ed exporter OpenTelemetry lo
ritenta, così i dati li tiene il mittente invece di essere scartati dal
relay.
/healthz risponde sempre 200. /readyz risponde:
- 200
okfinché un upstream è raggiungibile; -
200
degraded, con undetail, quando nessun upstream ha risposto al suo/readyzo ha preso una consegna negli ultimi 30 secondi. Gli upstream vengono sondati ogni 5 secondi; - 503 quando lo spool è pieno, o quando nessun upstream è raggiungibile e lo spool è pieno almeno all’80%.
Un load balancer quindi continua a mandare traffico a un relay che sta accumulando, e lo sposta via da uno che non può più prenderlo.
Metriche del relay
| Metrica | Tipo | Significato |
|---|---|---|
mcpd_relay_accepted_total | counter | Richieste scritte nello spool |
mcpd_relay_refused_full_total | counter | Richieste rifiutate con un 503 perché lo spool era pieno |
mcpd_relay_failovers_total | counter | Richieste consegnate a un upstream diverso dal primo |
mcpd_relay_dead_letter_total | counter | Richieste che un upstream ha rifiutato in modo definitivo, spostate in dead/ |
mcpd_relay_spool_entries | gauge | Richieste in attesa nello spool |
mcpd_relay_spool_bytes | gauge | Byte contenuti nello spool |
mcpd_relay_spool_oldest_seconds | gauge | Età della richiesta in attesa più vecchia, oppure 0 |
mcpd_relay_delivered_total | counter | Richieste accettate da un upstream, con le etichette upstream e order |
mcpd_relay_failed_attempts_total | counter | Tentativi che un upstream non ha accettato, con le stesse etichette |
mcpd_relay_upstream_up | gauge | 1 quando l’upstream ha risposto negli ultimi 30 secondi, altrimenti 0 |
mcpd_uptime_seconds | gauge | Secondi dall’avvio di questo processo |
Un mcpd_relay_failovers_total che sale vuol dire che il
primario sta fallendo e che il secondario porta il suo traffico.
mcpd_relay_spool_oldest_seconds dice quanto è indietro la
consegna.
Più regioni
Fai girare più repliche del daemon, in una regione o in più regioni, su un
solo storage Postgres o ClickHouse, con le esecuzioni in Postgres
(--runs-data postgres://...) o spente: uno storage delle
esecuzioni incorporato appartiene a un solo processo. Il
/readyz di ogni replica riflette già se raggiunge lo storage.
Metti davanti a loro un relay in ogni regione, con --upstream
che elenca prima il daemon più vicino e poi un daemon in un’altra regione.
Quattro impostazioni si comportano in modo diverso quando c’è più di una replica:
- Le chiavi di idempotenza stanno nello storage condiviso, quindi un nuovo invio che arriva su un’altra replica viene comunque riconosciuto. Postgres registra la chiave nella stessa transazione del batch.
- I tetti di quota al minuto sono contati per replica. Dividi il tetto che vuoi per il numero di repliche.
- Il punteggio dal vivo, il campionamento di coda e gli alert dei monitor girano su una replica alla volta. Le repliche fanno a turno attraverso un lease nello storage condiviso: chi lo tiene lo rinnova a ogni passata, lo lascia quando si spegne, e un’altra replica subentra entro tre passate se si ferma. Ogni replica valuta i suoi monitor e obiettivi, quindi ognuna risponde all’API dei monitor, e solo chi tiene il lease manda il webhook. Configurali allo stesso modo su ogni replica.
- L’analisi degli aspetti gira su ogni replica che la ha configurata. Configurala su una replica.
Deploy di esempio
deploy/docker/docker-compose.multi-region.yml avvia Postgres,
due daemon e un relay su una sola macchina: tutta la topologia in una
forma che puoi provare.
Il chart Helm accetta mode: relay. Tre file di valori sotto
deploy/helm/examples/ descrivono una regione ciascuno:
region-core.yaml, region-standby.yaml e
region-edge-relay.yaml, l’ultimo per una regione che fa girare
solo un relay.
Avanti
- Copiare i dati verso altri fornitori: Inoltro.
- Sonde, metriche e storage: Gestione.
- Ogni opzione: Configurazione.