Cele mai bune practici pentru reutilizarea containerului Lambda AWS

Optimizarea încălzirii începe la conectarea AWS Lambda la alte servicii

AWS Lambda oferă o scalabilitate ridicată datorită faptului că este lipsit de server și apatrid, permițând numeroaselor copii ale funcției lambda să fie reproduse instantaneu (așa cum este descris aici). Cu toate acestea, atunci când scrieți codul aplicației, este posibil să doriți accesul la unele date statutare. Aceasta înseamnă conectarea la o magazie de date, cum ar fi o instanță RDS sau S3. Cu toate acestea, conectarea la alte servicii de la AWS Lambda adaugă timp codului dumneavoastră de funcții. De asemenea, pot exista efecte secundare din scalabilitate ridicată, cum ar fi atingerea numărului maxim de conexiuni permise la o instanță RDS. O opțiune pentru a contracara este utilizarea reutilizării containerului în AWS Lambda pentru a persista conexiunea și a reduce timpul de rulare lambda.

Există câteva diagrame utile aici pentru a explica ciclul de viață al unei cereri lambda.

Următoarele apar în timpul unei porniri la rece, când funcția dvs. este invocată pentru prima dată sau după o perioadă de inactivitate:

  • Codul și dependențele sunt descărcate.
  • Un nou container este pornit.
  • Durata de rulare este blocată.

Ultima acțiune este să porniți codul dvs., ceea ce se întâmplă de fiecare dată când este invocată funcția lambda. Dacă containerul este reutilizat pentru o invocare ulterioară a funcției lambda, putem săriți înainte să porniți codul. Aceasta se numește start cald și acesta este pasul pe care îl putem optimiza atunci când ne conectăm la alte servicii prin definirea conexiunii în afara domeniului de aplicare a metodei handler.

Conectarea la alte servicii AWS de la Lambda

Exemplu: Conectați-vă la instanța RDS, pictogramele AWS provenite de aici

Avem un exemplu de bază și obișnuit de rulat - vrem să ne conectăm la o resursă de container pentru a obține date de îmbogățire. În acest exemplu, o sarcină utilă JSON vine cu un ID și Funcția Lambda se conectează la o instanță RDS care rulează PostgreSQL pentru a găsi numele corespunzător al ID-ului, astfel încât să putem returna sarcina utilă îmbogățită. Deoarece funcția lambda se conectează la RDS, care locuiește într-un VPC, acum funcția lambda trebuie să locuiască și într-o subrețea privată. Acest lucru adaugă câțiva pași la pornirea la rece - trebuie să fie atașată o interfață de rețea elastică VPC (ENI) (după cum se menționează în blogul lui Jeremy Daly, acest lucru adaugă timp pentru pornirea la rece).

Notă: am putea evita utilizarea unui VPC dacă ar fi să folosim un stoc de chei / valori cu DynamoDB în loc de RDS.

Voi trece peste două soluții la această sarcină, prima este soluția mea „naivă”, în timp ce a doua soluție se optimizează pentru perioade calde de pornire prin reutilizarea conexiunii pentru invocări ulterioare. Atunci vom compara performanțele fiecărei soluții.

Opțiunea 1 - Conectați-vă la RDS în Handler

Acest exemplu de cod arată cum aș putea aborda în mod naiv această sarcină - conexiunea bazei de date se încadrează în metoda handler. Există o simplă interogare selectată pentru a prelua numele ID-ului înainte de a returna sarcina utilă, care acum include numele.

Să vedem cum se realizează această opțiune în timpul unui test mic, cu o explozie de 2000 de invocări cu o concurgență de 20. Durata minimă este de 18 ms, cu o medie de 51ms și cu puțin peste 1 secundă (durata de pornire la rece).

Lambda Duration

Graficul de mai jos arată că există un număr maxim de opt conexiuni la baza de date.

Număr de conexiuni la baza de date RDS într-o fereastră de 5 minute.

Opțiunea 2 - Utilizați o conexiune globală

A doua opțiune este de a defini conexiunea ca o globală în afara metodei de manipulare. Apoi, în interiorul operatorului, adăugăm o verificare pentru a vedea dacă există conexiunea și ne conectăm doar dacă nu. Aceasta înseamnă că conexiunea se face o singură dată pe container. Setarea conexiunii în acest mod cu condiționalul în loc înseamnă că nu este necesar să facem o conexiune dacă nu este necesar de logica codului.

Nu mai închidem conexiunea la baza de date, deci conexiunea rămâne pentru o invocare ulterioară a funcției. Reutilizarea conexiunii reduce semnificativ duratele de pornire la cald - durata medie este de aproximativ 3 ori mai rapidă, iar cea minimă este de 1 ms în loc de 18 ms.

Lambda Durations

Conectarea la o instanță RDS este o sarcină care necesită mult timp și faptul că nu trebuie să vă conectați pentru fiecare invocare este benefic pentru performanță. Când ne conectăm la baza de date pentru o simplă interogare a bazei de date, obținem un număr maxim de conexiuni la baza de date de 20, care se potrivește nivelului de concurgență (am făcut 20 de invocări concurente x 100 de ori). Când explozia invocărilor se oprește, conexiunile se închid treptat.

