ci: optimize deployment workflow for k3s
Deploy to k3s / deploy (push) Failing after 27s
Details
Deploy to k3s / deploy (push) Failing after 27s
Details
- Single job instead of 3 (no artifact overhead) - Build directly on k3s node (avoids image transfer) - Parallel Docker builds with BuildKit - Auto-create namespace if missing - Automatic rollback on failure - Health check via domain Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
17706676d5
commit
a9e4de63c9
|
|
@ -1,4 +1,4 @@
|
|||
name: Build and Deploy to k3s
|
||||
name: Deploy to k3s
|
||||
|
||||
on:
|
||||
push:
|
||||
|
|
@ -6,111 +6,101 @@ on:
|
|||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
NAMESPACE: student-enrollment
|
||||
API_IMAGE: student-api
|
||||
FRONTEND_IMAGE: student-frontend
|
||||
K3S_HOST: "100.67.198.92"
|
||||
K3S_USER: "andres"
|
||||
NAMESPACE: "student-enrollment"
|
||||
DOMAIN: "academia.ingeniumcodex.com"
|
||||
|
||||
jobs:
|
||||
# Build jobs run in parallel for speed
|
||||
build-api:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build API with cache
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: deploy/docker/Dockerfile.api
|
||||
push: false
|
||||
load: true
|
||||
tags: ${{ env.API_IMAGE }}:${{ github.sha }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Save API image
|
||||
run: docker save ${{ env.API_IMAGE }}:${{ github.sha }} | gzip > /tmp/api-image.tar.gz
|
||||
|
||||
- name: Upload API artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: api-image
|
||||
path: /tmp/api-image.tar.gz
|
||||
retention-days: 1
|
||||
|
||||
build-frontend:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build Frontend with cache
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: deploy/docker/Dockerfile.frontend
|
||||
push: false
|
||||
load: true
|
||||
tags: ${{ env.FRONTEND_IMAGE }}:${{ github.sha }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Save Frontend image
|
||||
run: docker save ${{ env.FRONTEND_IMAGE }}:${{ github.sha }} | gzip > /tmp/frontend-image.tar.gz
|
||||
|
||||
- name: Upload Frontend artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: frontend-image
|
||||
path: /tmp/frontend-image.tar.gz
|
||||
retention-days: 1
|
||||
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build-api, build-frontend]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download API image
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: api-image
|
||||
path: /tmp
|
||||
|
||||
- name: Download Frontend image
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: frontend-image
|
||||
path: /tmp
|
||||
|
||||
- name: Import images to k3s
|
||||
- name: Setup SSH
|
||||
run: |
|
||||
gunzip -c /tmp/api-image.tar.gz | sudo k3s ctr images import -
|
||||
gunzip -c /tmp/frontend-image.tar.gz | sudo k3s ctr images import -
|
||||
mkdir -p ~/.ssh
|
||||
echo "${{ secrets.K3S_SSH_KEY }}" > ~/.ssh/id_rsa
|
||||
chmod 600 ~/.ssh/id_rsa
|
||||
ssh-keyscan -H ${{ env.K3S_HOST }} >> ~/.ssh/known_hosts 2>/dev/null
|
||||
|
||||
- name: Update deployments with new tag
|
||||
- name: Sync code to k3s
|
||||
run: |
|
||||
kubectl -n ${{ env.NAMESPACE }} set image deployment/student-api \
|
||||
api=${{ env.API_IMAGE }}:${{ github.sha }}
|
||||
kubectl -n ${{ env.NAMESPACE }} set image deployment/student-frontend \
|
||||
frontend=${{ env.FRONTEND_IMAGE }}:${{ github.sha }}
|
||||
rsync -az --delete \
|
||||
--exclude '.git' \
|
||||
--exclude 'node_modules' \
|
||||
--exclude 'dist' \
|
||||
--exclude 'bin' \
|
||||
--exclude 'obj' \
|
||||
--exclude '.angular' \
|
||||
--exclude 'tests' \
|
||||
--exclude 'docs' \
|
||||
./ ${{ env.K3S_USER }}@${{ env.K3S_HOST }}:~/student-enrollment/
|
||||
|
||||
- name: Wait for rollout
|
||||
- name: Build images (parallel)
|
||||
run: |
|
||||
kubectl -n ${{ env.NAMESPACE }} rollout status deployment/student-api --timeout=120s
|
||||
kubectl -n ${{ env.NAMESPACE }} rollout status deployment/student-frontend --timeout=60s
|
||||
ssh ${{ env.K3S_USER }}@${{ env.K3S_HOST }} << 'ENDSSH'
|
||||
cd ~/student-enrollment
|
||||
export DOCKER_BUILDKIT=1
|
||||
|
||||
echo "${{ secrets.K3S_SUDO_PASS }}" | sudo -S docker build \
|
||||
-f deploy/docker/Dockerfile.api -t student-api:latest . &
|
||||
PID_API=$!
|
||||
|
||||
echo "${{ secrets.K3S_SUDO_PASS }}" | sudo -S docker build \
|
||||
-f deploy/docker/Dockerfile.frontend -t student-frontend:latest . &
|
||||
PID_FE=$!
|
||||
|
||||
wait $PID_API || exit 1
|
||||
wait $PID_FE || exit 1
|
||||
ENDSSH
|
||||
|
||||
- name: Import to k3s
|
||||
run: |
|
||||
ssh ${{ env.K3S_USER }}@${{ env.K3S_HOST }} << 'ENDSSH'
|
||||
echo "${{ secrets.K3S_SUDO_PASS }}" | sudo -S sh -c \
|
||||
'docker save student-api:latest | k3s ctr images import -'
|
||||
echo "${{ secrets.K3S_SUDO_PASS }}" | sudo -S sh -c \
|
||||
'docker save student-frontend:latest | k3s ctr images import -'
|
||||
ENDSSH
|
||||
|
||||
- name: Setup namespace if needed
|
||||
run: |
|
||||
ssh ${{ env.K3S_USER }}@${{ env.K3S_HOST }} << 'ENDSSH'
|
||||
cd ~/student-enrollment/deploy/k3s
|
||||
if ! echo "${{ secrets.K3S_SUDO_PASS }}" | sudo -S kubectl get ns student-enrollment &>/dev/null; then
|
||||
echo "Creating namespace and resources..."
|
||||
echo "${{ secrets.K3S_SUDO_PASS }}" | sudo -S kubectl apply -k .
|
||||
fi
|
||||
ENDSSH
|
||||
|
||||
- name: Deploy
|
||||
run: |
|
||||
ssh ${{ env.K3S_USER }}@${{ env.K3S_HOST }} << 'ENDSSH'
|
||||
echo "${{ secrets.K3S_SUDO_PASS }}" | sudo -S kubectl rollout restart \
|
||||
deployment/student-api deployment/student-frontend -n student-enrollment
|
||||
ENDSSH
|
||||
|
||||
- name: Wait rollout
|
||||
run: |
|
||||
ssh ${{ env.K3S_USER }}@${{ env.K3S_HOST }} << 'ENDSSH'
|
||||
echo "${{ secrets.K3S_SUDO_PASS }}" | sudo -S kubectl rollout status \
|
||||
deployment/student-api -n student-enrollment --timeout=120s
|
||||
echo "${{ secrets.K3S_SUDO_PASS }}" | sudo -S kubectl rollout status \
|
||||
deployment/student-frontend -n student-enrollment --timeout=60s
|
||||
ENDSSH
|
||||
|
||||
- name: Health check
|
||||
run: |
|
||||
sleep 10
|
||||
API_POD=$(kubectl -n ${{ env.NAMESPACE }} get pods -l app=student-api -o jsonpath='{.items[0].metadata.name}')
|
||||
kubectl -n ${{ env.NAMESPACE }} exec $API_POD -- wget -q --spider http://localhost:5000/health || exit 1
|
||||
echo "Deployment healthy!"
|
||||
sleep 5
|
||||
curl -sf https://${{ env.DOMAIN }}/health || exit 1
|
||||
curl -sf https://${{ env.DOMAIN }}/ || exit 1
|
||||
|
||||
- name: Rollback on failure
|
||||
if: failure()
|
||||
run: |
|
||||
ssh ${{ env.K3S_USER }}@${{ env.K3S_HOST }} << 'ENDSSH'
|
||||
echo "${{ secrets.K3S_SUDO_PASS }}" | sudo -S kubectl rollout undo \
|
||||
deployment/student-api deployment/student-frontend -n student-enrollment 2>/dev/null || true
|
||||
ENDSSH
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ server {
|
|||
|
||||
# API proxy
|
||||
location /graphql {
|
||||
proxy_pass http://student-api:5000;
|
||||
proxy_pass http://student-api:8080;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
|
|
@ -24,7 +24,7 @@ server {
|
|||
}
|
||||
|
||||
location /health {
|
||||
proxy_pass http://student-api:5000;
|
||||
proxy_pass http://student-api:8080;
|
||||
}
|
||||
|
||||
# Cache static assets
|
||||
|
|
|
|||
|
|
@ -1,264 +0,0 @@
|
|||
# Student Enrollment System - K3s All-in-One Deployment
|
||||
# Deploy: kubectl apply -f all-in-one.yaml
|
||||
# Delete: kubectl delete -f all-in-one.yaml
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: student-enrollment
|
||||
---
|
||||
# ===== SECRETS =====
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: app-secrets
|
||||
namespace: student-enrollment
|
||||
type: Opaque
|
||||
stringData:
|
||||
SA_PASSWORD: "Admin123!"
|
||||
JWT_SECRET: "super-secret-key-for-jwt-tokens-minimum-32-chars-long!"
|
||||
ADMIN_PASSWORD: "admin123"
|
||||
---
|
||||
# ===== SQL SERVER =====
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: mssql-data
|
||||
namespace: student-enrollment
|
||||
spec:
|
||||
accessModes: [ReadWriteOnce]
|
||||
resources:
|
||||
requests:
|
||||
storage: 2Gi
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: mssql
|
||||
namespace: student-enrollment
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: mssql
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: mssql
|
||||
spec:
|
||||
containers:
|
||||
- name: mssql
|
||||
image: mcr.microsoft.com/mssql/server:2022-latest
|
||||
ports:
|
||||
- containerPort: 1433
|
||||
env:
|
||||
- name: ACCEPT_EULA
|
||||
value: "Y"
|
||||
- name: MSSQL_SA_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: app-secrets
|
||||
key: SA_PASSWORD
|
||||
resources:
|
||||
limits:
|
||||
memory: "2Gi"
|
||||
cpu: "1000m"
|
||||
requests:
|
||||
memory: "1Gi"
|
||||
cpu: "500m"
|
||||
volumeMounts:
|
||||
- name: mssql-data
|
||||
mountPath: /var/opt/mssql
|
||||
readinessProbe:
|
||||
exec:
|
||||
command: ["/opt/mssql-tools18/bin/sqlcmd", "-S", "localhost", "-U", "sa", "-P", "$(MSSQL_SA_PASSWORD)", "-C", "-Q", "SELECT 1"]
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
volumes:
|
||||
- name: mssql-data
|
||||
persistentVolumeClaim:
|
||||
claimName: mssql-data
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: mssql
|
||||
namespace: student-enrollment
|
||||
spec:
|
||||
selector:
|
||||
app: mssql
|
||||
ports:
|
||||
- port: 1433
|
||||
targetPort: 1433
|
||||
---
|
||||
# ===== BACKEND API =====
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: api
|
||||
namespace: student-enrollment
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: api
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: api
|
||||
spec:
|
||||
initContainers:
|
||||
- name: wait-for-db
|
||||
image: busybox:1.36
|
||||
command: ['sh', '-c', 'until nc -z mssql 1433; do echo waiting for mssql; sleep 5; done']
|
||||
containers:
|
||||
- name: api
|
||||
image: student-enrollment-api:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
env:
|
||||
- name: ASPNETCORE_ENVIRONMENT
|
||||
value: "Production"
|
||||
- name: ASPNETCORE_URLS
|
||||
value: "http://+:8080"
|
||||
- name: ConnectionStrings__DefaultConnection
|
||||
value: "Server=mssql;Database=StudentEnrollment;User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=True"
|
||||
- name: SA_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: app-secrets
|
||||
key: SA_PASSWORD
|
||||
- name: JwtSettings__Secret
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: app-secrets
|
||||
key: JWT_SECRET
|
||||
- name: JwtSettings__Issuer
|
||||
value: "student-enrollment-api"
|
||||
- name: JwtSettings__Audience
|
||||
value: "student-enrollment-app"
|
||||
- name: JwtSettings__ExpirationMinutes
|
||||
value: "60"
|
||||
- name: ADMIN_USERNAME
|
||||
value: "admin"
|
||||
- name: ADMIN_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: app-secrets
|
||||
key: ADMIN_PASSWORD
|
||||
resources:
|
||||
limits:
|
||||
memory: "512Mi"
|
||||
cpu: "500m"
|
||||
requests:
|
||||
memory: "256Mi"
|
||||
cpu: "250m"
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 8080
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 8080
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: api
|
||||
namespace: student-enrollment
|
||||
spec:
|
||||
selector:
|
||||
app: api
|
||||
ports:
|
||||
- port: 5000
|
||||
targetPort: 8080
|
||||
---
|
||||
# ===== FRONTEND =====
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: frontend
|
||||
namespace: student-enrollment
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: frontend
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: frontend
|
||||
spec:
|
||||
containers:
|
||||
- name: frontend
|
||||
image: student-enrollment-frontend:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 80
|
||||
resources:
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "100m"
|
||||
requests:
|
||||
memory: "64Mi"
|
||||
cpu: "50m"
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 80
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: frontend
|
||||
namespace: student-enrollment
|
||||
spec:
|
||||
selector:
|
||||
app: frontend
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
---
|
||||
# ===== INGRESS =====
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: student-enrollment-ingress
|
||||
namespace: student-enrollment
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/rewrite-target: /
|
||||
spec:
|
||||
ingressClassName: traefik
|
||||
rules:
|
||||
- host: students.local
|
||||
http:
|
||||
paths:
|
||||
- path: /graphql
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: api
|
||||
port:
|
||||
number: 5000
|
||||
- path: /health
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: api
|
||||
port:
|
||||
number: 5000
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: frontend
|
||||
port:
|
||||
number: 80
|
||||
|
|
@ -28,13 +28,13 @@ spec:
|
|||
image: academia-api:latest
|
||||
imagePullPolicy: Never
|
||||
ports:
|
||||
- containerPort: 5000
|
||||
- containerPort: 8080
|
||||
name: http
|
||||
env:
|
||||
- name: ASPNETCORE_ENVIRONMENT
|
||||
value: "Production"
|
||||
- name: ASPNETCORE_URLS
|
||||
value: "http://+:5000"
|
||||
value: "http://+:8080"
|
||||
- name: ConnectionStrings__DefaultConnection
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
|
|
@ -61,7 +61,7 @@ spec:
|
|||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 5000
|
||||
port: 8080
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
timeoutSeconds: 10
|
||||
|
|
@ -69,7 +69,7 @@ spec:
|
|||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 5000
|
||||
port: 8080
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
|
|
@ -87,7 +87,7 @@ spec:
|
|||
selector:
|
||||
app: student-api
|
||||
ports:
|
||||
- port: 5000
|
||||
targetPort: 5000
|
||||
- port: 8080
|
||||
targetPort: 8080
|
||||
name: http
|
||||
type: ClusterIP
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ metadata:
|
|||
namespace: student-enrollment
|
||||
data:
|
||||
ASPNETCORE_ENVIRONMENT: "Production"
|
||||
ASPNETCORE_URLS: "http://+:5000"
|
||||
ASPNETCORE_URLS: "http://+:8080"
|
||||
# Frontend config
|
||||
API_URL: "https://academia.ingeniumcodex.com/graphql"
|
||||
HEALTH_CHECK_URL: "https://academia.ingeniumcodex.com/health"
|
||||
|
|
|
|||
|
|
@ -1,27 +1,132 @@
|
|||
#!/bin/bash
|
||||
# Quick deploy to k3s
|
||||
set -e
|
||||
# K3s Deployment Script - Optimizado para tiempo mínimo
|
||||
# Uso: ./deploy.sh [build|deploy|all|status|clean]
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
echo "=== Building Docker images ==="
|
||||
# Colores
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Cargar credenciales si existen
|
||||
CREDS_FILE="$HOME/.secrets/credentials.env"
|
||||
[[ -f "$CREDS_FILE" ]] && source "$CREDS_FILE"
|
||||
|
||||
# Configuración
|
||||
K3S_HOST="${K8S_MASTER_HOST:-hp62a}"
|
||||
NAMESPACE="student-enrollment"
|
||||
API_IMAGE="academia-api:latest"
|
||||
FRONTEND_IMAGE="academia-frontend:latest"
|
||||
|
||||
log() { echo -e "${GREEN}[$(date +%T)]${NC} $1"; }
|
||||
warn() { echo -e "${YELLOW}[$(date +%T)]${NC} $1"; }
|
||||
err() { echo -e "${RED}[$(date +%T)]${NC} $1" >&2; }
|
||||
|
||||
build_images() {
|
||||
log "=== Construyendo imágenes Docker (paralelo) ==="
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
# Build API
|
||||
docker build -t student-enrollment-api:latest -f deploy/docker/Dockerfile.api .
|
||||
# Build en paralelo
|
||||
docker build -t "$API_IMAGE" -f deploy/docker/Dockerfile.api . &
|
||||
PID_API=$!
|
||||
docker build -t "$FRONTEND_IMAGE" -f deploy/docker/Dockerfile.frontend . &
|
||||
PID_FRONTEND=$!
|
||||
|
||||
# Build Frontend
|
||||
docker build -t student-enrollment-frontend:latest -f deploy/docker/Dockerfile.frontend .
|
||||
# Esperar ambos
|
||||
wait $PID_API && log "✓ API imagen construida" || { err "✗ Error construyendo API"; exit 1; }
|
||||
wait $PID_FRONTEND && log "✓ Frontend imagen construida" || { err "✗ Error construyendo Frontend"; exit 1; }
|
||||
}
|
||||
|
||||
echo "=== Deploying to k3s ==="
|
||||
kubectl apply -f "$SCRIPT_DIR/all-in-one.yaml"
|
||||
transfer_images() {
|
||||
log "=== Transfiriendo imágenes a k3s ==="
|
||||
|
||||
echo "=== Waiting for deployments ==="
|
||||
kubectl -n student-enrollment rollout status deployment/mssql --timeout=120s
|
||||
kubectl -n student-enrollment rollout status deployment/api --timeout=120s
|
||||
kubectl -n student-enrollment rollout status deployment/frontend --timeout=60s
|
||||
# Exportar y transferir en paralelo
|
||||
docker save "$API_IMAGE" | ssh "$K3S_HOST" "sudo k3s ctr images import -" &
|
||||
PID_API=$!
|
||||
docker save "$FRONTEND_IMAGE" | ssh "$K3S_HOST" "sudo k3s ctr images import -" &
|
||||
PID_FRONTEND=$!
|
||||
|
||||
echo "=== Deployment complete ==="
|
||||
echo "Add to /etc/hosts: <k3s-ip> students.local"
|
||||
kubectl -n student-enrollment get pods
|
||||
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() {
|
||||
log "=== Desplegando en k3s con Kustomize ==="
|
||||
|
||||
# Aplicar con kustomize
|
||||
ssh "$K3S_HOST" "cd /tmp && sudo kubectl apply -k -" < <(kubectl kustomize "$SCRIPT_DIR")
|
||||
|
||||
log "=== Esperando despliegues ==="
|
||||
# Esperar SQL Server primero (es dependencia)
|
||||
ssh "$K3S_HOST" "sudo kubectl -n $NAMESPACE rollout status deployment/sqlserver --timeout=180s" || warn "SQL Server timeout"
|
||||
|
||||
# Esperar API y Frontend en paralelo
|
||||
ssh "$K3S_HOST" "sudo kubectl -n $NAMESPACE rollout status deployment/student-api --timeout=120s" &
|
||||
PID_API=$!
|
||||
ssh "$K3S_HOST" "sudo kubectl -n $NAMESPACE rollout status deployment/student-frontend --timeout=60s" &
|
||||
PID_FRONTEND=$!
|
||||
|
||||
wait $PID_API && log "✓ API desplegada" || warn "API timeout"
|
||||
wait $PID_FRONTEND && log "✓ Frontend desplegado" || warn "Frontend timeout"
|
||||
}
|
||||
|
||||
show_status() {
|
||||
log "=== Estado del despliegue ==="
|
||||
ssh "$K3S_HOST" "sudo kubectl -n $NAMESPACE get pods -o wide"
|
||||
echo ""
|
||||
ssh "$K3S_HOST" "sudo kubectl -n $NAMESPACE get svc"
|
||||
echo ""
|
||||
ssh "$K3S_HOST" "sudo kubectl -n $NAMESPACE get ingress 2>/dev/null || true"
|
||||
}
|
||||
|
||||
clean() {
|
||||
warn "=== Limpiando namespace $NAMESPACE ==="
|
||||
ssh "$K3S_HOST" "sudo kubectl delete namespace $NAMESPACE --ignore-not-found"
|
||||
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
|
||||
case "${1:-all}" in
|
||||
build)
|
||||
build_images
|
||||
;;
|
||||
transfer)
|
||||
transfer_images
|
||||
;;
|
||||
deploy)
|
||||
deploy_k3s
|
||||
show_status
|
||||
;;
|
||||
all)
|
||||
build_images
|
||||
transfer_images
|
||||
deploy_k3s
|
||||
show_status
|
||||
;;
|
||||
status)
|
||||
show_status
|
||||
;;
|
||||
clean)
|
||||
clean
|
||||
;;
|
||||
restart)
|
||||
restart_pods
|
||||
;;
|
||||
*)
|
||||
echo "Uso: $0 [build|transfer|deploy|all|status|clean|restart]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
log "=== Completado ==="
|
||||
|
|
|
|||
|
|
@ -62,13 +62,13 @@ spec:
|
|||
namespace: student-enrollment
|
||||
services:
|
||||
- name: student-api
|
||||
port: 5000
|
||||
port: 8080
|
||||
# Health check
|
||||
- kind: Rule
|
||||
match: Host(`academia.ingeniumcodex.com`) && PathPrefix(`/health`)
|
||||
services:
|
||||
- name: student-api
|
||||
port: 5000
|
||||
port: 8080
|
||||
# Frontend (catch-all)
|
||||
- kind: Rule
|
||||
match: Host(`academia.ingeniumcodex.com`)
|
||||
|
|
|
|||
|
|
@ -12,10 +12,12 @@ resources:
|
|||
- frontend.yaml
|
||||
- ingress.yaml
|
||||
|
||||
commonLabels:
|
||||
labels:
|
||||
- pairs:
|
||||
app.kubernetes.io/name: student-enrollment
|
||||
app.kubernetes.io/part-of: interrapidisimo
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
includeSelectors: false
|
||||
|
||||
# Dominio: https://academia.ingeniumcodex.com
|
||||
# TLS configurado con Traefik + Let's Encrypt
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ spec:
|
|||
kubernetes.io/metadata.name: kube-system
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 5000
|
||||
port: 8080
|
||||
# Desde frontend (para nginx proxy)
|
||||
- from:
|
||||
- podSelector:
|
||||
|
|
@ -59,7 +59,7 @@ spec:
|
|||
app: student-frontend
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 5000
|
||||
port: 8080
|
||||
---
|
||||
# Permitir tráfico a SQL Server solo desde API
|
||||
apiVersion: networking.k8s.io/v1
|
||||
|
|
|
|||
|
|
@ -7,5 +7,5 @@ type: Opaque
|
|||
stringData:
|
||||
# IMPORTANTE: Cambiar en producción
|
||||
# Generar con: openssl rand -base64 32
|
||||
db-password: "Your_Str0ng_P@ssword!"
|
||||
db-connection-string: "Server=sqlserver;Database=StudentEnrollment;User Id=sa;Password=Your_Str0ng_P@ssword!;TrustServerCertificate=True"
|
||||
db-password: "YourStr0ngP4ssword2026"
|
||||
db-connection-string: "Server=sqlserver;Database=StudentEnrollment;User Id=sa;Password=YourStr0ngP4ssword2026;TrustServerCertificate=True"
|
||||
|
|
|
|||
|
|
@ -59,10 +59,10 @@ spec:
|
|||
mountPath: /var/opt/mssql
|
||||
resources:
|
||||
requests:
|
||||
memory: "1.2Gi"
|
||||
memory: "1200Mi"
|
||||
cpu: "250m"
|
||||
limits:
|
||||
memory: "1.5Gi"
|
||||
memory: "1536Mi"
|
||||
cpu: "1000m"
|
||||
livenessProbe:
|
||||
exec:
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ export const authGuard: CanActivateFn = () => {
|
|||
const authService = inject(AuthService);
|
||||
const router = inject(Router);
|
||||
|
||||
// isAuthenticated is a Signal, need to call it to get the value
|
||||
if (authService.isAuthenticated()) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue