DevOps, CI/CD e contêineres

Pipelines de entrega contínua: estágios, artefatos e estratégias de deploy

Intermediário10 min de leituraAtualizado

Esta página foi traduzida do inglês com a ajuda de IA e conferida com o original. Ler a versão em inglês

Um pipeline de deploy é o caminho automatizado de um commit até uma release rodando em produção, e cada estágio acrescenta confiança de que a mudança é segura. Este guia explica como desenhar os estágios, por que você gera o artefato uma única vez e o promove pelos ambientes, como versionar releases e evoluir o schema do banco de dados sem downtime, e quando escolher deploy rolling, blue-green ou canary.

O que você vai aprender

  • Diferenciar integração contínua, entrega contínua e deploy contínuo
  • Desenhar um pipeline cujos estágios fazem o build uma única vez e promovem o mesmo artefato pelos ambientes
  • Versionar artefatos com versionamento semântico e armazená-los em um repositório de artefatos
  • Aplicar migrações de banco de dados expand-and-contract que continuem compatíveis com a versão em execução
  • Escolher entre deploy rolling, blue-green e canary e planejar o rollback

A integração contínua prova que o código compila e que os testes passam. Isso é necessário, mas está longe de ser suficiente para uma release: o artefato empacotado ainda precisa ser configurado para um ambiente, implantado ao lado de um banco de dados cujo schema talvez precise mudar, verificado contra as dependências reais e colocado sob tráfego real sem derrubar requisições. Um pipeline de deploy automatiza esse caminho do commit até a produção, e cada estágio acrescenta evidências de que a mudança é segura. Entrega contínua é o estado em que o pipeline é confiável o bastante para que qualquer build verde possa ir para produção com o apertar de um botão; deploy contínuo é quando você tira o botão. Este guia trata de como desenhar esse pipeline.

Entrega, deploy e o que significa "pronto para release"

Três termos costumam ser confundidos:

  • Integração contínua — todo merge passa por build e testes automaticamente.
  • Entrega contínua — todo build que passa pelo pipeline é empacotado, versionado e verificado em ambientes parecidos com produção, de modo que pode ir para produção a qualquer momento por decisão manual.
  • Deploy contínuo — todo build que passa vai para produção automaticamente.

A distância entre o segundo e o terceiro é uma escolha de negócio, não técnica. O que não é opcional é o papel do pipeline: mostrar, para cada commit, se ele está pronto para release, e transformar a release em uma ação rotineira em vez de um evento. Se fazer uma release ainda envolve uma planilha de passos manuais e um fim de semana perdido, você tem um servidor de CI, não entrega contínua.

Os estágios de um pipeline

Um pipeline leva a mesma mudança por estágios que trocam velocidade por confiança. Uma sequência representativa para um serviço de backend:

EstágioAmbienteObjetivoAprovação
CommitRunner de CICompilação, testes unitários, análise estática, empacotamento, publicação do artefatoAutomática
IntegraçãoRunner de CI com containersTestes contra banco de dados e broker reais e contra os contratos dos serviços dependentesAutomática
AceitaçãoAmbiente de testes dedicado, implantado a partir do artefatoTestes funcionais de ponta a ponta, scans de segurançaAutomática
Staging / pré-produçãoDimensionamento e formato dos dados parecidos com produçãoTestes de performance, smoke tests, testes exploratórios manuais se necessárioAutomática ou aprovação manual
ProduçãoRealRollout progressivo, verificação de saúde, rollback em caso de falhaAprovação manual (entrega) ou automática (deploy)

Duas propriedades importam mais do que a lista exata. Primeiro, os estágios rodam em ordem, com semântica fail-fast: uma falha no estágio de commit significa que nada depois dele é executado, então falhas baratas aparecem em minutos e os ambientes caros não são desperdiçados. Segundo, todo estágio depois do primeiro trabalha sobre o mesmo artefato, implantado com a mesma automação usada em produção. Fazer o deploy no ambiente de aceitação com o script de deploy de produção é justamente o teste desse script.

Build uma vez, promova em todo lugar

A regra que molda todo o resto: gere o artefato uma única vez e depois promova-o. O estágio de commit produz um jar, uma imagem de container ou um zip de arquivos estáticos — com uma versão única — e o envia para um repositório de artefatos. Os estágios seguintes baixam exatamente esse artefato pela versão e fazem o deploy. Nada é recompilado.

