Skip to content

Specifiche infrastruttura hosting โ€‹

Deliverable D2.1.3: Specifiche Infrastruttura Hosting

Contesto e premesse โ€‹

L'infrastruttura MAPS viene costruita da zero su un account DigitalOcean dedicato al progetto GST-MAPS. Il provisioning utilizza Pulumi come strumento IaC (Infrastructure as Code) con lo stesso stack tecnologico descritto nel deliverable D2.1.2: DOKS per l'orchestrazione container, cert-manager per i certificati TLS, external-secrets per la gestione dei segreti, CloudNative PostgreSQL (CNPG) per il database.

Il dominio di riferimento รจ maps.gransassotech.it (o equivalente assegnato da GST). La gestione DNS avviene tramite AWS Route53, con un hosted zone dedicato al progetto.

Account e credenziali prerequisite โ€‹

Prima di avviare il provisioning, devono essere disponibili:

RisorsaTipoNote
Account DigitalOceanNuovo account GST o progetto separatoCon billing attivo
Token API DigitalOceanPersonal Access Token con scope read+writePer Pulumi provider DO
Account AWSPer Route53 e AWS Parameter StoreAccount GST dedicato al progetto
Hosted zone Route53Per il dominio maps.gransassotech.itZone ID necessario per Pulumi
IAM credentials AWSAccess Key + Secret Key con permessi Route53 + SSMPer external-dns e external-secrets
Bucket S3Per lo stato PulumiEs. gst-maps-pulumi-state
Chiave KMS AWSPer cifratura stato PulumiAlias pulumi-maps

Struttura del progetto Pulumi โ€‹

Il provisioning viene realizzato con uno stack Pulumi maps strutturato come segue:

pulumi/maps/
โ”œโ”€โ”€ __main__.py          # Entry point
โ”œโ”€โ”€ cluster.py           # Creazione cluster DOKS
โ”œโ”€โ”€ components.py        # Installazione componenti Helm
โ”œโ”€โ”€ security.py          # IAM, network policies, segreti
โ”œโ”€โ”€ dns.py               # Record DNS Route53
โ”œโ”€โ”€ services.py          # Namespace e configurazione servizi
โ”œโ”€โ”€ utils.py             # Helper
โ”œโ”€โ”€ Pulumi.yaml          # Definizione progetto
โ”œโ”€โ”€ Pulumi.maps.yaml     # Configurazione stack maps
โ”œโ”€โ”€ helm/
โ”‚   โ””โ”€โ”€ maps/            # Valori Helm per lo stack maps
โ”‚       โ”œโ”€โ”€ cert-manager.yaml
โ”‚       โ”œโ”€โ”€ external-secrets.yaml
โ”‚       โ”œโ”€โ”€ cnpg.yaml
โ”‚       โ”œโ”€โ”€ ingress-nginx.yaml
โ”‚       โ”œโ”€โ”€ external-dns.yaml
โ”‚       โ””โ”€โ”€ metrics-server.yaml
โ””โ”€โ”€ manifests/
    โ””โ”€โ”€ maps/            # Manifest Kubernetes aggiuntivi
        โ””โ”€โ”€ cluster-issuer.yaml

Configurazione del cluster DOKS โ€‹

Parametri del cluster โ€‹

yaml
# Pulumi.maps.yaml (estratto)
config:
  env: maps
  defaultDOregion: fra1
  route53Domains:
    - maps.gransassotech.it

  cluster:
    name: maps
    version: "1.33"          # Aggiornare alla versione stabile piรน recente disponibile
    control_plane_high_availability: false
    node_pools:
      - name: general-purpose
        size: s-2vcpu-4gb
        count: 2
      - name: workloads
        size: s-4vcpu-8gb
        autoscale:
          min: 1
          max: 3

Node pool: motivazioni โ€‹

