diff --git a/.github/workflows/deploy-to-openshift.yml b/.github/workflows/deploy-to-openshift.yml new file mode 100644 index 0000000..28ca2d8 --- /dev/null +++ b/.github/workflows/deploy-to-openshift.yml @@ -0,0 +1,88 @@ +name: Deploy to OpenShift (Manual Trigger) + +on: + workflow_dispatch: + inputs: + openshift_server: + description: 'OpenShift Server URL' + required: true + default: 'https://api.your-cluster.com' + openshift_token: + description: 'OpenShift Token' + required: true + type: string + namespace: + description: 'Target Namespace' + required: true + default: 'resource-governance' + image_tag: + description: 'Image Tag to Deploy' + required: false + default: 'latest' + +env: + IMAGE_NAME: resource-governance + REGISTRY: andersonid + +jobs: + deploy-to-openshift: + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install OpenShift CLI + run: | + curl -L https://mirror.openshift.com/pub/openshift-v4/clients/oc/latest/linux/oc.tar.gz | tar -xz -C /usr/local/bin/ + chmod +x /usr/local/bin/oc + + - name: Login to OpenShift + run: | + oc login ${{ inputs.openshift_server }} --token="${{ inputs.openshift_token }}" --insecure-skip-tls-verify=true + + - name: Deploy to OpenShift + run: | + # Usar tag fornecida ou latest + IMAGE_TAG="${{ inputs.image_tag }}" + if [ "$IMAGE_TAG" = "latest" ]; then + # Para latest, usar o commit SHA atual + IMAGE_TAG="${{ github.sha }}" + fi + + echo "🚀 Deploying image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:$IMAGE_TAG" + echo "📦 Target namespace: ${{ inputs.namespace }}" + + # Verificar se o namespace existe + if ! oc get namespace ${{ inputs.namespace }} > /dev/null 2>&1; then + echo "📋 Creating namespace ${{ inputs.namespace }}..." + oc create namespace ${{ inputs.namespace }} + fi + + # Aplicar manifests básicos + oc apply -f k8s/rbac.yaml -n ${{ inputs.namespace }} + oc apply -f k8s/configmap.yaml -n ${{ inputs.namespace }} + + # Atualizar deployment com nova imagem + oc set image deployment/${{ env.IMAGE_NAME }} ${{ env.IMAGE_NAME }}=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:$IMAGE_TAG -n ${{ inputs.namespace }} || true + + # Aplicar deployment, service e route + oc apply -f k8s/deployment.yaml -n ${{ inputs.namespace }} + oc apply -f k8s/service.yaml -n ${{ inputs.namespace }} + oc apply -f k8s/route.yaml -n ${{ inputs.namespace }} + + # Aguardar rollout + echo "⏳ Waiting for rollout..." + oc rollout status deployment/${{ env.IMAGE_NAME }} -n ${{ inputs.namespace }} --timeout=300s + + # Verificar status + echo "✅ Deployment completed!" + oc get deployment ${{ env.IMAGE_NAME }} -n ${{ inputs.namespace }} + oc get pods -n ${{ inputs.namespace }} -l app.kubernetes.io/name=${{ env.IMAGE_NAME }} + + # Obter URL da rota + ROUTE_URL=$(oc get route ${{ env.IMAGE_NAME }}-route -n ${{ inputs.namespace }} -o jsonpath='{.spec.host}' 2>/dev/null || echo "") + if [ -n "$ROUTE_URL" ]; then + echo "🌐 Application URL: https://$ROUTE_URL" + fi diff --git a/AUTO-DEPLOY-GUIDE.md b/AUTO-DEPLOY-GUIDE.md new file mode 100644 index 0000000..36faf27 --- /dev/null +++ b/AUTO-DEPLOY-GUIDE.md @@ -0,0 +1,277 @@ +# 🚀 Deploy Automático - Guia Completo + +## 📋 Visão Geral + +Este guia explica como configurar deploy automático após o GitHub Actions criar a imagem no Docker Hub. + +## 🔍 **SITUAÇÃO ATUAL:** + +### ✅ **GitHub Actions (Funcionando):** +- Builda a imagem automaticamente +- Faz push para Docker Hub +- **NÃO faz deploy no OpenShift** + +### ❌ **OpenShift (Manual):** +- **NÃO detecta** mudanças na imagem automaticamente +- Precisa de **rollout manual** +- Blue-Green só funciona se configurado + +## 🚀 **SOLUÇÕES PARA DEPLOY AUTOMÁTICO:** + +### **Opção 1: Deploy Manual via GitHub Actions** ⭐ (Recomendado) + +#### **Como usar:** +1. Vá para: `Actions` → `Deploy to OpenShift (Manual Trigger)` +2. Clique em `Run workflow` +3. Preencha os campos: + - **OpenShift Server URL**: `https://api.your-cluster.com` + - **OpenShift Token**: Seu token do OpenShift + - **Target Namespace**: `resource-governance` + - **Image Tag**: `latest` ou tag específica +4. Clique em `Run workflow` + +#### **Vantagens:** +- ✅ Seguro (requer confirmação manual) +- ✅ Funciona com qualquer cluster OpenShift +- ✅ Controle total sobre quando fazer deploy +- ✅ Logs detalhados no GitHub Actions + +--- + +### **Opção 2: Deploy Automático Local** 🔄 + +#### **Como usar:** +```bash +# Deploy automático com latest +./scripts/auto-deploy.sh + +# Deploy automático com tag específica +./scripts/auto-deploy.sh v1.0.0 + +# Deploy automático com commit SHA +./scripts/auto-deploy.sh 6f6e4ed19d2fbcccba548eeaf0d9e2624f41afba +``` + +#### **Vantagens:** +- ✅ Deploy automático +- ✅ Verifica se a imagem existe no Docker Hub +- ✅ Blue-Green deployment (zero downtime) +- ✅ Logs detalhados + +#### **Configuração:** +```bash +# 1. Fazer login no OpenShift +oc login https://api.your-cluster.com + +# 2. Executar deploy automático +./scripts/auto-deploy.sh latest +``` + +--- + +### **Opção 3: Webhook para Deploy Automático** 🌐 + +#### **Como configurar:** + +1. **Instalar dependências:** +```bash +pip install flask +``` + +2. **Configurar variáveis de ambiente:** +```bash +export IMAGE_NAME="resource-governance" +export REGISTRY="andersonid" +export NAMESPACE="resource-governance" +export AUTO_DEPLOY_SCRIPT="./scripts/auto-deploy.sh" +``` + +3. **Executar webhook server:** +```bash +python3 scripts/webhook-deploy.py +``` + +4. **Configurar webhook no Docker Hub:** + - Acesse: https://hub.docker.com/r/andersonid/resource-governance/webhooks + - Clique em `Create Webhook` + - **Webhook URL**: `http://your-server:8080/webhook/dockerhub` + - **Trigger**: `Push to repository` + - **Tag**: `latest` + +#### **Endpoints disponíveis:** +- `POST /webhook/dockerhub` - Webhook do Docker Hub +- `POST /webhook/github` - Webhook do GitHub +- `POST /deploy/` - Deploy manual +- `GET /health` - Health check +- `GET /status` - Status do serviço + +#### **Vantagens:** +- ✅ Deploy completamente automático +- ✅ Funciona com Docker Hub e GitHub +- ✅ API REST para controle +- ✅ Logs detalhados + +--- + +### **Opção 4: Cron Job para Deploy Automático** ⏰ + +#### **Como configurar:** + +1. **Criar script de verificação:** +```bash +#!/bin/bash +# scripts/check-and-deploy.sh + +# Verificar se há nova imagem +LATEST_SHA=$(curl -s "https://api.github.com/repos/andersonid/openshift-resource-governance/commits/main" | jq -r '.sha') +CURRENT_SHA=$(oc get deployment resource-governance -n resource-governance -o jsonpath='{.spec.template.spec.containers[0].image}' | cut -d: -f2) + +if [ "$LATEST_SHA" != "$CURRENT_SHA" ]; then + echo "Nova versão detectada: $LATEST_SHA" + ./scripts/auto-deploy.sh $LATEST_SHA +else + echo "Versão já está atualizada: $CURRENT_SHA" +fi +``` + +2. **Configurar cron job:** +```bash +# Executar a cada 5 minutos +*/5 * * * * /path/to/scripts/check-and-deploy.sh >> /var/log/auto-deploy.log 2>&1 +``` + +#### **Vantagens:** +- ✅ Deploy automático baseado em tempo +- ✅ Verifica mudanças automaticamente +- ✅ Simples de configurar + +--- + +## 🔧 **CONFIGURAÇÃO DO BLUE-GREEN DEPLOYMENT:** + +### **Estratégia Rolling Update (Zero Downtime):** +```yaml +# k8s/deployment.yaml +spec: + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 0 # Nunca derruba pods até o novo estar pronto + maxSurge: 1 # Permite 1 pod extra durante o rollout +``` + +### **Health Checks:** +```yaml +# k8s/deployment.yaml +livenessProbe: + httpGet: + path: /api/v1/health + port: 8080 + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + +readinessProbe: + httpGet: + path: /api/v1/health + port: 8080 + initialDelaySeconds: 15 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 5 + successThreshold: 2 +``` + +--- + +## 📊 **MONITORAMENTO DO DEPLOY:** + +### **Verificar Status:** +```bash +# Status do deployment +oc get deployment resource-governance -n resource-governance + +# Status dos pods +oc get pods -n resource-governance -l app.kubernetes.io/name=resource-governance + +# Logs do deployment +oc logs -f deployment/resource-governance -n resource-governance + +# Histórico de rollouts +oc rollout history deployment/resource-governance -n resource-governance +``` + +### **Verificar Imagem Atual:** +```bash +# Imagem atual do deployment +oc get deployment resource-governance -n resource-governance -o jsonpath='{.spec.template.spec.containers[0].image}' + +# Verificar se a imagem existe no Docker Hub +skopeo inspect docker://andersonid/resource-governance:latest +``` + +--- + +## 🚨 **TROUBLESHOOTING:** + +### **Problemas Comuns:** + +#### 1. **Deploy falha com "ImagePullBackOff"** +```bash +# Verificar se a imagem existe +skopeo inspect docker://andersonid/resource-governance:latest + +# Verificar logs do pod +oc describe pod -l app.kubernetes.io/name=resource-governance -n resource-governance +``` + +#### 2. **Rollout fica travado** +```bash +# Verificar status do rollout +oc rollout status deployment/resource-governance -n resource-governance + +# Forçar restart se necessário +oc rollout restart deployment/resource-governance -n resource-governance +``` + +#### 3. **Webhook não funciona** +```bash +# Verificar logs do webhook +python3 scripts/webhook-deploy.py + +# Testar webhook manualmente +curl -X POST http://localhost:8080/deploy/latest +``` + +--- + +## 🎯 **RECOMENDAÇÕES:** + +### **Para Desenvolvimento:** +- Use **Opção 1** (Deploy Manual via GitHub Actions) +- Controle total sobre quando fazer deploy +- Logs detalhados no GitHub + +### **Para Produção:** +- Use **Opção 2** (Deploy Automático Local) com cron job +- Configure webhook para deploy automático +- Monitore logs e status + +### **Para Equipes:** +- Use **Opção 3** (Webhook) com API REST +- Configure notificações +- Implemente rollback automático + +--- + +## 🔗 **LINKS ÚTEIS:** + +- **GitHub Actions**: https://github.com/andersonid/openshift-resource-governance/actions +- **Docker Hub**: https://hub.docker.com/r/andersonid/resource-governance +- **OpenShift CLI**: https://docs.openshift.com/container-platform/latest/cli_reference/openshift_cli/ + +--- + +**Desenvolvido por:** Anderson Nobre +**Suporte:** Abra uma issue no GitHub se tiver problemas diff --git a/scripts/auto-deploy.sh b/scripts/auto-deploy.sh new file mode 100755 index 0000000..230f862 --- /dev/null +++ b/scripts/auto-deploy.sh @@ -0,0 +1,117 @@ +#!/bin/bash + +# Script para deploy automático após GitHub Actions +# Este script pode ser executado localmente ou via webhook + +set -e + +# Cores para output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Configurações +IMAGE_NAME="resource-governance" +REGISTRY="andersonid" +NAMESPACE="resource-governance" +IMAGE_TAG=${1:-latest} + +echo -e "${BLUE}🚀 Auto-Deploy para OpenShift${NC}" +echo "================================" +echo "Imagem: ${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}" +echo "Namespace: ${NAMESPACE}" +echo "" + +# 1. Verificar login no OpenShift +if ! oc whoami > /dev/null 2>&1; then + echo -e "${RED}❌ Não logado no OpenShift. Por favor, faça login com 'oc login'.${NC}" + exit 1 +fi +echo -e "${GREEN}✅ Logado no OpenShift como: $(oc whoami)${NC}" +echo "" + +# 2. Verificar se a imagem existe no Docker Hub +echo -e "${BLUE}🔍 Verificando imagem no Docker Hub...${NC}" +if ! skopeo inspect docker://${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} > /dev/null 2>&1; then + echo -e "${RED}❌ Imagem ${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} não encontrada no Docker Hub!${NC}" + exit 1 +fi +echo -e "${GREEN}✅ Imagem encontrada no Docker Hub${NC}" +echo "" + +# 3. Verificar se o namespace existe +if ! oc get namespace ${NAMESPACE} > /dev/null 2>&1; then + echo -e "${BLUE}📋 Criando namespace ${NAMESPACE}...${NC}" + oc create namespace ${NAMESPACE} +else + echo -e "${GREEN}✅ Namespace ${NAMESPACE} já existe${NC}" +fi +echo "" + +# 4. Aplicar manifests básicos +echo -e "${BLUE}📋 Aplicando manifests básicos...${NC}" +oc apply -f k8s/rbac.yaml -n ${NAMESPACE} +oc apply -f k8s/configmap.yaml -n ${NAMESPACE} +echo "" + +# 5. Verificar se o deployment existe +if oc get deployment ${IMAGE_NAME} -n ${NAMESPACE} > /dev/null 2>&1; then + echo -e "${BLUE}🔄 Deployment existente encontrado. Iniciando atualização...${NC}" + + # Obter imagem atual + CURRENT_IMAGE=$(oc get deployment ${IMAGE_NAME} -n ${NAMESPACE} -o jsonpath='{.spec.template.spec.containers[0].image}') + echo "Imagem atual: ${CURRENT_IMAGE}" + echo "Nova imagem: ${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}" + + # Verificar se a imagem mudou + if [ "${CURRENT_IMAGE}" = "${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}" ]; then + echo -e "${YELLOW}⚠️ Imagem já está atualizada. Nenhuma ação necessária.${NC}" + exit 0 + fi + + # Atualizar deployment com nova imagem + echo -e "${BLUE}🔄 Atualizando imagem do deployment...${NC}" + oc set image deployment/${IMAGE_NAME} ${IMAGE_NAME}=${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} -n ${NAMESPACE} + + # Aguardar rollout + echo -e "${BLUE}⏳ Aguardando rollout (pode levar alguns minutos)...${NC}" + oc rollout status deployment/${IMAGE_NAME} -n ${NAMESPACE} --timeout=300s + echo -e "${GREEN}✅ Rollout concluído com sucesso!${NC}" + +else + echo -e "${BLUE}📦 Deployment não encontrado. Criando novo deployment...${NC}" + # Aplicar deployment, service e route + oc apply -f k8s/deployment.yaml -n ${NAMESPACE} + oc apply -f k8s/service.yaml -n ${NAMESPACE} + oc apply -f k8s/route.yaml -n ${NAMESPACE} + + # Aguardar rollout inicial + echo -e "${BLUE}⏳ Aguardando rollout inicial...${NC}" + oc rollout status deployment/${IMAGE_NAME} -n ${NAMESPACE} --timeout=300s + echo -e "${GREEN}✅ Rollout inicial concluído com sucesso!${NC}" +fi +echo "" + +# 6. Verificar status final +echo -e "${BLUE}📊 STATUS FINAL:${NC}" +echo "================" +oc get deployment ${IMAGE_NAME} -n ${NAMESPACE} +echo "" +oc get pods -n ${NAMESPACE} -l app.kubernetes.io/name=${IMAGE_NAME} +echo "" + +# 7. Obter URLs de acesso +ROUTE_URL=$(oc get route ${IMAGE_NAME}-route -n ${NAMESPACE} -o jsonpath='{.spec.host}' 2>/dev/null || echo "") +echo -e "${BLUE}🌐 URLs de acesso:${NC}" +if [ -n "$ROUTE_URL" ]; then + echo " OpenShift: https://$ROUTE_URL" +else + echo " OpenShift: Rota não encontrada ou não disponível." +fi +echo " Port-forward: http://localhost:8080 (se ativo)" +echo "" + +echo -e "${GREEN}✅ Auto-deploy concluído com sucesso!${NC}" +echo -e "${BLUE}🔄 Estratégia: Rolling Update com maxUnavailable=0 (zero downtime)${NC}" diff --git a/scripts/webhook-deploy.py b/scripts/webhook-deploy.py new file mode 100755 index 0000000..17dfec8 --- /dev/null +++ b/scripts/webhook-deploy.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python3 +""" +Webhook para deploy automático após GitHub Actions +Este script pode ser executado como um serviço para detectar mudanças no Docker Hub +""" + +import os +import json +import subprocess +import logging +from flask import Flask, request, jsonify +from datetime import datetime + +# Configuração do logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +app = Flask(__name__) + +# Configurações +IMAGE_NAME = os.getenv('IMAGE_NAME', 'resource-governance') +REGISTRY = os.getenv('REGISTRY', 'andersonid') +NAMESPACE = os.getenv('NAMESPACE', 'resource-governance') +SCRIPT_PATH = os.getenv('AUTO_DEPLOY_SCRIPT', './scripts/auto-deploy.sh') + +@app.route('/webhook/dockerhub', methods=['POST']) +def dockerhub_webhook(): + """Webhook para receber notificações do Docker Hub""" + try: + data = request.get_json() + + # Verificar se é uma notificação de push + if data.get('push_data', {}).get('tag') == 'latest': + logger.info(f"Recebida notificação de push para {REGISTRY}/{IMAGE_NAME}:latest") + + # Executar deploy automático + result = run_auto_deploy('latest') + + return jsonify({ + 'status': 'success', + 'message': 'Deploy automático iniciado', + 'result': result + }), 200 + else: + logger.info(f"Push ignorado - tag: {data.get('push_data', {}).get('tag')}") + return jsonify({'status': 'ignored', 'message': 'Tag não é latest'}), 200 + + except Exception as e: + logger.error(f"Erro no webhook: {e}") + return jsonify({'status': 'error', 'message': str(e)}), 500 + +@app.route('/webhook/github', methods=['POST']) +def github_webhook(): + """Webhook para receber notificações do GitHub""" + try: + # Verificar se é um push para main + if request.headers.get('X-GitHub-Event') == 'push': + data = request.get_json() + + if data.get('ref') == 'refs/heads/main': + logger.info("Recebida notificação de push para main branch") + + # Executar deploy automático + result = run_auto_deploy('latest') + + return jsonify({ + 'status': 'success', + 'message': 'Deploy automático iniciado', + 'result': result + }), 200 + else: + logger.info(f"Push ignorado - branch: {data.get('ref')}") + return jsonify({'status': 'ignored', 'message': 'Branch não é main'}), 200 + else: + logger.info(f"Evento ignorado: {request.headers.get('X-GitHub-Event')}") + return jsonify({'status': 'ignored', 'message': 'Evento não é push'}), 200 + + except Exception as e: + logger.error(f"Erro no webhook: {e}") + return jsonify({'status': 'error', 'message': str(e)}), 500 + +@app.route('/deploy/', methods=['POST']) +def manual_deploy(tag): + """Deploy manual com tag específica""" + try: + logger.info(f"Deploy manual solicitado para tag: {tag}") + + result = run_auto_deploy(tag) + + return jsonify({ + 'status': 'success', + 'message': f'Deploy manual iniciado para tag: {tag}', + 'result': result + }), 200 + + except Exception as e: + logger.error(f"Erro no deploy manual: {e}") + return jsonify({'status': 'error', 'message': str(e)}), 500 + +def run_auto_deploy(tag): + """Executar script de deploy automático""" + try: + logger.info(f"Executando deploy automático para tag: {tag}") + + # Executar script de deploy + result = subprocess.run( + [SCRIPT_PATH, tag], + capture_output=True, + text=True, + timeout=600 # 10 minutos timeout + ) + + if result.returncode == 0: + logger.info("Deploy automático concluído com sucesso") + return { + 'success': True, + 'stdout': result.stdout, + 'stderr': result.stderr + } + else: + logger.error(f"Deploy automático falhou: {result.stderr}") + return { + 'success': False, + 'stdout': result.stdout, + 'stderr': result.stderr + } + + except subprocess.TimeoutExpired: + logger.error("Deploy automático timeout") + return { + 'success': False, + 'error': 'Timeout' + } + except Exception as e: + logger.error(f"Erro ao executar deploy automático: {e}") + return { + 'success': False, + 'error': str(e) + } + +@app.route('/health', methods=['GET']) +def health(): + """Health check""" + return jsonify({ + 'status': 'healthy', + 'timestamp': datetime.now().isoformat(), + 'image': f'{REGISTRY}/{IMAGE_NAME}', + 'namespace': NAMESPACE + }), 200 + +@app.route('/status', methods=['GET']) +def status(): + """Status do serviço""" + try: + # Verificar se está logado no OpenShift + result = subprocess.run(['oc', 'whoami'], capture_output=True, text=True) + + return jsonify({ + 'status': 'running', + 'timestamp': datetime.now().isoformat(), + 'openshift_user': result.stdout.strip() if result.returncode == 0 else 'Not logged in', + 'image': f'{REGISTRY}/{IMAGE_NAME}', + 'namespace': NAMESPACE, + 'script_path': SCRIPT_PATH + }), 200 + + except Exception as e: + return jsonify({ + 'status': 'error', + 'message': str(e) + }), 500 + +if __name__ == '__main__': + port = int(os.getenv('PORT', 8080)) + debug = os.getenv('DEBUG', 'false').lower() == 'true' + + logger.info(f"Iniciando webhook server na porta {port}") + logger.info(f"Configurações: IMAGE_NAME={IMAGE_NAME}, REGISTRY={REGISTRY}, NAMESPACE={NAMESPACE}") + + app.run(host='0.0.0.0', port=port, debug=debug)