zkt26/sk1/README.md
2026-05-12 19:27:18 +02:00

214 lines
9.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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_<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`) 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/<script>-<timestamp>.log`. Set `export LOG_LEVEL=DEBUG` before 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)
```bash
docker compose up --build
# Open: http://localhost:5000
```
### Test credentials
| Role | Email | Password |
|---|---|---|
| Admin | `admin@a.com` | `azertyuiop` |
| Complex user | `complexe@gmail.com` | `azertyuiop` |
| Regular user | `user@gmail.com` | `azertyuiop` |
### Verify pods
```bash
kubectl get all -n vigimeteo
```
---
## 7. How to Perform a Database Backup
```bash
bash backup-db.sh
# Output: ./sql/backup_YYYYMMDD_HHMMSS.sql
```
Or directly from the Kubernetes pod:
```bash
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:
```bash
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:
```bash
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:**
```bash
kubectl logs -n vigimeteo deployment/vigimeteo-backend -f
```
**Frontend logs:**
```bash
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
- `kubectl` installed
- `DB_USER` and `DB_PASSWORD` environment variables exported:
```bash
export DB_USER="postgres"
export DB_PASSWORD="your_password"
bash prepare-app.sh
```
- Sufficient Azure subscription quota for `Standard_B2als_v2` VMs (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:
```bash
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](https://learn.microsoft.com/en-us/azure/aks/) | Official docs | Cluster setup, managed-csi storage |
| [Kubernetes docs](https://kubernetes.io/docs/) | Official docs | StatefulSet, Ingress, Secrets, PVC |
| [Eclipse Vert.x docs](https://vertx.io/docs/) | Official docs | Async HTTP server, JDBC client |
| [cert-manager docs](https://cert-manager.io/docs/) | Official docs | ClusterIssuer, ACME HTTP-01 |
| [ingress-nginx](https://kubernetes.github.io/ingress-nginx/) | Official docs | Controller deployment, annotations |
| [PostgreSQL docs](https://www.postgresql.org/docs/17/) | 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 |