ci: update deploy script and workflow for K3s deployment
CI/CD Pipeline / deploy (push) Failing after 31s Details
CI/CD Pipeline / smoke-tests (push) Has been skipped Details
CI/CD Pipeline / e2e-tests (push) Has been skipped Details
CI/CD Pipeline / rollback (push) Has been skipped Details

deploy.sh improvements:
- Updated namespace to 'academia'
- Added pull_code, run_tests, smoke_tests functions
- Smoke tests verify: health API, frontend, GraphQL, database
- Added rollback command
- Uses passwordless sudo

Workflow still configured but limited by runner memory.
Use deploy.sh directly on K3s server for reliable deployments.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Andrés Eduardo García Márquez 2026-01-09 08:18:58 -05:00
parent bdb637ec1e
commit 84e228fe3e
2 changed files with 164 additions and 77 deletions

View File

@ -31,9 +31,10 @@ jobs:
git fetch origin main git fetch origin main
git reset --hard origin/main git reset --hard origin/main
echo "=== Running tests ===" echo "=== Running Backend Tests ==="
dotnet test tests/Domain.Tests --verbosity minimal dotnet test tests/Domain.Tests --verbosity minimal
dotnet test tests/Application.Tests --verbosity minimal dotnet test tests/Application.Tests --verbosity minimal
dotnet test tests/Integration.Tests --verbosity minimal
echo "=== Building Docker images ===" echo "=== Building Docker images ==="
sudo docker build -f deploy/docker/Dockerfile.api -t student-api:latest . & sudo docker build -f deploy/docker/Dockerfile.api -t student-api:latest . &
@ -62,9 +63,11 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: deploy needs: deploy
steps: steps:
- name: Wait and Verify - name: Wait for services
run: sleep 15
- name: Basic Health Checks
run: | run: |
sleep 15
echo "Checking health..." echo "Checking health..."
curl -sf https://${{ env.DOMAIN }}/health | grep -q '"status":"Healthy"' curl -sf https://${{ env.DOMAIN }}/health | grep -q '"status":"Healthy"'
echo "Checking frontend..." echo "Checking frontend..."
@ -73,11 +76,48 @@ jobs:
curl -sf -X POST https://${{ env.DOMAIN }}/graphql \ curl -sf -X POST https://${{ env.DOMAIN }}/graphql \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d '{"query":"{ subjects { id } }"}' | grep -q '"subjects"' -d '{"query":"{ subjects { id } }"}' | grep -q '"subjects"'
echo "All checks passed!" echo "Basic checks passed!"
e2e-tests:
runs-on: ubuntu-latest
needs: smoke-tests
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'
cache-dependency-path: src/frontend/package-lock.json
- name: Install dependencies
working-directory: src/frontend
run: npm ci --legacy-peer-deps
- name: Install Playwright browsers
working-directory: src/frontend
run: npx playwright install chromium --with-deps
- name: Run E2E Smoke Tests
working-directory: src/frontend
env:
CI: true
BASE_URL: https://${{ env.DOMAIN }}
run: npx playwright test smoke.spec.ts --reporter=list
- name: Upload test artifacts
if: failure()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: src/frontend/playwright-report/
retention-days: 7
rollback: rollback:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: smoke-tests needs: e2e-tests
if: failure() if: failure()
steps: steps:
- name: Setup SSH and Rollback - name: Setup SSH and Rollback
@ -86,5 +126,7 @@ jobs:
echo "${{ secrets.K3S_SSH_KEY }}" > ~/.ssh/id_rsa echo "${{ secrets.K3S_SSH_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H ${{ env.K3S_HOST }} >> ~/.ssh/known_hosts 2>/dev/null ssh-keyscan -H ${{ env.K3S_HOST }} >> ~/.ssh/known_hosts 2>/dev/null
echo "Rolling back deployments..."
ssh ${{ env.K3S_USER }}@${{ env.K3S_HOST }} \ ssh ${{ env.K3S_USER }}@${{ env.K3S_HOST }} \
'sudo kubectl rollout undo deployment/student-api deployment/student-frontend -n academia' 'sudo kubectl rollout undo deployment/student-api deployment/student-frontend -n academia'
echo "Rollback complete"

View File

