Skip to main content

Deployment Guide

Guida completa per deployare Visla GPS su un nuovo progetto GCP o aggiornare l'esistente.

πŸš€ Deployment Completo (Nuovo Progetto)​

Step 1: Crea Progetto e Abilita APIs​

# Crea nuovo progetto
gcloud projects create visla-prod-2027
gcloud config set project visla-prod-2027

# Abilita APIs necessarie
gcloud services enable \
compute.googleapis.com \
container.googleapis.com \
sqladmin.googleapis.com \
secretmanager.googleapis.com \
artifactregistry.googleapis.com \
cloudbuild.googleapis.com

Step 2: Terraform (Crea Infrastruttura)​

cd infra/terraform

# Modifica variabili per nuovo progetto
cat > terraform.tfvars << EOF
project_id = "visla-prod-2027"
region = "europe-west8"
zone = "europe-west8-a"
environment = "prod"
EOF

# Inizializza e applica (~15 minuti)
terraform init
terraform apply

Questo crea:

  • βœ… Cluster GKE
  • βœ… Cloud SQL PostgreSQL
  • βœ… Artifact Registry
  • βœ… Secret Manager entries

Step 3: Configura Secrets​

# JWT Secret
openssl rand -base64 64 | \
gcloud secrets versions add visla-jwt-secret --data-file=-

# Stripe API Key
echo -n "sk_live_..." | \
gcloud secrets versions add visla-stripe-api-key --data-file=-

# Firebase (base64 del JSON)
base64 -i firebase-service-account.json | \
gcloud secrets versions add visla-firebase-key --data-file=-

Poi crea i secrets in Kubernetes:

# Connetti al cluster
gcloud container clusters get-credentials visla-k8s-cluster \
--zone europe-west8-a --project visla-prod-2027

# JWT Secret
kubectl create secret generic visla-jwt-secret \
--from-literal=JWT_SECRET=$(openssl rand -base64 64)

# Redis Password
kubectl create secret generic redis-password-secret \
--from-literal=REDIS_PASSWORD=$(openssl rand -base64 32)

# Database Passwords (uno per servizio)
for svc in auth devices positions events geofences billing commands notifications sharing websocket; do
kubectl create secret generic visla-db-${svc}-password \
--from-literal=DATABASE_PASSWORD=$(openssl rand -base64 24)
done

Step 4: Connetti GitHub (Opzionale ma Consigliato)​

Per deploy automatico ad ogni push:

  1. Vai su Cloud Build Triggers
  2. Click "Connect Repository"
  3. Seleziona GitHub
  4. Autorizza Google Cloud
  5. Per ogni repo (auth, devices, geofences...):
    • Trigger name: deploy-auth (o nome servizio)
    • Event: Push to branch β†’ main
    • Build config: cloudbuild.yaml nella root del repo

Dopo questa configurazione:

git push β†’ Cloud Build si attiva β†’ Nuova immagine β†’ Deploy automatico ✨

Step 5: Deploy Kubernetes Manifests​

# Deploy tutti i servizi
kubectl apply -f infra/k8s/deployments/

# Deploy ingress e middlewares
kubectl apply -f infra/k8s/ingress-routes.yaml
kubectl apply -f infra/k8s/middlewares.yaml

# Verifica
kubectl get pods
kubectl get svc

πŸ”„ Deploy Quotidiano​

Con Cloud Build Trigger (Automatico)​

Se hai configurato Step 4:

git add .
git commit -m "Fix something"
git push origin main
# βœ… Deploy automatico!

Manuale (Build Singolo Servizio)​

# Build e push
gcloud builds submit \
--tag europe-west8-docker.pkg.dev/visla-k8s-2026/visla/auth:latest \
./auth

# Restart deployment
kubectl rollout restart deployment/auth

# Verifica
kubectl rollout status deployment/auth
kubectl logs deployment/auth --tail=20

Manuale (Build Tutti i Servizi)​

gcloud builds submit \
--config=infra/cloudbuild-all.yaml \
--substitutions=SHORT_SHA=latest

πŸ“Š Environment Variables per Servizio​

Ogni servizio richiede specifiche variabili d'ambiente. Tutte usano porta 80 per comunicazione intra-cluster.

ServizioEnv Variables Richieste
authDATABASE_HOST, DATABASE_PORT, DATABASE_USER, JWT_SECRET, REDIS_URL
devicesDATABASE_*, JWT_SECRET, AUTH_SERVICE_URL=http://auth, BILLING_SERVICE_URL=http://billing
positionsDATABASE_*, JWT_SECRET, AUTH_SERVICE_URL=http://auth, DEVICES_SERVICE_URL=http://devices, REDIS_URL
eventsDATABASE_*, JWT_SECRET, AUTH_SERVICE_URL=http://auth, REDIS_URL
geofencesDATABASE_*, JWT_SECRET, AUTH_SERVICE_URL=http://auth, REDIS_URL
billingDATABASE_*, JWT_SECRET, AUTH_SERVICE_URL=http://auth, STRIPE_API_KEY
websocketDATABASE_*, JWT_SECRET, AUTH_SERVICE_URL=http://auth, DEVICES_SERVICE_URL=http://devices, REDIS_URL
notificationsDATABASE_*, JWT_SECRET, FIREBASE_CREDENTIALS
client-logsLOKI_URL, LOKI_USERNAME, LOKI_PASSWORD
Importante

