Bästa metoder för återanvändning av AWS Lambda Container

Optimera varma startar när du ansluter AWS Lambda till andra tjänster

AWS Lambda ger hög skalbarhet på grund av att den är serverlös och statslös, vilket gör att många kopior av lambda-funktionen kan spawnas omedelbart (som beskrivs här). Men när du skriver applikationskod kommer du sannolikt att ha tillgång till viss information. Detta innebär att du ansluter till en datastore som en RDS-instans eller S3. Att ansluta till andra tjänster från AWS Lambda lägger dock tid till din funktionskod. Det kan också finnas biverkningar från hög skalbarhet, som att uppnå det maximala antalet tillåtna anslutningar till en RDS-instans. Ett alternativ för att motverka detta är att använda behållareåteranvändning i AWS Lambda för att fortsätta anslutningen och minska lambdas driftstid.

Det finns några användbara diagram här för att förklara livscykeln för en lambda-begäran.

Följande inträffar under en kallstart, när din funktion åberopas för första gången eller efter en period av inaktivitet:

  • Koden och beroenden laddas ner.
  • En ny behållare startas.
  • Runtiden startas upp.

Den sista åtgärden är att starta din kod, vilket händer varje gång lambda-funktionen åberopas. Om behållaren återanvänds för en efterföljande åkallande av lambda-funktionen kan vi hoppa framåt för att starta koden. Detta kallas en varm start, och detta är det steg vi kan optimera när vi ansluter till andra tjänster genom att definiera anslutningen utanför omfattningen av hanteringsmetoden.

Ansluter till andra AWS-tjänster från Lambda

Exempel: Anslut till RDS-instans, AWS-ikoner härifrån

Vi har ett grundläggande och vanligt exempel att genomföra - vi vill ansluta till en behållarresurs för att hämta anrikningsdata. I det här exemplet kommer en JSON nyttolast med ett ID och Lambda-funktionen ansluts till en RDS-instans som kör PostgreSQL för att hitta motsvarande namn på ID så att vi kan returnera den berikade nyttolasten. Eftersom lambda-funktionen ansluter till RDS, som lever i en VPC, måste lambda-funktionen nu också leva i ett privat subnät. Detta lägger till ett par steg till den kalla starten - ett VPC elastiskt nätverksgränssnitt (ENI) måste bifogas (som nämnts i Jeremy Dalys blogg, detta ger tid till dina kallstarter).

Obs! Vi kan undvika att använda en VPC om vi skulle använda en nyckel / värde-lagring med DynamoDB istället för RDS.

Jag kommer att gå igenom två lösningar på den här uppgiften, den första är min 'naiva' lösning, medan den andra lösningen optimerar för varma starttider genom att återanvända anslutningen för efterföljande åkallelser. Sedan jämför vi resultaten för varje lösning.

Alternativ 1 - Anslut till RDS inom hanteraren

Detta kodexempel visar hur jag naivt kan närma sig denna uppgift - databasanslutningen är inom hanteringsmetoden. Det finns en enkel utvalda fråga för att hämta namnet på ID-en innan du returnerar nyttolasten, som nu innehåller namnet.

Låt oss se hur det här alternativet presterar under ett litet test med ett utbrott på 2000 invokationer med en samtidighet av 20. Minsta varaktighet är 18 ms med ett genomsnitt på 51 ms och drygt 1 sekund (kallstartvaraktighet).

Lambda Varaktighet

Grafen nedan visar att det finns ett maximalt antal åtta anslutningar till databasen.

Antal anslutningar till RDS-databas i ett 5-minuters fönster.

Alternativ 2 - Använd en global anslutning

Det andra alternativet är att definiera anslutningen som en global utanför hanteringsmetoden. Sedan inuti hanteraren lägger vi till en kontroll för att se om anslutningen finns, och ansluter endast om den inte gör det. Detta innebär att anslutningen endast görs en gång per behållare. Att ställa in anslutningen på detta sätt med villkoren på plats betyder att vi inte behöver ansluta om det inte krävs av kodlogiken.

Vi stänger inte längre anslutningen till databasen, så anslutningen kvarstår för en efterföljande kallelse av funktionen. Återanvändning av anslutningen minskar de varma startvaraktigheterna väsentligt - den genomsnittliga varaktigheten är ungefär 3 gånger snabbare och minsta är 1 ms snarare än 18 ms.

Lambda Varaktigheter

Att ansluta till en RDS-instans är en tidskrävande uppgift, och att inte behöva ansluta för varje anrop är fördelaktigt för prestanda. När vi ansluter till databasen för en enkel databasfråga uppnår vi ett maximalt databasanslutningsantal på 20, vilket matchar nivån av samtidighet (vi gjorde 20 samtidiga invokationer x 100 gånger). När utbrottet av invokationer stannar stängs anslutningarna gradvis.

