Quando o Ubuntu sai do ar: um playbook para CTOs sobre espelhos, proxies OCI e builds herméticos

Por Diogo Hudson Dias
DevOps engineer in a São Paulo server room setting up a local package mirror on a rack-mounted server

Se um DDoS contra a infraestrutura do Ubuntu consegue paralisar seu CI/CD por horas, sua cadeia de suprimentos de software está frágil demais. As indisponibilidades do Ubuntu na semana passada não foram exóticas — apenas dor de rede suficiente para fazer apt update travar, Docker pulls arrastarem e janelas de patch explodirem. A lição: assuma que os upstreams vão falhar na pior hora e projete para contornar isso.

Este post é o framework de decisão e a ficha de execução que eu gostaria que todo CTO tivesse na mesa: como endurecer sua organização com espelhos locais de apt, proxies de registry OCI, caches de pacotes de linguagens e builds herméticos. Não é teatro de fornecedor. É um esforço de duas a seis semanas que transforma o caos do upstream em não-evento.

O que realmente quebrou — e por que você sentiu

Quando os serviços do Ubuntu foram limitados, você provavelmente viu parte ou tudo isso:

  • Builds presos em apt update e apt install para imagens base ou AMIs.
  • docker pull ubuntu:22.04 ou imagens de runtime esgotando o tempo por causa de um registry público lento e sem cache.
  • Nós de Kubernetes falhando ao iniciar pods porque pulls de imagens do Docker Hub ou GHCR estavam com rate limit e sem proxy.
  • Janelas de patch saindo de 30 minutos para várias horas porque os espelhos estavam inconsistentes.

Nada disso é exclusivo do Ubuntu. Debian, Alpine, npm, PyPI, crates.io, Docker Hub — todo serviço público tem indisponibilidades e rate limits. A pergunta é se o seu pipeline degrada de forma elegante ou para de entregar.

Um framework de decisão: Cache, Espelho ou Hermético?

Você tem três alavancas, em ordem crescente de esforço e de redução de raio de impacto:

  1. Fazer cache de artefatos públicos atrás de proxies locais. Rápido de implementar. Ótimo para banda e para quedas curtas. Depende do upstream para cache misses.
  2. Espelhar repositórios específicos no seu próprio storage e servi-los internamente. Mais trabalho. Remove a maior parte da dependência da disponibilidade do upstream para versões conhecidas como boas.
  3. Builds herméticos com insumos fixados, imagens endereçadas por conteúdo e uma política de egress por allowlist. É o esforço mais pesado. Entrega reprodutibilidade e controle real da cadeia de suprimentos.

Escolha com base em dois fatores: sua cadência de releases e seu apetite a risco. Se você entrega semanalmente e atua em mercados regulados (fintech, saúde, infraestrutura), queira pelo menos Espelhos agora e Hermético no próximo trimestre. Se você está pré-produto ou com burn limitado, comece com Caches neste sprint e evolua depois.

A arquitetura-alvo (pragmática, não perfeita)

Aqui está o stack que implementamos para startups nos EUA com nosso time de plataforma baseado no Brasil — 6–8 horas de sobreposição e tipicamente 20–30% mais barato que um sprint de SRE nos EUA, mas, mais importante, feito em semanas, não em trimestres:

  • Proxy cache de registry OCI (Harbor, Artifactory ou vanilla registry:2 em modo proxy) para Docker Hub, GHCR, Quay e seus registries privados.
  • Cache e snapshots de apt via apt-cacher-ng para ganhos rápidos, mais um espelho curado e versionado com aptly ou reprepro para o Ubuntu/Debian que você de fato usa.
  • Repositórios de pacotes de linguagens com proxy local: Verdaccio para npm, devpi para PyPI, Nexus/Artifactory para Maven/Gradle, Athens ou goproxy para Go, um espelho de registry esparso para Cargo.
  • Camada de build hermético para seus serviços mais críticos usando Bazel ou Nix, com imagens base de container fixadas por digest, emissão de SBOM e assinatura de imagens via Sigstore cosign.
  • Política de egress que roteia CI e nós de build apenas pelos seus proxies e espelhos; em staging, um kill switch de um clique para a internet upstream para testar resiliência.

Isso funciona na nuvem (EKS/GKE/AKS) e on-prem. Você pode começar com caches em um dia, depois fazer snapshot/espelhar os caminhos quentes e, por fim, travar com builds herméticos à medida que refatora Dockerfiles e pipelines.

Passo 1: Fixe e faça proxy das suas imagens de container

A maior parte da lentidão no CI começa com FROM ubuntu:22.04 e um pull dinâmico de um registry público. Dois ajustes mudam o jogo:

  • Fixe por digest. Em vez de FROM ubuntu:22.04, use FROM ubuntu@sha256:… Isso elimina o drift de tags e garante reprodutibilidade bit a bit. Cada imagem base que você não fixa é uma indisponibilidade e um bug de supply chain esperando para acontecer.
  • Introduza um registry proxy. Suba Harbor ou registry:2 com proxy caching para docker.io, ghcr.io e quay.io. Aponte todos os agentes de build e clusters para o seu proxy como mirror padrão do registry. A maioria das organizações reduz os tempos de pull em 50–80% em cache hits e fica imune a rate limits públicos.

Trade-offs: você precisa operar um storage que escale (backends S3/GCS/Azure Blob funcionam), monitorar taxa de acertos de cache e fazer backup dos metadados. Mas a carga operacional é modesta — pense em uma VM pequena t3.medium mais storage de objetos para começar.

Passo 2: Conserte o apt com caches e snapshots

Comece com apt-cacher-ng para uma redução imediata na lentidão do apt. Um proxy, uma entrada de DNS, e os builds param de varrer os mirrors públicos a cada instalação de pacote.

Depois, evolua para um espelho com snapshotting para que seu conjunto de pacotes pare de mudar por baixo de você:

  • Espelhe apenas o que você usa. Use aptly ou reprepro para criar um espelho do Ubuntu/Debian curado para suas arquiteturas e componentes (por exemplo, jammy main, universe, security). Publique snapshots em S3 e sirva via CloudFront ou uma pequena VM NGINX na sua VPC.
  • Snapshot por release. Crie um snapshot datado sempre que você cortar uma branch de release; aponte o CI para essa URL de snapshot. Você terá resultados determinísticos de apt install e poderá avançar snapshots no seu ritmo.

Sim, você pode viver só de caches. Mas no dia em que um pacote for retirado ou uma dependência mudar por baixo de você, você vai desejar ter um snapshot para reverter.

Passo 3: Traga os ecossistemas de linguagens para dentro do perímetro

A indisponibilidade do Ubuntu não afetou só pacotes do SO. Se npm, PyPI ou crates.io piscarem, seu monorepo trava. Coloque tudo isso atrás dos seus próprios endpoints:

  • npm: Verdaccio com uplinks para registry.npmjs.org. Armazene aqui também seus pacotes privados.
  • PyPI: devpi ou Artifactory. Pré-cacheie seus wheels mais comuns — os builds ficam muito mais rápidos quando você evita compilações a partir do código-fonte.
  • Maven/Gradle: Sonatype Nexus ou Artifactory como proxy e host. Aponte o settings.xml para ele e pronto.
  • Go: rode Athens ou configure GOPROXY para o seu próprio servidor goproxy.
  • Rust: use o protocolo de registry esparso do Cargo e espelhe o índice; você pode hospedar um registry interno para crates proprietários.

Bônus: depois de fazer proxy desses ecossistemas, você pode fixar versões exatas em lockfiles sem temer 404s ou rate limits. Seus SBOMs de supply chain passam a ser significativos porque os insumos não flutuam mais.

Passo 4: Torne serviços críticos herméticos

Builds herméticos significam que todo insumo é declarado, fixado e obtido de um local confiável que você controla. É o mínimo se você leva SLSA e reprodutibilidade a sério. Uma forma pragmática de começar:

  • Escolha 2–3 serviços críticos (por exemplo, billing, auth, ingestion). Converta seus Dockerfiles para usar digests de imagem base fixados e mova a lógica de build para regras do Bazel ou derivações do Nix.
  • Emita SBOMs (CycloneDX ou SPDX) durante o build e assine as imagens com cosign usando seu KMS da nuvem. Exija verificação de assinatura no deploy via um admission controller (Kyverno ou OPA Gatekeeper).
  • Prefira bases distroless ou Wolfi

