Modulo 07

AWS, dall'account alla produzione

Fin qui abbiamo versionato codice, automatizzato build e test, containerizzato applicazioni, orchestrato container e descritto infrastruttura come codice. In questo modulo mettiamo tutto in un posto reale: Amazon Web Services, il cloud provider su cui gira una fetta enorme di internet.

Cos'è il cloud computing

Il cloud computing significa affittare capacità di calcolo, storage e rete da un fornitore, invece di comprare e gestire server fisici. AWS mette a disposizione centinaia di servizi, ma si possono raggruppare in tre livelli:

Nel corso useremo soprattutto servizi IaaS (EC2, VPC) e alcuni servizi gestiti "a metà strada" (RDS, Lambda), che tolgono lavoro operativo pur lasciandoti il controllo dell'applicazione.

Il modello di responsabilità condivisa

AWS descrive la sicurezza come una responsabilità divisa in due parti:

Questo è il motivo per cui la maggior parte degli incidenti di sicurezza su AWS non nasce da un difetto di AWS, ma da configurazioni sbagliate lasciate aperte da chi usa l'account: bucket S3 pubblici per errore, chiavi IAM esposte su GitHub, security group troppo permissivi.

Regioni e Availability Zone

AWS è distribuito fisicamente in tutto il mondo, organizzato in due livelli:

Regione — eu-west-1 (Irlanda) AZ eu-west-1a EC2 RDS (primario) AZ eu-west-1b EC2 RDS (standby) AZ eu-west-1c EC2 (riserva) ✓ se un'AZ va giù, le altre restano operative

Una regione contiene più Availability Zone isolate: distribuire le risorse su più AZ è la base della tolleranza ai guasti su AWS.

Distribuire un'applicazione su più AZ è la tecnica più semplice ed efficace per renderla resiliente: se un intero data center ha un problema, il traffico continua a essere servito dalle altre zone.

Creare l'account e proteggere il root user

Alla creazione di un account AWS ottieni un utente speciale chiamato root user, associato all'email con cui ti sei registrato. Il root user ha accesso illimitato a tutto l'account, compresa la fatturazione.

Non usare mai il root user per il lavoro quotidiano. È buona pratica: attivare subito l'MFA (autenticazione a due fattori) sul root, non creare chiavi di accesso programmatiche per il root, e creare invece un utente IAM amministrativo da usare al suo posto.

Passaggi consigliati appena creato l'account:

  1. Attiva l'MFA sul root user (Security Credentials → Multi-Factor Authentication).
  2. Imposta un budget e un allarme di fatturazione (ne parliamo più avanti).
  3. Crea un utente IAM con permessi amministrativi e usa quello da qui in poi.

IAM: utenti, gruppi, ruoli e policy

IAM (Identity and Access Management) è il servizio che controlla chi può fare cosa nel tuo account. È probabilmente il servizio più importante da capire bene, perché sta alla base della sicurezza di tutto il resto.

I concetti principali

Esempio di policy JSON

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject"
      ],
      "Resource": "arn:aws:s3:::corso-devops-bucket/*"
    }
  ]
}

Questa policy permette di leggere e scrivere oggetti solo dentro il bucket corso-devops-bucket, nient'altro. È l'esempio pratico del principio del minimo privilegio: dare solo i permessi strettamente necessari, mai "Action": "*" su "Resource": "*" a meno che non sia davvero indispensabile.

