Cloud Intelligence™Cloud Intelligence™

Cloud Intelligence™

Netbox cloud-native su Google Cloud Platform (GCP)

By Mike SparrJan 24, 20236 min read

Questa pagina è disponibile anche in English, Deutsch, Español, Français, 日本語 e Português.

Mettere ordine in reti e apparati grazie all'IP Address Management (IPAM)

Negli ultimi tempi ho notato un numero crescente di clienti alle prese con problemi di rete, in particolare di peering, dovuti a collisioni tra range di indirizzi IP. È un segnale evidente della necessità di pianificare e gestire gli indirizzi IP in modo strutturato all'interno dell'organizzazione.

Per tenere traccia degli IP si può usare un foglio di calcolo condiviso, ma esistono anche strumenti software dedicati. In questo articolo vediamo come eseguire un noto strumento open source di IP address management (IPAM), Netbox, in modalità cloud-native su Google Cloud Platform (GCP).

Stack tradizionale

Tradizionalmente Netbox viene eseguito su una o più macchine virtuali, con un web server in front-end. Esiste un'immagine Docker mantenuta dalla community, ma le uniche istruzioni disponibili ne prevedono l'esecuzione tramite docker compose. Questa architettura, però, ricalca quella di moltissime applicazioni che le aziende sviluppano o gestiscono, ed è quindi un ottimo esempio per illustrare anche le strategie di migrazione verso il cloud pubblico.

Fonte: Netbox — installazione standard di Netbox

Architettura cloud-native

Ho deciso di studiare a fondo il funzionamento dell'immagine Docker, le sue dipendenze e i parametri di configurazione, per poi distribuirla su GCP utilizzando esclusivamente servizi gestiti. Questo esempio mostra come affrontare la migrazione al cloud pubblico con un approccio "move and improve" oppure "rip and replace".

Installazione di Netbox rivista su GCP con servizi gestiti

Componenti applicativi

  • Applicazione Netbox (app Python basata sul framework Django)
  • Database PostgreSQL (Cloud SQL)
  • Redis (Cloud Memorystore)

Scelte progettuali

  • Database e cache gestiti (Cloud SQL, Cloud Memorystore)
  • Solo IP privati per database e cache (Private Service Access)
  • DNS privato per gli hostname dei database (Cloud DNS)
  • Secret memorizzati nel secret manager (Secret Manager)
  • Runtime serverless per container (Cloud Run, Artifact Registry)
  • Load balancer globale con TLS (HTTP(S) Load Balancing, Managed Certificate)
  • Firewall WAF (Cloud Armor)

Una delle difficoltà che vedo ricorrere più spesso nelle organizzazioni è la connessione tra servizi gestiti e applicazioni serverless tramite indirizzi IP privati. Questo esempio mostra come riservare range privati nella propria rete VPC e assegnarli ai servizi gestiti, creando un vero e proprio ponte di connettività.

Cloud DNS serve a definire hostname privati con cui le applicazioni si collegano ai database. Una scelta che garantisce maggiore flessibilità in futuro: in caso di sostituzione del database o di failover basta aggiornare i record DNS e le applicazioni continueranno a puntare allo stesso dominio. In teoria avrei potuto sfruttare il DNS forwarding e collegare il tutto al mio dominio pubblico, ma a livello interno non era necessario, quindi ho usato example.com.

Non avevamo bisogno della VM bastion (o jump host), ma ne ho avviata una per testare le connessioni durante la costruzione dell'ambiente. Di norma un bastion viene distribuito in un managed instance group (MIG) di dimensione 1 e senza indirizzi IP esterni.

Applicazione web sicura e con load balancing

Applicazione Netbox in hosting, esposta tramite Global Load Balancer davanti a un servizio Cloud Run

Per mostrare al meglio come tutti i pezzi si incastrano, ho usato un dominio personale e registrato un "record A" per l'indirizzo IP statico assegnato al Global Load Balancer; il certificato gestito è stato provisionato in automatico.

Per maggiore sicurezza, ho applicato una policy Cloud Armor (firewall WAF) al load balancer e ho limitato i range di IP (vedi sotto).

Codice di implementazione

Il codice qui sotto illustra passo passo i comandi che ho usato per configurare l'intero ambiente: rete, variabili d'ambiente e secret, database, artifact registry e immagini Docker, Cloud Run, load balancing e firewall WAF.

Complessità aggiuntive e considerazioni

Uno dei motivi per cui ho scelto Netbox come esempio per illustrare la modernizzazione e il deployment cloud-native delle applicazioni è proprio la sua complessità tecnica. L'applicazione include file system, sessioni, worker e persino processi di pulizia tramite cron giornaliero.

Estratto del Docker Compose dell'applicazione Netbox (notare netbox-worker e netbox-housekeeping)

L'estratto del file docker-compose.yaml mostrato sopra utilizza una funzionalità di YAML chiamata anchor, che non è specifica di docker-compose.

Esempio di funzionalità YAML (anchor) e merge key

Permette di duplicare le configurazioni in modo conciso e poi di sovrascrivere i comandi per eseguire script diversi a runtime.

Ricreare i worker su Cloud Run