Acum, când AWS a mărit durata lambda la 15 minute, aceasta înseamnă că conexiunile la baza de date ar putea dura mai mult și puteți fi în pericol să atingeți numărul de conexiuni RDS max. Conexiunile maxime implicite pot fi suprascrise în setările grupului de parametri RDS, deși creșterea numărului maxim de conexiuni poate duce la probleme cu alocarea memoriei. Instanțele mai mici pot avea o valoare max_conectări implicită mai mică de 100. Fiți atenți la aceste limite și adăugați doar logica aplicației pentru a vă conecta la baza de date atunci când este nevoie.

Utilizarea unei conexiuni globale pentru alte sarcini

Lambda Conectare la S3

O sarcină comună pe care trebuie să o îndeplinim cu Lambda este să accesăm date statutare din S3. Setul de cod de mai jos este un model AWS furnizat Python Lambda Function - pe care îl puteți naviga conectându-vă la consola AWS și dând clic aici. Puteți vedea în cod că clientul S3 este complet definit în afara operatorului atunci când este inițializat containerul, în timp ce pentru exemplul RDS, conexiunea globală a fost setată în interiorul operatorului. Ambele abordări vor seta variabilele globale, permițându-le să fie disponibile pentru invocări ulterioare.

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

Decriptarea variabilelor de mediu

Consola lambda vă oferă opțiunea de a cripta variabilele de mediu pentru securitate suplimentară. Următorul fragment de cod este un exemplu Java AWS furnizat de un script de ajutor pentru decriptarea variabilelor de mediu dintr-o funcție Lambda. Puteți naviga la fragmentul de cod urmând acest tutorial (în special pasul 6). Deoarece DECRYPTED_KEY este definit ca o clasă globală, funcția și logica decryptKey () sunt numite o singură dată pentru fiecare container lambda. Prin urmare, vom observa o îmbunătățire semnificativă a duratei de pornire caldă.

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

Utilizarea variabilelor globale în alte soluții FaaS

Această abordare nu este izolată de AWS Lambda. Metoda de utilizare a unei conexiuni globale poate fi aplicată și funcțiilor serverless ale altor furnizori de cloud. Pagina de sfaturi și trucuri Google Cloud Functions oferă o explicație bună pentru variabilele care nu sunt leneșe (atunci când variabila este întotdeauna inițializată în afara metodei handler) față de variabile leneșe (variabila globală este setată doar atunci când este nevoie).

Alte bune practici

Iată câteva alte bune practici de care trebuie să ții cont.

Testarea

Utilizarea FaaS facilitează utilizarea unei arhitecturi a microserviciilor. Iar funcționalitățile mici, discrete, merg mână în mână cu testarea eficientă a unității. Pentru a ajuta testele unității tale:

  • Nu uitați să excludeți dependențele de test din pachetul lambda.
  • Separați logica de metoda handler, așa cum ați face cu o metodă principală a unui program.

Dependențe și dimensiunea pachetului

Reducerea dimensiunii pachetului de implementare înseamnă că descărcarea codului va fi mai rapidă la inițializare și, prin urmare, vă va îmbunătăți timpul de pornire la rece. Eliminați bibliotecile neutilizate și codul mort pentru a reduce dimensiunea fișierului ZIP de desfășurare. AWS SDK este furnizat pentru rulările Python și JavaScript, astfel încât nu este necesar să le includeți în pachetul dvs. de implementare.

Dacă Node.js este runtime-ul dvs. Lambda preferat, puteți aplica minarea și uglificarea pentru a reduce dimensiunea codului funcției dvs. și pentru a reduce dimensiunea pachetului de implementare. Unele, dar nu toate aspectele legate de minificare și uglificare pot fi aplicate la alte perioade de rulare, de ex. nu puteți elimina spațiul alb din codul python, dar puteți elimina comentarii și scurta numele variabilelor.

Setarea memoriei

Experiment pentru a găsi cantitatea optimă de memorie pentru funcția Lambda. Plătiți pentru alocarea memoriei, deci dublarea memoriei înseamnă că trebuie să plătiți dublu pe milisecundă; dar calculează creșterea capacității cu memoria alocată, astfel încât poate reduce timpul de rulare la mai puțin de jumătate din ceea ce a fost. Există deja câteva instrumente utile pentru a selecta setarea optimă de memorie pentru dvs., cum ar fi aceasta.

A concluziona…

Un lucru de luat în considerare este dacă este necesară aplicarea metodei de reutilizare a conexiunii. Dacă funcția dvs. lambda este invocată rar, cum ar fi o dată pe zi, atunci nu veți beneficia de optimizare pentru pornirile calde. Există adesea un compromis între optimizarea performanței și lizibilitatea codului dvs. - termenul „uglificare” vorbește de la sine! În plus, adăugarea de variabile globale la codul dvs. pentru a reutiliza conexiunile la alte servicii poate face codul dvs. mai dificil de urmărit. Două întrebări îmi vin în minte:

  • Un nou membru al echipei va înțelege codul dvs.?
  • Voi și echipa dvs. veți putea să depanați codul în viitor?

Însă șansele sunt că ai ales Lambda pentru scala sa și dorești performanțe ridicate și costuri reduse, deci găsește echilibrul care se potrivește nevoilor echipei tale.

Aceste opinii sunt cele ale autorului. Dacă nu se menționează altfel în acest post, Capital One nu este afiliat și nici nu este avizat de niciuna dintre companiile menționate. Toate mărcile comerciale și alte proprietăți intelectuale utilizate sau afișate sunt proprietatea proprietarilor respectivi. Acest articol este © 2019 Capital One.