# Vigimétéo – Azure Kubernetes Deployment ## 1. Application Description **Vigimétéo** is a weather monitoring web app. Users can log in, browse meteorological stations and sensors, consult measurements (temperature, humidity, pressure, wind) and manage objects depending on their role (regular user, complex user, admin). **Stack:** React + Vite (frontend) · Java 17 / Vert.x (backend REST API) · PostgreSQL 17 (database) --- ## 2. Cloud & Kubernetes Infrastructure **Public cloud:** Microsoft Azure (region: Poland Central) | Azure Service | Purpose | |---|---| | Azure Kubernetes Service (AKS) – `Standard_B2als_v2` × 2 nodes | Runs all application workloads | | Azure Container Registry (ACR) – Basic SKU | Stores Docker images for backend and frontend | | Azure Disk (managed-csi, 1 Gi PVC) | Persistent storage for the PostgreSQL database | | Azure Load Balancer + Public IP | Exposes the NGINX ingress with a public FQDN | **Kubernetes objects:** | Kind | Name | Description | |---|---|---| | Namespace | `vigimeteo` | Isolates all app resources | | PersistentVolumeClaim | `vigimeteo-db-pvc` | 1 Gi Azure Disk for PostgreSQL data | | ConfigMap | `vigimeteo-db-init` | Holds `init_db.sql`, auto-run on first DB start | | Secret | `db-credentials` | DB credentials injected at deploy time, never in Git | | StatefulSet | `vigimeteo-db` | Single PostgreSQL replica with stable storage | | Deployment (×2) | `vigimeteo-backend` / `vigimeteo-frontend` | 2-replica backend and frontend pods | | Services | NodePort 30888 / 30500 + Headless | Internal and external service exposure | | Ingress | `vigimeteo-ingress` | Routes HTTPS traffic to the frontend via ingress-nginx | | ClusterIssuer | `letsencrypt-prod` | cert-manager ACME issuer for automatic TLS certificates | **Database schema:** 6 tables – `weather_objects`, `weather_data`, `range_data`, `users`, `categories`, `deletion_requests`. --- ## 3. Annual Cost Analysis (1 000 users/day – 50 GB) | Resource | SKU | Monthly (USD) | |---|---|---| | AKS – 2 × `Standard_B2als_v2` VMs | 2 vCPU, 4 GB RAM each | ~$60 | | Azure Container Registry | Basic SKU | ~$5 | | Azure Disk (Premium SSD 64 Gi) | managed-csi | ~$10 | | Azure Load Balancer | Standard | ~$18 | | Public IP (Static) | Standard SKU | ~$4 | | Outbound bandwidth (~1 000 users × 5 MB/day) | Per GB after free tier | ~$10 | | Let's Encrypt TLS | Free | $0 | | **Total** | | **~$107/month → ~$1 284/year** | *Assumptions: 50 GB stored on 64 Gi disk, moderate REST API traffic, no high-frequency streaming, first 100 GB/month egress free.* > 💡 Use `pause-app.sh` to scale nodes to 0 during off-hours → cost drops to ~$25/month. --- ## 4. Repository Files | File | Description | |---|---| | `prepare-app.sh` | **Recommended deploy script** – creates Azure infra (RG, ACR, AKS), builds & pushes Docker images, deploys all Kubernetes objects, sets up HTTPS. Includes structured logging, fixed DNS label and post-deploy health checks | | `remove-app.sh` | Deletes the entire `ExamApp-RG` resource group (irreversible) | | `pause-app.sh` | Scales AKS node pool to 0 – stops VM billing while keeping the disk | | `resume-app.sh` | Scales node pool back to 1 and redeploys all Kubernetes objects | | `backup-db.sh` | Dumps the live PostgreSQL database to `./sql/backup_.sql` via `pg_dump` | | `lib/logger.sh` | **Shared logging library** sourced by all scripts – structured INFO/WARN/ERROR/DEBUG output with timestamps, color-coded terminal output, and log file written to `./logs/` | | `logs/` | Auto-created directory that stores timestamped log files from each script run (excluded from Git) | | `namespace.yaml` | Kubernetes Namespace `vigimeteo` | | `statefulset.yaml` | PVC (`managed-csi`) + PostgreSQL StatefulSet + headless Service | | `deployment.yaml` | 2-replica Deployments for backend and frontend; DB credentials from Secret | | `service.yaml` | NodePort Services for backend (30888) and frontend (30500) | | `ingress.yaml` | NGINX Ingress routing HTTPS to the frontend; TLS via cert-manager | | `cluster-issuer.yaml` | Let's Encrypt ACME ClusterIssuer (HTTP-01 challenge) | | `docker-compose.yaml` | Local development alternative – runs all three services without any cloud | | `sql/init_db.sql` | Full PostgreSQL dump: schema + seed data (users, stations, measurements) | | `Back-end/` | Java 17 / Vert.x source code + Maven build + Dockerfile | | `Front-end/` | React + Vite source code + Nginx config + Dockerfile | --- ## 5. Configuration Summary - **DB credentials** are injected as a Kubernetes Secret (`db-credentials`) by `prepare-app.sh`. The backend reads them via `secretKeyRef` environment variables (`DB_HOST`, `DB_PORT`, `DB_NAME`, `DB_USER`, `DB_PASSWORD`). Credentials are never committed to Git. - **PostgreSQL init** – `init_db.sql` is loaded into a ConfigMap and mounted at `/docker-entrypoint-initdb.d/`. It runs automatically on first startup. `PGDATA` is set to a subdirectory (`/var/lib/postgresql/data/pgdata`) to avoid the Azure Disk `lost+found` initdb error. - **Security context** – `fsGroup: 999` lets Kubernetes chown the Azure Disk to the `postgres` GID before starting the container. - **Nginx reverse proxy** – `nginx.conf` proxies `/api/` requests to the backend service, avoiding CORS issues. - **TLS** – `cluster-issuer.yaml` requests a Let's Encrypt certificate via ACME HTTP-01. The ingress stores it in Secret `vigimeteo-tls-secret`. - **Images** – built locally and pushed to ACR by `prepare-app.sh`. The `MON_REGISTRE` placeholder in `deployment.yaml` is replaced on-the-fly with `sed`. - **Logging** – all scripts source `lib/logger.sh` which writes colour-coded, timestamped output to the terminal and appends plain-text logs to `./logs/