Consequências:

  • O artefato não pode conter valores específicos de ambiente. URLs de banco de dados, credenciais, feature flags e hostnames são injetados na hora do deploy por variáveis de ambiente, arquivos de configuração montados ou um serviço de configuração. É exatamente o princípio "config" do twelve-factor app.
  • O artefato precisa de um lugar estável: Sonatype Nexus, JFrog Artifactory, GitHub Packages ou um registry de containers como Harbor ou Amazon ECR. O repositório é a fonte da verdade sobre o que existe e o que foi implantado onde.
  • O artefato precisa ser imutável. Republicar a versão 2.3.1 com conteúdo diferente é mentir para todos os ambientes que já a baixaram. Os registries de containers garantem isso com digests (sha256:…); os repositórios de artefatos, com repositórios de "release" que recusam sobrescrita.
  • A rastreabilidade fica trivial: uma versão corresponde a um commit, a um número de build e a um conjunto de deploys.

Versionamento

Escolha um esquema e aplique-o de forma mecânica. Dois dominam.

O versionamento semântico (MAJOR.MINOR.PATCH) comunica compatibilidade: um patch corrige bugs, uma minor adiciona funcionalidades retrocompatíveis e uma major quebra a compatibilidade. É a escolha certa para bibliotecas e para qualquer API da qual outros times dependam. Builds de pré-release levam um sufixo — 2.4.0-rc.1 — e os metadados de build, um sufixo com +.

Versões baseadas em número de build ou em commit (1.0.437, 2026.09.07-a1b2c3d) servem para serviços que só são implantados, nunca consumidos como dependência. A versão responde "que commit é este?" em vez de "o que ela quebrou?".

Seja qual for o esquema, quem atribui a versão é o pipeline, não o editor de um desenvolvedor. No Maven, isso significa remover o -SNAPSHOT na hora da release com o release plugin, versions:set ou a propriedade CI-friendly ${revision}; para containers, significa taguear a imagem com a versão e levar o digest para o manifesto de deploy.

# CI-friendly Maven versioning: the pipeline supplies the revision
mvn -B -Drevision=1.4.$BUILD_NUMBER deploy

# Tag the image twice: a human-readable version and the commit
docker build -t registry.example.com/shop/orders:1.4.$BUILD_NUMBER \
             -t registry.example.com/shop/orders:$GIT_COMMIT .
docker push --all-tags registry.example.com/shop/orders

Migrações de banco de dados

Bancos de dados são a parte mais difícil da entrega contínua porque um schema, ao contrário de um container, não pode simplesmente ser substituído. Duas ferramentas dominam no mundo Java — Flyway (scripts SQL versionados) e Liquibase (changelogs em XML, YAML ou SQL) — e ambas funcionam do mesmo jeito: os scripts de migração ficam no controle de versão junto com o código, cada um tem uma versão, a ferramenta registra em uma tabela de metadados quais já foram aplicados e, na inicialização ou como um passo do pipeline, aplica em ordem os que faltam.

-- src/main/resources/db/migration/V12__add_order_channel.sql
ALTER TABLE orders ADD COLUMN channel VARCHAR(20);
UPDATE orders SET channel = 'web' WHERE channel IS NULL;

A disciplina que torna as migrações seguras é o expand and contract. Uma mudança de schema que a versão anterior do código não tolera quebra deploys rolling (instâncias antigas e novas rodam ao mesmo tempo) e torna o rollback impossível. Por isso, divida toda mudança incompatível em passos compatíveis:

  1. Expandir

    Adicione a nova coluna, tabela ou índice sem remover nada. A versão em execução a ignora.
  2. Fazer deploy de código que grava nas duas

    A nova versão grava na estrutura antiga e na nova e lê da antiga. O rollback é seguro: o código antigo continua encontrando tudo de que precisa.
  3. Backfill

    Uma migração ou um job batch copia os dados históricos para a nova estrutura.
  4. Fazer deploy de código que lê a nova estrutura

    Agora nada depende da coluna antiga, exceto a gravação dupla.
  5. Contrair

    Em uma release posterior, pare de gravar na estrutura antiga e remova-a.

