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:
- Vai su Cloud Build Triggers
- Click "Connect Repository"
- Seleziona GitHub
- Autorizza Google Cloud
- Per ogni repo (auth, devices, geofences...):
- Trigger name:
deploy-auth(o nome servizio) - Event: Push to branch β
main - Build config:
cloudbuild.yamlnella root del repo
- Trigger name:
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.
| Servizio | Env Variables Richieste |
|---|---|
| auth | DATABASE_HOST, DATABASE_PORT, DATABASE_USER, JWT_SECRET, REDIS_URL |
| devices | DATABASE_*, JWT_SECRET, AUTH_SERVICE_URL=http://auth, BILLING_SERVICE_URL=http://billing |
| positions | DATABASE_*, JWT_SECRET, AUTH_SERVICE_URL=http://auth, DEVICES_SERVICE_URL=http://devices, REDIS_URL |
| events | DATABASE_*, JWT_SECRET, AUTH_SERVICE_URL=http://auth, REDIS_URL |
| geofences | DATABASE_*, JWT_SECRET, AUTH_SERVICE_URL=http://auth, REDIS_URL |
| billing | DATABASE_*, JWT_SECRET, AUTH_SERVICE_URL=http://auth, STRIPE_API_KEY |
| websocket | DATABASE_*, JWT_SECRET, AUTH_SERVICE_URL=http://auth, DEVICES_SERVICE_URL=http://devices, REDIS_URL |
| notifications | DATABASE_*, JWT_SECRET, FIREBASE_CREDENTIALS |
| client-logs | LOKI_URL, LOKI_USERNAME, LOKI_PASSWORD |
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 Name | Keys | Usato da |
|---|---|---|
visla-jwt-secret | JWT_SECRET | Tutti i servizi |
redis-password-secret | REDIS_PASSWORD | positions, events, geofences, websocket |
visla-db-auth-password | DATABASE_PASSWORD | auth |
visla-db-devices-password | DATABASE_PASSWORD | devices |
visla-db-positions-password | DATABASE_PASSWORD | positions |
visla-db-events-password | DATABASE_PASSWORD | events |
visla-db-geofences-password | DATABASE_PASSWORD | geofences |
visla-db-billing-password | DATABASE_PASSWORD | billing |
visla-db-websocket-password | DATABASE_PASSWORD | websocket |
visla-loki-password | LOKI_USERNAME, LOKI_PASSWORD | client-logs |
visla-stripe-secret | STRIPE_API_KEY | billing |
π 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