DevOps para aplicações enterprise: CI/CD, Docker, Kubernetes e deploy em produção
Guia prático de DevOps empresarial: CI/CD com GitHub Actions, Docker multi-stage, Kubernetes vs ECS, monitoramento e blue-green deploy.
O deploy manual é o maior risco operacional de software enterprise
Toda equipe de desenvolvimento que ainda faz deploy manual (conecta no servidor via SSH, faz git pull, reinicia o serviço e torce para funcionar) está a um erro humano de derrubar o sistema em produção.
Em sistemas empresariais (ERP, SaaS, plataformas de gestão), onde cada minuto de downtime impacta operações reais de empresas, o deploy manual é inaceitável. A solução é DevOps: cultura, práticas e ferramentas que automatizam o caminho do código até a produção com segurança, velocidade e rastreabilidade.
Este post detalha as práticas de DevOps que aplicamos em projetos enterprise na Bradata, do CI/CD pipeline até monitoramento em produção, com exemplos reais e configurações que funcionam.
CI/CD: o pipeline que garante qualidade a cada commit
CI/CD (Continuous Integration / Continuous Delivery) é o coração do DevOps. Cada commit no repositório aciona uma sequência automatizada de verificações e, se tudo passar, o deploy acontece sem intervenção humana.
Anatomia de um pipeline CI/CD enterprise
Commit → Lint → Testes → Build → Security Scan → Deploy Staging → Testes E2E → Deploy Produção
Cada etapa é um gate: se falhar, o pipeline para e notifica a equipe.
CI/CD com GitHub Actions: configuração real
O GitHub Actions é a ferramenta de CI/CD mais adotada em projetos modernos. Uma configuração real de pipeline enterprise:
name: CI/CD Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
env:
NODE_VERSION: '20'
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
lint-and-test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_DB: test_db
POSTGRES_USER: test
POSTGRES_PASSWORD: test
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- run: npm ci
- name: Lint
run: npm run lint
- name: Type Check
run: npm run type-check
- name: Unit Tests
run: npm run test -- --coverage
env:
DATABASE_URL: postgresql://test:test@localhost:5432/test_db
- name: Upload Coverage
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
security-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
scan-ref: '.'
severity: 'CRITICAL,HIGH'
- name: Audit dependencies
run: npm audit --audit-level=high
build-and-push:
needs: [lint-and-test, security-scan]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max
deploy-staging:
needs: build-and-push
runs-on: ubuntu-latest
environment: staging
steps:
- name: Deploy to staging
run: |
# Deploy via kubectl, ECS, ou ferramenta específica
echo "Deploying ${{ github.sha }} to staging"
e2e-tests:
needs: deploy-staging
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run E2E tests against staging
run: npm run test:e2e
env:
BASE_URL: https://staging.app.exemplo.com.br
deploy-production:
needs: e2e-tests
runs-on: ubuntu-latest
environment: production
steps:
- name: Deploy to production
run: |
echo "Deploying ${{ github.sha }} to production"
Pontos importantes desse pipeline
- Serviço PostgreSQL no CI: testes rodam contra um banco real, não mocks. Isso pega bugs de query e migração
- Security scan com Trivy: vulnerabilidades em dependências e no código são detectadas antes do merge
- Cache do Docker:
cache-fromecache-tocom GitHub Actions cache reduzem tempo de build de 8 minutos para 2 minutos - Environments no GitHub:
stagingeproductioncom aprovações manuais opcionais e secrets separados - Testes E2E contra staging: antes de ir para produção, testes end-to-end validam o sistema completo
Docker: containerização para consistência de ambientes
Docker garante que o código roda da mesma forma no laptop do desenvolvedor, no CI e em produção. Para aplicações enterprise, o Dockerfile precisa ser otimizado.
Dockerfile multi-stage para Node.js/Next.js
# Stage 1: Dependencies
FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --only=production && \
cp -R node_modules /prod_modules && \
npm ci
# Stage 2: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NEXT_TELEMETRY_DISABLED=1
RUN npm run build
# Stage 3: Production
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
RUN addgroup --system --gid 1001 nodejs && \
adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget -qO- http://localhost:3000/api/health || exit 1
CMD ["node", "server.js"]
Por que multi-stage importa
| Aspecto | Single-stage | Multi-stage |
|---|---|---|
| Tamanho da imagem | 1.2 GB+ | 150-250 MB |
| Superfície de ataque | devDependencies inclusas | Só produção |
| Tempo de pull | 45-60s | 8-15s |
| Custo de registry | Alto | Baixo |
| Startup time | Lento | Rápido |
Docker Compose para desenvolvimento local
version: '3.8'
services:
app:
build:
context: .
target: deps # Usa o stage de deps para dev
volumes:
- .:/app
- /app/node_modules
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgresql://dev:dev@postgres:5432/app_dev
- REDIS_URL=redis://redis:6379
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_started
command: npm run dev
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: app_dev
POSTGRES_USER: dev
POSTGRES_PASSWORD: dev
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U dev"]
interval: 5s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
pgdata:
Kubernetes vs ECS: onde rodar containers em produção
Depois de containerizar a aplicação, a pergunta é: onde orquestrar os containers em produção?
Amazon ECS (Elastic Container Service)
ECS é a solução de orquestração da AWS. Mais simples que Kubernetes, ideal para equipes menores:
Vantagens:
- Integração nativa com todo ecossistema AWS (ALB, RDS, CloudWatch, IAM)
- Fargate elimina gerenciamento de servidores (serverless containers)
- Menor curva de aprendizado que Kubernetes
- Custo previsível com Fargate (paga por vCPU e memória usados)
Limitações:
- Vendor lock-in com AWS
- Menos flexível que Kubernetes para workloads complexos
- Comunidade menor, menos tooling open-source
Quando usar: equipes de 2-10 devs, aplicações com até 10-15 serviços, quando já está no ecossistema AWS.
Kubernetes (K8s)
Kubernetes é o padrão da indústria para orquestração de containers em escala:
Vantagens:
- Portabilidade entre clouds (AWS EKS, GCP GKE, Azure AKS, on-premise)
- Ecossistema enorme (Helm, Istio, ArgoCD, Prometheus, Grafana)
- Auto-scaling sofisticado (HPA, VPA, Cluster Autoscaler)
- Service mesh para observabilidade e segurança entre serviços
Limitações:
- Complexidade operacional alta (precisa de SRE dedicado)
- Curva de aprendizado íngreme
- Custo do control plane (EKS cobra por cluster)
- Overhead para aplicações simples
Quando usar: equipes com 10+ devs, mais de 10 serviços, requisitos de multi-cloud ou on-premise, quando já tem experiência com K8s.
Decisão prática que aplicamos na Bradata
Para a maioria dos projetos enterprise que desenvolvemos:
- MVP e primeiros 2 anos: ECS Fargate na AWS ou Cloud Run no GCP: simplicidade e custo baixo
- Escala média (10+ serviços): EKS (Kubernetes gerenciado na AWS) com Helm charts
- Escala enterprise ou multi-cloud: Kubernetes com GitOps (ArgoCD)
A migração de ECS para Kubernetes é relativamente simples porque o artefato é o mesmo (imagem Docker). O que muda é a configuração de deploy (Task Definition do ECS → Kubernetes manifests).
Estratégias de deploy: zero downtime em produção
Em sistemas enterprise, deploy com downtime é inaceitável. Três estratégias de zero-downtime deploy:
Rolling update
O padrão mais simples: novas instâncias são criadas gradualmente enquanto as antigas são removidas.
# Kubernetes Deployment com rolling update
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
spec:
replicas: 4
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
template:
spec:
containers:
- name: api
image: ghcr.io/empresa/api:v2.1.0
readinessProbe:
httpGet:
path: /api/health
port: 3000
initialDelaySeconds: 10
periodSeconds: 5
livenessProbe:
httpGet:
path: /api/health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
Como funciona: com 4 réplicas e maxUnavailable: 1, o Kubernetes atualiza 1 pod por vez. Enquanto o pod novo não passa no readiness probe, o pod antigo continua recebendo tráfego.
Blue-green deploy
Duas versões do ambiente rodam simultaneamente. O tráfego é migrado de uma vez do "blue" (versão atual) para "green" (versão nova).
Antes do deploy:
Load Balancer → Blue (v1.0) [100% do tráfego]
Green (v2.0) [0% - em teste]
Cutover:
Load Balancer → Blue (v1.0) [0% - standby]
Green (v2.0) [100% do tráfego]
Rollback (se necessário):
Load Balancer → Blue (v1.0) [100% - restaurado]
Green (v2.0) [0% - desligado]
Vantagens: rollback instantâneo (basta mudar o apontamento do load balancer), versão nova é testada em ambiente real antes de receber tráfego.
Custo: dobro de infraestrutura durante o deploy (dois ambientes completos rodando).
Canary deploy
Uma pequena porcentagem do tráfego (5%) é direcionada para a versão nova. Se as métricas estiverem saudáveis por 10 minutos, o tráfego é aumentado gradualmente (25%, 50%, 100%). Ideal para sistemas com alto tráfego onde um bug pode impactar milhares de usuários.
Qual estratégia usar
| Cenário | Estratégia recomendada |
|---|---|
| Aplicação web padrão, equipe pequena | Rolling update |
| Sistema crítico, precisa de rollback instantâneo | Blue-green |
| Alto tráfego, releases frequentes | Canary |
| MVP / early-stage | Rolling update |
Infraestrutura como código: Terraform
Infraestrutura definida em código (IaC) garante reprodutibilidade e versionamento do ambiente:
# Exemplo: ECS Fargate com Terraform
resource "aws_ecs_service" "api" {
name = "api-service"
cluster = aws_ecs_cluster.main.id
task_definition = aws_ecs_task_definition.api.arn
desired_count = 2
launch_type = "FARGATE"
network_configuration {
subnets = var.private_subnets
security_groups = [aws_security_group.api.id]
}
load_balancer {
target_group_arn = aws_lb_target_group.api.arn
container_name = "api"
container_port = 3000
}
deployment_configuration {
maximum_percent = 200
minimum_healthy_percent = 100
}
}
resource "aws_ecs_task_definition" "api" {
family = "api"
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
cpu = 512
memory = 1024
container_definitions = jsonencode([{
name = "api"
image = "${var.ecr_repository}:${var.image_tag}"
portMappings = [{
containerPort = 3000
protocol = "tcp"
}]
healthCheck = {
command = ["CMD-SHELL", "wget -qO- http://localhost:3000/api/health || exit 1"]
interval = 30
timeout = 5
retries = 3
startPeriod = 60
}
logConfiguration = {
logDriver = "awslogs"
options = {
"awslogs-group" = "/ecs/api"
"awslogs-region" = var.aws_region
"awslogs-stream-prefix" = "ecs"
}
}
}])
}
Princípio fundamental: tudo que está em produção precisa estar no código. Se um recurso foi criado manualmente no console da AWS, ele não existe oficialmente: pode ser deletado, esquecido ou inconsistente.
Monitoramento: os 4 sinais dourados
Uma aplicação em produção sem monitoramento é um avião sem instrumentos. Os 4 sinais dourados (Google SRE):
1. Latência
Tempo que cada request leva para ser processada.
- p50: metade das requests são mais rápidas que esse valor (experiência típica)
- p95: 95% das requests são mais rápidas (experiência de "quase todo mundo")
- p99: 1% das requests mais lentas (detecta problemas pontuais)
Alerta: p95 acima de 500ms por mais de 5 minutos.
2. Tráfego
Volume de requests por segundo (RPS). Monitore por endpoint e por status code.
Alerta: queda abrupta de tráfego (pode indicar que o sistema está inacessível) ou aumento inesperado (possível ataque ou viral).
3. Erros
Taxa de respostas com status 5xx (erro do servidor). Separar de 4xx (erro do cliente, geralmente esperado).
Alerta: taxa de 5xx acima de 1% por mais de 2 minutos.
4. Saturação
Uso de CPU, memória, disco e conexões de banco. Quanto mais próximo do limite, mais instável o sistema.
Alerta: CPU acima de 80% por mais de 10 minutos, memória acima de 85%, conexões de banco acima de 75% do pool.
Stack de monitoramento recomendada
| Ferramenta | Função | Alternativa |
|---|---|---|
| Grafana | Dashboards e visualização | Datadog |
| Prometheus | Coleta de métricas | CloudWatch Metrics |
| Loki | Agregação de logs | Elasticsearch, CloudWatch Logs |
| Jaeger | Tracing distribuído | Grafana Tempo, AWS X-Ray |
| Sentry | Tracking de erros | Bugsnag, Rollbar |
| PagerDuty | Alertas e escalação | OpsGenie, Grafana OnCall |
Gestão de secrets
Secrets (senhas de banco, API keys, certificados) nunca devem estar no código. A hierarquia correta: defaults no código para valores não-sensíveis, .env para desenvolvimento local (nunca commitado), GitHub Secrets para pipelines de CI/CD, e AWS Secrets Manager ou HashiCorp Vault para produção. Rotação automática é obrigatória: banco de dados a cada 30 dias, API keys a cada 90 dias, certificados TLS com renovação automática via cert-manager ou ACM.
Disaster recovery e migrações seguras
Backup e restore
- Banco de dados: snapshots automáticos diários + point-in-time recovery
- Storage (S3): versionamento habilitado + replicação cross-region para dados críticos
- Configuração: toda em código (Terraform), pode ser recriada do zero
- Teste de restore: executar restore mensal em ambiente isolado para validar integridade
Migrações de banco em produção
Migrações de banco de dados são o deploy mais arriscado. Regras fundamentais:
- Migrações são forward-only: nunca faça DROP COLUMN na mesma release que remove o código que usa a coluna
- Expand-contract pattern: adiciona coluna nova, migra dados, remove coluna antiga (em releases separadas)
- Sem locks: use
CREATE INDEX CONCURRENTLYpara evitar bloqueios em tabelas grandes - Teste com volume real: execute a migração em staging contra dados de produção antes do deploy
Checklist DevOps para aplicações enterprise
Antes de considerar que a infraestrutura está pronta para produção:
CI/CD:
- Pipeline automatizado com lint, testes e build a cada push
- Security scan de dependências e código
- Deploy automatizado para staging
- Deploy para produção com aprovação ou gate automático
- Rollback documentado e testado
Containers:
- Dockerfile multi-stage com imagem mínima
- Health check configurado no container
- Usuário non-root no container
- Nenhum secret hardcoded na imagem
Infraestrutura:
- Toda infra definida como código (Terraform/Pulumi)
- Secrets em secret manager (não em variáveis de ambiente do CI)
- Backup automático com teste de restore
- SSL/TLS em todos os endpoints
Monitoramento:
- Dashboards com os 4 sinais dourados
- Alertas configurados com escalação
- Logs centralizados e pesquisáveis
- Tracing distribuído (se mais de 3 serviços)
Conclusão: DevOps é investimento, não custo
Implementar práticas de DevOps robustas exige investimento inicial: pipeline de CI/CD, containerização, infraestrutura como código, monitoramento. Mas o retorno é composto: cada deploy fica mais rápido, mais seguro e mais rastreável.
Na Bradata, investimos as primeiras 2 semanas de cada projeto configurando o pipeline completo. Quando o projeto tem 50 deploys por mês durante 2 anos, o tempo economizado em deploys manuais e rollbacks emergenciais paga o investimento várias vezes. DevOps não é sobre ferramentas: é sobre tornar o deploy uma rotina invisível, não um evento estressante.
Precisa de um talento tech agora?
Fale com a Bradata e receba uma proposta em 24 horas úteis.