Nu när AWS har ökat tillskottet till lambdas varaktighet till 15 minuter betyder det att databasanslutningar kan hålla längre och du kan vara i fara att nå RDS max-anslutningsnumret. Standardmax-anslutningarna kan skrivas över i RDS-parametergruppens inställningar, även om ökning av det maximala antalet anslutningar kan leda till problem med minnesallokering. Mindre instanser kan ha ett standard max_connections-värde på mindre än 100. Tänk på dessa gränser och lägg bara till applikationslogik för att ansluta till databasen vid behov.

Använda en global anslutning för andra uppgifter

Lambda Ansluter till S3

En vanlig uppgift som vi kanske måste utföra med Lambda är att få tillgång till tillståndsdata från S3. Kodavsnittet nedan är en AWS tillhandahållen Python Lambda Function-ritning - som du kan navigera till genom att logga in på AWS-konsolen och klicka här. Du kan se i koden att S3-klienten är helt definierad utanför hanteraren när behållaren initialiseras, medan för RDS-exemplet den globala anslutningen sattes in i hanteraren. Båda metoderna kommer att ställa in de globala variablerna så att de kan vara tillgängliga för efterföljande invokationer.

s3-get-object lambda blueprint-kodavsnitt https://console.aws.amazon.com/lambda/home?region=us-east-1#/create/new?bp=s3-get-object-python

Dekryptera miljövariabler

Lambda-konsolen ger dig möjligheten att kryptera dina miljövariabler för ytterligare säkerhet. Följande kodavsnitt är ett AWS-tillhandahållet Java-exempel på ett hjälpskript för att dekryptera miljövariabler från en Lambda-funktion. Du kan navigera till kodavsnittet genom att följa denna handledning (specifikt steg 6). Eftersom DECRYPTED_KEY definieras som en global klass, kallas funktionen och logiken decryptKey () endast en gång per lambda-behållare. Därför kommer vi att se en betydande förbättring av varma startlängder.

https://console.aws.amazon.com/lambda/home?region=us-east-1#/functions och https://docs.aws.amazon.com/lambda/latest/dg/tutorial-env_console.html

Använda globala variabler i andra FaaS-lösningar

Denna strategi är inte isolerad mot AWS Lambda. Metoden för att använda en global anslutning kan också tillämpas på andra molnleverantörers serverfria funktioner. Google Cloud Functions tips och tricksida ger en bra förklaring för icke-lata variabler (när variabeln alltid initieras utanför hanteringsmetoden) kontra lata variabler (den globala variabeln ställs endast in vid behov) globala variabler.

Andra bästa metoder

Här är några andra bästa metoder att tänka på.

Testning

Att använda FaaS underlättar att ha en mikroservicearkitektur. Och att ha små, diskreta funktionalitetsdelar går hand i hand med effektiv enhetstestning. För att hjälpa dina enhetstester:

  • Kom ihåg att utesluta testberoenden från lambda-paketet.
  • Separera logiken från hanteringsmetoden, som du skulle göra med en huvudmetod i ett program.

Beroende och paketstorlek

Att minska storleken på installationspaketet innebär att nedladdningen av koden går snabbare vid initieringen och därför förbättrar dina kalla starttider. Ta bort oanvända bibliotek och dödkod för att minska ZIP-filstorleken. AWS SDK tillhandahålls för Python- och JavaScript-driftstider så det finns inget behov att inkludera dem i ditt distributionspaket.

Om Node.js är din föredragna Lambda-runtime, kan du tillämpa minifiering och förgrening för att minska storleken på din funktionskod och minimera storleken på ditt distributionspaket. Vissa men inte alla aspekter av minifiering och förgasning kan tillämpas på andra driftstider, t.ex. du kan inte ta bort blanksteg från pythonkoden men du kan ta bort kommentarer och förkorta variabla namn.

Ställa in minnet

Experiment för att hitta den optimala mängden minne för Lambdafunktionen. Du betalar för minnesallokering, så att fördubbla minnet innebär att du måste betala dubbelt per millisekund; men beräknad kapacitet ökar med tilldelat minne så att det potentiellt kan minska körtiden till mindre än hälften av vad det var. Det finns redan några användbara verktyg för att välja den optimala minnesinställningen för dig som den här.

Avslutar ...

En sak att tänka på är om det är nödvändigt att använda anslutningsanvändningsmetoden. Om din lambda-funktion endast åberopas sällan, till exempel en gång om dagen, kommer du inte att dra nytta av att optimera för varma startar. Det är ofta en avvägning att göra mellan att optimera för prestanda kontra läsbarhet för din kod - termen "förglasning" talar för sig själv! Att lägga till globala variabler till din kod för att återanvända anslutningar till andra tjänster kan dessutom göra din kod svårare att spåra. Två frågor kommer att tänka på:

  • Kommer en ny teammedlem att förstå din kod?
  • Kommer du och ditt team att kunna felsöka koden i framtiden?

Men chansen är stor att du har valt Lambda för sin skala och vill ha höga prestanda och låga kostnader, så hitta balansen som passar ditt team.

Dessa åsikter är författarens. Såvida inget annat anges i detta inlägg är Capital One inte anslutet till, och det stöds inte heller av något av de nämnda företagen. Alla varumärken och annan immateriell egendom som används eller visas är deras respektive ägare. Den här artikeln är © 2019 Capital One.