Rollout canário do auth native — JVM + native lado-a-lado¶
Documento obsoleto
Status: Obsoleto · Responsável: Gustavo Madruga · Atualizado em: 2026-09-14
Obsoleto
O corte terminou: o central-backend é native-only e o pipeline.yml não builda mais o jar
(decisão 0030). A página fica como registro do
procedimento; rollback hoje é o redeploy da tag imutável <sha>-native (Deploy).
Pré-requisito: o e2e native (
etc/tests/native/e2e-central-backend) verde e a decisão 0018. Este runbook é o corte em produção.
O auth é o SPOF do ecossistema — não se corta 100% pra native num passo. A estratégia: subir a
imagem native ao lado do JVM atual, com o Traefik do Coolify dividindo o tráfego por peso
(weighted round-robin); validar o native em produção com uma fração do tráfego; quando 100% ok,
desligar o JVM e seguir native-only (rollbacks futuros passam a ser o rollback normal de
container do Coolify).
⚠️ Validar na tua VM antes do corte real. Os nomes de serviço Traefik e o suporte a weighted entre dois recursos Coolify dependem da versão do Coolify/Traefik. Rode os passos de inspeção (§1) e teste os pesos com um recurso de baixo risco antes de aplicar no
auth. Coolify não tem UI de canário — isto é config dinâmica do Traefik, aplicada por arquivo.
0. Topologia¶
- Recurso A —
auth(JVM): o que já existe, roteandoauth.xadm.biz. Build Pack = Dockerfile (o Coolify builda o repo). - Recurso B —
auth-native: novo recurso Coolify, mesma env do A (chaves, DB, cipher, firebase), Build Pack = Docker Image apontando pra imagem native pronta no registry (§Build da imagem). - Traefik: em vez de cada recurso dono do router de
auth.xadm.biz(conflito), um router no arquivo dinâmico aponta pra um serviço weighted que balanceia entre A e B.
Build da imagem native (por que difere do JVM)¶
O native-image come ~6 GB de RAM + minutos de CPU — buildar no Coolify trava a VM de produção
(o coolify-prod-vm-freeze que motivou o e2e native off-host). Então, ao contrário do JVM (que o
Coolify builda do repo), a imagem native é buildada fora e o Coolify só puxa a imagem pronta.
O método é o Dockerfile.native hand-written na raiz do repo (builder native-image-community:25i2-ol8
→ ./gradlew nativeCompile -PnativeCI → runtime ubuntu:24.04), buildado e publicado pela CI — não
mais ./gradlew dockerBuildNative (plugin/wolfi). Quem builda e empurra é o workflow do GitHub
.github/workflows/build-native.yml (dispara em tags v*, publish-only, não faz deploy):
# a CI (GitHub) builda o Dockerfile.native e publica no registry:
# fonte.xadm.biz/xadm/central-backend:native-amd64 (+ :<sha>, e espelho ghcr.io)
# Coolify (Recurso B): Build Pack = Docker Image = fonte.xadm.biz/xadm/central-backend:native-amd64
#
# Para reproduzir/testar a MESMA imagem localmente (onde tiver Docker):
docker build -f Dockerfile.native -t central-backend:native-test .
Roda no runner GitHub (native-image é pesado, ~6 GB de builder; o runner hospedado aguenta, o
Forgejo self-hosted não — o .forgejo/workflows/ci.yml segue como gate JVM). O Recurso B só troca
qual tag puxa; o fluxo de deploy não muda.
1. Inspecionar os serviços Traefik (root da VM do Coolify)¶
# nomes dos serviços/routers que o Coolify gerou por container
docker exec coolify-proxy traefik version
ls -la /data/coolify/proxy/dynamic/
# lista os serviços conhecidos pelo Traefik (API interna, se habilitada)
docker exec coolify-proxy wget -qO- http://localhost:8080/api/http/services 2>/dev/null | tr ',' '\n' | grep -i auth
# ou pelos labels dos containers
docker inspect $(docker ps -q --filter name=auth) --format '{{.Name}} {{json .Config.Labels}}' | grep -i traefik
Anote os nomes de serviço dos dois containers (algo como auth-<id> e auth-native-<id>, com
sufixo @docker).
2. Definir o serviço weighted + router (arquivo dinâmico)¶
Crie /data/coolify/proxy/dynamic/auth-canary.yml (ajuste os name: aos anotados no §1):
http:
routers:
auth-canary:
rule: "Host(`auth.xadm.biz`)"
entryPoints: ["https"]
tls:
certResolver: letsencrypt # o mesmo resolver que os outros recursos usam
service: auth-canary
services:
auth-canary:
weighted:
services:
- name: "auth-<id>@docker" # Recurso A (JVM)
weight: 90
- name: "auth-native-<id>@docker" # Recurso B (native)
weight: 10
Importante: desabilite o router de domínio que o Coolify cria automaticamente em CADA recurso (senão dois routers disputam
auth.xadm.biz). No Coolify, em cada recurso, use um domínio interno/placeholder (ex.:auth-a.internal,auth-b.internal) para o Traefik ainda criar o serviço de cada um, mas o router público deauth.xadm.bizser só o do arquivo acima. Confirme que só um router casa o Host:docker exec coolify-proxy wget -qO- http://localhost:8080/api/http/routers | tr ',' '\n' | grep auth.xadm.biz.
O Traefik recarrega o arquivo dinâmico sozinho (watch). Sem restart.
3. Escalar o peso (validação progressiva)¶
Comece 90/10, observe, suba. Editar o weight: e salvar basta (hot-reload):
# ex.: 50/50 — editar auth-canary.yml e trocar os weights, depois:
docker exec coolify-proxy wget -qO- http://localhost:8080/api/http/services | tr ',' '\n' | grep -A2 auth-canary
A cada degrau (10 → 50 → 100%), monitorar (§4). Sinal ruim em qualquer degrau → rollback (§5).
4. Monitoração (o que olhar durante o canário)¶
- Emissão/validação de JWT: logs do
auth-native—Login pAbast OK, ausência de500/INTERNAL_SERVER_ERRORem/api/auth/**,/.well-known/jwks.jsone/api/apps/**. O PowerSync cacheia o JWKS — a chave é a mesma (mesmokid=auth-1), então não há invalidação. - GlitchTip (bug.xadm.biz): picos de erro do recurso
auth-native. - RSS/boot:
docker statsdo container native (esperado ~43 MiB, boot ~1 s). - Comparar A vs B: as respostas dos dois recursos devem ser idênticas (mesmo contrato).
docker logs -f $(docker ps -q --filter name=auth-native) | grep -iE "login|error|exception|500"
5. Rollback imediato¶
Voltar o peso do native a 0 (ou remover o serviço B do weighted) — hot-reload, sem deploy:
# editar auth-canary.yml: weight do auth-native -> 0 (ou weight do JVM -> 100 e remover B)
# Traefik recarrega sozinho; todo o tráfego volta pro JVM em segundos.
Se o arquivo dinâmico em si for o problema, rm /data/coolify/proxy/dynamic/auth-canary.yml e
reabilitar o router de domínio no Recurso A (JVM) pela UI do Coolify.
6. Collapse (native-only)¶
Native em 100% e estável por uma janela combinada:
- Reabilitar
auth.xadm.bizdireto no Recurso B (native) pela UI do Coolify (router próprio). - Remover
/data/coolify/proxy/dynamic/auth-canary.yml. - Desligar/remover o Recurso A (JVM).
- A partir daqui, rollback = o rollback normal de container/deploy do Coolify no Recurso B.
Notas¶
- Build da imagem native: off-host + push pro registry do Forgejo (ver §Build da imagem). O Coolify não compila native no deploy (build-spike travaria a VM).
- Env idêntica: o Recurso B precisa das MESMAS
AUTH_PRIVATE_KEY/AUTH_PUBLIC_KEY,AUTH_CIPHER_KEY,DATASOURCES_*,AUTH_FIREBASE_PROJECT_ID.AUTH_FIREBASE_JWKS_URLfica no default (Google) — o override só existe pro e2e.