Isso exige mais releases do que um único ALTER TABLE ... RENAME, e esse é justamente o objetivo: cada release é pequena, compatível com as vizinhas e reversível.

Estratégias de deploy

Com o artefato e o schema prontos, o último estágio é virar o tráfego. As estratégias diferem em custo de capacidade, exposição e velocidade de rollback.

Recreate derruba a versão antiga e depois sobe a nova. É simples, e é a única opção quando duas versões realmente não podem coexistir, mas significa downtime. Aceitável para sistemas batch internos, não para uma API pública.

Rolling substitui as instâncias algumas de cada vez — os Deployments do Kubernetes fazem isso por padrão, controlados por maxSurge e maxUnavailable. Sem downtime e sem capacidade extra, mas a versão antiga e a nova atendem tráfego ao mesmo tempo durante todo o rollout, então a mudança precisa ser compatível nos dois sentidos (o que a disciplina expand-and-contract garante).

# Deployment excerpt: rolling update that never drops below full capacity
spec:
  replicas: 6
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 2          # up to 8 pods during the rollout
      maxUnavailable: 0    # never fewer than 6 ready pods
  template:
    spec:
      containers:
        - name: orders
          image: registry.example.com/shop/orders@sha256:3f1c…   # immutable reference
          readinessProbe:
            httpGet: { path: /actuator/health/readiness, port: 8080 }

É a readiness probe que torna o rolling seguro: o Kubernetes só envia tráfego para um pod novo depois que a probe tem sucesso, e só remove um antigo quando já há pods novos prontos em número suficiente.

Blue-green mantém dois ambientes completos. O blue atende a produção; o green recebe a nova versão, passa pelos smoke tests e então o roteador ou o load balancer vira todo o tráfego para o green. Fazer rollback é virar de volta. Os custos são o dobro de capacidade durante a virada e um banco de dados compartilhado pelas duas cores, o que, mais uma vez, exige mudanças de schema compatíveis.

Canary envia uma pequena porcentagem do tráfego (1%, depois 10%, depois 50%) para a nova versão enquanto observa a taxa de erros e a latência, e avança ou aborta automaticamente. É a estratégia de menor risco para serviços voltados ao usuário, mas exige boas métricas e uma camada de divisão de tráfego — um service mesh, um ingress controller com roteamento ponderado ou uma ferramenta como Argo Rollouts ou Flagger.

Feature flags são ortogonais: o código é implantado para todo mundo, mas a funcionalidade fica desligada até que uma flag a ative para um grupo de usuários. Flags desacoplam o deploy da liberação e permitem um "rollback" instantâneo trocando a flag, ao custo de caminhos de código morto que em algum momento precisam ser limpos.

Rollback e verificação

Todo pipeline precisa de uma resposta para "o que acontece quando a produção está errada?" que leve minutos, não uma sala de guerra.

  • Faça um roll forward refazendo o deploy do artefato anterior. Como os artefatos são imutáveis e versionados, a versão anterior é uma entrada conhecida e saudável para o mesmo passo de deploy. kubectl rollout undo faz isso para um Deployment; em blue-green, é uma mudança no roteador.
  • Nunca faça rollback do banco de dados. Com expand-and-contract, o código anterior funciona com o novo schema; uma "down migration" sobre dados de produção é muito mais arriscada do que o bug do qual você está tentando fugir.
  • Automatize o health check depois do deploy. Um smoke test que chama alguns endpoints e um curto período observando a taxa de erros pegam a maioria das releases ruins antes que um usuário as reporte. Argo Rollouts e Flagger tornam o abort automático; um pipeline mais simples pode rodar um script e falhar o estágio.

Aprovações e compliance

Ambientes regulados costumam exigir um aprovador separado, uma trilha de auditoria e um registro de mudança. Um pipeline atende a isso melhor do que uma reunião: o passo input de um pipeline Jenkins ou uma regra de proteção de ambiente no GitHub registra quem aprovou o quê e quando, a versão do artefato e o commit fazem parte do log, e o próprio script de deploy é versionado e revisável. O que os auditores precisam é evidência de controle, e um pipeline automatizado produz muito mais disso do que qualquer processo manual já produziu.

