Pular para conteúdo

Entrega

São 09:40 do dia da venda. Um bug no inventory faz uma reserva de assento expirar dois minutos antes da hora, e a correção é uma mudança de uma linha. Em um time sem pipeline de entrega, a correção espera: alguém precisa fazer o build, alguém precisa copiar o jar, alguém precisa reiniciar servidores um a um, e ninguém quer fazer isso vinte minutos antes do maior pico de tráfego do ano. Em um time com um bom pipeline, a correção é aceita, construída, varrida, publicada para 5 % do tráfego, verificada automaticamente contra os SLOs e promovida — ou revertida — sem que ninguém toque em um servidor.

Esta página constrói esse pipeline em quatro camadas: a infraestrutura é código, cada mudança é integrada e testada continuamente, o cluster puxa seu estado desejado do Git, e cada release é exposta gradualmente e julgada por métricas.

flowchart LR
    pr["Pull request<br/>correção do inventory"] --> ci["CI: build · teste · varredura<br/>SBOM · assinatura · push da imagem"]
    ci --> cfg["Repo de config:<br/>digest da imagem atualizado"]
    cfg --> argo["Argo CD:<br/>sincroniza o cluster com o Git"]
    argo --> canary["Canary 5 % → 25 % → 50 %"]
    canary --> an{"Análise de métricas<br/>taxa de erros, p99"}
    an -->|"saudável"| full["100 % — promovido"]
    an -->|"degradado"| rb["Rollback automático"]

Infraestrutura como código

O ambiente de produção do BoxOffice tem uma VPC, sub-redes, um cluster Kubernetes, um PostgreSQL gerenciado, um Redis gerenciado, um cluster Kafka, buckets, registros DNS e papéis IAM — algumas centenas de recursos. Criados com cliques em um console, eles não podem ser revisados, reproduzidos ou reconstruídos após um desastre, e o staging se afasta silenciosamente da produção. Infraestrutura como código (IaC) descreve esses recursos em arquivos versionados que uma ferramenta aplica.

Propriedade Por que importa para o BoxOffice
Declarativa Você escreve o que deve existir; a ferramenta calcula o plano de criar/atualizar/apagar
Revisável Uma mudança no tamanho da instância do banco é um pull request com diff e aprovador
Reproduzível Staging e um ambiente de teste de carga são os mesmos módulos com variáveis diferentes — paridade para testes de desempenho
Recuperável Depois da perda de uma região, o ambiente é recriado a partir do código (os dados vêm dos backups — veja Recuperação de desastres)

O Terraform (HashiCorp) é a ferramenta de IaC mais usada. Em 2023 a HashiCorp mudou sua licença para a Business Source License; o fork comunitário OpenTofu, hospedado pela Linux Foundation, mantém a licença de código aberto MPL 2.0 e a mesma linguagem (HCL) e o mesmo fluxo de CLI1. O trecho abaixo funciona em ambos:

infra/modules/data/main.tf
variable "env"            { type = string }                 # "staging" | "prod"
variable "db_instance"    { type = string }                 # e.g. "db.r6g.large"
variable "private_subnets" { type = list(string) }

resource "aws_db_subnet_group" "boxoffice" {
  name       = "boxoffice-${var.env}"
  subnet_ids = var.private_subnets
}

resource "aws_db_instance" "inventory" {
  identifier                  = "boxoffice-inventory-${var.env}"
  engine                      = "postgres"
  engine_version              = "17"
  instance_class              = var.db_instance
  allocated_storage           = 100
  multi_az                    = var.env == "prod"           # standby in another AZ
  db_subnet_group_name        = aws_db_subnet_group.boxoffice.name
  username                    = "boxoffice"
  manage_master_user_password = true                        # password kept in Secrets Manager
  backup_retention_period     = 7
  deletion_protection         = var.env == "prod"
  tags = { project = "boxoffice", env = var.env, service = "inventory" }
}

resource "aws_s3_bucket" "tickets" {
  bucket = "boxoffice-tickets-${var.env}"
  tags   = { project = "boxoffice", env = var.env }
}