Creare un utente IAM (console)

  1. Vai su IAM → Users → Create user.
  2. Dai un nome, ad esempio andrea-admin.
  3. Scegli se serve accesso alla console, accesso programmatico, o entrambi.
  4. Assegna un gruppo con la policy AdministratorAccess (solo per l'utente amministrativo iniziale; per tutto il resto, policy più specifiche).
  5. Attiva l'MFA anche su questo utente.

Ruoli: perché sono meglio delle chiavi

Quando un'istanza EC2 o una funzione Lambda deve accedere ad altri servizi AWS (es. leggere da S3), la pratica corretta è assegnare un ruolo IAM alla risorsa, non incollare chiavi di accesso nel codice o nelle variabili d'ambiente. Il ruolo fornisce credenziali temporanee, ruotate automaticamente, senza che nessuna chiave venga mai scritta su disco o commitata per errore su Git.

Regola pratica: se ti stai chiedendo "dove metto la Access Key ID e la Secret Access Key nel codice", la risposta quasi sempre giusta è: da nessuna parte, usa un ruolo IAM.

AWS CLI

La AWS CLI permette di gestire l'account da terminale, utile sia per lavoro manuale veloce sia per script di automazione.

Installazione (macOS)

brew install awscli
aws --version

Configurazione delle credenziali

aws configure

Il comando chiede quattro valori:

Le credenziali vengono salvate in ~/.aws/credentials. Per verificare che tutto funzioni:

aws sts get-caller-identity
Il file ~/.aws/credentials e qualunque chiave AWS non vanno mai committati su Git. Se una chiave finisce per errore in una repository pubblica, disattivala immediatamente da IAM e generane una nuova.

VPC: la rete privata

Una VPC (Virtual Private Cloud) è una rete privata isolata, tutta tua, dentro AWS. Ogni risorsa "di rete" (EC2, RDS, load balancer) vive dentro una VPC. Ogni account ha una VPC di default già pronta, ma in un progetto reale conviene crearne una su misura.

I mattoncini di una VPC

VPC — 10.0.0.0/16 ☁ Internet Internet GW Subnet pubblica — eu-west-1a Load Balancer NAT Gateway Bastion host (accesso SSH) Subnet privata — eu-west-1a EC2 / ECS EC2 / ECS RDS (nessun accesso diretto da internet) Il Load Balancer nella subnet pubblica riceve il traffico da internet e lo inoltra ai server nella subnet privata, non raggiungibile direttamente.

Schema tipico a due livelli: subnet pubblica per ciò che deve essere raggiungibile da internet, subnet privata per applicazione e database.

Creare una VPC minima da CLI

aws ec2 create-vpc --cidr-block 10.0.0.0/16

aws ec2 create-subnet \
  --vpc-id vpc-xxxxxxxx \
  --cidr-block 10.0.1.0/24 \
  --availability-zone eu-west-1a

In pratica, quasi nessuno crea una VPC a mano comando per comando: si usa Terraform (modulo 06) o la console per i primi esperimenti, poi si passa all'Infrastructure as Code.

EC2: server virtuali

EC2 (Elastic Compute Cloud) è il servizio che affitta server virtuali, chiamati istanze. È il servizio AWS più fondamentale: praticamente ogni altro servizio di calcolo (ECS, EKS, RDS) gira sopra istanze EC2 dietro le quinte.

Concetti chiave

Lanciare un'istanza da CLI

aws ec2 run-instances \
  --image-id ami-0c55b159cbfafe1f0 \
  --instance-type t3.micro \
  --key-name mia-chiave \
  --security-group-ids sg-xxxxxxxx \
  --subnet-id subnet-xxxxxxxx \
  --count 1

Connettersi via SSH

chmod 400 mia-chiave.pem
ssh -i mia-chiave.pem ubuntu@INDIRIZZO_IP_PUBBLICO
Su un'istanza Ubuntu l'utente predefinito è ubuntu; su Amazon Linux è ec2-user. chmod 400 è obbligatorio: SSH rifiuta chiavi private con permessi troppo aperti.

Esempio di user data

Per installare Node.js e avviare l'app del modulo GitHub Actions in automatico all'avvio:

#!/bin/bash
sudo apt update -y
sudo apt install -y nodejs npm
cd /home/ubuntu
git clone https://github.com/utente/progetto.git
cd progetto
npm install
npm run start &

EBS: dischi per EC2

EBS (Elastic Block Store) è lo storage a blocchi collegato alle istanze EC2 — l'equivalente di un disco rigido virtuale. Ogni istanza ha almeno un volume EBS come disco principale (root volume).

aws ec2 create-snapshot --volume-id vol-xxxxxxxx --description "Backup pre-aggiornamento"

S3: storage a oggetti

S3 (Simple Storage Service) è lo storage a oggetti di AWS: file di qualsiasi tipo e dimensione, organizzati in bucket. È probabilmente il servizio AWS più usato in assoluto, anche da chi non tocca mai EC2: backup, asset statici, log, dataset, siti web statici.

Concetti chiave

Creare un bucket e caricare file da CLI

aws s3 mb s3://corso-devops-bucket

aws s3 cp ./index.html s3://corso-devops-bucket/

aws s3 sync ./sito-statico s3://corso-devops-bucket/

Ospitare un sito statico su S3

Lo stesso sito di questo corso potrebbe essere pubblicato su S3 invece che su GitHub Pages:

  1. Crea un bucket con lo stesso nome del dominio (best practice, non obbligatorio).
  2. Attiva "Static website hosting" nelle proprietà del bucket, indicando index.html come documento principale.
  3. Aggiungi una bucket policy che permetta la lettura pubblica degli oggetti.
  4. Carica i file con aws s3 sync.
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::corso-devops-bucket/*"
    }
  ]
}
Un bucket pubblico va bene per un sito statico pensato per essere pubblico, ma è la causa più comune di fughe di dati su AWS quando viene applicato per errore a bucket che contengono dati sensibili. Controlla sempre "Block public access" prima di renderlo pubblico consapevolmente.

RDS: database gestiti

RDS (Relational Database Service) gestisce database relazionali (PostgreSQL, MySQL, MariaDB, SQL Server, Oracle) senza che tu debba occuparti di patching, backup manuali o replica.

aws rds create-db-instance \
  --db-instance-identifier corso-devops-db \
  --db-instance-class db.t3.micro \
  --engine postgres \
  --master-username admin \
  --master-user-password "PasswordSicura123!" \
  --allocated-storage 20
In produzione, la connessione dall'applicazione a RDS passa dalla subnet privata della VPC (vedi il diagramma sopra): il database non deve avere un indirizzo IP pubblico raggiungibile da internet.

Load Balancer e Auto Scaling

Quando un'applicazione gira su più istanze EC2, serve qualcosa che distribuisca il traffico tra loro e che sostituisca automaticamente le istanze in difficoltà.

Elastic Load Balancing (ELB)

Auto Scaling Group (ASG)

Un Auto Scaling Group mantiene automaticamente un certo numero di istanze EC2 in esecuzione, basandosi su una launch template (che descrive come creare una nuova istanza identica alle altre) e su regole di scala:

Load Balancer + Auto Scaling Group è la combinazione classica per rendere un'applicazione EC2 resiliente e scalabile senza intervento manuale: esattamente lo stesso principio delle replicas e del readinessProbe visti nel modulo Kubernetes, applicato a livello di macchine virtuali invece che di Pod.

Route 53: DNS

Route 53 è il servizio DNS di AWS: traduce nomi a dominio (es. corso-devops.it) in indirizzi IP o in altre risorse AWS.

aws route53 change-resource-record-sets \
  --hosted-zone-id ZXXXXXXXXXXXXX \
  --change-batch file://record.json

CloudFront: CDN

CloudFront è la rete di distribuzione dei contenuti (CDN) di AWS: copia i tuoi contenuti (statici o dinamici) sui suoi edge location sparsi nel mondo, così ogni utente li scarica dal punto più vicino a lui invece che dal server originale.

Lambda e API Gateway

Lambda esegue codice senza che tu debba gestire alcun server: carichi una funzione, AWS la esegue quando serve e la fattura solo per il tempo effettivo di esecuzione. È il servizio "serverless" per eccellenza.

Come si attiva una funzione Lambda

Esempio minimo (Node.js)

export const handler = async (event) => {
  return {
    statusCode: 200,
    body: JSON.stringify({ message: "Hello da Lambda!" }),
  };
};
aws lambda create-function \
  --function-name hello-lambda \
  --runtime nodejs20.x \
  --role arn:aws:iam::123456789012:role/lambda-execution-role \
  --handler index.handler \
  --zip-file fileb://function.zip
Lambda è ideale per compiti brevi ed episodici (webhook, elaborazione immagini, API leggere). Per applicazioni sempre attive con traffico costante, EC2 o i container (sotto) restano spesso più economici e prevedibili.

ECS, Fargate ed EKS

Dopo aver visto Docker e Kubernetes nei moduli precedenti, su AWS ci sono tre modi principali per eseguire container in produzione:

Il file deployment.yaml e service.yaml scritti nel modulo Kubernetes funzionano, con pochissime modifiche, anche su un cluster EKS reale: è uno dei motivi per cui vale la pena imparare Kubernetes prima di scegliere un cloud specifico.

CloudWatch: monitoraggio

CloudWatch raccoglie metriche, log e allarmi da (quasi) tutti i servizi AWS.

aws cloudwatch put-metric-alarm \
  --alarm-name cpu-alta \
  --metric-name CPUUtilization \
  --namespace AWS/EC2 \
  --statistic Average \
  --period 300 \
  --threshold 80 \
  --comparison-operator GreaterThanThreshold \
  --evaluation-periods 2 \
  --alarm-actions arn:aws:sns:eu-west-1:123456789012:notifiche-team

Un'architettura completa

Mettendo insieme i servizi visti finora, ecco come potrebbe apparire un'applicazione web reale in produzione su AWS:

Utente Route 53 DNS CloudFront CDN ALB Load Balancer ECS / EC2 ECS / EC2 RDS Multi-AZ S3 Asset statici CloudWatch Log e allarmi

Route 53 risolve il dominio, CloudFront serve i contenuti dal punto più vicino all'utente, l'ALB smista il traffico tra i container/istanze in due AZ diverse, che leggono/scrivono su RDS ed espongono i propri log e metriche a CloudWatch.

Costi e Free Tier

AWS fattura quasi tutto "a consumo": si paga per quello che si usa, spesso al secondo o all'ora. Il Free Tier offre una quota gratuita su molti servizi per i primi 12 mesi (e alcuni servizi gratuiti sempre, entro certi limiti), utile per esercitarsi senza spendere.

Come tenere sotto controllo la spesa

  1. In Billing → Budgets, crea un budget mensile con notifica quando superi una soglia (es. avviso al superamento di 5€).
  2. Ricorda di spegnere o eliminare le risorse create per esercitarti: istanze EC2 fermate ma non terminate continuano comunque a costare per lo storage EBS collegato.
  3. Un NAT Gateway, un Elastic IP non associato a un'istanza, e RDS Multi-AZ sono tra le voci di costo più spesso dimenticate accese per errore.
Prima di chiudere una sessione di pratica, controlla EC2, RDS, NAT Gateway ed Elastic IP: sono le risorse che più spesso continuano silenziosamente a generare costi anche quando "sembra" che tu abbia finito.

AWS con Terraform

Il modulo precedente ha già mostrato come dichiarare un'istanza EC2 con Terraform usando il provider aws. Con i concetti di questo modulo, quello stesso file inizia ad avere senso completo:

resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
}

resource "aws_subnet" "public" {
  vpc_id                  = aws_vpc.main.id
  cidr_block              = "10.0.1.0/24"
  map_public_ip_on_launch = true
}

resource "aws_security_group" "web" {
  vpc_id = aws_vpc.main.id

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_instance" "web" {
  ami                    = "ami-0c55b159cbfafe1f0"
  instance_type          = "t3.micro"
  subnet_id              = aws_subnet.public.id
  vpc_security_group_ids = [aws_security_group.web.id]
}

Ogni riga di questo file corrisponde a un servizio spiegato in questo modulo: VPC, subnet, security group, istanza EC2. È lo stesso identico risultato che otterresti cliccando manualmente nella console AWS, ma scritto, versionato, e riproducibile con terraform apply.

Cosa abbiamo imparato

Il modello di responsabilità condivisa, regioni e Availability Zone, come proteggere l'account con IAM, configurare la CLI, costruire una VPC con subnet pubbliche e private, lanciare istanze EC2, salvare dati su S3, gestire database con RDS, distribuire il carico con Load Balancer e Auto Scaling, risolvere domini con Route 53, distribuire contenuti con CloudFront, eseguire codice serverless con Lambda, orchestrare container con ECS/EKS e monitorare tutto con CloudWatch. Con questo modulo il corso è completo dal primo git init fino a un'infrastruttura cloud reale, sicura e riproducibile.