AUTH_SERVICE_URL, DEVICES_SERVICE_URL, BILLING_SERVICE_URL devono puntare a porta 80 (es. http://auth), NON alle porte interne (8081, 8084, etc.)!


πŸ” Secrets Kubernetes​

Secret NameKeysUsato da
visla-jwt-secretJWT_SECRETTutti i servizi
redis-password-secretREDIS_PASSWORDpositions, events, geofences, websocket
visla-db-auth-passwordDATABASE_PASSWORDauth
visla-db-devices-passwordDATABASE_PASSWORDdevices
visla-db-positions-passwordDATABASE_PASSWORDpositions
visla-db-events-passwordDATABASE_PASSWORDevents
visla-db-geofences-passwordDATABASE_PASSWORDgeofences
visla-db-billing-passwordDATABASE_PASSWORDbilling
visla-db-websocket-passwordDATABASE_PASSWORDwebsocket
visla-loki-passwordLOKI_USERNAME, LOKI_PASSWORDclient-logs
visla-stripe-secretSTRIPE_API_KEYbilling

πŸ› Errori Comuni e Soluzioni​

❌ 502 Bad Gateway​

Causa: Mismatch tra porta del container e targetPort del Service.

# Verifica quale porta usa il container
kubectl logs deployment/positions --tail=5
# Output: "Uvicorn running on http://0.0.0.0:8090"

# Verifica targetPort nel service
kubectl get svc positions -o yaml | grep targetPort
# Se dice 8083 ma il container usa 8090 β†’ 502!

Fix: Aggiorna il deployment YAML:

ports:
- containerPort: 8090 # Deve matchare la porta nel logs

❌ 401 Unauthorized - "Could not validate credentials"​

Causa: Il servizio non riesce a raggiungere il servizio auth per validare il token.

# Verifica AUTH_SERVICE_URL
kubectl exec deployment/devices -- env | grep AUTH_SERVICE_URL

Fix: Aggiungi nel deployment:

- name: AUTH_SERVICE_URL
value: "http://auth" # NON http://auth:8081!

❌ 401 senza CORS Headers (Frontend)​

Causa: Il frontend riceve 401 ma senza headers CORS, quindi non puΓ² leggere l'errore.

Fix: Assicurati che l'endpoint /validate in auth ritorni CORS headers anche su 401:

# In auth/routes/openid.py
response.headers["Access-Control-Allow-Origin"] = origin

❌ "Failed to get devices for user X"​

Causa: Manca DEVICES_SERVICE_URL nel deployment.

kubectl exec deployment/positions -- env | grep DEVICES
# Se vuoto β†’ errore!

Fix: Aggiungi nel deployment:

- name: DEVICES_SERVICE_URL
value: "http://devices"

❌ "CONNECTION ERROR to Loki: Name or service not known"​

Causa: client-logs cerca http://loki:3100 ma usi Grafana Cloud Loki.

Fix: Aggiungi le variabili Loki:

- name: LOKI_URL
value: "https://logs-prod-xxx.grafana.net/loki/api/v1/push"
envFrom:
- secretRef:
name: visla-loki-password # contiene LOKI_USERNAME e LOKI_PASSWORD

❌ "redis-password" secret key not found​

Causa: Il secret usa chiave diversa da quella attesa.

kubectl get secret redis-password-secret -o yaml
# Verifica che la key sia REDIS_PASSWORD, non redis-password

Fix: Nel deployment usa la key corretta:

- name: REDIS_PASSWORD
valueFrom:
secretKeyRef:
name: redis-password-secret
key: REDIS_PASSWORD # NON "redis-password"

❌ iOS "Impossibile leggere i dati perchΓ© non sono presenti"​

Causa: Il backend restituisce JSON con campi mancanti o null che iOS non riesce a decodificare.

Fix: Assicurati che tutti i campi required nel modello iOS siano presenti nella risposta API. Esempio: geofence.area deve essere una stringa, non null.

❌ WebSocket "Risposta del server non valida"​

Causa: Il servizio websocket non riesce a comunicare con altri servizi.

kubectl logs deployment/websocket --tail=30 | grep -i error

Fix: Aggiungi URLs mancanti:

- name: DEVICES_SERVICE_URL
value: "http://devices"
- name: AUTH_SERVICE_URL
value: "http://auth"

πŸ”§ Comandi Debug Utili​

# Vedi tutti i pod
kubectl get pods

# Logs di un servizio
kubectl logs deployment/auth --tail=50

# Env vars di un pod
kubectl exec deployment/auth -- env | sort

# Descrivi pod per vedere eventi
kubectl describe pod <pod-name>

# Testa connettivitΓ  interna
kubectl exec deployment/positions -- curl -s http://auth/health

# Restart deployment
kubectl rollout restart deployment/auth

πŸ“‹ Checklist Pre-Deploy​

  • Terraform applicato
  • Secrets creati in Secret Manager
  • Secrets creati in Kubernetes (JWT, Redis, DB passwords)
  • Database creato e migrato
  • DNS configurato (Cloudflare)
  • AUTH_SERVICE_URL in tutti i servizi che lo richiedono
  • DEVICES_SERVICE_URL in positions e websocket
  • Porte corrette (containerPort = porta effettiva del servizio)
  • Redis key = REDIS_PASSWORD (non redis-password)
  • Cloud Build Trigger configurato (opzionale)
  • Deployments applicati
  • Ingress routes applicati
  • Test endpoints API