Files
LOGINMASTER-DeploymentScript/README.md
T
Luca d3a9739f2f Fix race su rs.initiate: attendi i 3 pod Mongo + directConnection in mongosh
Due bug che bloccavano deploy.sh su tenant con provisioning PVC lento:

1. Race DNS: lo step 8 aspettava solo mongodb-tenant-0 prima di rs.initiate.
   Se i pod 1/2 erano ancora in provisioning, i loro record DNS nel service
   headless non esistevano e rs.initiate falliva il quorum check con
   "Could not find address for mongodb-tenant-1...". Ora si attende che tutti
   e 3 i pod siano Ready.

2. mongosh "connection <monitor> closed": con --host 127.0.0.1, appena il
   replica set è inizializzato mongosh passa in modalità topology e prova a
   monitorare il PRIMARY via hostname annunciato, chiudendo la connessione.
   Sostituito con URI directConnection=true, compatibile con la localhost
   exception per la creazione del primo utente.

Allineati gli hint diagnostici in deploy.sh e l'esempio rs.status() nel README
al nuovo form directConnection.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 12:18:14 +02:00

230 lines
10 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.
# LoginMaster Tenant — Deployment Tools
Toolkit per deployare un tenant LoginMaster (api-tenant + admin-tenant + MongoDB replica set) su qualsiasi cluster Kubernetes — vSphere, AWS EKS, Azure AKS, DigitalOcean DOKS, Scaleway Kapsule, Hetzner, ecc.
## Cosa viene deployato
```
namespace: <a tua scelta, es. loginmaster-tenant-acme>
├── MongoDB replica set rs0 (3 pod, StatefulSet)
│ ├── TLS server cert via cert-manager (CA interna self-signed, 10 anni)
│ ├── auth replica set via keyFile, auth client via password
│ └── PVC RWO 20Gi per nodo (storage class scelta dall'utente)
├── tenant-api (Deployment, 2 repliche, rolling update zero-downtime)
│ └── connessione Mongo via TLS, valida server cert via la CA interna
├── tenant-admin (Deployment, 1 replica)
├── Ingress nginx con cert Let's Encrypt per i due hostname pubblici
├── NetworkPolicy che isola il tenant:
│ ├── Mongo raggiungibile solo dai pod tenant-api e dai membri replica set
│ └── api/admin raggiungibili solo dall'ingress controller
└── Secret per: credenziali Mongo, MASTER_ENCRYPTION_KEY, registry, SMTP password
```
## Prerequisiti
### Sul cluster
| Cosa | Note |
|---|---|
| **cert-manager** | Con `ClusterIssuer` `letsencrypt-prod` per i certificati Ingress pubblici. Gli `Issuer` interni per il TLS Mongo sono creati dallo script. |
| **ingress-nginx** | Installato in un namespace con la label `kubernetes.io/metadata.name: ingress-nginx` (default da K8s 1.22+). |
| **CNI con NetworkPolicy** | Calico, Cilium, weave, kube-router. Le `NetworkPolicy` non sono enforcement-effective senza un CNI che le implementi. |
| **Storage class default** | Auto-rilevata dallo script via annotation `storageclass.kubernetes.io/is-default-class`. Se il cluster non ne ha una marcata, lo script chiede manualmente. |
### Sulla macchina
```
brew install kubectl gettext openssl
brew link --force gettext # per envsubst
```
### Credenziali
- Accesso al registry `hub.codebaker.it/loginmaster-tenant/` (username + password)
- Context kubectl configurato per il cluster target
## Deploy iniziale
```bash
./deploy.sh
```
Lo script chiede 14 parametri in modo interattivo. Default sensati per Codebaker (in `[parentesi]`):
| Prompt | Default | Note |
|---|---|---|
| Context kubectl | corrente | Quello selezionato in `~/.kube/config` |
| Namespace | `loginmaster-tenant` | Uno per tenant. Lo script aborta se esiste già |
| Tag api-tenant | `prod-1.3.8` | |
| Tag admin-tenant | `prod-1.3.7` | Le due immagini possono essere a versioni diverse |
| Hostname API | `api.tenant.example.com` | Da sostituire col proprio dominio. Deve risolvere all'ingress controller |
| Hostname admin | `admin.tenant.example.com` | Idem |
| `LOGINMASTER_API_URL` | `https://api.loginmaster.it/1.3/` | API LoginMaster centrale |
| `ADMIN_ALLOWED_ORIGIN` | `https://admin.loginmaster.it` | Origin del pannello LoginMaster globale (≠ pannello del tenant) |
| Storage class | default cluster | Auto-rilevata |
| Storage size | `20Gi` | Per ogni membro Mongo (totale: 3× questo) |
| Mongo CPU/mem | 250m2 / 1Gi2Gi | Aumenta su carichi reali (Mongo cache cresce con la RAM) |
| SMTP | `N` | Se sì, chiede host/port/secure/user/pass |
| Registry user/password | — / — | Credenziali fornite per accedere a `hub.codebaker.it` |
| Confirm | `N` | `Y` per procedere |
A fine deploy lo script salva `.deploy-credentials.txt` (chmod 600) con MASTER_ENCRYPTION_KEY, password root + applicativa Mongo, URI di connessione. **Spostalo in un password manager e cancellalo dal disco.**
### Re-run protection
`deploy.sh` aborta se nel namespace esiste già il Secret `mongodb-tenant-auth`: rigenerarlo causerebbe la perdita del replica set (nuovo keyfile → membri non si parlano più).
Per ridistribuire da zero: `kubectl delete namespace <ns>` e rilancia.
Per aggiornare le immagini: usa `update.sh`.
## Aggiornare un tenant esistente
```bash
./update.sh # interattivo, prefilla con i tag attuali del cluster
./update.sh prod-1.3.9 # stesso tag per api e admin
./update.sh prod-1.3.9 prod-1.3.8 # tag separati
```
Lo script:
1. Mostra le immagini attuali e chiede conferma per il rolling update di api e/o admin.
2. Se l'update fallisce, stampa il comando `kubectl rollout undo` da copiare.
3. In coda chiede se **riapplicare il ConfigMap** dal template — utile quando una nuova versione dell'API legge env var nuove. Riapplica `templates/03-configmap.yaml` usando i valori correnti dal cluster e fa `rollout restart` di tenant-api per propagarli.
### Rollback manuale
`kubectl` mantiene le ultime 10 revisioni dei Deployment:
```bash
kubectl -n <ns> rollout history deployment/tenant-api
kubectl -n <ns> rollout undo deployment/tenant-api
kubectl -n <ns> rollout undo deployment/tenant-api --to-revision=N
```
## Operazioni comuni
### Verificare lo stato
```bash
kubectl -n <ns> get pods,ingress,certificate
kubectl -n <ns> logs deploy/tenant-api --tail=100 -f
```
### Stato replica set Mongo
```bash
ROOT_PASS=$(kubectl -n <ns> get secret mongodb-tenant-auth -o jsonpath='{.data.root-password}' | base64 -d)
kubectl -n <ns> exec mongodb-tenant-0 -c mongodb -- mongosh --quiet \
'mongodb://127.0.0.1:27017/?directConnection=true&tls=true&tlsCAFile=/etc/mongo-tls/ca.crt&tlsAllowInvalidHostnames=true' \
-u admin -p "$ROOT_PASS" --authenticationDatabase admin \
--eval 'rs.status()'
```
### Backup / restore
Lo script non gestisce backup: usa `mongodump`/`mongorestore` con le credenziali applicative, oppure snapshot lato cloud sui PV Mongo.
### Scale orizzontale
```bash
kubectl -n <ns> scale deployment/tenant-api --replicas=4
```
Mongo è un replica set fisso a 3 membri (PDB `minAvailable: 2` lo presuppone): non scalare a meno di non rivedere PDB e quorum.
## Cleanup
```bash
kubectl delete namespace <ns>
```
> **Attenzione**: i PV con `reclaim policy: Retain` (es. `vsphere-csi`, `sbs-default-retain`) **sopravvivono** alla cancellazione del namespace e dei PVC. Vanno rimossi a mano (`kubectl delete pv <name>`) e i dischi sottostanti dal pannello del cloud provider per non lasciare costi residui.
## Sicurezza
- **TLS interno Mongo**: cert-manager emette CA self-signed (10 anni) e cert membri (1 anno). Mongo è in `requireTLS`. Niente traffico in chiaro nel namespace.
- **Auth replica set**: keyFile (HMAC condiviso). I membri non si parlano se il keyfile non combacia.
- **Auth client**: password generata, mai in argv di kubectl, mai in log audit (le `mongo_eval*` di `deploy.sh` autenticano via `db.auth()` su stdin).
- **NetworkPolicy**: blocca traffico cross-namespace e intra-namespace non autorizzato.
- **Pod Mongo non-root** (uid/gid 999) con `fsGroup` per ownership PVC.
- **Secret K8s** (base64-encoded, non encrypted at rest se non configurato a livello cluster). MASTER_ENCRYPTION_KEY, password Mongo, password registry e SMTP risiedono qui — abilita encryption-at-rest a livello etcd se la compliance lo richiede.
### Limiti noti
- **Rinnovo cert TLS Mongo**: cert-manager rinnova il Secret automaticamente, ma **mongod non ricarica il cert a runtime**. Quando cert-manager riemette il Secret (default 30 giorni prima della scadenza, quindi ~ogni 11 mesi sui cert membri), serve `kubectl rollout restart statefulset/mongodb-tenant`. Il PDB garantisce che resti almeno 1 PRIMARY + 1 SECONDARY → zero downtime sul replica set, ma è una azione manuale.
- **Warning `PodSecurity "restricted"`**: il pod Mongo richiede uno user-defined `runAsUser` e l'init come root, quindi triggera un warn (non enforce). Se la policy è in enforce mode, il deploy va rivisto (init come root non è compatibile con `restricted` puro).
## Note per cloud provider specifici
| Provider | Storage class default tipica | Reclaim default |
|---|---|---|
| AWS EKS | `gp3` (recenti) o `gp2` | `Delete` |
| Azure AKS | `default` o `managed-csi` | `Delete` |
| DigitalOcean DOKS | `do-block-storage` | `Delete` |
| Scaleway Kapsule | `sbs-default-retain`, `scw-bssd` | varia |
| GCP GKE | `standard-rwo`, `premium-rwo` | `Delete` |
| vSphere | `vsphere-csi` | `Retain` |
| Hetzner | `hcloud-volumes` | `Delete` |
**Reclaim `Delete`** = cancellando il namespace perdi i dati Mongo. Per produzione: scegliere SC con `Retain` o avere snapshot/backup esterni. Lo script non sovrascrive il reclaim della SC.
**Volume binding mode**: tutte le SC cloud moderne usano `WaitForFirstConsumer`, che fa partire i 3 pod Mongo correttamente in cluster multi-AZ. Le SC custom con `Immediate` su cluster multi-AZ rischiano cross-zone mount fail.
## Struttura del repo
```
.
├── deploy.sh # bootstrap di un nuovo tenant (idempotente: aborta su rerun)
├── update.sh # rolling update + rollback hint + apply ConfigMap opzionale
└── templates/
├── 00-namespace.yaml
├── 01-mongodb-tls.yaml # cert-manager Issuer + CA + cert membri
├── 02-networkpolicy.yaml # isolamento intra-namespace
├── 03-configmap.yaml # tenant-api-config
├── 04-mongodb.yaml # StatefulSet 3 repliche, init container per keyfile/PEM tmpfs
├── 05-tenant-api.yaml # Deployment 2 repliche + Service + PDB
├── 06-tenant-admin.yaml # Deployment 1 replica + Service
└── 07-ingress.yaml # ingress-nginx + Let's Encrypt
```
## Troubleshooting
### Pod Mongo in CrashLoopBackOff
Probabilmente cert-manager non ha emesso `mongodb-tenant-tls`. Verifica:
```bash
kubectl -n <ns> get certificate
kubectl -n <ns> describe certificate mongodb-tenant-tls
```
### Cert ingress `tenant-tls` resta `Ready: False`
DNS dei due hostname non risolve all'IP dell'ingress controller, oppure la HTTP-01 challenge fallisce per altri motivi:
```bash
kubectl -n <ns> describe certificate tenant-tls
kubectl -n <ns> get challenge
```
### Replica set non elegge un PRIMARY
Verifica connettività intra-namespace (NetworkPolicy può bloccare se la label `app: mongodb-tenant` viene cambiata) e che i 3 pod si vedano via DNS:
```bash
kubectl -n <ns> exec mongodb-tenant-0 -c mongodb -- nslookup mongodb-tenant-1.mongodb-tenant-headless
```
### `ImagePullBackOff` su admin/api
Tag inesistente sul registry. Verifica:
```bash
curl -u USER:PASS https://hub.codebaker.it/v2/loginmaster-tenant/admin-tenant/tags/list
```
Storicamente `api-tenant` esce più frequentemente di `admin-tenant`: usa tag separati con `./update.sh API_TAG ADMIN_TAG`.
### Errori PVC su provisioning
Storage class non esistente o quota cloud esaurita:
```bash
kubectl get sc
kubectl -n <ns> describe pvc mongodb-data-mongodb-tenant-0
```