output "inventory_endpoint" { value = aws_db_instance.inventory.endpoint }
infra/envs/prod/main.tf
terraform {
  backend "s3" {                                  # shared, locked state
    bucket       = "boxoffice-tfstate"
    key          = "prod/terraform.tfstate"
    region       = "us-east-1"
    use_lockfile = true
  }
}

module "data" {
  source          = "../../modules/data"
  env             = "prod"
  db_instance     = "db.r6g.large"
  private_subnets = module.network.private_subnet_ids
}
tofu init          # download providers, configure the state backend
tofu plan          # show what would change — reviewed in the pull request
tofu apply         # apply exactly the reviewed plan

A ferramenta mantém um arquivo de estado que mapeia cada recurso do código para um ID de recurso real. Ele precisa ser remoto e com lock (dois engenheiros aplicando ao mesmo tempo o corrompem) e é sensível (pode conter segredos). Quando alguém muda um recurso à mão, o próximo plan mostra a deriva (drift) — um plan agendado na CI que alerta sobre deriva mantém o console honesto.

Ferramenta Estilo Observações
Terraform / OpenTofu HCL declarativa, qualquer provedor O padrão comum; enorme ecossistema de providers
Pulumi Linguagens de uso geral (TypeScript, Python, Go, Java) Laços e abstrações em uma linguagem de verdade; mesmo modelo plan/apply
AWS CloudFormation / CDK, Azure Bicep, GCP Infrastructure Manager Nativas do provedor Sem arquivo de estado para gerenciar; uma nuvem só
Crossplane Recursos customizados do Kubernetes reconciliados por controladores Infraestrutura gerenciada pelo mesmo laço GitOps das aplicações

Integração contínua

Integração contínua (CI) significa que toda mudança é integrada à branch principal com frequência e verificada automaticamente por build e testes. Entrega contínua (CD) significa que toda mudança verificada pode ser liberada a qualquer momento; deploy contínuo vai um passo além e libera cada mudança automaticamente2.

A CI do BoxOffice roda no GitHub Actions. Em um pull request ela faz build e testa; na main ela também publica uma imagem assinada e propõe a nova versão ao repositório de configuração:

.github/workflows/inventory.yaml
name: inventory
on:
  pull_request: { paths: [ "inventory/**" ] }
  push:         { branches: [ main ], paths: [ "inventory/**" ] }

permissions:
  contents: read
  packages: write        # push to GitHub Container Registry
  id-token: write        # keyless signing with Sigstore
  attestations: write

jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      digest: ${{ steps.push.outputs.digest }}
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with: { distribution: temurin, java-version: "21", cache: maven }
      - name: Unit and integration tests (Testcontainers)
        run: mvn -B -f inventory/pom.xml verify
      - uses: docker/setup-buildx-action@v3
      - uses: docker/login-action@v3
        with: { registry: ghcr.io, username: "${{ github.actor }}", password: "${{ secrets.GITHUB_TOKEN }}" }
      - name: Build and push image
        id: push
        uses: docker/build-push-action@v6
        with:
          context: inventory
          push: ${{ github.ref == 'refs/heads/main' }}
          tags: ghcr.io/boxoffice/inventory:${{ github.sha }}
          sbom: true                      # SBOM attached to the image
          provenance: mode=max            # SLSA provenance
      - name: Scan for critical vulnerabilities
        uses: aquasecurity/trivy-action@0.28.0
        with:
          image-ref: ghcr.io/boxoffice/inventory:${{ github.sha }}
          severity: CRITICAL
          exit-code: "1"
      - name: Sign image (keyless)
        if: github.ref == 'refs/heads/main'
        run: cosign sign --yes ghcr.io/boxoffice/inventory@${{ steps.push.outputs.digest }}

  propose-release:
    needs: build
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - name: Bump the image digest in the config repo (staging overlay)
        run: |
          git clone https://x-access-token:${{ secrets.CONFIG_REPO_TOKEN }}@github.com/<your-org>/boxoffice-config.git
          cd boxoffice-config/apps/inventory/overlays/staging
          kustomize edit set image inventory=ghcr.io/boxoffice/inventory@${{ needs.build.outputs.digest }}
          git commit -am "inventory: ${{ github.sha }}" && git push

