Ciao a tutti, Jules Martin qui, di nuovo su agntmax.com!
Oggi voglio parlare di qualcosa che mi ha assillato, e probabilmente ha assillato molti di voi, nell’ultimo anno o giù di lì: l’aumento dei costi per l’infrastruttura cloud, in particolare per quanto riguarda le funzioni serverless. Siamo stati tutti conquistati dal sogno del “paga solo per ciò che usi”, e per molto tempo, sembrava una realtà. Ma ultimamente, ho visto le fatture aumentare, a volte inspiegabilmente, anche quando i modelli di traffico sembrano stabili. È come se ci stessero ritagliando piccole spese grazie alla stessa flessibilità che abbiamo abbracciato. Quindi, esploriamo qualcosa di molto specifico e attuale: Domare il Mostro Serverless: Smascherare e Ridurre i Costi Nascosti di AWS Lambda.
Il mio percorso in questo è iniziato circa sei mesi fa. Abbiamo un microservizio centrale che gestisce l’autenticazione degli utenti e la gestione delle sessioni. È costruito quasi interamente su AWS Lambda, API Gateway, DynamoDB e Cognito. Per molto tempo, i costi erano perfettamente prevedibili. Poi, l’estate scorsa, la nostra fattura AWS per quel servizio specifico è aumentata di circa il 15%. Nessuna nuova funzionalità, nessun picco di traffico significativo. Inizialmente, l’ho attribuito a qualche fluttuazione stagionale o a un piccolo bug che non avevo ancora trovato. Ma quando la fattura del mese successivo è arrivata ancora più alta, sapevo che dovevo indagare. Non si trattava solo di un picco; era una tendenza, e ci stava costando soldi veri.
L’Illusione dei Tier “Gratuiti” e la Realtà delle Invocazioni “Minime”
Uno dei principali punti di vendita delle soluzioni serverless, soprattutto per le startup o i team più piccoli, è il generoso tier gratuito. Ed è davvero generoso! Un milione di invocazioni gratuite al mese per Lambda, oltre a una quantità significativa di tempo di calcolo. Il problema è che, man mano che la tua applicazione cresce, quelle invocazioni “gratuite” scompaiono più velocemente di una fetta di pizza a un meetup tecnologico. Ciò che spesso viene trascurato è il volume enorme di invocazioni minute, apparentemente insignificanti, che si sommano. Pensa ai cron job, ai controlli di salute interni o persino ai meccanismi di riprova di altri servizi. Ognuna di queste conta.
La mia indagine sul nostro servizio di autenticazione ha rivelato esattamente questo. Avevamo una funzione Lambda, chiamiamola auth-token-refresher, progettata per aggiornare periodicamente i token di servizio interni. Era programmata per essere eseguita ogni cinque minuti. Sembra innocuo, giusto? 288 invocazioni al giorno. Moltiplicalo per 30 giorni, e ottieni 8.640 invocazioni al mese. Aggiungi i nostri ambienti di sviluppo, staging e produzione, e improvvisamente ci sono oltre 25.000 invocazioni solo per un piccolo compito di manutenzione. Avevamo una dozzina di tali funzioni. Improvvisamente, le nostre invocazioni “minime” non erano più così minime.
Trovare i Colpevoli: Le Metriche di CloudWatch sono il Tuo Migliore Amico
Il primo passo per domare questa bestia è sapere dove stanno andando i tuoi soldi. AWS CloudWatch è indispensabile qui. Non limitarti a guardare il cruscotto di fatturazione a alto livello; esplora le metriche specifiche per le tue funzioni Lambda.
Ecco su cosa mi sono concentrato:
- Invocazioni: Questa è la metrica più diretta. Contatori di invocazioni elevati per funzioni che non gestiscono traffico diretto degli utenti sono bandiere rosse immediate.
- Durata: Quanto tempo dura ogni invocazione? Durate più lunghe significano costi di calcolo più elevati.
- Utilizzo della Memoria: Stai sovradimensionando la memoria per le tue funzioni? Paga per ciò che assegni, non per ciò che utilizzi.
- Frequenza di Errori: Alte frequenze di errori possono portare a riprovi, il che significa più invocazioni e cicli di calcolo sprecati.
Per il nostro auth-token-refresher, ho esaminato la sua metrica `Invocations`. Sicuramente, stava funzionando a meraviglia, ogni cinque minuti. La durata era minima, solo circa 50ms. Ma il volume complessivo stava contribuendo al nostro costo totale delle invocazioni.
Esempio Pratico 1: Consolidare e Pianificare in Modo Intelligente
La soluzione per auth-token-refresher e diverse altre funzioni di manutenzione simili era sorprendentemente semplice: consolidamento. Invece di avere funzioni Lambda individuali attivate da eventi CloudWatch (o EventBridge al giorno d’oggi) con programmazioni separate, ho creato una singola Lambda “Maintenance Runner”.
Questo “Maintenance Runner” è attivato da una singola regola di evento CloudWatch, diciamo, una volta all’ora. All’interno di questo runner, ho un semplice dispatcher che controlla l’ora attuale ed esegue i compiti necessari. Ad esempio:
import os
import datetime
def lambda_handler(event, context):
current_hour = datetime.datetime.now().hour
current_minute = datetime.datetime.now().minute
# Compito 1: Aggiorna il token di autenticazione (eseguiva ogni 5 minuti)
if current_minute % 10 == 0: # Esegui ogni 10 minuti ora
print("Eseguendo aggiornamento token di autenticazione...")
# Chiama la logica reale di aggiornamento del token o un'altra funzione interna
refresh_auth_token()
# Compito 2: Pulisci i log vecchi (eseguiva ogni ora)
if current_hour % 1 == 0 and current_minute == 0: # Esegui all'inizio dell'ora
print("Eseguendo pulizia dei log...")
cleanup_old_logs()
# Compito 3: Controlla lo stato del servizio esterno (eseguiva ogni 30 minuti)
if current_minute == 0 or current_minute == 30:
print("Controllando lo stato del servizio esterno...")
check_external_service()
return {
'statusCode': 200,
'body': 'Compiti di manutenzione eseguiti.'
}
def refresh_auth_token():
# ... logica reale di aggiornamento del token ...
pass
def cleanup_old_logs():
# ... logica reale di pulizia dei log ...
pass
def check_external_service():
# ... logica reale di controllo del servizio esterno ...
pass
Questo semplice cambiamento ha immediatamente ridotto il conteggio delle invocazioni per questi compiti di manutenzione da centinaia di migliaia al mese a poche migliaia. I risparmi sui costi erano tangibili, non solo nelle invocazioni Lambda ma anche nell’associato assorbimento dei log di CloudWatch e nelle chiamate all’API Gateway (se qualcuna di queste era esposta tramite API Gateway).
La Trappola del Sovradimensionamento della Memoria
Questo è un altro fattore di costo sottile che spesso viene trascurato. Quando crei una funzione Lambda, assegni una certa quantità di memoria (ad esempio, 128MB, 256MB, 512MB). Paga per quella memoria assegnata, indipendentemente da quanto la tua funzione utilizzi realmente. Inoltre, la potenza della CPU scala proporzionalmente all’assegnazione di memoria. Quindi, se assegni 1GB di memoria per un semplice script Python che ha bisogno solo di 128MB, non stai solo pagando troppo per la memoria; stai anche pagando troppo per i cicli della CPU di cui non ha bisogno.
L’ho imparato a mie spese con una Lambda di elaborazione dati che era inizialmente configurata con 1GB di memoria “giusto per sicurezza”. Quando ho esaminato le sue metriche CloudWatch per l’utilizzo della memoria, è rimasta costantemente sotto i 200MB, anche durante i carichi di picco. Stavamo fondamentalmente pagando per 800MB di RAM inutilizzata e il corrispondente incremento della CPU.
Esempio Pratico 2: Ottimizzare l’Assegnazione di Memoria con Lambda Power Tuning
Stabilire manualmente l’impostazione ottimale di memoria può essere noioso. Devi distribuire, testare, monitorare, adattare e ripetere. Fortunatamente, esiste un fantastico strumento open-source chiamato AWS Lambda Power Tuning (sviluppato da Alex Casalboni in AWS) che rende questo processo facile.
È un’applicazione serverless che ti aiuta a visualizzare e identificare l’impostazione ottimale della memoria per le tue funzioni Lambda in base ai costi e alle prestazioni. La distribuisci sul tuo account AWS, e poi puoi usarla per testare le tue funzioni.
Ecco come funziona generalmente:
- Distribuisci lo strumento Power Tuning tramite il Serverless Application Repository o SAM.
- Invochi una macchina a stati (creata dallo strumento) con ARN della tua funzione Lambda e un payload.
- La macchina a stati invoca la tua Lambda più volte con varie configurazioni di memoria (ad esempio, 128MB, 256MB, 512MB, 1024MB, ecc.).
- Analizza i log di esecuzione e fornisce una visualizzazione che mostra i compromessi di costo e velocità per ciascuna impostazione di memoria.
Per la mia Lambda di elaborazione dati, eseguendo il Power Tuner è emerso che 256MB era il punto ideale per i costi, con una degradazione delle prestazioni trascurabile rispetto a 1GB. Abbiamo immediatamente abbassato l’assegnazione di memoria a 256MB, risultando in una riduzione del costo di calcolo del 75% per quella funzione specifica. Questo non è stato un evento isolato; da allora ho reso prassi standard eseguire nuove funzioni o quelle rivalutate attraverso questo strumento.
Per usarlo, dopo la distribuzione, normalmente avvieresti la macchina a stati con qualcosa del genere (aggiustando ARN e payload):
aws stepfunctions start-execution \
--state-machine-arn "arn:aws:states:REGION:ACCOUNT_ID:stateMachine:powerTuningStateMachine" \
--input '{ "lambdaARN": "arn:aws:lambda:REGION:ACCOUNT_ID:function:YOUR_FUNCTION_NAME", "num": 100, "payload": {}, "parallel": 5 }'
L’output fornisce un grafico chiaro, mostrando esattamente dove i tuoi costi e la velocità si intersecano per prestazioni ottimali. È un cambiamento significativo per l’ottimizzazione dei costi.
Verbosity dei Log e Avvii a Freddo
Due altre aree che spesso si insinuano sono la verbosity dei log e gli avvii a freddo. I log di CloudWatch non sono gratuiti. Ogni riga che la tua funzione Lambda stampa viene assimilata e memorizzata, e paghi per questo. Anche se un buon logging è cruciale per il debug, un logging eccessivo (ad esempio, stampare oggetti interi o ripetere messaggi di stato inutilmente) può rapidamente gonfiare la tua fattura dei log di CloudWatch.
Ho trovato alcune funzioni che registravano l’intero corpo della richiesta HTTP a ogni invocazione. Anche se utile per lo sviluppo iniziale, in produzione questo era solo rumore e costo. Un rapido aggiustamento per registrare solo i metadati essenziali (ID richiesta, codice di stato, endpoint) ha ridotto drasticamente il nostro assorbimento dei log.
I cold start, pur non essendo un “costo” diretto nello stesso modo, influenzano l’esperienza dell’utente e possono indirettamente portare a più tentativi o a durate di fatturazione più lunghe se la tua funzione deve attendere risorse. Anche se AWS ha fatto progressi significativi nella riduzione dei tempi di cold start, ottimizzare la dimensione del pacchetto della tua funzione ed evitare logiche di inizializzazione complesse al di fuori del gestore possono comunque fare la differenza. Per le funzioni critiche e sensibili alla latenza, la concorrenza fornita è un’opzione, ma fai attenzione, perché paghi per quella concorrenza allocata anche quando è inattiva.
Esempio Pratico 3: Logging Intelligente e Variabili Ambientali
Per il logging, la soluzione più semplice è spesso la migliore. Utilizza le variabili ambientali per controllare i livelli di log. In Python, ad esempio, puoi farlo:
import os
import logging
LOG_LEVEL = os.environ.get('LOG_LEVEL', 'INFO').upper()
logging.basicConfig(level=LOG_LEVEL)
logger = logging.getLogger()
def lambda_handler(event, context):
logger.debug("Questo è un messaggio di debug, visibile solo se LOG_LEVEL è DEBUG")
logger.info("Elaborazione evento: %s", event.get('request_id'))
try:
# ... logica della funzione ...
logger.debug("Elaborazione completata per request_id: %s", event.get('request_id'))
return {
'statusCode': 200,
'body': 'Successo'
}
except Exception as e:
logger.error("Errore nell'elaborazione di request_id %s: %s", event.get('request_id'), str(e), exc_info=True)
return {
'statusCode': 500,
'body': 'Errore'
}
Impostando LOG_LEVEL su INFO in produzione e DEBUG in sviluppo/staging, puoi ridurre significativamente la tua bolletta di CloudWatch Logs senza sacrificare l’osservabilità quando ne hai bisogno.
Un altro trucco è essere consapevoli di ciò che viene inizializzato al di fuori del gestore. Qualsiasi codice direttamente nello scope globale della tua funzione Lambda verrà eseguito durante il cold start. Se hai operazioni costose come il pooling delle connessioni al database o grandi importazioni di librerie, considera di posticiparle fino a quando non sono effettivamente necessarie all’interno del gestore o assicurati che siano efficientemente memorizzate nella cache per le successive invocazioni calde.
Conclusioni Utili per la Tua Crociata contro i Costi Serverless
Bene, abbiamo coperto parecchio. Ecco un riassunto dei passi pratici che puoi intraprendere subito per iniziare a ridurre quei costi Lambda subdoli:
- Monitora incessantemente: Non limitarti a dare un’occhiata alla tua bolletta AWS complessiva. Esplora le metriche di CloudWatch per Invocazioni, Durata e Utilizzo della Memoria per ogni funzione Lambda. Imposta allarmi per picchi imprevisti.
- Consolida i cron job: Se hai molte piccole funzioni Lambda programmate, considera di combinarle in un singolo “Runner di Manutenzione” che distribuisce i compiti in base a un programma meno frequente. Questo riduce drasticamente i conteggi delle invocazioni.
- Ottimizza l’allocazione della memoria: Utilizza strumenti come AWS Lambda Power Tuning per trovare l’impostazione ottimale della memoria per le tue funzioni. Non limitarti a indovinare e sovrallocare. Ricorda, più memoria significa più CPU, e paghi per entrambi.
- Controlla la verbosità del logging: Implementa livelli di log guidati da variabili ambientali (ad esempio,
INFOper la produzione,DEBUGper lo sviluppo). Evita di registrare l’intero corpo della richiesta o stati interni eccessivi in produzione. La tua bolletta di CloudWatch Logs ti ringrazierà. - Rivedi le funzioni non utilizzate: Audita periodicamente le tue funzioni Lambda. Ci sono funzioni vecchie, sperimentali o deprecate ancora attive e che generano costi? Eliminale!
- Tieni d’occhio la dimensione dei pacchetti: Pacchetti di distribuzione più piccoli significano avvii a freddo più rapidi e minori costi di archiviazione. Includi solo le dipendenze necessarie.
- Comprendi il tuo modello di pricing: Rileggi la pagina sui prezzi di Lambda. Comprendi come vengono fatturate le invocazioni, i GB-seconds e il trasferimento dati. La conoscenza è potere, specialmente quando si tratta del tuo portafoglio.
Domare il mostro serverless non significa evitarlo; significa essere intelligenti e intenzionali nell’uso che ne facciamo. La flessibilità e la scalabilità sono inestimabili, ma senza la giusta vigilanza, quei “piccoli” costi possono accumularsi in una parte significativa del tuo budget. Vai avanti, monitora, ottimizza e risparmia!
Questo è tutto per oggi. Fammi sapere nei commenti se hai altri suggerimenti o trucchi per ottimizzare i costi Lambda!
🕒 Published: