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ágio | Ambiente | Objetivo | Aprovação |
|---|---|---|---|
| Commit | Runner de CI | Compilação, testes unitários, análise estática, empacotamento, publicação do artefato | Automática |
| Integração | Runner de CI com containers | Testes contra banco de dados e broker reais e contra os contratos dos serviços dependentes | Automática |
| Aceitação | Ambiente de testes dedicado, implantado a partir do artefato | Testes funcionais de ponta a ponta, scans de segurança | Automática |
| Staging / pré-produção | Dimensionamento e formato dos dados parecidos com produção | Testes de performance, smoke tests, testes exploratórios manuais se necessário | Automática ou aprovação manual |
| Produção | Real | Rollout progressivo, verificação de saúde, rollback em caso de falha | Aprovaçã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.1com 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:
Expandir
Adicione a nova coluna, tabela ou índice sem remover nada. A versão em execução a ignora.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.Backfill
Uma migração ou um job batch copia os dados históricos para a nova estrutura.Fazer deploy de código que lê a nova estrutura
Agora nada depende da coluna antiga, exceto a gravação dupla.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 undofaz 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 -Pprodque 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-SNAPSHOTpode 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.