Use Distroless ou Chainguard Wolfi imagens mínimas para reduzir drasticamente as partes móveis. Observação: o musl libc do Alpine pode quebrar algumas dependências de IA/ML e ligadas ao glibc; Wolfi fornece variantes compatíveis com glibc, e Distroless tem opções com glibc. Teste antes de adotar em massa.

Trade-offs: Bazel/Nix adicionam curva de aprendizado e trabalho no CI. Faça onde trará mais retorno primeiro. Ao longo de um trimestre, mova o restante da frota conforme você tocar nos serviços.

Passo 5: Trave o egress e simule falhas

Resiliência que você não exercita é teatro. Dois movimentos operacionais a tornam real:

  • Restrinja o egress do CI/build aos seus proxies e espelhos. Na nuvem, use egress gateways da VPC e security groups; on-prem, uma allowlist no firewall. O objetivo é simples: se um build não consegue alcançar um upstream diretamente, você saberá que não está dependendo dele sem perceber.
  • Faça drills de indisponibilidade. Uma vez por sprint, acione uma feature flag que bloqueia a internet de saída em um cluster de staging e na sub-rede do seu CI. Seus builds terminam? Novos pods são agendados? Você vai encontrar lacunas — corrija-as.

Meça o sucesso com um KPI direto: porcentagem de builds que concluem com a conectividade a upstreams bloqueada. Comece abaixo de 20%. Mire em 90%+ dentro de um trimestre para os serviços principais.

Quanto isso custa (e economiza)

Primeiro, a conta de nuvem: um proxy de registry pequeno e proxies de artefatos custam algumas centenas de dólares por mês em tempo de VM e storage. Espelhos com snapshots adicionam storage S3/Blob que escala com o volume de pacotes; espere dezenas a algumas centenas de GB ao longo dos meses. Em troca, você economiza em:

  • Tempo de desenvolvedor: se sua organização roda 300 builds/dia e 20% deles travam por 10 minutos durante um soluço no upstream, são 600 minutos/dia perdidos. A uma taxa total de engenharia de US$ 150/hora, você está queimando US$ 1,5 mil/dia em nada — facilmente mais que o custo mensal de uma configuração decente de proxy/espelho.
  • Taxas de egress: caches pull-through reduzem downloads repetidos entre CI e clusters. Em setups multi-região, isso economiza dinheiro de verdade.
  • Ruído de incidentes: acionar a infra às 1h da manhã para explicar por que o apt está lento é a definição de trabalho evitável.

Tempo para implementar com um engenheiro de plataforma sênior ou um par de SRE nearshore:

  • Semana 1: proxy de registry no ar, apt-cacher-ng ativo, proxies de npm/PyPI configurados, CI roteado pelos proxies.
  • Semanas 2–3: espelho de apt curado + snapshots, primeiro build hermético de serviço com SBOM + assinatura, allowlist de egress aplicada em CI/staging.
  • Semanas 4–6: expanda o padrão hermético para os 5 principais serviços, faça rollout das políticas de admission em produção, drills rotineiros de caos.

Trade-offs que você deve reconhecer de saída

  • Você agora está operando mais infra. Sim — serviços pequenos e entediantes. Mantenha-os em código (Terraform/manifests do Kubernetes), com métricas e backups.
  • Fixar versões desacelera o fluxo do “é só atualizar”. Esse é o ponto. Você atualiza no seu cronograma, com testes, não numa terça aleatória durante um DDoS.
  • Builds herméticos não são de graça. Bazel/Nix adicionam carga cognitiva. Comece onde importa; não force isso em tudo no dia um.
  • Imagens mínimas podem prejudicar a depuração. Adicione uma variante de debug ou use sidecars efêmeros para troubleshooting, não as imagens de produção.

Endurecendo Kubernetes para que pods ainda façam pull

Dois padrões evitam que pulls de imagem virem seu próximo incidente às 3h da manhã:

  • Registries locais por nó: rode um daemonset que forneça um cache pull-through local por nó. Isso impede que N pods em M nós saiam atropelando um proxy remoto.
  • Aqueça o cache: faça pre-pull de imagens críticas com um daemonset no deploy ou asse-as nas AMIs de nó com Packer. Defina imagePullPolicy como IfNotPresent para serviços estáveis a fim de reduzir churn.

