228 lines
7.8 KiB
Bash
Executable File
228 lines
7.8 KiB
Bash
Executable File
#!/bin/bash
|
||
set -euo pipefail
|
||
|
||
# ==========================================
|
||
# LOGGER — source the shared library
|
||
# ==========================================
|
||
# shellcheck source=lib/logger.sh
|
||
source "$(dirname "$0")/lib/logger.sh" "deploy"
|
||
trap 'log_error "Failure at line $LINENO. See $LOG_FILE"' ERR
|
||
|
||
# ==========================================
|
||
# VARIABLES
|
||
# ==========================================
|
||
RESOURCE_GROUP="ExamApp-RG"
|
||
LOCATION="polandcentral"
|
||
ACR_NAME="examappregistrycharles"
|
||
AKS_NAME="ExamApp-AKS"
|
||
DB_NAME="postgres"
|
||
NODE_COUNT=2
|
||
NODE_VM_SIZE="Standard_B2als_v2"
|
||
|
||
# Fixed DNS label: avoids recreating a new label on every run
|
||
# (which would break the existing TLS certificate)
|
||
DNS_LABEL="vigimeteo-prod"
|
||
|
||
# DB credentials — required, never stored in Git
|
||
if [ -z "${DB_USER:-}" ] || [ -z "${DB_PASSWORD:-}" ]; then
|
||
log_error "DB_USER and DB_PASSWORD must be exported before running this script."
|
||
log_error " export DB_USER=\"postgres\""
|
||
log_error " export DB_PASSWORD=\"your_password\""
|
||
exit 1
|
||
fi
|
||
|
||
log_info "========================================"
|
||
log_info " Vigimeteo – Deployment started"
|
||
log_info " Full log: $LOG_FILE"
|
||
log_info "========================================"
|
||
|
||
# ==========================================
|
||
log_info "STEP 1 — Cloud infrastructure"
|
||
# ==========================================
|
||
|
||
log_debug "Creating / verifying Resource Group '$RESOURCE_GROUP'..."
|
||
az group create --name "$RESOURCE_GROUP" --location "$LOCATION" --output none
|
||
log_info "Resource Group '$RESOURCE_GROUP' ready."
|
||
|
||
# ACR
|
||
if az acr show --name "$ACR_NAME" --resource-group "$RESOURCE_GROUP" --output none 2>/dev/null; then
|
||
log_info "ACR '$ACR_NAME' already exists."
|
||
else
|
||
log_info "Creating ACR '$ACR_NAME'..."
|
||
if ! az acr create \
|
||
--resource-group "$RESOURCE_GROUP" \
|
||
--name "$ACR_NAME" --sku Basic \
|
||
--location "$LOCATION" --output none; then
|
||
ACR_NAME="examapp$(date +%s | tail -c 8)"
|
||
log_warn "Name unavailable — using fallback name: $ACR_NAME"
|
||
az acr create \
|
||
--resource-group "$RESOURCE_GROUP" \
|
||
--name "$ACR_NAME" --sku Basic \
|
||
--location "$LOCATION" --output none
|
||
fi
|
||
log_info "ACR '$ACR_NAME' created."
|
||
fi
|
||
ACR_LOGIN_SERVER=$(az acr show \
|
||
--name "$ACR_NAME" --resource-group "$RESOURCE_GROUP" \
|
||
--query loginServer --output tsv)
|
||
log_debug "ACR login server: $ACR_LOGIN_SERVER"
|
||
|
||
# AKS
|
||
if az aks show \
|
||
--resource-group "$RESOURCE_GROUP" --name "$AKS_NAME" \
|
||
--output none 2>/dev/null; then
|
||
log_info "Cluster '$AKS_NAME' already exists."
|
||
else
|
||
log_info "Creating AKS cluster '$AKS_NAME' (3–5 min)..."
|
||
az aks create \
|
||
--resource-group "$RESOURCE_GROUP" \
|
||
--name "$AKS_NAME" \
|
||
--node-count "$NODE_COUNT" \
|
||
--node-vm-size "$NODE_VM_SIZE" \
|
||
--location "$LOCATION" \
|
||
--generate-ssh-keys \
|
||
--attach-acr "$ACR_NAME" \
|
||
--output none
|
||
log_info "Cluster '$AKS_NAME' created with $NODE_COUNT nodes ($NODE_VM_SIZE)."
|
||
fi
|
||
|
||
az aks get-credentials \
|
||
--resource-group "$RESOURCE_GROUP" --name "$AKS_NAME" \
|
||
--overwrite-existing
|
||
log_info "kubectl configured."
|
||
|
||
# ==========================================
|
||
log_info "STEP 2 — Build and push Docker images"
|
||
# ==========================================
|
||
|
||
az acr login --name "$ACR_NAME" --resource-group "$RESOURCE_GROUP"
|
||
|
||
for service in backend frontend; do
|
||
dir=""
|
||
[ "$service" = "backend" ] && dir="./Back-end/"
|
||
[ "$service" = "frontend" ] && dir="./Front-end/"
|
||
image="$ACR_LOGIN_SERVER/vigimeteo-$service:latest"
|
||
|
||
log_info "Building $service → $image"
|
||
docker build -t "$image" "$dir"
|
||
log_info "Pushing $service..."
|
||
docker push "$image"
|
||
log_info "$service pushed successfully."
|
||
done
|
||
|
||
# ==========================================
|
||
log_info "STEP 3 — Kubernetes deployment"
|
||
# ==========================================
|
||
|
||
kubectl apply -f namespace.yaml
|
||
log_debug "Namespace applied."
|
||
|
||
kubectl create secret generic db-credentials \
|
||
--namespace vigimeteo \
|
||
--from-literal=host="vigimeteo-db.vigimeteo.svc.cluster.local" \
|
||
--from-literal=port="5432" \
|
||
--from-literal=dbname="$DB_NAME" \
|
||
--from-literal=username="$DB_USER" \
|
||
--from-literal=password="$DB_PASSWORD" \
|
||
--dry-run=client -o yaml | kubectl apply -f -
|
||
log_info "Secret 'db-credentials' applied."
|
||
|
||
kubectl create configmap vigimeteo-db-init \
|
||
--namespace vigimeteo \
|
||
--from-file=init_db.sql=./sql/init_db.sql \
|
||
--dry-run=client -o yaml | kubectl apply -f -
|
||
log_info "ConfigMap 'vigimeteo-db-init' applied."
|
||
|
||
kubectl apply -f statefulset.yaml
|
||
kubectl apply -f service.yaml
|
||
sed "s|MON_REGISTRE|$ACR_LOGIN_SERVER|g" deployment.yaml | kubectl apply -f -
|
||
log_info "StatefulSet, Services and Deployment applied."
|
||
|
||
# ==========================================
|
||
log_info "STEP 4 — HTTPS exposure (ingress + TLS)"
|
||
# ==========================================
|
||
|
||
log_info "Applying ingress-nginx..."
|
||
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.2/deploy/static/provider/cloud/deploy.yaml
|
||
|
||
# Wait for the ingress-nginx controller pod to be Ready before applying ingress.yaml.
|
||
# Without this wait the admission webhook has no endpoints yet and kubectl returns
|
||
# an InternalError when validating the Ingress resource.
|
||
log_info "Waiting for ingress-nginx controller to be Ready (up to 90s)..."
|
||
kubectl wait --namespace ingress-nginx \
|
||
--for=condition=ready pod \
|
||
--selector=app.kubernetes.io/component=controller \
|
||
--timeout=90s
|
||
log_info "ingress-nginx controller is Ready."
|
||
|
||
log_info "Applying cert-manager..."
|
||
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.1/cert-manager.yaml
|
||
|
||
log_info "Waiting for Azure public IP (up to 4 min)..."
|
||
PUBLIC_IP=""
|
||
ATTEMPTS=0
|
||
MAX_ATTEMPTS=24 # 24 × 10s = 4 min
|
||
while [ -z "$PUBLIC_IP" ] && [ "$ATTEMPTS" -lt "$MAX_ATTEMPTS" ]; do
|
||
sleep 10
|
||
ATTEMPTS=$((ATTEMPTS + 1))
|
||
PUBLIC_IP=$(kubectl get svc ingress-nginx-controller \
|
||
-n ingress-nginx \
|
||
-o jsonpath='{.status.loadBalancer.ingress[0].ip}' 2>/dev/null || true)
|
||
log_debug "Attempt $ATTEMPTS/$MAX_ATTEMPTS — IP: ${PUBLIC_IP:-pending}"
|
||
done
|
||
|
||
if [ -z "$PUBLIC_IP" ]; then
|
||
log_error "Public IP not obtained after 4 minutes."
|
||
log_error "Diagnose with: kubectl get svc -n ingress-nginx"
|
||
exit 1
|
||
fi
|
||
log_info "Public IP obtained: $PUBLIC_IP"
|
||
|
||
NODE_RG=$(az aks show \
|
||
--resource-group "$RESOURCE_GROUP" --name "$AKS_NAME" \
|
||
--query nodeResourceGroup -o tsv)
|
||
PUBLIC_IP_NAME=$(az network public-ip list \
|
||
--resource-group "$NODE_RG" \
|
||
--query "[?ipAddress!=null]|[?contains(ipAddress, '$PUBLIC_IP')].[name]" \
|
||
-o tsv)
|
||
|
||
# Fixed DNS label → idempotent, does not break an existing TLS certificate
|
||
az network public-ip update \
|
||
--resource-group "$NODE_RG" \
|
||
--name "$PUBLIC_IP_NAME" \
|
||
--dns-name "$DNS_LABEL" \
|
||
--output none
|
||
FQDN=$(az network public-ip show \
|
||
--resource-group "$NODE_RG" --name "$PUBLIC_IP_NAME" \
|
||
--query dnsSettings.fqdn -o tsv)
|
||
log_info "FQDN: $FQDN"
|
||
|
||
sed "s|MON_DOMAINE|$FQDN|g" ingress.yaml | kubectl apply -f -
|
||
kubectl apply -f cluster-issuer.yaml
|
||
|
||
# ==========================================
|
||
log_info "STEP 5 — Post-deployment health checks"
|
||
# ==========================================
|
||
|
||
log_info "Waiting for all vigimeteo pods to be Ready (up to 3 min)..."
|
||
if kubectl wait pod \
|
||
--all \
|
||
--namespace vigimeteo \
|
||
--for=condition=Ready \
|
||
--timeout=180s; then
|
||
log_info "All pods are Ready ✅"
|
||
else
|
||
log_warn "Some pods are not yet Ready — current state:"
|
||
kubectl get pods -n vigimeteo | tee -a "$LOG_FILE"
|
||
log_warn "Deployment was submitted — verify manually."
|
||
fi
|
||
|
||
log_debug "Detailed pod status:"
|
||
kubectl get pods -n vigimeteo -o wide 2>&1 | tee -a "$LOG_FILE"
|
||
|
||
log_info "========================================"
|
||
log_info "DEPLOYMENT COMPLETE"
|
||
log_info " URL : https://$FQDN"
|
||
log_info " DB : vigimeteo-db.vigimeteo.svc.cluster.local"
|
||
log_info " Log : $LOG_FILE"
|
||
log_info "========================================" |