Best Practices voor AWS Lambda Container Hergebruik

Het optimaliseren van warme starts bij het verbinden van AWS Lambda met andere services

AWS Lambda biedt een hoge schaalbaarheid omdat het serverloos en stateloos is, waardoor veel exemplaren van de lambda-functie direct kunnen worden uitgezet (zoals hier beschreven). Wanneer u echter toepassingscode schrijft, wilt u waarschijnlijk toegang tot enkele stateful gegevens. Dit betekent verbinding maken met een gegevensopslag, zoals een RDS-exemplaar of S3. Verbinding maken met andere services van AWS Lambda voegt echter tijd toe aan uw functiecode. Er kunnen ook bijwerkingen optreden door hoge schaalbaarheid, zoals het bereiken van het maximale aantal toegestane verbindingen met een RDS-exemplaar. Een optie om dit tegen te gaan, is het hergebruik van de container in AWS Lambda om de verbinding te behouden en de looptijd van de lambda te verkorten.

Er zijn enkele nuttige diagrammen hier om de levenscyclus van een lambda-aanvraag uit te leggen.

Het volgende gebeurt tijdens een koude start, wanneer uw functie voor de eerste keer of na een periode van inactiviteit wordt geactiveerd:

  • De code en afhankelijkheden worden gedownload.
  • Er wordt een nieuwe container gestart.
  • De looptijd is bootstrapped.

De laatste actie is om uw code te starten, wat gebeurt telkens wanneer de lambda-functie wordt aangeroepen. Als de container opnieuw wordt gebruikt voor een volgende aanroep van de lambdafunctie, kunnen we doorgaan met het starten van de code. Dit wordt een warme start genoemd en dit is de stap die we kunnen optimaliseren bij het verbinden met andere services door de verbinding buiten het bereik van de handlermethode te definiëren.

Verbinding maken met andere AWS-services van Lambda

Voorbeeld: Verbinding maken met RDS-instantie, AWS-pictogrammen hier vandaan komen

We hebben een eenvoudig en algemeen voorbeeld om door te nemen - we willen verbinding maken met een containerresource om verrijkingsgegevens op te halen. In dit voorbeeld wordt een JSON-payload geleverd met een ID en maakt de Lambda-functie verbinding met een RDS-exemplaar met PostgreSQL om de overeenkomstige naam van de ID te vinden, zodat we de verrijkte payload kunnen retourneren. Omdat de lambda-functie verbinding maakt met RDS, die in een VPC leeft, moet de lambda-functie nu ook in een privé-subnet leven. Dit voegt een paar stappen toe aan de koude start - een VPC elastische netwerkinterface (ENI) moet worden bevestigd (zoals vermeld in de blog van Jeremy Daly, dit voegt tijd toe aan uw koude start).

Opmerking: we zouden het gebruik van een VPC kunnen vermijden als we een sleutel / waarde-opslag zouden gebruiken met DynamoDB in plaats van RDS.

Ik zal twee oplossingen voor deze taak bespreken, de eerste is mijn ‘naïeve’ oplossing, terwijl de tweede oplossing optimaliseert voor warme starttijden door de verbinding opnieuw te gebruiken voor volgende aanroepen. Vervolgens vergelijken we de prestaties van elke oplossing.

Optie 1 - Maak verbinding met RDS in de handler

Dit codevoorbeeld laat zien hoe ik deze taak naïef zou kunnen benaderen - de databaseverbinding bevindt zich binnen de handlermethode. Er is een eenvoudige selectiequery om de naam van de ID op te halen voordat de payload wordt geretourneerd, die nu de naam bevat.

Laten we eens kijken hoe deze optie presteert tijdens een kleine test met een burst van 2000 aanroepen met een gelijktijdigheid van 20. De minimale duur is 18 ms met een gemiddelde van 51 ms en iets meer dan 1 seconde maximaal (de koude startduur).

Lambdaduur

De onderstaande grafiek laat zien dat er maximaal acht verbindingen met de database zijn.

Aantal verbindingen met RDS-database in een venster van 5 minuten.

Optie 2 - Gebruik een globale verbinding

De tweede optie is om de verbinding te definiëren als een globale buiten de handlermethode. Vervolgens voegen we in de handler een vinkje toe om te zien of de verbinding bestaat en maken we alleen verbinding als dat niet het geval is. Dit betekent dat de verbinding slechts eenmaal per container wordt gemaakt. Het op deze manier instellen van de verbinding met de conditionele op zijn plaats betekent dat we geen verbinding hoeven te maken als dit niet vereist is door de codelogica.

We sluiten de verbinding met de database niet meer, dus de verbinding blijft bestaan ​​voor een volgende aanroep van de functie. Hergebruik van de verbinding vermindert de duur van de warme start aanzienlijk - de gemiddelde duur is ongeveer 3 keer sneller en het minimum is 1 ms in plaats van 18 ms.

Lambda-duur

Verbinding maken met een RDS-exemplaar is een tijdrovende taak en het niet nodig zijn om voor elke aanroep verbinding te maken, is goed voor de prestaties. Wanneer we verbinding maken met de database voor een eenvoudige databasequery, bereiken we een maximale databaseverbinding van 20, wat overeenkomt met het niveau van gelijktijdigheid (we hebben 20 gelijktijdige aanroepen x 100 keer gedaan). Wanneer de burst van invocaties stopt, worden de verbindingen geleidelijk gesloten.