Combine isso com verificação de assinatura: só admita imagens assinadas pela sua chave, obtidas via seu proxy. Se uma imagem maliciosa aparecer em um registry público durante uma indisponibilidade, não será você quem vai puxá-la.

Bônus de compliance: reprodutibilidade que as pessoas respeitam

SOC 2, ISO 27001 e especialmente SLSA empurram você para builds rastreáveis e reproduzíveis. Com digests fixados, SBOMs e artefatos assinados, sua evidência deixa de ser PowerPoint e vira verdade criptográfica. Se o seu roadmap inclui vender para enterprises ou setor público, você vai precisar dessa postura de qualquer jeito. Fazer isso agora por resiliência paga em dobro.

Plano 30–60–90 dias que você realmente consegue executar

Dias 0–30

  • Suba Harbor ou registry:2 em modo proxy com backend de object storage; direcione CI e clusters para ele.
  • Faça deploy do apt-cacher-ng; adicione uma configuração global de proxy do apt para builders e imagens AMI.
  • Configure Verdaccio e devpi; reconfigure os gerenciadores de pacotes no CI.
  • Comece a fixar por digest as imagens base para todos os Dockerfiles tocados neste mês.
  • Introduza um bloqueio de egress em staging e rode seu primeiro drill com upstream desligado.

Dias 31–60

  • Construa um espelho curado com aptly ou reprepro para Ubuntu/Debian; publique snapshots em endpoints internos; aponte o CI para os snapshots.
  • Converta dois serviços críticos para builds herméticos com Bazel ou Nix; emita SBOMs e assine imagens com cosign; imponha verificação de assinatura em staging.
  • Faça pre-pull de imagens de runtime críticas via daemonset; asse-as nas imagens de nó onde for prático.
  • Instrumente métricas: taxa de acerto de cache, sucesso de build com upstream desligado, tempo médio de pull de imagem.

Dias 61–90

  • Expanda builds herméticos para seus 5–8 principais serviços; faça rollout das políticas de admission em produção.
  • Estabeleça gestão de mudanças para avanço de snapshots e updates de imagens base com cadência semanal e planos de rollback.
  • Rode um game day completo de indisponibilidade: bloqueie upstreams por 4 horas; produza um post-mortem com lacunas e ações.

Por que agora

Dois sinais chegaram na mesma semana: a oscilação da infraestrutura do Ubuntu e uma falha de segurança no Linux amplamente discutida, encontrada via varredura assistida por IA. Tradução: os upstreams continuam frágeis e os atacantes estão ficando melhores em achar as brechas. Ambos os problemas recompensam a mesma resposta — reduza partes móveis, puxe de lugares em que você confia e torne seus builds reproduzíveis.

Você não precisa reescrever a plataforma em 12 meses. Você precisa de um mês de trabalho focado e da vontade de trancar as portas que você já pretendia fechar. Se não tiver capacidade, empreste a quem tem; nossos SREs no Brasil entregam esse padrão em escopo fechado. De qualquer forma, não espere a próxima indisponibilidade para aprender a mesma lição duas vezes.

Principais lições

  • Pare de depender de upstreams públicos em tempo de build. Adicione caches proxy para OCI e ecossistemas de linguagens neste sprint.
  • Espelhe e faça snapshot dos pacotes do SO que você realmente usa; aponte o CI para snapshots para instalações determinísticas do apt.
  • Fixe bases de containers por digest, emita SBOMs e assine com cosign; imponha verificação com um admission controller.
  • Restrinja o egress do CI aos proxies e rode drills regulares com upstream desligado; mire em 90%+ de sucesso de builds com a internet bloqueada.
  • Comece builds herméticos onde importa e expanda ao longo de um trimestre; bases mínimas como Distroless ou Wolfi reduzem risco e bloat.
  • O custo é baixo comparado ao tempo de desenvolvedor perdido; os benefícios de compliance são um bônus de que você vai precisar de qualquer jeito.

Ready to scale your engineering team?

Tell us about your project and we'll get back to you within 24 hours.

Start a conversation