Skip to main content
O Midaz aplica migrações de esquema PostgreSQL por meio de imagens dedicadas de runner (uma para o Ledger, uma para o Tracer), desacopladas dos binários da aplicação. As aplicações não migram mais na inicialização. Elas sobem contra um esquema que o runner já migrou.
Esse modelo é o que roda sob make up no desenvolvimento local e sob o Helm chart no Kubernetes. Se você usa make up, não precisa de nenhuma etapa manual. O compose trava a aplicação no runner de migração.

Por que um runner dedicado


O antigo migrador embutido no processo rodava a cada inicialização da aplicação, a partir do diretório de trabalho. Sob um contexto de segurança de pod reforçado (distroless:nonroot, runAsUser: 1000, readOnlyRootFilesystem: true, capabilities: drop [ALL]), esse caminho falhava com permission denied e entrava em crash loop no pod. O runner dedicado evita isso:
  • As migrações rodam uma vez, como uma etapa separada, antes de a aplicação iniciar.
  • A imagem da aplicação não inclui mais o SQL de migração e nunca escreve no esquema.
  • A própria imagem do runner roda sem problemas sob o mesmo contexto de segurança reforçado (veja Postura de segurança).

Imagens do runner


Cada release inclui duas imagens: As duas imagens compartilham a mesma estrutura:
  • Base: migrate/migrate v4.19.1 (fixada).
  • Roda como USER 65532:65532 (non-root, independente de UID).
  • Entrypoint em shell POSIX que monta uma DSN a partir de variáveis de ambiente, executa migrate ... up e encerra.
  • Não escreve nada em disco. O Postgres controla o progresso das migrações na tabela schema_migrations, então readOnlyRootFilesystem: true é seguro.
O runner do Ledger aplica onboarding primeiro, depois transaction, em uma única invocação. O runner do Tracer aplica seu único banco de dados em uma única passagem.

Contrato de ambiente


Cada entrypoint aceita um override de URL pronto ou as variáveis individuais DB_*. O override de URL tem precedência quando definido.

Runner do Ledger

Runner do Tracer

Formato da DSN

Quando o entrypoint monta a DSN a partir das variáveis DB_*, ele produz:
A senha é codificada em percent-encoding para que caracteres reservados de URI (% @ : / ? # & + space [ ]) não quebrem a DSN. O % é codificado primeiro para que escapes já inseridos não sejam codificados duas vezes. O entrypoint registra apenas marcadores de fase (applying onboarding migrations, applying transaction migrations, applying tracer migrations, migrations complete). Ele nunca imprime credenciais nem a DSN montada.

Desenvolvimento local


make up

A partir da raiz do repositório, make up:
  1. Inicia a infraestrutura e aguarda até que o Postgres esteja saudável.
  2. Inicia os serviços de compose ledger-migrate e tracer-migrate, que rodam uma única vez.
  3. Inicia cada serviço de aplicação, que usa depends_on para seu serviço de migração com condition: service_completed_successfully.
A aplicação nunca inicia contra um banco de dados não migrado.

Targets de make no host

Cada componente expõe targets de Make no host, que rodam migrações diretamente contra um banco de dados local, por exemplo quando você desenvolve uma nova migração. Os targets usam a CLI fixa do golang-migrate e leem as configurações de conexão de .env. Ledger (components/ledger): Tracer (components/tracer):

Exemplo: rodar o runner do Ledger manualmente

Raramente você precisa invocar a imagem do runner manualmente, porque o travamento do compose faz isso por você. Quando precisar, aponte o runner para seus bancos de dados via DB_* (ou *_DATABASE_URL) e rode uma vez:
O runner sai com 0 em caso de sucesso (incluindo o caso idempotente de no-op) e com código diferente de zero em caso de falha.

Deploy no Kubernetes


Em produção, rode cada runner de migração como um Job do Kubernetes (normalmente um PreSync Job do Argo CD ou um hook do Helm) que é concluído antes do rollout da aplicação. O Helm chart já configura isso para você. As seções abaixo cobrem as propriedades que importam se você criar seus próprios manifestos.
Para deploys multi-tenant, o runner é intencionalmente independente de tenant: ele migra os bancos de dados apontados pelo seu ambiente, uma vez. O fan-out por tenant é uma responsabilidade do deploy. Rode o Job uma vez por banco de dados de tenant, com os valores de DB_* (ou override de URL) daquele tenant.

Postura de segurança

O runner funciona sob um contexto de segurança de pod reforçado:
  • Non-root: a imagem define USER 65532:65532.
  • Independente de UID: os arquivos de migração pertencem ao root e têm permissão de leitura para todos, então qualquer UID pode lê-los.
  • Seguro para readOnlyRootFilesystem: o migrate não escreve nenhum estado no sistema de arquivos.
A mesma imagem roda sem problemas sob runAsNonRoot: true, readOnlyRootFilesystem: true e capabilities: drop [ALL].

TLS

Os entrypoints respeitam o sslmode definido no ambiente (DB_ONBOARDING_SSLMODE, DB_TRANSACTION_SSLMODE ou DB_SSL_MODE), com padrão disable. Recomenda-se que deploys de produção definam require (ou algo mais restritivo). O runner em shell não reproduz a aplicação de TLS em código, a classificação de erros ou a telemetria que as aplicações usam para suas próprias conexões. Você controla o TLS das migrações apenas por meio do sslmode, em troca de um runner mínimo e sem dependências.

Convenções e idempotência


  • Pareadas up/down: cada migração tem um arquivo .up.sql e um .down.sql.
  • Idempotente: rodar migrate up quando o esquema já está na versão mais recente é um no-op. Rodar o runner novamente após uma aplicação parcial ou completa é seguro.
  • CLI fixa: as imagens do runner e os targets make do host fixam todos o golang-migrate na versão v4.19.1.

Atualizando a partir de versões anteriores


Você não precisa de nenhuma migração manual de dados ao atualizar a partir de uma versão do Midaz em que a aplicação migrava na inicialização. O esquema em disco é o mesmo. Para adotar o novo modelo:
  1. Baixe o release que inclui as imagens do runner.
  2. No Kubernetes, adicione os Jobs midaz-ledger-migrations e midaz-tracer-migrations ao seu pipeline de deploy para que rodem antes do rollout da aplicação. Se você usa o Helm chart oficial, isso já é feito para você.
  3. No desenvolvimento local, o make up continua funcionando como antes. O compose agora trava automaticamente as aplicações nos novos serviços de migração de execução única.

Páginas relacionadas