Il pool general-purpose (2 nodi fissi, 2vCPU/4GB) ospita i componenti di sistema: ingress-nginx, cert-manager, external-dns, external-secrets. Il pool workloads (1-3 nodi autoscaling, 4vCPU/8GB) ospita i servizi applicativi: Prefect, PostgreSQL (CNPG), OpenMetadata, CKAN. Il dimensionamento รจ coerente con quello descritto nel deliverable D2.1.2: in condizioni ordinarie 1 nodo workloads รจ sufficiente, con picchi ETL che attivano l'autoscaling fino a 3 nodi.

Componenti Helm da installare โ€‹

ComponenteChartRepoNamespaceNote
metrics-servermetrics-serverkubernetes-sigskube-systemMetriche HPA
cert-managercert-managerjetstackcert-managerCRDs abilitati
ingress-nginxingress-nginxkubernetes-nginxingress-nginxLoadBalancer DO
external-dnsexternal-dnskubernetes-sigskube-systemProvider AWS Route53
external-secretsexternal-secretsexternal-secretsexternal-secretsCRDs abilitati
cnpgcloudnative-pgcloudnative-pgcnpg-systemOperator PostgreSQL

I valori Helm per ciascun componente sono configurazioni standard per ognuno dei chart elencati, con i soli adattamenti relativi al dominio e alle credenziali del progetto.

Manifest: ClusterIssuer Let's Encrypt โ€‹

yaml
# manifests/maps/cluster-issuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    email: admin@gransassotech.org
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt-prod
    solvers:
      - http01:
          ingress:
            ingressClassName: nginx

Gestione DNS โ€‹

Record richiesti โ€‹

RecordTipoDestinazione
maps.k8s.maps.gransassotech.itAIP Load Balancer DOKS (gestito da Pulumi/external-dns)
prefect.maps.gransassotech.itCNAMEmaps.k8s.maps.gransassotech.it
metadata.maps.gransassotech.itCNAMEmaps.k8s.maps.gransassotech.it
ckan.maps.gransassotech.itCNAMEmaps.k8s.maps.gransassotech.it

Il record A del Load Balancer viene creato automaticamente da Pulumi tramite setup_public_lb_dns_record(). I record CNAME per i singoli servizi vengono creati automaticamente da external-dns alla lettura degli Ingress Kubernetes.

Credenziali IAM per external-dns โ€‹

Pulumi crea automaticamente un utente IAM external-dns-maps con policy limitata a route53:ChangeResourceRecordSets sulla hosted zone di maps.gransassotech.it. Le credenziali vengono iniettate come Kubernetes Secret nel namespace kube-system.

Gestione segreti โ€‹

Struttura AWS Parameter Store โ€‹

I segreti applicativi sono archiviati in AWS Parameter Store con il seguente schema di path:

/maps/{service}/{parameter}

Esempi:
/maps/postgres/password
/maps/prefect/secret-key
/maps/openmetadata/jwt-secret
/maps/ckan/api-key

External Secrets Operator โ€‹