Repare no que o pipeline não faz: ele nunca executa kubectl apply contra a produção e não guarda credenciais do cluster. Ele só escreve no Git. O cluster puxa.

No código do BoxOffice: CI e Deploy (lab 11)

O Hands-on 11 — CI constrói uma versão menor desse pipeline para um único repositório: um workflow CI testa todos os serviços numa matriz, roda um orçamento de desempenho com k6 contra o catalog (veja Planejamento de capacidade — orçamentos de desempenho) e só então — na main — constrói, escaneia, atesta e publica a imagem. Leia a linha needs:: nenhuma imagem existe se os testes e o orçamento não passaram. O scan bloqueia só vulnerabilidades críticas que têm correção (um gate que falha por tudo acaba desligado), e a assinatura usa as atestações de build do GitHub em vez do cosign. Um workflow separado, Deploy, escreve a nova tag (sha-…, não um digest) em k8s/overlays/gitops no mesmo repositório — o "repositório de configuração" é uma pasta.

.github/workflows/ci.yaml (image job: order of the jobs, the vulnerability gate, provenance)
image:
  name: image (${{ matrix.service }})
  needs: [test, performance]
  if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    - name: Gate - no CRITICAL vulnerability that has a fix
      uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
      with:
        image-ref: boxoffice-${{ matrix.service }}:ci
        format: table
        severity: CRITICAL
        ignore-unfixed: true
        exit-code: "1"
    - name: Attest build provenance (who built it, from which commit, how)
      uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
      with:
        subject-name: ${{ env.IMAGE }}
        subject-digest: ${{ steps.push.outputs.digest }}
        push-to-registry: true
.github/workflows/deploy.yaml (the only thing CD does: a commit)
- name: Set the catalog image of the gitops overlay to this commit
  env:
    SHA: ${{ github.event.workflow_run.head_sha }}
  run: |
    OWNER="${GITHUB_REPOSITORY_OWNER,,}"
    cd k8s/overlays/gitops
    kustomize edit set image "boxoffice/catalog=ghcr.io/${OWNER}/boxoffice-catalog:sha-${SHA}"
    git diff --stat
- name: Commit and push (a commit made with GITHUB_TOKEN does not trigger CI again)
  env:
    SHA: ${{ github.event.workflow_run.head_sha }}
  run: |
    git config user.name "github-actions[bot]"
    git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
    git add k8s/overlays/gitops/kustomization.yaml
    git diff --cached --quiet && { echo "already deployed"; exit 0; }
    git commit -m "deploy: catalog sha-${SHA::7}"
    git push

Mantenha a CI rápida

Um pipeline que leva 40 minutos é um pipeline que as pessoas contornam. Faça cache das dependências (cache: maven), rode os testes unitários antes dos testes de integração lentos, construa imagens com cache de camadas e execute jobs independentes em paralelo. A pesquisa DORA associa lead times curtos a mais estabilidade, não menos3.


GitOps com Argo CD

O GitOps aplica o laço de controle do Kubernetes à própria entrega. Os princípios OpenGitOps são: o estado desejado é declarativo, versionado e imutável (no Git), puxado automaticamente por um agente no cluster e continuamente reconciliado4.

sequenceDiagram
    participant Dev as Engenheira
    participant Git as Repo de config (Git)
    participant Argo as Argo CD (no cluster)
    participant K8s as API do Kubernetes
    Dev->>Git: PR "inventory → sha256:ab12…" (revisado, aceito)
    Argo->>Git: polling / webhook: novo commit
    Argo->>K8s: diff desejado vs real → aplica
    K8s-->>Argo: recursos saudáveis
    Note over Argo,K8s: alguém roda kubectl edit à mão…
    Argo->>K8s: deriva detectada → revertida para o Git (self-heal)
config/argocd/inventory-prod.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: inventory-prod
  namespace: argocd
spec:
  project: boxoffice
  source:
    repoURL: https://github.com/<your-org>/boxoffice-config.git
    targetRevision: main
    path: apps/inventory/overlays/prod
  destination:
    server: https://kubernetes.default.svc
    namespace: boxoffice
  syncPolicy:
    automated:
      prune: true        # delete resources removed from Git
      selfHeal: true     # revert manual changes
    syncOptions: [ CreateNamespace=true ]
