Steeds meer teams bouwen hun applicaties op AWS Lambda. Dat is begrijpelijk: AWS regelt de infrastructuur en je developers besteden hun tijd aan functionaliteit. Lambda staat daarnaast bekend als goedkoop. Ik heb uitgerekend en gemeten of dat klopt. Voor veel webapplicaties klopt het niet.
EC2 en Lambda vergeleken: de eenvoudige rekensom
EC2 en Lambda rekenen allebei af op CPU en geheugen. Bij een Lambda-functie stel je alleen het geheugen in. AWS koppelt daar een vaste verhouding aan van 1 vCPU per 1.769 MB, die je niet kunt wijzigen. Die verhouding ligt dicht bij de compute-optimized instances van EC2 (de c-familie), met 1 vCPU per 2 GB geheugen.
Een c6g.large heeft 2 vCPU’s en 4 GB geheugen. Een volledige maand draaien in de regio Ierland (eu-west-1) kost $53,29 on-demand, zonder reserveringen, Savings Plans of Spot.
Voor dezelfde rekenkracht op Lambda zet je het geheugen op 3.538 MB. Stel dat de functie 1 seconde draait en precies 1 request per seconde krijgt. Dan draait er 24/7 één functie, en dat kost $121,60 per maand: 2,29 keer zoveel als de vergelijkbare EC2-capaciteit.
| EC2 c6g.large | AWS Lambda | |
|---|---|---|
| Uitvoering | On-demand, eu-west-1 (Ierland) | Arm, 3.538 MB geheugen (2 vCPU’s) |
| Uitgangspunten | 2 vCPU’s, 4 GiB geheugen Een volledige maand aan Geen reserveringen, Savings Plans of Spot |
1 request per seconde Elke aanroep duurt 1.000 ms Dus 24/7 één actieve functie |
| Kosten per maand | $53,29 | $121,60 (2,29× zo duur) |
Dubbel betalen om geen instances meer te beheren kan een bewuste keuze zijn. Voor veel applicaties is het verschil alleen veel groter dan een factor twee.
Ook een Lambda-functie staat stil
De meeste applicaties halen data op van elders: een rij uit een database, een object uit een S3-bucket of een antwoord van een externe API. Een typisch request doorloopt deze stappen: het request parsen, rechten controleren, een query naar de database sturen, het antwoord verwerken en de response terugsturen naar de client.
Eén stap ontbreekt in dat rijtje: wachten op de database. Die wachttijd duurt meestal tussen de 5 en 1.000 ms. De functie verwerkt in die tijd niets, maar je betaalt wel voor de duur.
Een EC2-instance staat in die tijd niet stil, een Lambda-functie wel. API Gateway start voor elk request een aparte Lambda-aanroep: 1.000 requests per seconde zijn 1.000 aanroepen per seconde. Rekent een aanroep gemiddeld 4 ms en wacht hij 200 ms, dan betaal je 204 ms, en dat 1.000 keer per seconde. De kosten op Lambda stijgen dus lineair met de responstijd van de services waar je op wacht.
Op EC2 gebeurt dat niet. Afhankelijk van de applicatie verwerkt zelfs een kleine instance duizenden requests tegelijk, hoe traag de downstream-service ook antwoordt.
De loadtest op EC2
Om dat te meten, bouwde ik de opstelling hieronder. Een c6g.xlarge stuurt de load naar een c6g.large met een eenvoudige Node.js-applicatie op Express. Die applicatie doet één fetch() naar een externe API en geeft het resultaat terug. De externe API wacht een instelbare tijd voordat hij antwoordt. Die responstijd heb ik gevarieerd van 20 tot 1.000 ms.
Elke test stuurt met de tool hey 50.000 requests over 5.000 verbindingen. Hieronder staan de twee uitersten.
| Meting | API-responstijd 20 ms | API-responstijd 1.000 ms |
|---|---|---|
| Totale duur | 21,40 s | 27,40 s |
| Snelste response | 0,025 s | 1,001 s |
| Gemiddelde response | 1,13 s | 1,92 s |
| Langzaamste response | 20,06 s | 17,43 s |
| Requests per seconde | 2.336 | 1.824 |
50.000 requests over 5.000 gelijktijdige verbindingen, gemeten met hey op een c6g.large
De snelste response werd ongeveer een seconde trager, maar de throughput daalde maar met ongeveer 500 requests per seconde: van 2.336 naar 1.824. Benut je een vloot instances steeds volledig, dan stijgen de kosten per request op EC2 daardoor met ongeveer 28%.
Voor een Lambda-functie met precies dezelfde applicatie zouden de kosten met 607% stijgen.
EC2 is hier zoveel efficiënter omdat de instance alle requests bundelt. Wacht een request op netwerk-I/O, dan zorgt de async-afhandeling van Node.js ervoor dat de CPU intussen andere verbindingen afhandelt. Een Lambda-aanroep krijgt een eigen rekenomgeving voor precies één request en kan dat voordeel niet benutten.
Wanneer Lambda wel en niet past
In productie zie je deze verhoudingen zelden zo zuiver terug. Lambda heeft ingebouwde fault tolerance. Voor dezelfde resilience op EC2 verdeel je instances over 3 availability zones en draai je met minstens 33% overcapaciteit. Daarnaast is de load in productie zelden stabiel en draaien instances vrijwel nooit continu op 100%.
Bij webapplicaties op grote schaal tellen deze effecten wel mee. Lambda is een goede dienst, maar niet voor elke applicatie de goedkoopste keuze. Bij veel requests en trage downstream-services betaal je op Lambda vooral voor wachttijd. Reken dat door voordat je een architectuur kiest. In het Cloud Cost Optimization Assessment rekenen we ook die architectuurkeuzes door.
Hoe SUE cloudkosten verlaagt en laag houdt, lees je op de pagina cloud kostenoptimalisatie en FinOps. De gids cloudkosten verlagen laat zien welke maatregelen het meest opleveren.