#!/bin/bash # BCards Deploy Script - Containers Independentes # Deploy automatizado da aplicação nos servidores OCI com Docker Compose set -e # Configurações SERVER_IPS=("141.148.162.114" "129.146.116.218") SSH_USER="ubuntu" REGISTRY="registry.redecarneir.us" APP_NAME="bcards-test-app" CONTAINER_NAME="bcards-app" NETWORK_NAME="bcards-network" # Versão da aplicação (pode ser passada como parâmetro) VERSION=${1:-"latest"} # Cores para output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' log() { echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}" } error() { echo -e "${RED}[ERROR] $1${NC}" exit 1 } warn() { echo -e "${YELLOW}[WARNING] $1${NC}" } info() { echo -e "${BLUE}[INFO] $1${NC}" } # Função para executar comando em servidor remoto run_remote() { local server_ip=$1 local command=$2 ssh -o StrictHostKeyChecking=no ${SSH_USER}@${server_ip} "$command" } # Verificar conectividade com servidores check_connectivity() { log "Verificando conectividade com os servidores..." for server_ip in "${SERVER_IPS[@]}"; do if ! ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=no ${SSH_USER}@${server_ip} "echo 'ok'" >/dev/null 2>&1; then error "Não foi possível conectar ao servidor $server_ip" fi info "✓ Servidor $server_ip conectado" done } # Verificar se Docker está ativo check_docker() { log "Verificando status do Docker..." for server_ip in "${SERVER_IPS[@]}"; do if ! run_remote $server_ip "sudo docker info" >/dev/null 2>&1; then error "Docker não está ativo no servidor $server_ip. Execute setup-servers.sh primeiro." fi info "✓ Docker está ativo no servidor $server_ip" done } # Build da aplicação local (se necessário) build_application() { log "Building aplicação localmente..." cd test-app # Build da imagem Docker info "Building imagem Docker..." docker build -t ${APP_NAME}:${VERSION} . # Tag para registry docker tag ${APP_NAME}:${VERSION} ${REGISTRY}/${APP_NAME}:${VERSION} docker tag ${APP_NAME}:${VERSION} ${REGISTRY}/${APP_NAME}:latest info "✓ Aplicação construída com sucesso" cd .. } # Push da imagem para registry push_to_registry() { log "Enviando imagem para registry..." # Push da imagem docker push ${REGISTRY}/${APP_NAME}:${VERSION} docker push ${REGISTRY}/${APP_NAME}:latest info "✓ Imagem enviada para registry" } # Deploy em containers independentes deploy_containers() { log "Fazendo deploy dos containers independentes..." local server_count=1 for server_ip in "${SERVER_IPS[@]}"; do info "Deploy no servidor $server_ip (Server $server_count)..." # Criar docker-compose específico para cada servidor cat > /tmp/docker-compose-server${server_count}.yml << EOF version: '3.8' services: bcards-app: image: ${REGISTRY}/${APP_NAME}:${VERSION} container_name: ${CONTAINER_NAME}-server${server_count} restart: unless-stopped ports: - "8080:8080" environment: - ASPNETCORE_ENVIRONMENT=Production - SERVER_NAME=BCards Server ${server_count} - SERVER_COLOR=$([ $server_count -eq 1 ] && echo "#28a745" || echo "#007bff") networks: - bcards-network mem_limit: 512m mem_reservation: 256m healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/health"] interval: 30s timeout: 10s retries: 3 start_period: 40s logging: driver: "json-file" options: max-size: "10m" max-file: "3" networks: bcards-network: external: true EOF # Copiar docker-compose para o servidor scp /tmp/docker-compose-server${server_count}.yml ${SSH_USER}@${server_ip}:~/bcards/docker-compose.yml # Pull da imagem info "Fazendo pull da imagem no servidor $server_ip..." run_remote $server_ip "sudo docker pull ${REGISTRY}/${APP_NAME}:${VERSION}" # Parar container antigo se existir run_remote $server_ip "sudo docker compose -f ~/bcards/docker-compose.yml down" || true # Iniciar novo container info "Iniciando container no servidor $server_ip..." run_remote $server_ip "cd ~/bcards && sudo docker compose up -d" ((server_count++)) done info "✓ Containers deployados com sucesso" } # Aguardar containers ficarem saudáveis wait_for_health() { log "Aguardando containers ficarem saudáveis..." local max_attempts=30 local attempt=1 while [ $attempt -le $max_attempts ]; do local healthy_containers=0 for server_ip in "${SERVER_IPS[@]}"; do local container_health=$(run_remote $server_ip "sudo docker inspect --format='{{.State.Health.Status}}' ${CONTAINER_NAME}-server* 2>/dev/null" | grep -c "healthy" || echo "0") ((healthy_containers += container_health)) done if [ "$healthy_containers" -eq "2" ]; then info "✓ Todos os containers estão saudáveis" return 0 fi info "Tentativa $attempt/$max_attempts - Aguardando containers ficarem saudáveis... ($healthy_containers/2)" sleep 10 ((attempt++)) done warn "Timeout aguardando containers ficarem saudáveis" check_container_status return 1 } # Verificar saúde dos containers check_container_status() { log "Verificando saúde dos containers..." local server_count=1 for server_ip in "${SERVER_IPS[@]}"; do info "Status do servidor $server_ip:" run_remote $server_ip "sudo docker ps --filter name=${CONTAINER_NAME}" info "Health check do servidor $server_ip:" run_remote $server_ip "sudo docker inspect --format='{{.State.Health.Status}}' ${CONTAINER_NAME}-server${server_count} 2>/dev/null" || true info "Logs dos últimos 50 eventos do servidor $server_ip:" run_remote $server_ip "sudo docker logs --tail 50 ${CONTAINER_NAME}-server${server_count}" || true ((server_count++)) done } # Teste de conectividade externa test_connectivity() { log "Testando conectividade externa..." # Teste HTTP if curl -f -s "http://bcards.site/health" >/dev/null 2>&1; then info "✓ HTTP health check passou" else warn "HTTP health check falhou" fi # Teste HTTPS (se SSL estiver configurado) if curl -f -s "https://bcards.site/health" >/dev/null 2>&1; then info "✓ HTTPS health check passou" else warn "HTTPS health check falhou ou SSL não configurado" fi } # Rollback em caso de falha rollback() { log "Executando rollback..." for server_ip in "${SERVER_IPS[@]}"; do info "Rollback no servidor $server_ip..." # Parar container atual run_remote $server_ip "sudo docker compose -f ~/bcards/docker-compose.yml down" || true # Tentar usar imagem latest anterior run_remote $server_ip "sudo docker pull ${REGISTRY}/${APP_NAME}:latest" # Atualizar docker-compose para usar latest run_remote $server_ip "sed -i 's/:${VERSION}/:latest/g' ~/bcards/docker-compose.yml" # Reiniciar com versão anterior run_remote $server_ip "cd ~/bcards && sudo docker compose up -d" done info "Rollback executado" } # Limpeza de imagens antigas cleanup_old_images() { log "Limpando imagens antigas..." for server_ip in "${SERVER_IPS[@]}"; do info "Limpando imagens no servidor $server_ip..." run_remote $server_ip " sudo docker image prune -f sudo docker container prune -f " done info "✓ Limpeza concluída" } # Função principal main() { log "Iniciando deploy para versão $VERSION..." # Verificações pré-deploy check_connectivity check_docker # Build e push if [[ "$2" != "--skip-build" ]]; then build_application push_to_registry else info "Pulando build (--skip-build especificado)" fi # Deploy deploy_containers # Verificações pós-deploy if wait_for_health; then check_container_status test_connectivity cleanup_old_images log "Deploy concluído com sucesso!" info "Aplicação disponível em: http://bcards.site" info "Health check: http://bcards.site/health" else error "Deploy falhou - serviços não ficaram saudáveis" if [[ "$3" == "--auto-rollback" ]]; then rollback fi fi } # Mostrar help show_help() { echo "BCards Deploy Script" echo "" echo "Usage: $0 [VERSION] [OPTIONS]" echo "" echo "VERSION:" echo " Versão da aplicação para deploy (default: latest)" echo "" echo "OPTIONS:" echo " --skip-build Pula o build local da aplicação" echo " --auto-rollback Executa rollback automático em caso de falha" echo " --help Mostra esta ajuda" echo "" echo "Examples:" echo " $0 # Deploy versão latest" echo " $0 v1.2.3 # Deploy versão específica" echo " $0 latest --skip-build # Deploy sem rebuild" echo "" echo "Servers: ${SERVER_IPS[*]}" echo "Registry: $REGISTRY" } # Parse argumentos if [[ "$1" == "--help" || "$1" == "-h" ]]; then show_help exit 0 fi # Executar função principal main "$@"