CD por push (a CI roda kubectl apply) GitOps por pull (Argo CD, Flux)
Credenciais A CI tem credenciais de cluster-admin — um alvo valioso Só o agente dentro do cluster pode mudar o cluster
Deriva Não detectada até o próximo deploy Detectada e, opcionalmente, revertida continuamente
Trilha de auditoria Espalhada pelos logs da CI git log do repo de configuração
Rollback Rodar de novo um pipeline antigo git revert
Vários clusters Um passo de pipeline por cluster Cada cluster puxa o próprio caminho

A promoção entre ambientes vira um pull request que copia o digest de overlays/staging para overlays/prod — revisado, auditável e trivialmente reversível.

No código do BoxOffice: uma Application do Argo CD (lab 11)

O Hands-on 11 — GitOps tem um só ambiente, portanto uma só Application, que segue k8s/overlays/gitops na main, com prune e selfHeal ligados. O lab testa o segundo de propósito: escale o redis para 0 com kubectl e, em segundos, o Argo CD acusa OutOfSync e restaura 1 réplica. O preço de puxar é latência: o Argo CD consulta o Git a cada 3 minutos, o que o lab conta no seu lead time (cerca de 10–20 minutos do push até o rollout completo).

k8s/argocd/application.yaml (lab 11)
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: boxoffice
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/your-user/boxoffice.git
    targetRevision: main
    path: k8s/overlays/gitops
  destination:
    server: https://kubernetes.default.svc
    namespace: boxoffice
  syncPolicy:
    automated:
      prune: true                    # delete what was removed from Git
      selfHeal: true                 # revert manual changes in the cluster
    syncOptions:
      - CreateNamespace=true

Estratégias de deploy

Fazer o deploy (colocar código novo nos servidores) e liberar (expô-lo aos usuários) são passos diferentes. As estratégias abaixo diferem em como os separam:

Estratégia Como Indisponibilidade Capacidade extra Rollback Raio de impacto
Recreate Para todas as antigas, inicia todas as novas Sim Nenhuma Novo deploy da antiga (lento) 100 %
Rolling update Substitui alguns pods por vez (maxSurge, maxUnavailable) Não Pequena (surge) Avançar/voltar gradualmente Cresce com o rollout
Blue/green Roda a nova versão completa ao lado da antiga; troca o roteador de uma vez Não 2× durante a troca Trocar de volta — segundos 100 % na troca, mas desfazer é instantâneo
Canary Envia uma pequena % do tráfego à nova versão e aumenta enquanto as métricas estão saudáveis Não Pequena Peso em 0 — segundos 1–5 % no início
Shadow (espelhamento) Copia o tráfego real para a nova versão; descarta suas respostas Não Até 2× Nada a reverter 0 % (cuidado com efeitos colaterais: pagamentos!)
Feature flags Faz deploy do código desligado; ativa por usuário/segmento em tempo de execução Não Nenhuma Desligar a flag O segmento escolhido

Rolling updates são o padrão do Kubernetes e bastam para a maioria das mudanças. Para mudanças arriscadas no caminho de compra — inventory, order, payment — o BoxOffice usa canaries e, para funcionalidades de produto, feature flags (com uma API neutra de fornecedor como a OpenFeature), que desacoplam totalmente deploy de release: o código vai para produção na terça, e a funcionalidade é ativada para 1 % dos fãs na quinta.

O banco de dados não faz rollback

Duas versões de order rodam lado a lado durante qualquer rolling update ou canary, então toda mudança de esquema precisa ser compatível com versões anteriores. Use expand/contract (mudança paralela): (1) expand — adicione a nova coluna, anulável; (2) faça deploy de código que escreve nas duas e lê a nova com fallback; (3) faça o backfill; (4) contract — remova a coluna antiga em uma release posterior. Nunca renomeie nem apague uma coluna na mesma release que deixa de usá-la.


Canary com análise automática de métricas

Um canary é tão bom quanto a decisão de promovê-lo. Olhar um dashboard e "achar que está tudo bem" não é uma decisão. O Argo Rollouts substitui um Deployment por um Rollout que desloca o tráfego em etapas e executa uma análise em cada etapa, consultando o backend de métricas e abortando automaticamente quando o canary está pior que seus limiares5.

