Beste fremgangsmåter for gjenbruk av AWS Lambda Container

Optimalisering av varme starter når du kobler AWS Lambda til andre tjenester

AWS Lambda gir høy skalerbarhet på grunn av å være serverløs og statsløs, noe som gjør at mange kopier av lambda-funksjonen kan gyte øyeblikkelig (som beskrevet her). Imidlertid, når du skriver applikasjonskode, vil du sannsynligvis ha tilgang til noen tilstrekkelige data. Dette betyr å koble til en datastore som en RDS-forekomst eller S3. Koble til andre tjenester fra AWS Lambda gir imidlertid tid til funksjonskoden din. Det kan også være bivirkninger fra høy skalerbarhet, for eksempel å nå det maksimale antall tillatte tilkoblinger til en RDS-forekomst. Et alternativ for å motvirke dette er å bruke gjenbruk av containere i AWS Lambda for å vedvare forbindelsen og redusere lambda-driftstiden.

Det er noen nyttige diagrammer her for å forklare livssyklusen til en lambda-forespørsel.

Følgende oppstår under en kald start, når funksjonen blir påkalt for første gang eller etter en periode med inaktivitet:

  • Koden og avhengighetene lastes ned.
  • En ny container startes.
  • Kjøretiden startes opp.

Den siste handlingen er å starte koden din, som skjer hver gang lambda-funksjonen påberopes. Hvis beholderen gjenbrukes for en påfølgende påkallelse av lambda-funksjonen, kan vi hoppe videre til å starte koden. Dette kalles en varm start, og dette er trinnet vi kan optimalisere når vi kobler til andre tjenester ved å definere forbindelsen utenfor omfanget av behandlermetoden.

Koble til andre AWS-tjenester fra Lambda

Eksempel: Koble til RDS-forekomst, AWS-ikoner hentet herfra

Vi har et grunnleggende og vanlig eksempel å løpe gjennom - vi ønsker å koble til en beholderressurs for å hente anrikningsdata. I dette eksemplet kommer en JSON nyttelast med en ID, og ​​Lambda-funksjonen kobles til en RDS-forekomst som kjører PostgreSQL for å finne det tilsvarende navnet på IDen, slik at vi kan returnere den berikede nyttelasten. Fordi lambda-funksjonen kobles til RDS, som bor i en VPC, trenger lambda-funksjonen nå å leve i et privat undernett også. Dette legger til et par trinn til den kalde starten - et VPC elastisk nettverksgrensesnitt (ENI) må festes (som nevnt i Jeremy Dalys blogg, dette gir tid til kaldstart).

Merk: vi kunne unngå å bruke en VPC hvis vi skulle bruke en nøkkel / verdi lagring med DynamoDB i stedet for RDS.

Jeg vil gå over to løsninger på denne oppgaven, den første er min ‘naive’ løsning, mens den andre løsningen optimaliserer for varme starttider ved å bruke forbindelsen til påfølgende påkallinger. Så sammenligner vi ytelsen til hver løsning.

Alternativ 1 - Koble til RDS i håndtereren

Dette kodeeksemplet viser hvordan jeg naivt kan tilnærme meg denne oppgaven - databaseforbindelsen er innenfor behandlingsmetoden. Det er et enkelt valg for å hente navnet på ID-en før du returnerer nyttelasten, som nå inkluderer navnet.

La oss se hvordan dette alternativet presterer i løpet av en liten test med et eksplosjon på 2000 påkallinger med en samtidighet av 20. Minste varighet er 18 ms med et gjennomsnitt på 51 ms og litt over 1 sekund (kald startvarighet).

Lambda Varighet

Grafen nedenfor viser at det er et maksimalt antall åtte forbindelser til databasen.

Antall tilkoblinger til RDS-database i et 5-minutters vindu.

Alternativ 2 - Bruk en global tilkobling

Det andre alternativet er å definere forbindelsen som en global utenfor behandlingsmetoden. Så inne i behandleren legger vi til en sjekk for å se om forbindelsen eksisterer, og bare koble til hvis den ikke gjør det. Dette betyr at tilkoblingen bare blir opprettet en gang per container. Å stille tilkoblingen på denne måten med den betingede på plass betyr at vi ikke trenger å opprette en tilkobling hvis ikke det kreves av kodelogikken.

Vi lukker ikke lenger forbindelsen til databasen, så forbindelsen gjenstår for en påfølgende påkallelse av funksjonen. Gjenbruk av tilkoblingen reduserer varm startvarighet betydelig - gjennomsnittlig varighet er omtrent tre ganger raskere og minimum er 1 ms i stedet for 18 ms.

Lambda Varigheter

Å koble til en RDS-forekomst er en tidkrevende oppgave, og det å ikke koble seg til hver påkallelse er gunstig for ytelsen. Når vi kobler til databasen for en enkel databaseforespørsel, oppnår vi et maksimalt antall databaseforbindelser på 20, som samsvarer med nivået av samtidighet (vi foretok 20 samtidige invokasjoner x 100 ganger). Når utbruddet av påkallinger stopper, lukkes forbindelsene gradvis.

