Pular para o conteúdo
Desenvolvimento

Construindo pipelines DevOps com Docker e entrega contínua

Admin6 min de leitura
Construindo pipelines DevOps com Docker e entrega contínua

Construindo pipelines DevOps com Docker e entrega contínua

Tecnologia e Inovação

Nota: Este artigo foca em estratégias e ferramentas que permitem transformar o ciclo de vida de software em um fluxo automatizado, confiável e repetível. Não é necessário conhecimento avançado de orquestração de clusters; o objetivo é mostrar como montar a base do pipeline usando Docker e integração contínua.


Introdução

Nos últimos anos, a cultura DevOps se consolidou como o caminho mais eficiente para reduzir o tempo entre a escrita de código e a entrega de valor ao cliente. Dois pilares fundamentais desse movimento são:

  • Containerização – empacotar a aplicação e todas as suas dependências em um artefato portátil.
  • Entrega Contínua (CI/CD) – automatizar a construção, teste e publicação de artefatos em ambientes de produção.
  • Quando combinados, esses elementos criam um fluxo de trabalho onde cada commit pode ser validado, testado e, se aprovado, promovido automaticamente para o próximo estágio. O resultado é maior velocidade, menor risco e maior confiança nas mudanças.

    Neste artigo, você vai descobrir:

    Como estruturar um Dockerfile enxuto e seguro. Como definir um pipeline de CI/CD usando GitHub Actions (ou outra plataforma similar). Como automatizar o provisionamento de infraestrutura com scripts de linha de comando. Boas práticas para versionamento, controle de qualidade e monitoramento do pipeline.

    Vamos colocar a mão na massa!


    1. Preparando a base: Dockerfile otimizado

    Um Dockerfile bem escrito reduz o tempo de build, diminui a superfície de ataque e facilita a manutenção. A seguir, um exemplo de Dockerfile para uma aplicação Node.js que usa a estratégia de multistage build.

    # ---------- Etapa 1: Build ----------
    

    FROM node:20-alpine AS builder WORKDIR /app

    Copiar apenas o package.json e o lock para aproveitar o cache

    COPY package.json ./ RUN npm ci --only=production

    Copiar o código-fonte e compilar (se houver)

    COPY . . RUN npm run build

    ---------- Etapa 2: Runtime ----------

    FROM node:20-alpine WORKDIR /app

    Copiar apenas os artefatos necessários da etapa anterior

    COPY --from=builder /app/dist ./dist COPY --from=builder /app/node_modules ./node_modules COPY --from=builder /app/package.json ./

    Definir variáveis de ambiente seguras

    ENV NODE_ENV=production ENV PORT=3000

    Expor a porta da aplicação

    EXPOSE 3000

    Usuário não-root para segurança

    RUN addgroup -S appgroup && adduser -S appuser -G appgroup USER appuser

    Comando de inicialização

    CMD ["node", "dist/index.js"]

    Por que esse Dockerfile funciona bem?

    Boa práticaBenefício
    MultistageReduz o tamanho final da imagem ao descartar arquivos de compilação.
    Cache de dependênciaspackage.json é copiado antes do código-fonte, permitindo que o npm ci seja reutilizado entre builds.
    Usuário não-rootMinimiza riscos caso a aplicação seja comprometida.
    Variáveis de ambienteFacilita a configuração em diferentes ambientes (dev, staging, prod).

    2. Automatizando a integração: workflow de CI/CD

    A maioria das equipes usa plataformas como GitHub Actions, GitLab CI ou Azure Pipelines. Vamos criar um workflow simples que:

  • Compila a aplicação.
  • Executa testes unitários.
  • Constrói a imagem Docker.
  • Publica a imagem em um registro privado.
  • Desencadeia o provisionamento do ambiente de teste.
  • Atenção: Substitua os placeholders (, , etc.) pelos valores reais do seu projeto.

    # .github/workflows/ci-cd.yml
    

    name: CI/CD Pipeline

    on: push: branches: [ main ] pull_request: branches: [ main ]

    jobs: build-test: runs-on: ubuntu-latest steps: - name: Checkout do código uses: actions/checkout@v3

    - name: Configurar Node.js uses: actions/setup-node@v3 with: node-version: '20'

    - name: Instalar dependências run: npm ci

    - name: Executar testes run: npm test

    - name: Build da aplicação run: npm run build

    docker-build: needs: build-test runs-on: ubuntu-latest steps: - name: Checkout do código uses: actions/checkout@v3

    - name: Login no registro de containers uses: docker/login-action@v2 with: registry: <YOUR_REGISTRY> username: ${{ secrets.REGISTRY_USER }} password: ${{ secrets.REGISTRY_PASS }}

    - name: Build e push da imagem uses: docker/build-push-action@v4 with: context: . push: true tags: <YOUR_REGISTRY>/my-app:${{ github.sha }}

    provision-test: needs: docker-build runs-on: ubuntu-latest env: AWS_REGION: us-east-1 steps: - name: Checkout do código uses: actions/checkout@v3

    - name: Instalar AWS CLI run: | curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" unzip awscliv2.zip sudo ./aws/install

    - name: Deploy no ambiente de teste run: | chmod +x scripts/deploy-test.sh ./scripts/deploy-test.sh ${{ github.sha }}

    Explicação das etapas

    JobFunção
    build-testCompila o código e garante que a base está estável.
    docker-buildConstrói a imagem Docker e a envia para o registro, versionada pelo SHA do commit.
    provision-testExecuta um script que cria (ou atualiza) recursos de teste na nuvem, usando a imagem recém‑publicada.

    3. Provisionamento automático com script de linha de comando

    Em vez de usar ferramentas declarativas que contenham os termos proibidos, podemos aproveitar a CLI da nuvem (AWS, Azure ou GCP) para criar recursos de forma programática. O exemplo abaixo demonstra como lançar um cluster de containers e implantar a imagem recém‑gerada usando a AWS CLI.

    Pré‑requisitos:

    - Credenciais configuradas (aws configure).

    - Permissões para criar/gerenciar ECS, ALB e VPC.

    #!/usr/bin/env bash
    

    scripts/deploy-test.sh

    Uso: ./deploy-test.sh <image-tag>

    set -euo pipefail

    IMAGE_TAG=$1 CLUSTER_NAME="devops-test-cluster" SERVICE_NAME="my-app-service" TASK_FAMILY="my-app-task" REGION="us-east-1"

    1️⃣ Criar (ou garantir) o cluster ECS

    aws ecs describe-clusters --clusters $CLUSTER_NAME --region $REGION \ || aws ecs create-cluster --cluster-name $CLUSTER_NAME --region $REGION

    2️⃣ Registrar definição de tarefa

    TASK_DEF=$(cat <<EOF { "family": "$TASK_FAMILY", "networkMode": "awsvpc", "requiresCompatibilities": ["FARGATE"], "cpu": "256", "memory": "512", "containerDefinitions": [ { "name": "my-app", "image": "<YOUR_REGISTRY>/my-app:$IMAGE_TAG", "portMappings": [{ "containerPort": 3000, "protocol": "tcp" }], "essential": true, "environment": [ { "name": "NODE_ENV", "value": "production" } ] } ] } EOF ) aws ecs register-task-definition --cli-input-json "$TASK_DEF" --region $REGION

    3️⃣ Criar ou atualizar o serviço

    aws ecs describe-services --cluster $CLUSTER_NAME --services $SERVICE_NAME --region $REGION \ || aws ecs create-service \ --cluster $CLUSTER_NAME \ --service-name $SERVICE_NAME \ --task-definition $TASK_FAMILY \ --desired-count 1 \ --launch-type FARGATE \ --network-configuration "awsvpcConfiguration={subnets=[subnet-xxxxxx],securityGroups=[sg-xxxxxx],assignPublicIp=ENABLED}" \ --region $REGION

    4️⃣ Forçar nova implantação

    aws ecs update-service \ --cluster $CLUSTER_NAME \ --service $SERVICE_NAME \ --force-new-deployment \ --region $REGION

    echo "✅ Deploy concluído! A aplicação está rodando com a imagem $IMAGE_TAG"

    Dicas de segurança

    Credenciais – Use secrets da plataforma CI/CD; nunca exponha chaves no código. Políticas de IAM – Conceda apenas as permissões necessárias (princípio do menor privilégio). Variáveis de ambiente – Mantenha segredos (tokens, chaves) fora da imagem, usando o mecanismo de injeção da nuvem.


    4. Boas práticas para um pipeline resiliente

    ÁreaRecomendações
    VersionamentoTagueie imagens com o SHA do commit e, opcionalmente, com semver (v1.2.3).
    Testes
    Inclua testes unitários, de integração e, se possível, testes de

    Artigos relacionados