config/apps/inventory/base/rollout.yaml
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata: { name: inventory }
spec:
  replicas: 10
  selector: { matchLabels: { app: inventory } }
  template:
    metadata: { labels: { app: inventory } }
    spec:
      containers:
        - name: inventory
          image: ghcr.io/boxoffice/inventory@sha256:ab12c3d4e5f6
  strategy:
    canary:
      canaryService: inventory-canary
      stableService: inventory-stable
      trafficRouting:
        plugins:
          argoproj-labs/gatewayAPI: { httpRoute: inventory, namespace: boxoffice }
      analysis:                                  # runs in the background from step 1
        templates: [ { templateName: inventory-slo } ]
        startingStep: 1
      steps:
        - setWeight: 5
        - pause: { duration: 10m }
        - setWeight: 25
        - pause: { duration: 10m }
        - setWeight: 50
        - pause: { duration: 10m }
config/apps/inventory/base/analysis.yaml
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata: { name: inventory-slo }
spec:
  metrics:
    - name: error-rate
      interval: 1m
      failureLimit: 2                       # two bad minutes → abort and roll back
      successCondition: result[0] < 0.005   # < 0.5 % of requests fail
      provider:
        prometheus:
          address: http://prometheus.monitoring:9090
          query: |
            sum(rate(http_server_requests_seconds_count{service="inventory-canary",status=~"5.."}[2m]))
            /
            sum(rate(http_server_requests_seconds_count{service="inventory-canary"}[2m]))
    - name: p99-latency
      interval: 1m
      failureLimit: 2
      successCondition: result[0] < 0.300   # p99 below 300 ms (the hold SLO)
      provider:
        prometheus:
          address: http://prometheus.monitoring:9090
          query: |
            histogram_quantile(0.99, sum by (le) (
              rate(http_server_requests_seconds_bucket{service="inventory-canary"}[2m])))

No código do BoxOffice: o canary do catalog (lab 11)

O Hands-on 11 — Canary usa os mesmos dois objetos com números menores: 25% → 2 min → 50% → 2 min e, a cada 30 s, uma taxa de erro abaixo de 1% e um p99 abaixo de 500 ms; failureLimit: 1 significa que a segunda medição ruim aborta. Sem um roteador de tráfego, os pesos são obtidos por número de réplicas, então são aproximados. A guarda len(result) == 0 || isNaN(...) conta "ainda sem tráfego no canary" como aprovação. Na versão ruim do lab (30% das requisições falham), as medições foram [], 0.068 e 0.285, e o rollout foi abortado: os fãs que caíram no canary viram erros por cerca de um minuto; ninguém mais percebeu.

k8s/overlays/gitops/catalog-rollout.yaml (the steps)
strategy:
  canary:
    stableService: catalog-stable
    canaryService: catalog-canary
    analysis:                      # runs in the background from the first step
      templates:
        - templateName: catalog-slo
      startingStep: 1
    steps:
      - setWeight: 25
      - pause: { duration: 2m }
      - setWeight: 50
      - pause: { duration: 2m }
      # after the last step the canary becomes the new stable
k8s/overlays/gitops/catalog-analysis.yaml (the error-rate check)
metrics:
  - name: error-rate
    interval: 30s
    failureLimit: 1                # a second bad measurement aborts and rolls back
    successCondition: len(result) == 0 || isNaN(result[0]) || result[0] < 0.01
    provider:
      prometheus:
        address: http://prometheus.monitoring:9090
        query: |
          sum(rate(http_server_requests_seconds_count{service="catalog-canary", status=~"5.."}[1m]))
          /
          sum(rate(http_server_requests_seconds_count{service="catalog-canary"}[1m]))

Quanto tempo um canary precisa rodar?

Um canary a 5 % vê poucas requisições, e erros raros precisam de muitas requisições para aparecer. Antes de escolher a duração das pausas, estime quantas requisições do canary você precisa para distinguir uma regressão real de ruído. A tabela usa o tamanho de amostra padrão por aproximação normal para comparar duas proporções (α unilateral = 5 %, poder de 80 %), com uma taxa de erros de base ilustrativa de 0,1 % e 400 requisições por segundo chegando ao inventory:

