Blue-green deployment scripting

Conceitos Fundamentais do Blue-Green Deployment

Blue-green deployment é uma estratégia de release que mantém dois ambientes de produção idênticos, chamados convencionalmente de "blue" e "green". Apenas um deles recebe tráfego de produção ativo por vez. Quando uma nova versão da aplicação precisa ser implantada, ela é colocada no ambiente inativo, testada e, se aprovada, o roteador ou balanceador de carga comuta o tráfego para esse novo ambiente.

O principal benefício dessa abordagem é a redução drástica do downtime — a comutação é praticamente instantânea. Além disso, o rollback se torna trivial: basta reverter o tráfego para o ambiente anterior, que ainda contém a versão estável. Em termos de arquitetura, o balanceador de carga (como Nginx, HAProxy ou um cloud load balancer) é o ponto central de decisão, pois direciona as requisições para o ambiente ativo.

Estrutura de Diretórios e Versionamento de Ambientes

Para implementar blue-green deployment com scripts Bash, a organização do sistema de arquivos é fundamental. Adotaremos a seguinte estrutura:

/deploy/
├── blue/
│   ├── app/          # Código da aplicação
│   ├── config/       # Configurações específicas
│   └── version.txt   # Versão implantada
├── green/
│   ├── app/
│   ├── config/
│   └── version.txt
└── state/
    └── active.env    # Arquivo contendo "blue" ou "green"

O arquivo /deploy/state/active.env armazena o ambiente atualmente em produção. Um script simples de leitura desse estado permite que qualquer processo no servidor saiba para onde o tráfego está sendo direcionado.

#!/bin/bash
# read_active_env.sh
ACTIVE_ENV=$(cat /deploy/state/active.env)
echo "Ambiente ativo: $ACTIVE_ENV"

Script de Preparação e Sincronização de Artefatos

O primeiro passo de um deploy é preparar o ambiente inativo com a nova versão. O script abaixo identifica qual ambiente está inativo, copia os artefatos e valida a integridade via checksum.

#!/bin/bash
# prepare_deploy.sh
set -euo pipefail

ARTIFACT_DIR="/deploy"
REPO_URL="https://github.com/empresa/app.git"
COMMIT_HASH="${1:-main}"

# Determinar ambiente inativo
ACTIVE_ENV=$(cat $ARTIFACT_DIR/state/active.env)
if [ "$ACTIVE_ENV" == "blue" ]; then
    TARGET_ENV="green"
else
    TARGET_ENV="blue"
fi

echo "Preparando deploy no ambiente: $TARGET_ENV"

# Clonar repositório no ambiente alvo
rm -rf $ARTIFACT_DIR/$TARGET_ENV/app
git clone --depth 1 --branch $COMMIT_HASH $REPO_URL $ARTIFACT_DIR/$TARGET_ENV/app

# Gerar e validar checksum
cd $ARTIFACT_DIR/$TARGET_ENV/app
find . -type f -exec sha256sum {} \; > ../checksums.txt

# Instalar dependências
npm install --production  # Exemplo para Node.js
# ou: pip install -r requirements.txt  # Para Python

# Ajustar permissões
chmod -R 755 $ARTIFACT_DIR/$TARGET_ENV/app

# Registrar versão
echo $COMMIT_HASH > $ARTIFACT_DIR/$TARGET_ENV/version.txt
echo "Preparação concluída para $TARGET_ENV"

Lógica de Comutação (Switch) Entre Ambientes

A comutação é o momento crítico do deploy. O script abaixo lê o estado atual, valida a saúde do novo ambiente e, se tudo estiver correto, atualiza o balanceador de carga.

#!/bin/bash
# switch_deploy.sh
set -euo pipefail

ARTIFACT_DIR="/deploy"
ACTIVE_ENV=$(cat $ARTIFACT_DIR/state/active.env)

if [ "$ACTIVE_ENV" == "blue" ]; then
    NEW_ENV="green"
    OLD_ENV="blue"
else
    NEW_ENV="blue"
    OLD_ENV="green"
fi

echo "Comutando de $OLD_ENV para $NEW_ENV"

# Validar saúde do novo ambiente
if ! /usr/local/bin/health_check.sh $NEW_ENV; then
    echo "ERRO: Health check falhou para $NEW_ENV. Abortando switch."
    exit 1
fi

# Atualizar configuração do Nginx (exemplo)
sed -i "s/proxy_pass http:\/\/$OLD_ENV;/proxy_pass http:\/\/$NEW_ENV;/g" /etc/nginx/sites-enabled/app.conf
nginx -s reload

# Atualizar estado
echo $NEW_ENV > $ARTIFACT_DIR/state/active.env

echo "Switch concluído. Ambiente ativo: $NEW_ENV"

Health Checks e Validação Pós-Deploy

Health checks são a garantia de que o novo ambiente está funcionando antes de receber tráfego. O script abaixo implementa verificações com timeout e retry.