Per ciascun servizio applicativo, Pulumi crea:

  1. Un utente IAM external-secrets-{service}-maps con policy di accesso in lettura ai parametri ssm:GetParameter* sul path /maps/{service}/*
  2. Le credenziali IAM come Kubernetes Secret nel namespace del servizio
  3. Una risorsa SecretStore che referenzia le credenziali
  4. Una risorsa ExternalSecret che mappa i parametri SSM verso Kubernetes Secrets

I segreti vengono sincronizzati con refresh interval di 1 ora.

Segreti gestiti direttamente da Pulumi โ€‹

I segreti di sistema (non applicativi) vengono archiviati cifrati in Pulumi.maps.yaml tramite AWS KMS:

  • external-dns-credentials: credenziali IAM per external-dns
  • gitlab-runner-secret: token runner CI/CD (se configurato)

Database: CloudNative PostgreSQL (CNPG) โ€‹

Il database PostgreSQL del progetto MAPS viene gestito tramite l'operatore CNPG, giร  installato nel cluster. La risorsa Cluster CNPG per MAPS specifica:

yaml
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: maps-postgres
  namespace: maps
spec:
  instances: 2
  imageName: ghcr.io/cloudnative-pg/postgis:17
  postgresql:
    parameters:
      shared_buffers: "256MB"
      max_connections: "100"
  storage:
    size: 100Gi
    storageClass: do-block-storage
  backup:
    barmanObjectStore:
      destinationPath: s3://gst-maps-backups/postgres
      s3Credentials:
        accessKeyId:
          name: maps-backup-credentials
          key: ACCESS_KEY_ID
        secretAccessKey:
          name: maps-backup-credentials
          key: SECRET_ACCESS_KEY
      wal:
        retention: "7d"
    retentionPolicy: "30d"

CNPG gestisce automaticamente: replica sincrona tra le 2 istanze, failover automatico, backup WAL continuo su S3, point-in-time recovery.

Storage per backup โ€‹

รˆ richiesto un bucket S3 dedicato (DigitalOcean Spaces o AWS S3) gst-maps-backups con le relative credenziali di accesso in scrittura per CNPG.

Namespace Kubernetes per MAPS โ€‹

I servizi applicativi MAPS vengono distribuiti nel namespace maps:

yaml
apiVersion: v1
kind: Namespace
metadata:
  name: maps
  labels:
    environment: production
    project: gst-maps

Procedura di provisioning โ€‹

Ordine di esecuzione โ€‹

L'esecuzione di pulumi up --stack maps dalla directory pulumi/maps/ esegue le seguenti operazioni nell'ordine:

  1. Creazione cluster DOKS con i due node pool
  2. Creazione policy di rete e utente IAM per external-dns
  3. Creazione segreti Kubernetes di sistema
  4. Installazione componenti Helm (in parallelo dove possibile)
  5. Applicazione manifest Kubernetes (ClusterIssuer, ecc.)
  6. Configurazione record DNS del Load Balancer su Route53
  7. Configurazione namespace e segreti dei servizi applicativi

Prerequisiti locali โ€‹

bash
# Strumenti necessari sulla macchina di provisioning
pulumi >= 3.0
python >= 3.11
poetry
kubectl
doctl (DigitalOcean CLI)
aws CLI (configurato con profilo maps)

Comandi โ€‹

bash
# 1. Configurare profilo AWS
export AWS_PROFILE=maps

# 2. Login stato Pulumi su S3
pulumi login s3://gst-maps-pulumi-state

# 3. Selezione stack
pulumi stack select maps

# 4. Preview (verifica senza applicare)
pulumi preview

# 5. Applicazione
pulumi up

Accesso ai servizi dopo il deployment โ€‹

ServizioURLAutenticazione
Prefect UIhttps://prefect.maps.gransassotech.itCredenziali Prefect
OpenMetadatahttps://metadata.maps.gransassotech.itAdmin OIDC
CKANhttps://ckan.maps.gransassotech.itAdmin CKAN
PostgreSQLInterno al cluster (porta 5432)Solo da pod interni

Stima dei costi DigitalOcean โ€‹

VoceConfigurazioneCosto mensile
DOKS control planeManaged$12
Pool general-purpose2x s-2vcpu-4gb$48 ($24/nodo)
Pool workloads (min)1x s-4vcpu-8gb$48
Pool workloads (max)3x s-4vcpu-8gb$144
Block storage CNPG100 GB$10
Block storage Bronze100 GB$10
Load BalancerIngress HTTPS$12
Spaces (backup)250 GB$5
Totale (min)~$145/mese
Totale (max, picco ETL)~$241/mese

La differenza rispetto alla stima del D2.1.2 ($311/mese) รจ dovuta all'adozione di CNPG self-hosted invece del Managed PostgreSQL ($80/mese), e al meccanismo di autoscaling che riduce i costi nelle fasi di inattivitร .