Regression to detect Canary requests needed Time at 5 % weight Time at 25 % weight
0.1% → 1.0% errors 835 0.7 min 0.1 min
0.1% → 0.5% errors 2,312 1.9 min 0.4 min
0.1% → 0.2% errors 18,527 15.4 min 3.1 min

Regressões grandes aparecem em um ou dois minutos; dobrar uma taxa de erros pequena exige um quarto de hora a 5 %. É por isso que as etapas de um canary são medidas em minutos, não segundos, por que a análise compara o canary com a linha de base estável rodando ao mesmo tempo (mesmo tráfego, mesmo horário), e por que serviços com pouco tráfego se beneficiam de pausas mais longas ou de tráfego sintético. O SRE workbook do Google discute esses trade-offs no capítulo sobre canarying de releases6.

BoxOffice: nada de canary durante a venda

Mesmo com análise automática, o BoxOffice declara um congelamento de mudanças no caminho de compra de 30 minutos antes de uma venda até o fim dela: o tráfego é incomum demais para a linha de base significar algo, e o custo de um rollout ruim é máximo. A correção das 09:40 da introdução, portanto, espera — a menos que o responsável pela venda autorize uma exceção, por um caminho também escrito com antecedência: um canary expresso (5 % → 100 % com 5 minutos de análise) às 09:42. Escrever essa regra com antecedência faz parte da engenharia de entrega.


Medindo a entrega: as métricas DORA

O programa de pesquisa DORA (DevOps Research and Assessment) encontrou quatro métricas que predizem o desempenho de entrega de software e que se movem juntas em times de alto desempenho3:

Métrica Pergunta Vazão ou estabilidade
Frequência de deploy Com que frequência fazemos deploy em produção? Vazão
Lead time de mudanças Quanto tempo do commit até rodar em produção? Vazão
Taxa de falha de mudanças Que fração dos deploys causa uma falha que exige correção? Estabilidade
Tempo para restaurar o serviço Quanto tempo para se recuperar de tal falha? Estabilidade

A descoberta principal — velocidade e estabilidade não são um trade-off — é o argumento para tudo nesta página: mudanças pequenas, frequentes e verificadas automaticamente são mais rápidas e mais seguras do que mudanças grandes, raras e manuais.


IA no pipeline

Os pipelines cada vez mais incluem etapas de IA: revisão automática de pull requests, testes gerados, agentes que propõem correções para builds quebrados, análise de canary assistida por IA, notas de release e resumos de incidentes — e as questões de segurança e governança que elas levantam. Esses temas têm aula própria: Entrega assistida por IA. As bases desta página — builds reproduzíveis, GitOps, rollouts controlados por métricas — são o que torna seguro adotar essas etapas de IA.


Trade-offs

Decisão Escolha A quando… Escolha B quando…
Rolling vs canary Mudança de baixo risco, bons testes, rollback rápido Caminho crítico, comportamento difícil de testar sob tráfego real
Blue/green vs canary Você precisa de uma troca instantânea tudo-ou-nada (ex.: mudança de protocolo) e pode pagar 2× de capacidade por pouco tempo Você quer limitar o raio de impacto e julgar por métricas
Feature flag vs canary Expor comportamento de produto a usuários escolhidos Validar um build novo como um todo
Pipeline de monorepo vs por serviço Ferramentas compartilhadas, mudanças atômicas entre serviços Times independentes e cadências de release próprias

Na entrevista

Quando o entrevistador perguntar "como você publica isso com segurança?", responda em camadas: imagens imutáveis construídas uma vez, GitOps para que a produção seja igual ao Git, canary no caminho crítico com rollback automático por métricas de SLO (taxa de erros e p99 comparados com a linha de base), feature flags para mudanças de produto e migrações expand/contract para que duas versões rodem lado a lado. Mencione que a duração do canary depende do volume de tráfego — isso mostra que você entende a estatística, não só a ferramenta.