Nu AWS de toegestane lambdaduur heeft verhoogd tot 15 minuten, betekent dit dat databaseverbindingen langer kunnen duren en u mogelijk het RDS max-verbindingsnummer kunt bereiken. De standaard max. Verbindingen kunnen worden overschreven in de RDS-parametergroepinstellingen, hoewel het verhogen van het maximale aantal verbindingen kan leiden tot problemen met geheugentoewijzing. Kleinere exemplaren kunnen een standaard max_connections-waarde van minder dan 100 hebben. Houd rekening met deze limieten en voeg alleen applicatielogica toe om verbinding te maken met de database wanneer dat nodig is.

Een globale verbinding gebruiken voor andere taken

Lambda aansluiten op S3

Een veel voorkomende taak die we mogelijk moeten uitvoeren met Lambda is toegang krijgen tot stateful gegevens van S3. Het onderstaande codefragment is een door AWS geleverde Python Lambda-functie blauwdruk - waar u naartoe kunt navigeren door u aan te melden bij de AWS-console en hier te klikken. U kunt in de code zien dat de S3-client volledig is gedefinieerd buiten de handler wanneer de container is geïnitialiseerd, terwijl voor het RDS-voorbeeld de globale verbinding binnen de handler is ingesteld. Beide benaderingen stellen de globale variabelen in, zodat ze beschikbaar zijn voor volgende aanroepen.

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

Decoderende omgevingsvariabelen

De lambda-console biedt u de mogelijkheid om uw omgevingsvariabelen te coderen voor extra beveiliging. Het volgende codefragment is een door AWS verstrekt Java-voorbeeld van een helperscript voor het decoderen van omgevingsvariabelen uit een Lambda-functie. U kunt naar het codefragment navigeren door deze zelfstudie te volgen (specifiek stap 6). Omdat DECRYPTED_KEY wordt gedefinieerd als een algemene klasse, worden de functie decryptKey () en logica slechts eenmaal per lambda-container aangeroepen. Daarom zullen we een significante verbetering in warme startduren zien.

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

Gebruik van globale variabelen in andere FaaS-oplossingen

Deze aanpak staat niet op zichzelf tegen AWS Lambda. De methode voor het gebruik van een wereldwijde verbinding kan ook worden toegepast op de serverloze functies van andere cloudaanbieders. De pagina met tips en trucs voor Google Cloud Functions geeft een goede uitleg voor niet-luie variabelen (wanneer de variabele altijd buiten de handlermethode wordt geïnitialiseerd) versus luie variabelen (de globale variabele wordt alleen ingesteld als dat nodig is) globale variabelen.

Andere beste praktijken

Hier zijn enkele andere best practices om in gedachten te houden.

testen

Het gebruik van FaaS vergemakkelijkt het hebben van een microservices-architectuur. En het hebben van kleine, discrete functionaliteit gaat hand in hand met effectieve eenheidstests. Ter ondersteuning van uw unit-tests:

  • Vergeet niet om testafhankelijkheden uit te sluiten van het lambda-pakket.
  • Scheid logica van de handlermethode, zoals u zou doen met een hoofdmethode van een programma.

Afhankelijkheden en pakketgrootte

Het verkleinen van het implementatiepakket betekent dat het downloaden van de code sneller zal zijn bij initialisatie en dus uw koude starttijden zal verbeteren. Verwijder ongebruikte bibliotheken en dode code om de ZIP-bestandsgrootte van de implementatie te verminderen. AWS SDK is beschikbaar voor Python en JavaScript-runtimes, dus het is niet nodig om deze in uw implementatiepakket op te nemen.

Als Node.js uw favoriete Lambda-runtime is, kunt u minification en uglification toepassen om de grootte van uw functiecode te verkleinen en de grootte van uw implementatiepakket te minimaliseren. Sommige maar niet alle aspecten van minificatie en uglificatie kunnen worden toegepast op andere looptijden, bijv. u kunt witruimte niet uit de python-code verwijderen, maar u kunt opmerkingen verwijderen en namen van variabelen inkorten.

Geheugen instellen

Experimenteer om de optimale hoeveelheid geheugen voor de Lambda-functie te vinden. U betaalt voor geheugentoewijzing, dus verdubbeling van het geheugen betekent dat u dubbel per milliseconde moet betalen; maar de rekencapaciteit neemt toe met toegewezen geheugen, waardoor de looptijd mogelijk kan worden verkort tot minder dan de helft van wat het was. Er zijn al enkele handige tools om de optimale geheugeninstelling voor u te selecteren, zoals deze.

Concluderen…

Een ding om te overwegen is of het toepassen van de methode voor hergebruik van de verbinding noodzakelijk is. Als uw lambdafunctie slechts zelden wordt gebruikt, bijvoorbeeld eenmaal per dag, profiteert u niet van optimalisatie voor warme starts. Er is vaak een afweging te maken tussen optimalisatie voor prestaties versus leesbaarheid van uw code - de term "uglification" spreekt voor zich! Als u algemene variabelen toevoegt aan uw code om verbindingen met andere services opnieuw te gebruiken, kan uw code mogelijk moeilijker te traceren zijn. Ik denk aan twee vragen:

  • Zal een nieuw teamlid uw code begrijpen?
  • Zullen u en uw team de code in de toekomst kunnen debuggen?

Maar de kans is groot dat u Lambda hebt gekozen voor zijn schaal en hoge prestaties en lage kosten wilt, dus zoek de balans die past bij de behoeften van uw team.

Deze meningen zijn die van de auteur. Tenzij anders vermeld in deze functie, is Capital One niet verbonden aan, noch wordt het goedgekeurd door een van de genoemde bedrijven. Alle handelsmerken en andere intellectuele eigendommen die worden gebruikt of weergegeven zijn eigendom van hun respectieve eigenaars. Dit artikel is © 2019 Capital One.