Per ricreare questo tipo di funzionalità su Cloud Run sono disponibili i flag -- cmd e - -args. È sufficiente duplicare i comandi usati per il deploy dell'applicazione principale, cambiare il nome e sovrascrivere il CMD per eseguire un entrypoint script diverso, come nell'esempio:

gcloud run deploy $WORKER_NAME \
    --platform managed \
    --allow-unauthenticated \
    --vpc-connector $CONNECTOR_NAME \
    --ingress=internal-and-cloud-load-balancing \
    --region $GCP_REGION \
    --image $IMAGE_PATH \
    --set-env-vars "ALLOWED_HOSTS=$ALLOWED_HOSTS" \

    ...

    --cmd "/opt/netbox/venv/bin/python" \
    --args "/opt/netbox/netbox/manage.py" \
    --args "rqworker"

Job di housekeeping giornaliero con Cloud Run e Cloud Scheduler

Il job di housekeeping giornaliero si può eseguire creando un servizio duplicato su Cloud Run e pianificandone l'invocazione quotidiana tramite Cloud Scheduler.

gcloud run deploy $HOUSEKEEPING_NAME \
    --platform managed \
    --allow-unauthenticated \
    --vpc-connector $CONNECTOR_NAME \
    --ingress=internal-and-cloud-load-balancing \
    --region $GCP_REGION \
    --image $IMAGE_PATH \
    --set-env-vars "ALLOWED_HOSTS=$ALLOWED_HOSTS" \

    ...

    --cmd "/opt/netbox/housekeeping.sh"

Una volta distribuito il servizio di housekeeping, abilitiamo l'API di Cloud Scheduler:

gcloud services enable cloudscheduler.googleapis.com

Creiamo poi un service account, gli assegniamo i permessi di invoker e creiamo il job pianificato:

# fetch the service URL
export SVC_URL=$(gcloud run services describe $HOUSEKEEPING_NAME \
  --platform managed --region $GCP_REGION --format="value(status.url)")

#########################################################
# create cloud scheduler job
#########################################################
export SA_NAME="cloud-scheduler-runner"
export SA_EMAIL="${SA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com"

# create service account
gcloud iam service-accounts create $SA_NAME \
    --display-name "${SA_NAME}"

# add sa binding to cloud run app
gcloud run services add-iam-policy-binding $HOUSEKEEPING_NAME \
    --platform managed \
    --region $GCP_REGION \
    --member=serviceAccount:$SA_EMAIL \
    --role=roles/run.invoker

# create the job to invoke service every day at 2:30 AM
gcloud scheduler jobs create http housekeeping-job --schedule "30 2 * * *" \
    --http-method=GET \
    --uri=$SVC_URL \
    --oidc-service-account-email=$SA_EMAIL \
    --oidc-token-audience=$SVC_URL

File system

Il mio obiettivo era dimostrare che è possibile scomporre un'applicazione complessa come Netbox e distribuirla in cloud usando Cloud Run e altri servizi gestiti. Per questa specifica applicazione potrebbe non essere la soluzione ideale, ma è fattibile.

Se vi serve davvero il file system, oggi le piattaforme serverless presentano alcuni limiti: in alternativa potete eseguire l'applicazione su Kubernetes Engine o anche solo su una VM di Compute Engine. Potete eseguire una VM come container, soluzione molto elegante, e poi collegare dischi e volumi all'occorrenza.

Un piccolo trucco per avere un "file system" essenziale su Cloud Run è invece sfruttare Secret Manager, come ho fatto nel codice di esempio e nello snippet qui sotto.

# create secret for all vars
gcloud secrets create $SECRET_ID --replication-policy="automatic"
gcloud secrets versions add $SECRET_ID --data-file=${PWD}/$SECRET_FILE

# mount file path in cloud run
gcloud run deploy $SERVICE --image $IMAGE_URL \
    --update-secrets="/env/netbox.env"=$SECRET_ID:$SECRET_VERSION

Best practice: service account separati

Negli esempi mostrati ho usato un service account dedicato solo per l'add-on Cloud Scheduler, ma la best practice è creare service account distinti per ogni servizio e per il bastion (VM), assegnando solo i ruoli IAM strettamente necessari. In questo modo si rispetta il principio del minimo privilegio.

Per il servizio Cloud Run conviene quindi creare un service account dedicato, ad esempio "netbox-runner", a cui assegnare solo i ruoli necessari, come:

Spero che questo esempio mostri come si possano modernizzare le applicazioni esistenti sfruttando i servizi gestiti del cloud pubblico. Se l'obiettivo è semplicemente avviare Netbox, gli snippet di codice qui sopra dovrebbero bastare; in alternativa potete valutare un'esecuzione su VM o K8s.

Potete anche convertire l'esempio funzionante in Terraform usando soluzioni di terze parti come Terraformer, oppure i tool di bulk export nativi di GCP, che eseguono il reverse engineering dell'infrastruttura esistente generando il relativo codice Terraform.

Se la vostra organizzazione si trova ad affrontare problemi analoghi di collisione tra IP nella configurazione delle reti, forse è arrivato il momento di mettere in pratica l'IPAM, con un foglio di calcolo condiviso o con uno strumento diffuso come Netbox.