Relay e regiões
Um coletor ao lado dos seus agentes, e o trecho entre regiões percorrido com novas tentativas.
O que é um relay
mcpd relay roda o mesmo binário como um coletor que não
armazena nada. Ele aceita toda requisição HTTP de ingestão que um daemon
aceita, grava a requisição em um spool no disco, responde ao remetente e
depois envia a requisição aos daemons indicados em --upstream.
Ele não abre nenhum armazenamento.
Rode um em cada região onde os seus agentes rodam. Um remetente passa a falar com um coletor a poucos milissegundos de distância, e o trecho entre regiões é percorrido pelo relay, com novas tentativas, e não por cada agente sozinho.
um relay de borda com dois 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} O que ele responde
Qualquer POST, em qualquer caminho que não seja o das sondas, é aceito. Os
caminhos OTLP /v1/traces, /v1/logs e
/v1/metrics respondem 200 com um partialSuccess
OTLP, que é o que um exportador OpenTelemetry espera. Todos os outros
caminhos respondem 202 com o cabeçalho mcpd-relay: spooled e o
corpo {"accepted":0,"duplicate":0,"rejected":0}: a
requisição está guardada e será entregue, e as contagens reais pertencem ao
upstream, que ainda não a viu. Todo SDK lê qualquer 2xx como sucesso.
O próprio relay confere --ingest-key antes de gravar qualquer
coisa, e aplica --max-body-bytes e
--request-timeout-seconds como um daemon faz. A requisição já
está no spool, <spool-dir>/relay, antes de a resposta
ser enviada.
Entrega e failover
Quatro workers esvaziam o spool. Cada requisição vai para o primeiro
upstream, na ordem de --upstream, que não esteja em pausa. Uma
falha que admite nova tentativa coloca aquele upstream em pausa, com espera
exponencial e variação aleatória ou pelo tempo que o
Retry-After dele indicar, e a requisição vai na hora para o
upstream seguinte. Quando a pausa termina, o primário volta a ser tentado
primeiro, então o tráfego retorna a ele por conta própria.
Uma recusa definitiva, como um 400, não é enviada ao upstream seguinte: ele
recusaria os mesmos bytes. A requisição é movida para dead/ no
diretório do spool e contada.
Sem contagem dupla
A entrega é pelo menos uma vez, e um reenvio é armazenado uma única vez.
Toda requisição leva um cabeçalho Idempotency-Key: o do
remetente, quando ele enviou um, ou então relay- seguido de um
resumo criptográfico do caminho, da codificação do conteúdo e do corpo.
Toda porta de ingestão do daemon respeita o cabeçalho, e uma chave no corpo
de um lote nativo tem prioridade sobre ele. Uma requisição que o relay
reenvia depois de um timeout, ou que envia ao secundário depois que o
primário a aceitou e a resposta se perdeu, é reconhecida e relatada como
duplicada.
Chaves
Com --upstream-key, o relay apresenta essa chave a todo
upstream como token Bearer. Sem ela, o cabeçalho
Authorization ou x-mcpd-key do remetente é
guardado junto com a requisição no spool, em um arquivo que só o usuário do
relay consegue ler, e repassado. A segunda forma preserva o projeto e o
escopo de cada remetente do jeito que o upstream os vê.
Contrapressão e prontidão
O spool é limitado por --spool-max-bytes, 1 GiB para o relay,
a menos que você defina outro valor. Quando ele está cheio, uma requisição
é recusada com um 503 e Retry-After: 5. Todo SDK e todo
exportador OpenTelemetry tentam de novo nesse caso, então o remetente
segura os dados em vez de o relay descartá-los.
/healthz sempre responde 200. /readyz responde:
- 200
okenquanto algum upstream estiver alcançável; -
200
degraded, com umdetail, quando nenhum upstream respondeu ao próprio/readyznem aceitou uma entrega nos últimos 30 segundos. Os upstreams são sondados a cada 5 segundos; - 503 quando o spool está cheio, ou quando nenhum upstream está alcançável e o spool está com pelo menos 80% de ocupação.
Um balanceador de carga, portanto, continua enviando para um relay que está acumulando, e desvia o tráfego de um que não consegue mais recebê-lo.
Métricas do relay
| Métrica | Tipo | Significado |
|---|---|---|
mcpd_relay_accepted_total | counter | Requisições gravadas no spool |
mcpd_relay_refused_full_total | counter | Requisições recusadas com 503 porque o spool estava cheio |
mcpd_relay_failovers_total | counter | Requisições entregues a um upstream que não era o primeiro |
mcpd_relay_dead_letter_total | counter | Requisições que um upstream recusou de vez, movidas para dead/ |
mcpd_relay_spool_entries | gauge | Requisições esperando no spool |
mcpd_relay_spool_bytes | gauge | Bytes guardados no spool |
mcpd_relay_spool_oldest_seconds | gauge | Idade da requisição mais antiga em espera, ou 0 |
mcpd_relay_delivered_total | counter | Requisições que um upstream aceitou, com os rótulos upstream e order |
mcpd_relay_failed_attempts_total | counter | Tentativas que um upstream não aceitou, com os mesmos rótulos |
mcpd_relay_upstream_up | gauge | 1 quando o upstream respondeu nos últimos 30 segundos, 0 caso contrário |
mcpd_uptime_seconds | gauge | Segundos desde que este processo iniciou |
Um mcpd_relay_failovers_total que sobe significa que o
primário está falhando e o secundário está levando o tráfego dele.
mcpd_relay_spool_oldest_seconds mostra o quanto a entrega está
atrasada.
Várias regiões
Rode várias réplicas do daemon, em uma ou mais regiões, sobre um único
armazenamento Postgres ou ClickHouse, com as execuções no Postgres
(--runs-data postgres://...) ou desligadas: um armazenamento
de execuções embutido pertence a um processo.
O /readyz de cada réplica já reflete se ela alcança o
armazenamento. Coloque um relay em cada região na frente delas, com
--upstream listando primeiro o daemon mais próximo e, em
segundo, um daemon de outra região.
Quatro ajustes se comportam de outro jeito quando há mais de uma réplica:
- As chaves de idempotência ficam no armazenamento compartilhado, então um reenvio que cai em outra réplica continua sendo reconhecido. O Postgres registra a chave na mesma transação que o lote.
- Os tetos de cota por minuto são contados por réplica. Divida o teto que você quer pelo número de réplicas.
- A avaliação ao vivo, a amostragem de cauda e os alertas de monitores rodam em uma réplica de cada vez. As réplicas se revezam por meio de um lease no armazenamento compartilhado: quem o detém o renova a cada passada, o libera quando desliga, e outra réplica assume em até três passadas se ele parar. Toda réplica avalia seus monitores e objetivos, então cada uma responde à API de monitores, e só quem detém o lease envia o webhook. Configure-os do mesmo jeito em todas as réplicas.
- A análise de aspectos roda em toda réplica que a tem configurada. Configure-a em uma única réplica.
Implantações de exemplo
deploy/docker/docker-compose.multi-region.yml sobe o Postgres,
dois daemons e um relay em uma máquina, que é a topologia inteira em uma
forma que você pode experimentar.
O chart Helm aceita mode: relay. Três arquivos de valores em
deploy/helm/examples/ descrevem uma região cada:
region-core.yaml, region-standby.yaml e
region-edge-relay.yaml, este último para uma região que roda
somente um relay.
A seguir
- Copiar os dados para outros fornecedores: Encaminhamento.
- Sondas, métricas e os armazenamentos: Como executar.
- Cada opção: Configuração.