Erros comuns

  • Builds específicos por ambiente. Um mvn package -Pprod que embute URLs de produção no jar significa que o staging testou outra coisa. Externalize a configuração.
  • Artefatos snapshot em produção. Uma 1.4-SNAPSHOT pode mudar sem você perceber entre staging e produção. Use apenas versões de release.
  • Passos manuais escondidos no pipeline. Um estágio que diz "rode o script SQL do ticket" não está automatizado. Se uma pessoa precisa fazer algo, o pipeline deve fazer isso por ela e deixar registrado.
  • Testar o deploy pela primeira vez em produção. Faça o deploy em todos os ambientes anteriores com a automação de produção. Scripts de deploy também têm bugs.
  • Down migrations como plano de rollback. Raramente são testadas e com frequência são destrutivas. Em vez disso, projete mudanças de schema compatíveis com versões futuras.
  • Rollout sem readiness checks. Um rolling update em que cada pod se declara pronto no instante em que o processo sobe serve erros a partir de instâncias que ainda não terminaram o warm-up. Ligue a readiness probe a algo que faça sentido.
  • Tratar o pipeline como pronto. O lead time vai crescendo à medida que estágios são adicionados. Meça o tempo do commit até a produção e elimine as esperas.

Principais conclusões

  • Um pipeline de deploy é a sequência ordenada e automatizada de estágios que prova que cada commit está pronto para release; entrega contínua é ter esse pipeline, deploy contínuo é deixá-lo fazer a release sem pedir permissão.
  • Gere o artefato uma única vez, versione-o, armazene-o de forma imutável e promova os mesmos bytes por todos os ambientes, com a configuração injetada na hora do deploy.
  • Atribua as versões no pipeline: versionamento semântico para tudo de que outros dependem, versões baseadas em build ou commit para o que é apenas implantado.
  • Aplique mudanças de banco de dados como migrações versionadas em passos expand-and-contract, para que releases consecutivas sejam compatíveis nos dois sentidos e o rollback nunca toque no schema.
  • Escolha a estratégia de deploy por risco e capacidade: rolling para a maioria dos serviços, blue-green para virada e rollback instantâneos, canary quando as métricas conseguem avaliar uma fatia do tráfego.
  • Rollback significa refazer o deploy do artefato anterior em minutos; verifique cada deploy automaticamente com smoke tests e monitoramento da taxa de erros.

Fontes e leituras complementares

Perguntas frequentes

Qual é a diferença entre entrega contínua e deploy contínuo?
Entrega contínua significa que toda mudança que passa pelo pipeline está comprovadamente pronta para deploy e pode ir para produção com uma única ação, mas quem decide quando é uma pessoa. O deploy contínuo elimina essa decisão: todo build verde vai para produção automaticamente. A entrega é pré-requisito do deploy; muitos times regulados ou que atendem clientes diretamente param na entrega de propósito, para poder escolher o momento das releases.
Por que não devo refazer o build da aplicação para cada ambiente?
Porque o artefato que você testou em staging seria diferente do que roda em produção: outra execução do compilador, possivelmente outra resolução de dependências e qualquer valor específico do ambiente embutido nele. Gere um único artefato, armazene-o com uma versão e injete as diferenças entre ambientes na hora do deploy, via configuração. Assim, uma execução verde em staging vira evidência sobre exatamente os mesmos bytes que vão para produção.
Como faço o deploy de uma mudança no banco de dados sem downtime?
Divida-a em passos retrocompatíveis. Primeiro adicione a nova coluna ou tabela, faça o deploy de um código que grava na estrutura antiga e na nova, faça o backfill dos dados, faça o deploy de um código que só lê da nova estrutura e remova a antiga em uma release posterior. Cada deploy é compatível com a versão anterior do código, então um rollback nunca exige rollback do schema.
Quando uma canary release é melhor do que blue-green?
Use canary quando você consegue observar a taxa de erros e a latência da nova versão em uma fatia do tráfego real e quer que o risco aumente aos poucos. Blue-green é mais simples e oferece virada e rollback instantâneos, mas expõe todos os usuários de uma vez e exige o dobro de capacidade durante a virada. Se você não tem métricas boas o suficiente para avaliar um canary, blue-green com rollback rápido é a escolha mais segura.