Boas práticas

  • Faça o job da imagem depender dos testes e do orçamento de desempenho — um artefato que falhou numa verificação nunca existe, então não pode ser implantado por engano. No BoxOffice: needs: [test, performance] em ci.yaml (lab 11 — CI).
  • Fixe as actions de terceiros por SHA do commit, com a versão num comentário — uma tag pode ser movida para código malicioso; um SHA não. No BoxOffice: todo uses: de ci.yaml e deploy.yaml, mantido atualizado pelo Dependabot (lab 11 — cadeia de suprimentos).
  • Dê a cada job as menores permissões de que ele precisa — contents: read por padrão; só o job da imagem pode escrever pacotes e atestações. No BoxOffice: os blocos permissions: de ci.yaml.
  • Rode os deploys um de cada vez — dois deploys simultâneos podem gravar as tags na ordem errada. No BoxOffice: concurrency: deploy com cancel-in-progress: false em deploy.yaml (lab 11 — GitOps).
  • Decida o que "sem dados" significa numa análise de canary — no início, o canary ainda não tem tráfego; uma análise que falha com resultado vazio aborta todo rollout, uma que o ignora para sempre promove às cegas. No BoxOffice: a guarda len(result) == 0 de catalog-analysis.yaml (lab 11 — Canary).

Armadilhas

  • Construir um artefato diferente por ambiente: o que você testou não é o que você publica. Construa uma vez, promova por digest.
  • Canary sem análise automática: uma pessoa olhando gráficos promove tudo.
  • Canary julgado por CPU: julgue por SLIs voltados ao usuário (erros, latência), comparados com a linha de base estável.
  • Espelhar um serviço com efeitos colaterais: POST /payments espelhados cobram cartões reais duas vezes.
  • Estado do IaC em um notebook: perdido, sem lock ou cheio de segredos.
  • Feature flags de vida longa: cada flag é um desvio no código; remova as flags depois do rollout.

Como se conecta

flowchart LR
    IAC["IaC: Terraform / OpenTofu<br/>plan, apply, state remoto"] --> INFRA["VPC, cluster,<br/>serviços de dados gerenciados"]
    INFRA --> DR["recriar após perder uma região<br/>→ Recuperação de desastres"]
    CI["CI: build, teste, scan,<br/>SBOM, assinatura"] --> IMG["imagem por digest"]
    IMG --> GIT["repo de configuração:<br/>estado desejado no Git"]
    GIT --> ARGO["Argo CD puxa,<br/>reconcilia o drift"]
    ARGO --> STRAT["rolling, blue/green,<br/>canary, feature flags"]
    STRAT --> EXP["expand/contract:<br/>schema retrocompatível"]
    STRAT --> AN["análise do canary: taxa de erro,<br/>p99 vs baseline estável"]
    AN --> SLO["SLIs e SLOs<br/>→ Confiabilidade, Observabilidade"]
    AN --> N["tamanho da amostra:<br/>minutos, não segundos"]
    STRAT --> DORA["métricas DORA"]
    CI --> AI["passos de IA no pipeline<br/>→ Entrega assistida por IA"]

Componentes da paleta: vpc (criada por IaC) · sql · cache · kafka (gerenciados, declarados em Terraform) · ingress (o Gateway API divide o tráfego do canary) · metrics (consultadas pela análise do canary) · app (inventory, order, payment com canary). Cenários: — a entrega acontece fora do simulador; as métricas que julgam um canary são as do observability-stack. Continua em: Observabilidade — SLOs, Confiabilidade — recuperação de desastres, Performance — budgets no CI, Entrega assistida por IA, Hands-on 13 — canary, Marcos do projeto.



  1. OpenTofu. OpenTofu documentation; HashiCorp. Terraform language documentation. ↩

  2. HUMBLE, J.; FARLEY, D. Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation. Addison-Wesley, 2010. ↩

  3. FORSGREN, N.; HUMBLE, J.; KIM, G. Accelerate: The Science of Lean Software and DevOps. IT Revolution, 2018; DORA research. ↩↩

  4. OpenGitOps. GitOps Principles v1.0; Argo CD. Documentation. ↩

  5. Argo Rollouts. Canary strategy and analysis. ↩

  6. BEYER, B. et al. (eds.). The Site Reliability Workbook, cap. 16 "Canarying Releases". O'Reilly, 2018. ↩