@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
# K3s Deployment Script - Optimizado para tiempo mínimo # K3s Deployment Script con Tests y Smoke Tests
# Uso: ./deploy.sh [build|deploy|all|status|clean] # Uso: ./deploy.sh [all|test|build|deploy|smoke|status|clean|rollback]
set -euo pipefail set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
@ -12,120 +12,165 @@ GREEN='\033[0;32m'
YELLOW='\033[1;33m' YELLOW='\033[1;33m'
NC='\033[0m' NC='\033[0m'
# Cargar credenciales si existen
CREDS_FILE="$HOME/.secrets/credentials.env"
[[ -f "$CREDS_FILE" ]] && source "$CREDS_FILE"
# Configuración # Configuración
K3S_HOST="${K8S_MASTER_HOST:-hp62a}" NAMESPACE="academia"
NAMESPACE="student-enrollment" DOMAIN="academia.ingeniumcodex.com"
API_IMAGE="academia-api:latest" API_IMAGE="student-api:latest"
FRONTEND_IMAGE="academia-frontend:latest" FRONTEND_IMAGE="student-frontend:latest"
log() { echo -e "${GREEN}[$(date +%T)]${NC} $1"; } log() { echo -e "${GREEN}[$(date +%T)]${NC} $1"; }
warn() { echo -e "${YELLOW}[$(date +%T)]${NC} $1"; } warn() { echo -e "${YELLOW}[$(date +%T)]${NC} $1"; }
err() { echo -e "${RED}[$(date +%T)]${NC} $1" >&2; } err() { echo -e "${RED}[$(date +%T)]${NC} $1" >&2; exit 1; }
pull_code() {
log "=== Actualizando código ==="
cd "$PROJECT_ROOT"
git fetch origin main
git reset --hard origin/main
log "✓ Código actualizado"
}
run_tests() {
log "=== Ejecutando tests ==="
cd "$PROJECT_ROOT"
dotnet test tests/Domain.Tests --verbosity minimal || err "Domain tests fallaron"
dotnet test tests/Application.Tests --verbosity minimal || err "Application tests fallaron"
log "✓ Todos los tests pasaron"
}
build_images() { build_images() {
log "=== Construyendo imágenes Docker (paralelo) ===" log "=== Construyendo imágenes Docker (paralelo) ==="
cd "$PROJECT_ROOT" cd "$PROJECT_ROOT"
# Build en paralelo sudo docker build -t "$API_IMAGE" -f deploy/docker/Dockerfile.api . &
docker build -t "$API_IMAGE" -f deploy/docker/Dockerfile.api . &
PID_API=$! PID_API=$!
docker build -t "$FRONTEND_IMAGE" -f deploy/docker/Dockerfile.frontend . & sudo docker build -t "$FRONTEND_IMAGE" -f deploy/docker/Dockerfile.frontend . &
PID_FRONTEND=$! PID_FE=$!
# Esperar ambos wait $PID_API && log "✓ API imagen construida" || err "Error construyendo API"
wait $PID_API && log "✓ API imagen construida" || { err "✗ Error construyendo API"; exit 1; } wait $PID_FE && log "✓ Frontend imagen construida" || err "Error construyendo Frontend"
wait $PID_FRONTEND && log "✓ Frontend imagen construida" || { err "✗ Error construyendo Frontend"; exit 1; }
} }
transfer_images() { import_images() {
log "=== Transfiriendo imágenes a k3s ===" log "=== Importando imágenes a K3s ==="
sudo sh -c "docker save $API_IMAGE | k3s ctr images import -"
# Exportar y transferir en paralelo sudo sh -c "docker save $FRONTEND_IMAGE | k3s ctr images import -"
docker save "$API_IMAGE" | ssh "$K3S_HOST" "sudo k3s ctr images import -" & log "✓ Imágenes importadas"
PID_API=$!
docker save "$FRONTEND_IMAGE" | ssh "$K3S_HOST" "sudo k3s ctr images import -" &
PID_FRONTEND=$!
wait $PID_API && log "✓ API transferida" || { err "✗ Error transfiriendo API"; exit 1; }
wait $PID_FRONTEND && log "✓ Frontend transferida" || { err "✗ Error transfiriendo Frontend"; exit 1; }
} }
deploy_k3s() { deploy_k8s() {
log "=== Desplegando en k3s con Kustomize ===" log "=== Desplegando en K3s ==="
cd "$SCRIPT_DIR"
sudo kubectl apply -k .
sudo kubectl rollout restart deployment/student-api deployment/student-frontend -n $NAMESPACE
# Aplicar con kustomize log "=== Esperando rollout ==="
ssh "$K3S_HOST" "cd /tmp && sudo kubectl apply -k -" < <(kubectl kustomize "$SCRIPT_DIR") sudo kubectl rollout status deployment/student-api -n $NAMESPACE --timeout=180s
sudo kubectl rollout status deployment/student-frontend -n $NAMESPACE --timeout=60s
log "✓ Deploy completado"
}
log "=== Esperando despliegues ===" smoke_tests() {
# Esperar SQL Server primero (es dependencia) log "=== Ejecutando smoke tests ==="
ssh "$K3S_HOST" "sudo kubectl -n $NAMESPACE rollout status deployment/sqlserver --timeout=180s" || warn "SQL Server timeout" sleep 10
# Esperar API y Frontend en paralelo # Health check
ssh "$K3S_HOST" "sudo kubectl -n $NAMESPACE rollout status deployment/student-api --timeout=120s" & if curl -sf https://$DOMAIN/health | grep -q '"status":"Healthy"'; then
PID_API=$! log "✓ Health check API"
ssh "$K3S_HOST" "sudo kubectl -n $NAMESPACE rollout status deployment/student-frontend --timeout=60s" & else
PID_FRONTEND=$! err "Health check falló"
fi
wait $PID_API && log "✓ API desplegada" || warn "API timeout" # Frontend
wait $PID_FRONTEND && log "✓ Frontend desplegado" || warn "Frontend timeout" if curl -sf https://$DOMAIN/ | grep -q 'Sistema de Estudiantes'; then
log "✓ Frontend check"
else
err "Frontend check falló"
fi
# GraphQL
if curl -sf -X POST https://$DOMAIN/graphql \
-H "Content-Type: application/json" \
-d '{"query":"{ subjects { id name } }"}' | grep -q '"subjects"'; then
log "✓ GraphQL check"
else
err "GraphQL check falló"
fi
# Database
if curl -sf https://$DOMAIN/health | grep -q '"name":"database","status":"Healthy"'; then
log "✓ Database check"
else
err "Database check falló"
fi
log "✓ Todos los smoke tests pasaron"
} }
show_status() { show_status() {
log "=== Estado del despliegue ===" log "=== Estado del cluster ==="
ssh "$K3S_HOST" "sudo kubectl -n $NAMESPACE get pods -o wide" sudo kubectl get pods -n $NAMESPACE -o wide
echo "" echo ""
ssh "$K3S_HOST" "sudo kubectl -n $NAMESPACE get svc" sudo kubectl get svc -n $NAMESPACE
echo "" }
ssh "$K3S_HOST" "sudo kubectl -n $NAMESPACE get ingress 2>/dev/null || true"
rollback() {
warn "=== Rollback ==="
sudo kubectl rollout undo deployment/student-api deployment/student-frontend -n $NAMESPACE
log "✓ Rollback completado"
} }
clean() { clean() {
warn "=== Limpiando namespace $NAMESPACE ===" warn "=== Limpiando namespace $NAMESPACE ==="
ssh "$K3S_HOST" "sudo kubectl delete namespace $NAMESPACE --ignore-not-found" sudo kubectl delete namespace $NAMESPACE --ignore-not-found
log "✓ Namespace eliminado" log "✓ Namespace eliminado"
} }
restart_pods() {
log "=== Reiniciando pods (rolling restart) ==="
ssh "$K3S_HOST" "sudo kubectl -n $NAMESPACE rollout restart deployment/student-api"
ssh "$K3S_HOST" "sudo kubectl -n $NAMESPACE rollout restart deployment/student-frontend"
log "✓ Pods reiniciados"
}
# Main # Main
case "${1:-all}" in case "${1:-help}" in
all)
pull_code
run_tests
build_images
import_images
deploy_k8s
smoke_tests
show_status
;;
test)
run_tests
;;
build) build)
build_images build_images
;; import_images
transfer)
transfer_images
;; ;;
deploy) deploy)
deploy_k3s deploy_k8s
show_status
;; ;;
all) smoke)
build_images smoke_tests
transfer_images
deploy_k3s
show_status
;; ;;
status) status)
show_status show_status
;; ;;
rollback)
rollback
;;
clean) clean)
clean clean
;; ;;
restart)
restart_pods
;;
*) *)
echo "Uso: $0 [build|transfer|deploy|all|status|clean|restart]" echo "Uso: $0 [all|test|build|deploy|smoke|status|rollback|clean]"
exit 1 echo ""
echo " all - Pull, test, build, deploy y smoke tests"
echo " test - Solo ejecutar tests"
echo " build - Construir e importar imágenes"
echo " deploy - Solo desplegar a K8s"
echo " smoke - Solo ejecutar smoke tests"
echo " status - Ver estado del cluster"
echo " rollback - Revertir último deploy"
echo " clean - Eliminar namespace"
exit 0
;; ;;
esac esac