9.6 KiB
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.shto 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_<timestamp>.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) byprepare-app.sh. The backend reads them viasecretKeyRefenvironment variables (DB_HOST,DB_PORT,DB_NAME,DB_USER,DB_PASSWORD). Credentials are never committed to Git. - PostgreSQL init –
init_db.sqlis loaded into a ConfigMap and mounted at/docker-entrypoint-initdb.d/. It runs automatically on first startup.PGDATAis set to a subdirectory (/var/lib/postgresql/data/pgdata) to avoid the Azure Disklost+foundinitdb error. - Security context –
fsGroup: 999lets Kubernetes chown the Azure Disk to thepostgresGID before starting the container. - Nginx reverse proxy –
nginx.confproxies/api/requests to the backend service, avoiding CORS issues. - TLS –
cluster-issuer.yamlrequests a Let's Encrypt certificate via ACME HTTP-01. The ingress stores it in Secretvigimeteo-tls-secret. - Images – built locally and pushed to ACR by
prepare-app.sh. TheMON_REGISTREplaceholder indeployment.yamlis replaced on-the-fly withsed. - Logging – all scripts source
lib/logger.shwhich writes colour-coded, timestamped output to the terminal and appends plain-text logs to./logs/<script>-<timestamp>.log. Setexport LOG_LEVEL=DEBUGbefore running any script to enable verbose output. - Fixed DNS label – the public IP DNS label is always
vigimeteo-prod(not random), ensuring the FQDN and TLS certificate remain valid across re-deployments.
6. How to View and Use the Application
Cloud (AKS)
After prepare-app.sh completes, the FQDN is printed:
👉 https://vigimeteo-prod.polandcentral.cloudapp.azure.com
Open the URL in a browser. The TLS certificate is ready within ~2 minutes.
The DNS label vigimeteo-prod is fixed — the URL never changes between re-deployments.
Local (Docker Compose)
docker compose up --build
# Open: http://localhost:5000
Test credentials
| Role | Password | |
|---|---|---|
| Admin | admin@a.com |
azertyuiop |
| Complex user | complexe@gmail.com |
azertyuiop |
| Regular user | user@gmail.com |
azertyuiop |
Verify pods
kubectl get all -n vigimeteo
7. How to Perform a Database Backup
bash backup-db.sh
# Output: ./sql/backup_YYYYMMDD_HHMMSS.sql
Or directly from the Kubernetes pod:
POD=$(kubectl get pod -n vigimeteo -l app=vigimeteo-db -o jsonpath='{.items[0].metadata.name}')
kubectl exec -n vigimeteo "$POD" -- pg_dump -U postgres -d postgres --clean > ./sql/backup.sql
Restore:
kubectl exec -i -n vigimeteo "$POD" -- psql -U postgres -d postgres < ./sql/backup.sql
8. How to View Access Records from the Internet
Ingress (NGINX) logs – all inbound HTTP/HTTPS requests:
INGRESS_POD=$(kubectl get pod -n ingress-nginx -l app.kubernetes.io/component=controller -o jsonpath='{.items[0].metadata.name}')
kubectl logs -n ingress-nginx "$INGRESS_POD" -f
Backend logs:
kubectl logs -n vigimeteo deployment/vigimeteo-backend -f
Frontend logs:
kubectl logs -n vigimeteo deployment/vigimeteo-frontend -f
9. Conditions for Running prepare-app.sh and remove-app.sh
prepare-app.sh
All of the following must be true before running:
- Azure CLI installed and logged in (
az login) - Docker daemon running
kubectlinstalledDB_USERandDB_PASSWORDenvironment variables exported:export DB_USER="postgres" export DB_PASSWORD="your_password" bash prepare-app.sh- Sufficient Azure subscription quota for
Standard_B2als_v2VMs (2 nodes)
The script is idempotent – safe to re-run if the cluster or ACR already exist.
Deploy logs are written to ./logs/deploy-<timestamp>.log automatically.
Optional – enable verbose debug output:
export LOG_LEVEL=DEBUG
bash prepare-app.sh
remove-app.sh
- Azure CLI installed and logged in
- A backup has been taken if data must be preserved (
bash backup-db.sh) - Run:
bash remove-app.sh
⚠️ Deletes all Azure resources permanently. Data is not recoverable.
10. External Resources & Generative AI
| Resource | Type | Usage |
|---|---|---|
| Azure AKS docs | Official docs | Cluster setup, managed-csi storage |
| Kubernetes docs | Official docs | StatefulSet, Ingress, Secrets, PVC |
| Eclipse Vert.x docs | Official docs | Async HTTP server, JDBC client |
| cert-manager docs | Official docs | ClusterIssuer, ACME HTTP-01 |
| ingress-nginx | Official docs | Controller deployment, annotations |
| PostgreSQL docs | Official docs | pg_dump, pg_isready, PGDATA |
| Stack Overflow | Q&A forum | Debugging Azure Disk initdb errors, Vert.x JDBC issues |
| Google Gemini (LLM) | Generative AI | Architecture advice, manifest debugging, script generation, README writing. All suggestions were reviewed and tested before applying. |
| GitHub Copilot (LLM) | Generative AI | Inline code completion for Java handlers and React components |