Files
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

10 KiB
Raw Permalink Blame History

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

./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

./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:

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

kubectl -n <ns> get pods,ingress,certificate
kubectl -n <ns> logs deploy/tenant-api --tail=100 -f

Stato replica set Mongo

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

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

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:

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:

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:

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:

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:

kubectl get sc
kubectl -n <ns> describe pvc mongodb-data-mongodb-tenant-0