Cos'è AWS Lambda e come funziona
Cos'è AWS Lambda e come funziona: modello a eventi, trigger, handler, memoria e timeout, layer, concorrenza e quando invece conviene un server normale.
Hai un pezzo di codice che deve girare quando succede qualcosa: arriva una richiesta HTTP, qualcuno carica un file, scatta l'una di notte. Tenere acceso un server intero per quello sembra sproporzionato, e in effetti lo è. In questo articolo trovi come funziona AWS Lambda — modello di esecuzione, trigger, struttura della funzione, e il dettaglio sulla memoria che fa spendere meno a chi lo conosce.
Cos'è
AWS Lambda è un servizio che esegue una tua funzione in risposta a un evento: tu carichi il codice, Amazon lo avvia quando serve, lo fa rispondere e lo spegne — e ti fattura solo il tempo in cui è stato in esecuzione.
Non esiste una macchina da raggiungere, un sistema operativo da aggiornare, un processo da tenere acceso: esiste una funzione e un elenco di cose che la fanno partire. È l'implementazione più conosciuta dell'idea descritta in cosa sono le serverless functions, dentro l'ecosistema raccontato in cos'è AWS.
Il confronto con Cloudflare Workers non lo rifaccio qui: cold start, prezzi e differenze di runtime sono già in Cloudflare Workers o AWS Lambda. Questo articolo è il "come si usa".
Il modello di esecuzione
Tutto Lambda sta in tre parole: evento, funzione, risposta. Qualcosa produce un evento; AWS lo trasforma in un oggetto JSON e chiama la tua funzione passandoglielo come primo argomento; la funzione restituisce un valore e l'invocazione finisce.
Da questo discendono due conseguenze, ed è da lì che nasce la maggior parte degli errori di chi arriva da un backend tradizionale.
Non c'è stato tra un'invocazione e l'altra. Niente carrello in memoria, contatore in una variabile globale, sessione in un oggetto: la chiamata successiva potrebbe essere servita da un'altra istanza che di quella variabile non sa nulla.
Le istanze sono però riutilizzate. AWS non butta via l'ambiente dopo ogni chiamata: lo tiene caldo e ci fa passare le richieste successive. Il codice fuori dall'handler viene eseguito una volta per istanza, quello dentro a ogni invocazione. È lì che si mettono le connessioni al database e i client SDK: inizializzarli fuori significa pagarli una volta invece che sempre.
La struttura di una funzione
Una Lambda in Node è questo, e non molto di più:
// index.mjs
import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";
// FUORI dall'handler: eseguito una volta per istanza, riusato
const s3 = new S3Client({ region: "eu-south-1" });
export const handler = async (event, context) => {
const nome = event.queryStringParameters?.nome ?? "mondo";
console.log("richiesta ricevuta", { requestId: context.awsRequestId });
return {
statusCode: 200,
headers: { "content-type": "application/json" },
body: JSON.stringify({ messaggio: `ciao ${nome}` }),
};
};
I pezzi sono tre:
handlerè il punto di ingresso. Il nome non è magico: lo dichiari nella configurazione comefile.funzione(quiindex.handler).eventè l'evento, e la sua forma dipende da chi ha invocato la funzione. Una richiesta da API Gateway haqueryStringParametersebody; un evento di S3 ha un arrayRecords. Non esiste "l'oggetto event": ne esiste uno per ogni sorgente, ed è la prima cosa da guardare nella documentazione del trigger.contextcontiene i metadati: identificativo della richiesta, tempo rimasto prima del timeout, memoria assegnata.
In Python, Go o Ruby cambia la sintassi e non il concetto: una funzione che riceve l'evento e il contesto e restituisce un valore.
Il formato della risposta non è libero: se la funzione sta dietro un gateway HTTP deve restituire statusCode e body come sopra, altrimenti il chiamante riceve un errore 502 che sembra inspiegabile.
I trigger: cosa fa partire una funzione
È la parte che rende Lambda diversa da "un modo di ospitare una API". Le sorgenti di evento sono decine; quelle che incontrerai davvero sono quattro.
API Gateway (o un Function URL). Una richiesta HTTP diventa un'invocazione: costruisci una REST API dove ogni rotta è una funzione, oppure una funzione sola che smista le rotte. Il Function URL è la scorciatoia: un endpoint HTTPS attaccato direttamente alla funzione.
S3. Un file viene caricato e parte la funzione: miniature, estrazione di testo da un PDF, validazione di un caricamento, conversioni. Il dettaglio dello storage è in cos'è S3.
Code e stream (SQS, Kinesis, DynamoDB Streams). Lambda legge dalla coda e invoca la funzione con un lotto di messaggi: l'API accoda e risponde subito, la funzione smaltisce dopo. Attenzione, però: se la funzione fallisce il messaggio viene riprovato, quindi il codice deve reggere di essere eseguito due volte senza combinare danni.
EventBridge Scheduler (cron). Il lavoro pianificato: pulizie notturne, riepiloghi, riconciliazioni. Sostituisce il crontab su una macchina tenuta accesa solo per quello, ed è l'uso di Lambda con il miglior rapporto tra utilità e sforzo.
Memoria, timeout e il trucco che fa costare meno
Le due manopole di configurazione sono la memoria e il timeout.
Il timeout è il tetto oltre il quale l'esecuzione viene interrotta, e il valore predefinito è basso: la prima funzione che chiama tre API esterne in sequenza muore lì, e molti pensano di avere un bug nel codice.
La memoria è più interessante. Su Lambda la CPU non si configura: scala insieme alla memoria che assegni. Raddoppi la memoria e raddoppi la potenza di calcolo.
La conseguenza è controintuitiva e vale soldi veri. La fattura è proporzionale a durata × memoria: se alzi la memoria paghi di più al millisecondo, ma la funzione ha più CPU e finisce prima. Se il lavoro è legato alla CPU, il tempo cala più di quanto salga il prezzo unitario, e il costo totale scende mentre la funzione diventa più veloce.
| Configurazione | Durata | Costo relativo | Nota |
|---|---|---|---|
| Memoria minima | Lunga | Alto | Il caso peggiore: lenta e cara |
| Memoria media | Molto più breve | Spesso il minimo | Il punto da cercare |
| Memoria alta | Poco più breve | Torna a salire | Oltre il punto di saturazione |
I valori esatti dipendono dal tuo codice: misura la funzione a due o tre configurazioni, e lo strumento ufficiale (AWS Lambda Power Tuning) restituisce il grafico costo/durata.
Attenzione al caso opposto: se la funzione passa il tempo ad aspettare una API esterna, la CPU in più non serve e alzare la memoria fa solo salire il conto.
Variabili d'ambiente, layer, pacchetto
Le variabili d'ambiente si leggono nel modo consueto (process.env.X, os.environ["X"]), ma su Lambda sono visibili nella console a chiunque abbia accesso alla funzione: per le credenziali vere lo standard è tenerle in Secrets Manager o Parameter Store e leggerle fuori dall'handler, così si paga una chiamata per istanza e non per invocazione. Il principio generale è in cosa sono le variabili d'ambiente.
I layer sono archivi di codice condiviso montati accanto alla funzione: evitano di duplicare le stesse dipendenze in dieci funzioni. Non è il caso di innamorarsene — un layer è una dipendenza invisibile nel repository, versionata a parte — e per le dipendenze normali il bundler va benissimo.
Il pacchetto è uno ZIP oppure un'immagine container. Lo ZIP è il percorso standard; l'immagine serve quando il codice è grande, e ti permette di riusare quello che sai già su cos'è un container.
La concorrenza
Una istanza di Lambda serve una richiesta alla volta. Non c'è parallelismo dentro l'istanza: se arrivano cento richieste insieme, AWS avvia cento istanze. È il motivo per cui Lambda scala in modo spettacolare, e anche il motivo per cui può fare danni.
Due impostazioni da conoscere:
- La concorrenza riservata garantisce a una funzione un numero di esecuzioni contemporanee e allo stesso tempo gliene impone il tetto: metterla bassa su una funzione secondaria evita che si prenda tutta la capacità dell'account.
- La concorrenza pre-allocata tiene istanze già pronte, ed è il rimedio al cold start su un endpoint sensibile alla latenza. Si paga anche senza traffico.
Il danno classico è il database: cento istanze che aprono cento connessioni a Postgres lo saturano. È il problema descritto in cos'è Neon, e si risolve con un pooler in mezzo o un tetto di concorrenza sulla funzione.
Cosa fa perdere ore o soldi
Inizializzare il client dentro l'handler. Connessione al database o client SDK creati a ogni invocazione: latenza aggiunta e connessioni sprecate. Vanno fuori.
Lasciare il timeout predefinito. È il primo scoglio di chiunque, e il messaggio d'errore non è esplicito quanto dovrebbe.
Scrivere su disco pensando che resti. La directory temporanea vive quanto l'istanza: ciò che deve persistere va su S3 o su un database.
Dimenticare che le code riprovano. Una funzione che invia una email e poi fallisce a metà manda la stessa email a ogni tentativo.
Non guardare i log. Finiscono in CloudWatch, che ha un suo costo e cresce all'infinito per impostazione predefinita: la scadenza di conservazione va impostata il primo giorno.
Mettere la funzione in una rete privata senza saperlo. Serve per raggiungere un database non esposto, ma per farla uscire verso internet aggiunge un componente in più con un costo fisso non trascurabile.
Gestire tutto a mano dalla console. Funziona per la prima funzione, diventa ingestibile alla quinta. Lo strumento è l'infrastruttura come codice — SAM, CDK o Terraform — con la CI/CD che fa il deploy.
Quando Lambda è la scelta giusta
Conviene se: il carico è irregolare o a picchi, e tenere un server acceso significa pagare aria; il lavoro è reattivo per natura (un file caricato, un messaggio in coda, un orario); sei già dentro AWS e devi collegare pezzi di infrastruttura tra loro.
Conviene un server normale se: il traffico è costante — a carico stabile una macchina a canone fisso costa meno e non ha cold start; ti serve un processo sempre attivo o un WebSocket; il lavoro dura più di qualche minuto; l'applicazione è un monolite che funziona. Le alternative sono un VPS o Railway.
C'è poi il costo che non è in fattura: una architettura di trenta funzioni è più difficile da capire, testare e seguire quando qualcosa va storto di una applicazione sola. Lambda è magnifica usata dove serve e un modo raffinato di complicarsi la vita usata ovunque.
In sintesi
Lambda esegue una funzione in risposta a un evento e ti fattura il tempo di esecuzione. Niente macchina, niente processo acceso, nessuno stato tra una chiamata e l'altra.
La forma dell'evento dipende dal trigger: API Gateway, S3, code e cron producono oggetti diversi.
Il codice fuori dall'handler viene eseguito una volta per istanza. Ci vanno le connessioni e i client: è l'ottimizzazione più semplice e la più trascurata.
La CPU scala con la memoria, quindi alzare la memoria può far spendere meno. Se il lavoro è legato alla CPU, misura prima di accettare la predefinita.
La concorrenza è il moltiplicatore da tenere d'occhio: una istanza per richiesta significa che sotto carico Lambda apre molte connessioni al tuo database.
Per il confronto con l'alternativa al bordo della rete leggi Cloudflare Workers o AWS Lambda; per il contesto generale del modello, cos'è il cloud computing.