#!/bin/bash
# health_check.sh
set -euo pipefail

TARGET_ENV="${1:-green}"
MAX_RETRIES=5
TIMEOUT=10
SLEEP_INTERVAL=3

echo "Executando health check para ambiente: $TARGET_ENV"

# Configurar porta baseada no ambiente
if [ "$TARGET_ENV" == "blue" ]; then
    PORT=3001
else
    PORT=3002
fi

for i in $(seq 1 $MAX_RETRIES); do
    # Teste de conectividade básica
    if curl -s --connect-timeout $TIMEOUT http://localhost:$PORT/health | grep -q '"status":"ok"'; then
        echo "Health check passou na tentativa $i"
        exit 0
    fi

    # Teste de endpoint crítico
    if curl -s --connect-timeout $TIMEOUT http://localhost:$PORT/api/v1/status | grep -q '"db":"connected"'; then
        echo "Banco de dados conectado na tentativa $i"
        exit 0
    fi

    echo "Tentativa $i falhou. Aguardando $SLEEP_INTERVAL segundos..."
    sleep $SLEEP_INTERVAL
done

echo "ERRO: Health check falhou após $MAX_RETRIES tentativas"
exit 1

Rollback Automatizado e Gerenciamento de Falhas

Quando o health check falha, o rollback deve ser automático. O script abaixo detecta a falha e reverte para o ambiente anterior.

#!/bin/bash
# rollback.sh
set -euo pipefail

ARTIFACT_DIR="/deploy"
ACTIVE_ENV=$(cat $ARTIFACT_DIR/state/active.env)

if [ "$ACTIVE_ENV" == "blue" ]; then
    ROLLBACK_ENV="green"
else
    ROLLBACK_ENV="blue"
fi

echo "INICIANDO ROLLBACK para $ROLLBACK_ENV"

# Reverter configuração do Nginx
sed -i "s/proxy_pass http:\/\/$ACTIVE_ENV;/proxy_pass http:\/\/$ROLLBACK_ENV;/g" /etc/nginx/sites-enabled/app.conf
nginx -s reload

# Atualizar estado
echo $ROLLBACK_ENV > $ARTIFACT_DIR/state/active.env

# Notificar falha (exemplo com Slack)
curl -X POST -H "Content-type: application/json" \
    --data "{\"text\":\"🚨 Rollback executado: deploy em $ACTIVE_ENV falhou, revertido para $ROLLBACK_ENV\"}" \
    https://hooks.slack.com/services/TOKEN

echo "Rollback concluído. Ambiente ativo: $ROLLBACK_ENV"
logger -t deploy "Rollback automático: $ACTIVE_ENV -> $ROLLBACK_ENV"

Integração com Ferramentas de CI/CD e Logging

Para integrar com pipelines CI/CD, os scripts bash podem ser chamados diretamente. Abaixo, um exemplo de pipeline para GitHub Actions:

name: Blue-Green Deploy
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Preparar deploy
        run: |
          ssh deploy@servidor "bash /deploy/scripts/prepare_deploy.sh ${{ github.sha }}"

      - name: Executar health check
        run: |
          ssh deploy@servidor "bash /deploy/scripts/health_check.sh green"

      - name: Comutar tráfego
        run: |
          ssh deploy@servidor "bash /deploy/scripts/switch_deploy.sh"

Para logging estruturado, cada script deve gerar saída no formato JSON para facilitar a auditoria:

#!/bin/bash
# Exemplo de log estruturado
LOG='{"timestamp":"'"$(date -u +"%Y-%m-%dT%H:%M:%SZ")"'","action":"deploy","environment":"green","status":"success","version":"v2.1.0"}'
echo $LOG >> /var/log/deployments.log

A rotação de logs pode ser configurada com logrotate:

/var/log/deployments.log {
    daily
    rotate 30
    compress
    missingok
    notifempty
}

O cleanup de artefatos antigos garante que o ambiente inativo não acumule versões desnecessárias:

#!/bin/bash
# cleanup.sh
ARTIFACT_DIR="/deploy"
ACTIVE_ENV=$(cat $ARTIFACT_DIR/state/active.env)

if [ "$ACTIVE_ENV" == "blue" ]; then
    CLEAN_ENV="green"
else
    CLEAN_ENV="blue"
fi

# Remover versões antigas, mantendo apenas a atual
find $ARTIFACT_DIR/$CLEAN_ENV -mindepth 1 -maxdepth 1 ! -name "current" -exec rm -rf {} \;
echo "Cleanup concluído para $CLEAN_ENV"

Este conjunto de scripts forma uma base sólida para implementar blue-green deployment com Bash. A simplicidade dos scripts permite fácil manutenção e adaptação a diferentes stacks tecnológicas, mantendo os benefícios fundamentais: zero downtime durante deploys e rollback instantâneo em caso de falhas.

Referências