Nå som AWS har økt lambda-varigheten til 15 minutter, betyr dette at databaseforbindelser kan vare lenger, og du kan være i fare for å nå RDS maks-tilkoblingsnummeret. Standardmaks-tilkoblinger kan overskrives i RDS-parametergruppens innstillinger, selv om å øke det maksimale antallet tilkoblinger kan føre til problemer med minnetildeling. Mindre forekomster kan ha en standard max_connections-verdi under 100. Vær oppmerksom på disse grensene, og legg bare til applikasjonslogikk for å koble til databasen når det er nødvendig.

Bruke en global tilkobling for andre oppgaver

Lambda Connecting to S3

En vanlig oppgave vi kanskje må utføre med Lambda er å få tilgang til tilstrekkelige data fra S3. Kodebiten nedenfor er en AWS gitt Python Lambda Function-blåkopi - som du kan navigere til ved å logge deg på AWS-konsollen og klikke her. Du kan se i koden at S3-klienten er fullstendig definert utenfor behandleren når beholderen initialiseres, mens for RDS-eksempelet var den globale forbindelsen satt inne i behandleren. Begge tilnærminger vil angi de globale variablene slik at de kan være tilgjengelige for påfølgende påkallinger.

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

Dekryptering av miljøvariabler

Lambda-konsollen gir deg muligheten til å kryptere miljøvariablene for ekstra sikkerhet. Følgende kodebit er et AWS gitt Java-eksempel på et hjelpeskript for å dekryptere miljøvariabler fra en Lambda-funksjon. Du kan navigere til kodebiten ved å følge denne opplæringen (nærmere bestemt trinn 6). Fordi DECRYPTED_KEY er definert som en klasse global, kalles decryptKey () -funksjonen og logikken bare én gang per lambda-container. Derfor vil vi se en betydelig forbedring i varm startvarighet.

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

Bruke globale variabler i andre FaaS-løsninger

Denne tilnærmingen er ikke isolert til AWS Lambda. Metoden for å bruke en global tilkobling kan også brukes på andre skylverandørers serverløse funksjoner. Tips og triks-siden for Google Cloud Functions gir en god forklaring på ikke-late variabler (når variabelen alltid initialiseres utenom behandlingsmetoden) versus late variabler (den globale variabelen er bare angitt når det er nødvendig) globale variabler.

Andre beste fremgangsmåter

Her er noen andre gode fremgangsmåter å huske på.

testing

Å bruke FaaS letter det å ha en mikroservicearkitektur. Og å ha små, diskrete funktionaliteter går hånd i hånd med effektiv enhetstesting. For å hjelpe dine enhetstester:

  • Husk å ekskludere testavhengigheter fra lambda-pakken.
  • Skill logikken vekk fra behandlingsmetoden, som du ville gjort med en hovedmetode i et program.

Avhengigheter og pakkestørrelse

Å redusere størrelsen på distribusjonspakken betyr at nedlasting av koden vil være raskere ved initialisering og derfor vil forbedre kaldtidspunktene dine. Fjern ubrukte biblioteker og dødkode for å redusere ZIP-filstørrelsen. AWS SDK leveres for Python- og JavaScript-driftstider, så det er ikke nødvendig å inkludere dem i distribusjonspakken.

Hvis Node.js er din foretrukne Lambda-runtime, kan du bruke minifisering og forglassing for å redusere størrelsen på funksjonskoden din og minimere størrelsen på distribusjonspakken. Noen men ikke alle aspekter ved minifisering og uglifisering kan brukes på andre driftstider, f.eks. Du kan ikke fjerne mellomrom fra python-koden, men du kan fjerne kommentarer og forkorte variabelnavn.

Stille inn minnet

Eksperimenter for å finne den optimale mengden minne for Lambdafunksjonen. Du betaler for minnetildeling, så å doble minnet betyr at du må betale dobbelt per millisekund; men beregningskapasiteten øker med tildelt minne, slik at den potensielt kan redusere kjøretiden til under halvparten av hva den var. Det er allerede noen nyttige verktøy for å velge den optimale minneinnstillingen for deg som denne.

Å konkludere…

Én ting å vurdere er om det er nødvendig å bruke tilkoblingsgjenbruksmetoden. Hvis lambda-funksjonen bare påberopes sjelden, for eksempel en gang om dagen, vil du ikke dra nytte av å optimalisere for varme starter. Det er ofte en avveining å gjøre mellom å optimalisere for ytelse versus lesbarhet av koden din - uttrykket “uglification” taler for seg selv! I tillegg kan det å legge globale variabler til koden din for å gjenbruke tilkoblinger til andre tjenester potensielt gjøre koden din vanskeligere å spore. To spørsmål kommer opp i tankene:

  • Vil et nytt teammedlem forstå koden din?
  • Vil du og teamet ditt kunne feilsøke koden i fremtiden?

Men sjansen er stor for at du har valgt Lambda for sin skala og vil ha høy ytelse og lave kostnader, så finn balansen som passer teamets behov.

Disse meningene er forfatterens. Med mindre annet er nevnt i dette innlegget, er Capital One ikke tilknyttet, og heller ikke godkjent av noen av selskapene som er nevnt. Alle varemerker og annen intellektuell eiendom som brukes eller vises er eierskapet til deres respektive eiere. Denne artikkelen er © 2019 Capital One.