Dane oryginału

Original edition copyright © 2019-2020 Neuri Consulting LLP. Title of English-language original: Running Serverless. Introduction to AWS Lambda and the Serverless Application Model by Gojko Adžić, ISBN 978-0-9930881-5-5. Polish-language edition © 2020 by Polish Scientific Publishers PWN Wydawnictwo Naukowe PWN Spółka Akcyjna. All Rights reserved.

Przekład Wojciech Gawroński

Projekt okładki polskiego wydania na podstawie oryginału INT-MEDIA Dawid Mazur

Wydawca Wioleta Szczygielska-Dybciak

Redaktor prowadzący Jolanta Kowalczuk

Redaktor Jolanta Sierakowska

Redaktor techniczny Maria Czekaj

Koordynator produkcji Anna Bączkowska

Zastrzeżonych nazw firm i produktów użyto w książce wyłącznie w celu identyfikacji.

Książka, którą nabyłeś, jest dziełem twórcy i wydawcy. Prosimy, abyś przestrzegał praw, jakie im przysługują. Jej zawartość możesz udostępnić nieodpłatnie osobom bliskim lub osobiście znanym. Ale nie publikuj jej w internecie. Jeśli cytujesz jej fragmenty, nie zmieniaj ich treści i koniecznie zaznacz, czyje to dzieło. A kopiując jej część, rób to jedynie na użytek osobisty.

Szanujmy cudzą własność i prawo

Więcej na www.legalnakultura.pl

Polska Izba Książki

Skład wersji elektronicznej na zlecenie Wydawnictwa Naukowego PWN

Monika Lipiec /Woblink

Copyright © for the Polish edition by Wydawnictwo Naukowe PWN SA

Warszawa 2020

ISBN 978-83-01-21355-9

eBook został przygotowany na podstawie wydania papierowego z 2020 r., (wyd. I)

Warszawa 2020

Wydawnictwo Naukowe PWN SA

02-460 Warszawa, ul. Gottlieba Daimlera 2

tel. 22 69 54 321, faks 22 69 54 288

infolinia 801 33 33 88

e-mail: pwn@pwn.com.pl; reklama@pwn.pl

www.pwn.pl

Podziękowania

Wielu ludzi pomogło mi uczynić tę książkę lepszą, sugerując dodatkowe źródła, usprawnienia lub znajdując oraz pomagając w niej poprawić błędy. Dziękuję następującym osobom:

Alan Richardson, Aleksandar Simović, Alex Casalboni, Alvaro Garcia, Chris Haggstrom, Christian Pfisterer, Cyrille Martraire, Damjan Vujnović, Daniel Bryant, Daniel Zivkovic, Darryl Hughes, David Howell, Dejan Dimić, Gavin Manning, Ivica Kolenkaš, João Rosa, Lynn Langit, Marcus Hammarberg, Mashooq Badar, Michael Bolton, Michael Fagan, Mike Roberts, Paul Julius, Phil Hunt, Pieter Herroelen, Rich Vaughan, Rob Park, Shaolang Ai, Slobodan Stojanović, Vicenç García Altés.

Bardzo dziękuję Nikoli Korać, która ilustrowała tę książkę, oraz Mary White, która ją redagowała, za to, że obie uczyniłyście ją bardziej przystępną.

O autorze

Gojko Adžić jest współzałożycielem i współtwórcą MindMup, narzędzia typu SaaS (Software as a Service), które umożliwia wspólną pracę nad mapami myśli – jego rozwiązanie było jednym z pierwszych produktów opartych na platformie AWS Lambda. Jest także jednym z głównych kontrybutorów Claudia.js, popularnego narzędzia, które służy do wdrażania aplikacji typu serverless. Autor posiada również tytuł AWS Serverless Hero, który został mu nadany w 2019 r.

Gojko jest także partnerem w firmie Neuri Consulting LLP, zdobywcą nagrody European Software Testing Outstanding Achievement Award z 2016 roku oraz zdobywcą tytułu Most Influential Agile Testing Professional z roku 2011. Książka Gojko Specification by Example zdobyła nagrodę Jolt Award za najlepszą książkę roku 2012, a jego blog wygrał UK Agile Award za najlepszą publikację online w roku 2010. Jest również autorem poniższych książek i publikacji w języku angielskim:

- Człowiek vs komputer;

- Fifty Quick Ideas to Improve Your Tests;

- Fifty Quick Ideas to Improve Your User Stories;

- Impact Mapping;

- Specyfikacja na przykładach;

- Bridging the Communication Gap.

Aby móc się z nim skontaktować, pisz na gojko@neuri.co.uk lub odwiedź stronę: https://gojko.net.

Aktualizacje

Platformy typu serverless ewoluują bardzo szybko, a AWS w zasadzie co kilka miesięcy zmienia coś, co dotyczy AWS Lambda. To pokazuje, jak trudno dostarczyć coś trwałego i długofalowego w postaci książki. Co więcej, po zakończeniu pisania pierwszej wersji książki, tuż przed oddaniem jej do druku, AWS wypuścił aktualizację dla środowiska uruchomieniowego Node.js na platformie Lambda oraz zmiany dotyczące narzędzia SAM, co spowodowało, że dwa rozdziały stały się zbędne i zmusiło mnie to do napisania ich od nowa. Zdecydowałem o przesunięciu daty druku, tak aby opracować informacje w możliwie najświeższy sposób.

Ta wersja książki opiera się na wersji platformy i na stanie ekosystemu ze stycznia 2020, wykorzystując środowisko uruchomieniowe AWS Lambda w wersji Node.js 12.x oraz narzędzie SAM w wersji 0.40.0.

Planuję utrzymywać aktualność elektronicznej wersji książki po wdrożeniu nowości przez AWS. Jeśli chcesz dowiedzieć się o zmianach oraz zapoznać się z tym, co się zmieniło od momentu opublikowania jej wersji drukowanej, wejdź na stronę https://runningserverless.com. Jeśli kupiłeś wydanie papierowe lub wersję elektroniczną, zapisz się na wyżej wymienionej stronie w celu otrzymania darmowej aktualizacji w formie elektronicznej.

Spis treści

Podziękowania

O autorze

Aktualizacje

Wprowadzenie

1. Serverless w 5 minut

Model cenowy usług serverless

Ograniczenia techniczne na platformie AWS Lambda

Kiedy używać Lambdy?

Część I. Podstawowe zadania deweloperskie

2. Praca na lokalnej maszynie

Konfiguracja zależności dla SAM

Instalacja narzędzi JavaScriptowych

Instalacja narzędzia AWS SAM CLI

Konfiguracja danych dostępowych

Korzystanie z konta o ograniczonych uprawnieniach

Wykorzystanie lokalnego profilu użytkownika

3. Tworzymy usługę sieciową

Infrastruktura opisana za pomocą kodu źrdłowego

Model programowania dla funkcji Lambda

Wdrażanie aplikacji SAM

Analiza stworzonych zasobόw

4. Lokalna praca i rozwiązywanie problemόw

Pobieranie logόw z wykonania funkcji

Symulowanie funkcji Lambda lokalnie

Debugowanie funkcji Lambda

Walidacja szablonόw CloudFormation

Praca w zespole

Konfiguracja potoku wdrożeniowego

5. Bezpieczne wdrożenia

Konfiguracja funkcji

Wersje oraz aliasy

Stopniowe wdrożenia

Część II. Praca z usługami dostępnymi na platformie AWS

6. Obsługa żądań HTTP

Zdarzenia usługi API Gateway

Dostosowywanie odpowiedzi HTTP

Analiza błędόw integracji z usługą API Gateway

Przetwarzanie parametrόw żądań

Wykorzystanie ustawień globalnych do konfiguracji pośredniej usługi API Gateway

Tworzenie sparametryzowanych szablonόw CloudFormation

7. Wykorzystanie usług do zapisu danych

Opcje dostępne w chmurze

Uprawnienia nadawane funkcjom Lambda

Generowanie unikalnych identyfikatorόw

Wykorzystanie dodatkowych usług AWS z funkcji Lambda

Autoryzacja dostępu z użyciem polityk bezpieczeństwa IAM

Jak radzić sobie z limitami czasu odpowiedzi?

8. Taniej, szybciej, Serverless

Podpisywanie żądań

Ochrona plikόw na S3

9. Obsługa zdarzeń przychodzących z usług AWS

Generowanie zdarzeń testowych

Praca z plikami

Praca z asynchronicznymi zdarzeniami

Unikanie odniesień cyklicznych

Obsługa błędόw w operacjach asynchronicznych

Warunkowe tworzenie zasobόw

Część III. Projektowanie aplikacji w architekturze serverless

10. Wykorzystanie komponentόw aplikacyjnych

Usługa AWS Serverless Application Repository

Warstwy funkcji Lambda

Publikacja komponentu w usłudze SAR

11. Zarządzanie sesjami i komunikacją z użytkownikami

Wyciągnięcie stanu sesji z funkcji Lambda

Wydzielenie statycznych zasobόw z funkcji Lambda

Wykorzystanie usługi S3 jako serwera WWW

Wgrywanie plikόw na S3

Wykorzystanie niestandardowych zasobόw do rozszerzania CloudFormation

12. Utrzymanie oraz odporność na błędy

Parametryzowane ścieżki API

Architektura portόw i adapterόw

Ochrona przed nadużyciem

13. Dobre praktyki projektowania aplikacji typu serverless

Myśl o zadaniach do wykonania, nie funkcjach

Jedna czy kilka funkcji Lambda?

Wspόłdzielenie zachowań

Wspόłdzielenie konfiguracji

Co dalej?

Przypisy

Wprowadzenie

Ta książka pomoże Ci rozpocząć pracę z AWS Lambda oraz narzędziem Serverless Application Model (SAM). Lambda jest usługą dostępną na platformie Amazon Web Services do uruchamiania kodu, który jest sterowany zdarzeniami, a SAM to rozwiązanie otwartoźródłowe (open source), które w znaczący sposób upraszcza konfigurację i wdrażanie oparte na wyżej wymienionej usłudze. Wspólnie pozwalają na tworzenie w łatwy sposób automatycznie skalujących się API oraz serwisów zaprojektowanych pod wdrożenia klasy serverless. W kolejnych rozdziałach dowiesz się, jak:

- stworzyć aplikacje, które w pełni wykorzystają potencjał architektury serverless;

- zbudować automatycznie skalujące się API;

- obsługiwać wykonywanie zadań w tle oraz różne modele obsługi zdarzeń;

- skonfigurować potok wdrożeniowy, tak aby praca w zespole była efektywna;

- testować oraz rozwiązywać problemy w aplikacjach zaprojektowanych na platformę AWS Lambda;

- sprawdzać i monitorować rozwiązania klasy serverless.

Książka została przygotowana na podstawie moich własnych doświadczeń zdobytych w trakcie budowy narzędzia MindMup, służącego do przygotowań map myśli w formie zespołowej pracy zdalnej, które było jednym z pierwszych rozwiązań opartych na usłudze AWS Lambda. MindMup zmigrował na architekturę serverless z klasycznego rozwiązania opartego na hostingu w trakcie roku 2016, tak aby skorzystać z mechanizmów skalowania na żądanie. Dzięki temu zredukowaliśmy nasze koszty operacyjne o około dwie trzecie, zwiększając w tym samym czasie możliwości aplikacji, przyspieszając proces wytwarzania oprogramowania oraz obniżenie czasu wprowadzenia na rynek. Wnioski z tej migracji zostały zebrane w postaci jednego z pierwszych opracowań naukowych na ten temat oraz były podstawą do przygotowania wystąpień na wielu konferencjach na całym świecie.

Kiedy piszę te słowa w styczniu 2020 roku, mając prawie czteroletnie doświadczenie w pracy z systemem klasy serverless, bardzo łatwo jest zauważyć pojawiające się wzorce architektoniczne oraz wybrać te eksperymenty, które przerodziły się w dobre pomysły, i wyeliminować te, które prowadziły donikąd. Ta książka ma Ci pomóc przejść tę drogę szybciej, tak aby skorzystać z dobrych praktyk oraz uniknąć pułapek.

Kolejne rozdziały pokażą, jak zbudować taką aplikację w praktyce. Rozpoczniemy od prostego statycznego API i iteracyjnie rozbudujemy ją do pełnej i prawdziwej aplikacji służącej do skalowania obrazów, gotowej do przyjęcia milionów użytkowników, ze wszystkimi usługami wspierającymi wdrożenie i zarządzanie takim produktem. Przygotowany system będzie przypominał to, z czym wielu z Was pracuje na co dzień. W ten sposób przygotujemy solidny fundament pod ewentualne przyszłe wykorzystanie, włącznie z kopiowaniem fragmentów na początek. W trakcie tworzenia i wdrażania różnych części aplikacji dowiesz się, jak działa AWS Lambda i usługi pokrewne, poznasz także wskazówki, techniki i narzędzia, które pomagają w tworzeniu rozwiązań klasy serverless.

Omawiany przykład wyewoluował z kilkunastu otwartych warsztatów i wystąpień konferencyjnych przeze mnie przeprowadzonych. Wraz z kolegami wykorzystywaliśmy przykłady zamieszczone w tej książce podczas nauki setek programistów, jak budować aplikacje w architekturze serverless. Dzięki temu przygotowane przykłady zostały sprawdzone w praktyce i usprawniono korzystanie z uwag oraz doświadczeń płynących z warsztatów.

Dlaczego AWS SAM?

Kiedy rozpoczęliśmy migrację MindMup do architektury serverless, SAM nie był jeszcze dostępny. Oznaczało to, że musieliśmy przygotować podobne narzędzie własnoręcznie. Gdybyśmy podchodzili teraz do tego problemu, wykorzystanie SAM byłoby oczywistym wyborem.

Jeśli interesujesz się tym tematem, to wiesz o tym, że SAM nie jest ani jedynym, ani najpopularniejszym, ani także nie najlepszym czy najłatwiejszym narzędziem w użyciu (biorąc pod uwagę, że piszę te słowa w czerwcu 2019). Jednakże SAM ma dwie znaczące zalety: jest ono efektywne, jeśli chodzi o pracę z nim, oraz jest ono wspierane i przygotowywane przez AWS. Szczególnie to ostatnie jest szalenie ważne, ponieważ oznacza solidne komercyjne wsparcie i jest bezpiecznym wyborem na przyszłość.

Użytkownicy tego narzędzia są beneficjentami wolności związanej z faktem, że jest to rozwiązanie o otwartym kodzie źródłowym, oraz ze świetną integracją z platformą dostawcy, która jest charakterystyczna dla komercyjnych rozwiązań, o zamkniętym kodzie. W tym przypadku kod jest dostępny na platformie Github, więc każdy może go przeanalizować i ocenić. Dzięki tym dwóm faktom dookoła narzędzia powstała znacząca społeczność, która chętnie pomaga w sytuacji ewentualnych problemów i wyzwań. Dodatkowo większość kontrybutorów to pracownicy AWS. Z racji tego, że cały ekosystem serverless szybko się zmienia, fakt, że autorzy tego narzędzia mają bezpośredni dostęp do produktów i usług na AWS oraz zespołów, które za nimi stoją, jest nie do przecenienia – przykładem jest chociażby to, jak szybko to narzędzie się rozwija.

Wykorzystanie AWS SAM powinno być relatywnie łatwe dla większości organizacji, ponieważ jest ono zbudowane de facto na standardzie dla wdrożeń na platformie AWS o nazwie CloudFormation (powiemy więcej o tym narzędziu w rozdziale 3). Dla firm, które już teraz używają CloudFormation, wykorzystanie AWS SAM oznacza tylko drobne zmiany w procesie przygotowania szablonów w tym narzędziu. Nie ma potrzeby przygotowywania nowego procesu czy nauki kompletnie nowego narzędzia. Dodatkowo, SAM w znacznym stopniu redukuje powtarzalność w celu przygotowania infrastruktury klasy serverless, co ułatwia pracę w szczególności początkującym użytkownikom CloudFormation.

Grupa docelowa

To jest podręcznik techniczny i w związku z tym uważam, że będzie pomocny dla dwóch grup programistów oraz architektów oprogramowania:

- tych bez wcześniejszego doświadczenia w zakresie aplikacji typu serverless, ale zainteresowanych nauką na temat nowych podejść związanych z architekturą aplikacji korzystających z chmury;

- tych pracujących z AWS Lambda, ale korzystających z innych narzędzi i chcących poznać AWS SAM oraz podejście nazwane przez inżynierów Amazona Serverless Application Model.

Drogi Czytelniku, nie potrzebujesz żadnej wiedzy na temat chmury, aby zrozumieć przedstawione przykłady, ale będziesz musiał umieć przeanalizować i przeczytać kod w języku JavaScript.

W momencie, gdy piszę te słowa, AWS Lambda pozwala na uruchamianie kodu napisanego na sześciu platformach: OpenJDK (dla języka Java i pokrewnych), .NET Core (dla języka C# i pokrewnych), Go, Ruby, Python oraz JavaScript (z wykorzystaniem Node.js). Dodatkowo, jest możliwe przygotowanie własnego środowiska uruchomieniowego w celu wsparcia innych platform oraz języków programowania.

Wybór języka programowania, w którym będziesz programować swoje funkcje, nie jest szalenie ważny, ale aby zachować spójność i uczynić tę książkę łatwiejszą do przyswajania wiedzy, wybrałem tylko jeden z nich. Wybór padł na JavaScript, ponieważ jest to język najbliższy uniwersalnemu dla wszystkich programistów w roku 2020. Wszystkie dobre praktyki przedstawione w książce mogą być z łatwością zaaplikowane na każdej z pozostałych wspieranych platform, a różnice, jakie będą występowały między platformami, zostały wyjaśnione w treści książki. Kompletny i działający kod źródłowy wszystkich przykładów oraz przykłady w innych językach programowania możesz pobrać z oficjalnej strony: https://runningserverless.com.

Jak czytać tę książkę?

Pierwszy rozdział niniejszej książki to szybkie wprowadzenie w podstawowe pojęcia aplikacji typu serverless oraz omówienie wad i zalet, jakie wynikają z wykorzystania tego podejścia. Jeśli jest to dla Ciebie kompletnie nowe, zachęcam do przeczytania i potraktowania tego jako zwiastun tego, czego możesz spodziewać się później. Jeżeli jednak ta tematyka jest Ci bliska i posiadasz doświadczenie w tym zakresie, możesz śmiało pominąć tę część książki.

Drugi rozdział wyjaśnia, jak skonfigurować narzędzie SAM, tak aby móc tworzyć i testować funkcje Lambda na lokalnej maszynie. Zgodnie z tą instrukcją przygotowujemy również takie środowisko, aby na bieżąco wypróbowywać przykłady znajdujące się w książce. Jeśli nie planujesz korzystać z przykładów, możesz śmiało pominąć ten rozdział.

Rozdziały od drugiego włącznie są kompletnym przewodnikiem, jak zbudować wysoce skalowalną aplikację, która w pełni korzysta z chmury. To ważne, aby czytać rozdziały 2–12 w zadanej kolejności. Na początku nauczysz się, jak wykonać najprostsze wdrożenie i jak przygotować projekt, tak aby mógł nad nim pracować cały zespół. W trakcie rozbudowy naszego przykładu dowiesz się o wysokopoziomowych aspektach, takich jak architektura aplikacji, jak dobrać właściwe usługi dostępne w portfolio AWS oraz jak wykorzystać potencjał, który drzemie w serverless. W każdym rozdziale znajdziesz ważne wskazówki i dlatego polecam, żeby nie omijać żadnej sekcji, nawet jeśli znasz ten konkretny temat.

Ostatni rozdział wyjaśnia wzorce architektoniczne oraz modele wdrożeniowe dla typowych przykładów aplikacji klasy serverless. Z racji tego, że tematyka ta jest jeszcze relatywnie młoda i trudno mówić o zestawie złotych rad oraz dobrych praktykach to ten rozdział pomoże Ci wykorzystać pełen potencjał AWS Lambda i pokrewnych usług, jeśli to Ty staniesz przed wyzwaniem zaprojektowania systemu z wykorzystaniem tej architektury.

Kod źrόdłowy

Aby skupić się na najważniejszych elementach i niepotrzebnie nie rozdmuchiwać przykładów, starałem się przygotować okrojone do niezbędnego minimum fragmenty kodu źródłowego. Każda osoba, która ma doświadczenie z językiem JavaScript, będzie w stanie odtworzyć samodzielnie kompletną aplikację, bazując na przygotowanych przykładach, ale przygotowany kod jest zoptymalizowany, tak aby go łatwo zrozumieć i przeczytać, a nie kopiować.

Pełny kod aplikacji i wszystkich przykładów jest dostępny na stronie https://runningserverless.com. Najważniejsze listingi posiadają w tytule ścieżkę do pliku. Na przykład poniższy fragment można znaleźć w kompletnej paczce z kodem źródłowym w liniach 126 i 127 znajdujących się w pliku template-with-dlq.yaml, wewnątrz katalogu ch9 (rozdział 9).

Przykłady komend

Narzędzia AWS wywoływane z linii komend bardzo często wymagają długiej listy parametrów, która przekracza długość linii na małym ekranie lub stronie książki. Jeśli faktycznie zabraknie miejsca na taką linijkę, w kolejnym wierszu znajdziesz małą strzałkę, która podpowie Ci, że należy traktować całość jako jeden ciąg. Oto przykład:

Aby wizualnie rozróżnić wywołania komend od jej wyniku, same komendy, które należy wywołać, nie będą miały numerów linii. Rezultaty zaś będą taki numer posiadały. Kolejny listing pokazuje, jak wygląda wywołanie komendy:

aws sts get-caller-identity

Następny listing pokazuje rezultat poprzedniej komendy, wraz z numerami linii dołączonymi do wydruku:

Wskazόwki

Ważne miejsca i wskazówki będą jasno oznaczone w tekście i będą zaprezentowane za pomocą następującego obramowania:

Kod źrόdłowy

Nie kopiuj kodu źródłowego zamieszczonego w książce. Pobierz całą paczkę ze strony: https://runningserverless.com.

Będę korzystał z identycznego mechanizmu do wyjaśnienia kluczowych pojęć oraz ważnych informacji wartych zapamiętania, w szczególności jeśli będą wymagały specjalnej uwagi lub przeczą intuicji.

1. Serverless w 5 minut

Ten rozdział wyjaśnia finansowe i techniczne ograniczenia tworzenia aplikacji typu serverless. Dowiesz się, kiedy oraz dlaczego AWS Lambda jest dobrym rozwiązaniem do budowy aplikacji oraz kiedy powinieneś używać czegoś innego.

Aplikacje typu serverless są na najniższym technicznym poziomie oprogramowaniem, które działa w środowisku dostawcy usług w pełni odpowiedzialnego za infrastrukturę oraz zadania operacyjne, takie jak zarządzanie siecią oraz przychodzącymi żądaniami, skalowanie na żądanie, monitorowanie oraz przywracanie po awariach.

Zamiast kontenerów, które opakowują nasze aplikacje z logiką biznesową systemem operacyjnym, serwerami (takimi jak web serwery albo brokerzy wiadomości) z logiką biznesową, aplikacje typu serverless dostarczają kod, który zostanie wykonany w odpowiedzi na zdarzenie oraz konfigurację źródeł takich zdarzeń, które wywołują napisany kod.

W chmurze Amazon Web Services środowisko uruchomieniowe dla takich architektur nosi nazwę AWS Lambda. Wspiera ono szeroką gamę źródeł zdarzeń, włączając w to przychodzące żądania HTTP, wiadomości w kolejkach, przychodzące wiadomości e-mail, zmiany zachodzące na poziomie rekordów w bazie danych, uwierzytelnienie użytkownika, wiadomości przychodzące w ramach połączenia typu WebSocket, synchronizację urządzenia klienta oraz wiele innych.

Ponieważ twórcy aplikacji nie przygotowują tej części kodu, która odpowiada za komunikację ze źródłem zdarzeń po stronie środowiska AWS Lambda, a jedynie obsługę samego zdarzenia, określamy takie aplikacje mianem serverless (bez serwerów). Jednak to pojęcie zostało straszliwie zniekształcone przez marketing i w związku z tym w Internecie możesz znaleźć masę słabych żartów, jak cały czas mamy do czynienia w takim środowisku gdzieś na dole z serwerami. Zdecydowanie lepszym porównaniem będzie WiFi. Kiedy przeglądasz internet z wykorzystaniem takiego połączenia, zwanego z angielska wireless, Twoje urządzenie korzysta z routera oddalonego o kilka metrów od Ciebie. I mimo że masz dostęp do internetu, nie musisz zarządzać oraz przejmować się połączeniem kablowym do wysyłania pakietów do internetu, dopiero najprawdopodobniej router będzie połączony kablem z siecią dostawcy. Identycznie jest w przypadku serverless. Oczywiście w tle mamy do czynienia z serwerami, siecią, maszynami wirtualnymi, ale nie musisz się nimi przejmować, aby uruchomić swój kod.

W porównaniu z uruchamianiem aplikacji w kontenerach lub bezpośrednim zarządzaniem maszynami wirtualnymi aplikacje typu serverless mają dwie olbrzymie zalety:

- krótszy czas wprowadzenia produktu na rynek, co skutkuje skuteczniejszą innowacją oraz dostarczaniem szybciej wartości klientom;

- obniżony koszt zadań operacyjnych dzięki lepszemu wykorzystaniu zasobów.

Badania przeprowadzone przez firmę IDC[1] w roku 2018 sugerują, że firmy, które skorzystały z podejścia serverless do budowy swoich aplikacji, średnio „obniżyły pięcioletni koszt operacyjny o 60% oraz były o 89% szybsze we wdrożeniach”. To jest zgodne z moim doświadczeniem. Po migracji MindMup z klasycznego modelu, gdzie płaciliśmy za zarezerwowane zasoby, do AWS Lambda zredukowaliśmy koszty utrzymania infrastruktury o ponad dwie trzecie. Kolejny popularny przykład to Yubl, prezentowany przez Yana Cui na różnorakich międzynarodowych konferencjach. Obniżyli oni koszty operacyjne o 95%, przenosząc się na Lambdę. Oba przykłady są dogłębnie omówione w opracowaniu naukowym Serverless Computing: Economic and Architectural Impact[2], którego jestem współautorem wraz z Robertem Chatleyem.

W marcu 2018 roku firma Cloudability opublikowała wyniki badania State of the Cloud[3]. W badaniu pojawia się teza wraz z uzasadnieniem, że wykorzystanie architektury serverless wzrosło o ponad 667% w ciągu roku. To nie jest zaskakujący wynik, biorąc pod uwagę, że połączenie obniżonych kosztów operacyjnych i krótszego czasu wprowadzenia produktu na rynek niesie ze sobą bardzo silną biznesową motywację dla firm, aby skorzystać z usługi AWS Lambda i podobnych. Jeśli się głębiej nad tym zastanowić, to wspólny mianownik dla obu wymienionych zalet jest taki sam. To model cenowy usług serverless.

Model cenowy usług serverless

Praktycznie rzecz ujmując, AWS Lambda i podobne platformy to serwisy do zarządzania kontenerami na sterydach. Dostarczają one standardowych środowisk uruchomieniowych w celu szybkiego tworzenia aplikacji oraz algorytmów do automatycznego skalowania kontenerów w zależności od ilości pracy. Mimo że zbudowanie takiej platformy samodzielnie jest technicznym wyzwaniem, od strony twórców aplikacji to tylko drobny krok naprzód w kilkunastoletniej podróży, jaką odbywamy z wirtualizacją i parawirtualizacją. To właśnie dlatego część z architektów, w szczególności tych, którzy stworzyli niejeden system oparty na konteneryzacji, czasami skarży się, że serverless to marketingowa wydmuszka. AWS jednak zrobił rewolucyjną rzecz, którą notabene reszta dostawców zaczęła bardzo szybko kopiować, dotyczącą strony finansowej tego podejścia. Model cenowy rozwiązań klasy serverless jest dużo ważniejszy niż technologia.

Kiedy korzystasz z AWS Lambda, aby uruchamiać swój kod, płacisz tylko za to, jak długo to trwało, a nie za zarezerwowaną dostępność środowiska, na którym zostanie uruchomiony. Jeżeli aplikacja nic nie robi, nie płacisz nic. Jeśli nagle pojawi się milion użytkowników, Lambda stworzy tyle kontenerów, ile będzie potrzebowała, aby obsłużyć taki ruch, i usunie je zaraz po tym, jak nie będą potrzebne. Dodatkowo nie będziesz płacić za okresy przestoju, gdy na infrastrukturze nie ma uruchomionego kodu lub gdy Twój kod czeka na nadchodzące zadania.

Rezerwacja minimalnej przepustowości

W grudniu 2019 roku AWS umożliwił klientom rezerwację minimalnej przepustowości dla funkcji Lambda, co pozwala upewnić się, że zawsze będziemy mieli określoną liczbę procesów oczekujących na przychodzące żądania. W żargonie AWS nazywa się to zarezerwowaną współbieżnością (reserved concurrency). W przypadku takiej funkcjonalności płacimy stałą kwotę za zarezerwowaną przepustowość, niezależnie od tego, czy ją wykorzystujemy, czy nie. Większość aplikacji nie musi korzystać z takiej funkcjonalności, pod warunkiem że zostanie odpowiednio zaprojektowana.

Cena w takim modelu zależy od dwóch czynników: maksymalnego zużycia pamięci w danym zadaniu oraz czasu poświęconego na wykonywanie kodu. Dla przykładu, zakładając, że skonfigurowaliśmy naszą funkcję z górną granicą zużycia pamięci na 512 MB w jednym z regionów w Stanach Zjednoczonych, AWS podliczy koszty następująco:

- $0.0000002 za pojedyncze żądanie;

- $0.000000833 za wykonanie 100 ms w zadeklarowanym przedziale zużycia pamięci.

Porównanie modelu płatności za zarezerwowane maszyny wirtualne i za zużycie nie jest proste, ponieważ zależy od wielu czynników. Cena takiej maszyny w najprostszym przypadku zależy od oczekiwanego obciążenia, a cena związana z wykorzystaniem maszyny zależy od zadania, jakie jest na niej wykonywane. Oto dwa przykłady z dwóch różnych biegunów.

Dla zadania wykonywanego nieregularnie, co pięć minut, przez 100 ms oraz potrzebującego 512 MB pamięci cena za funkcję Lambda to w dużym zaokrągleniu mniej niż 1 cent z dolara amerykańskiego na miesiąc. Wykorzystanie podobnej konfiguracji z usługi AWS Elastic Compute Cloud (EC2), zakładając, że mamy dwie identyczne maszyny, aby zapewnić wysoką dostępność w razie problemów, zgodnie z rekomendacjami AWS będzie to kosztowało 9 dolarów amerykańskich, trzy rzędy wielkości więcej. Zauważmy, że dla AWS Lambda nie musisz rezerwować zastępczej usługi w celu zapewnienia wysokiej dostępności, ponieważ to jest już wliczone w cenę.

Z drugiej strony pojedyncza funkcja Lambda, która otrzymuje ciągle żądania i która nigdy się nie zatrzymuje, kosztuje miesięcznie około 27 dolarów amerykańskich. Porównując same tylko koszty maszyn wirtualnych, już widać istotną różnicę. Jednak to tylko przy założeniu, że same maszyny wirtualne wystarczą, aby utrzymać taki stan bez przerwy z takim obciążeniem. Dużo bardziej prawdopodobne jest, że dla pracy ciągłej będziemy potrzebować cały klaster maszyn wraz z maszynami zapasowymi. Dodatkowo praca z taką usługą wymaga operatorów, którzy planują możliwości klastra i którzy dbają o skalowanie całej floty, tak aby wytrzymała ona ciągłe obciążenie. W przypadku Lambdy to wszystko jest zawarte w cenie, wliczając dodatkowo obsługę błędów, logowanie, monitorowanie i wersjonowanie.

Różnica między opłatami za zarezerwowane usługi a opłatami za wykorzystanie usługi jest również istotna dla testów oraz środowisk testowych. Kiedy firma płaci za zarezerwowane usługi, kopie środowisk produkcyjnych podrażają koszty operacyjne, nawet jeśli nie są używane. To dlatego najczęściej tego typu środowiska są odchudzane oraz przycinane do granic przyzwoitości i, koniec końców, odbiegają w znaczącym stopniu od produkcji. Dzięki rozliczaniu za zużycie środowiska nic nie kosztują, jeśli nie są używane, co oznacza, że znika motywacja do przycinania ich na siłę. Dla wielu organizacji środowiska testowe w architekturze klasy serverless są praktycznie darmowe.

W przypadku aplikacji stworzonych w tej architekturze to dostawca kontroluje infrastrukturę, nie twórcy aplikacji. To oznacza, że zespoły deweloperskie mogą się skupić na rzeczach, które są istotą działania biznesu, są ważne z punktu widzenia przewagi biznesowej i stanowią wyraźny wyróżnik na rynku, zamiast tracić czas na zadania operacyjne czy też te związane z infrastrukturą. Po migracji MindMup ze środowiska maszyn wirtualnych na AWS Lambda zdaliśmy sobie sprawę, że możemy usunąć znaczną część kodu źródłowego, który był związany z zadaniami dotyczącymi infrastruktury, i od końca 2016 nie napisaliśmy ani jednej linijki dotyczącej takiego zadania. Nie musieliśmy spędzać czasu na budowaniu i integracji narzędzi do monitorowania oraz skalowania systemu ani nie musieliśmy się martwić problemami operacyjnymi. Lambda pomogła nam znacznie szybciej wyjść od koncepcji do działającego oprogramowania dostarczonego użytkownikom.

Wpływ modelu płatności na architekturę aplikacji oraz wdrożenia

W aplikacjach typu serverless programiści tworzą kolejne funkcje, tak aby wykonać określony proces biznesowy, używając platformy do koordynacji, zarządzania stanem i komunikacji z klientami. W przypadku AWS model kosztów jest zorganizowany dookoła wykorzystanych, a nie zarezerwowanych zasobów. Amazon Simple Storage Service (S3), skalowalny system plików, rozlicza użytkowników za przesłane bajty do i z serwisu. Amazon Simple Notification Service (SNS), który odpowiada za mechanizm rozsyłania wiadomości i subskrypcji, rozlicza nas za każdą wysłaną wiadomość. Cała platforma jest tak zaprojektowana, że całkowity koszt Twojej aplikacji zależy od tego, jak często ją wykorzystujesz. W tym przypadku AWS Lambda to uniwersalny klej, który spaja te wszystkie usługi razem.

W swojej prezentacji Why the Fuss about Serverless[4] Simon Wardley słusznie zauważył, że rozwiązania klasy serverless to właściwe rozwiązania pasujące do definicji platform as a service, która została zawłaszczona przez zupełnie inne rozwiązania z powodu błędnego podejścia marketingowego. Bez wątpienia historia zatoczy koło i za kilka lat okaże się, że to, co będzie nosiło nazwę serverless, nie będzie w ogóle podobne do obecnych rozwiązań. Jednak obecnie takie rozwiązania posiadają następujące trzy cechy:

- Dostawcy infrastruktury są odpowiedzialni za przychodzące żądania.

- Koszt jest wyliczany na podstawie zużycia, z dokładnością do pojedynczego żądania.

- Zadania operacyjne (wdrożenie, skalowanie, bezpieczeństwo, dostępność i monitoring) są wliczone w cenę.

Wspomniane trzy czynniki tworzą interesujące środowisko. Dla przykładu, biorąc pod uwagę rozliczanie zużycia za każde żądanie bez dodatkowych kosztów związanych z utrzymaniem środowiska, koszt wysłania miliona zapytań do pojedynczej wersji naszej aplikacji będzie taki sam jak do wysłania ich do dwóch lub więcej różnych wersji. Liczba żądań jest związana z kosztem, a nie ilością środowisk.

Gdyby zastanowić się nad historią, poczynając już od czasów architektury typu mainframe, wszystkie wdrożenia miały tę samą wadę, związaną z kosztem i wyzwaniem stworzenia kolejnego środowiska. Właśnie dlatego środowiska preprodukcyjne lub testowe wyglądały inaczej (były mniejsze) niż produkcyjne, programiści walczyli wraz z testerami o władzę i zasoby tego pojedynczego środowiska integracyjnego. Konteneryzacja oraz maszyny wirtualne radykalnie zmniejszyły czas związany z przygotowaniem nowych środowisk, ale nie obniżyły drastycznie kosztów utrzymania dla wielu środowisk. Dwie kopie środowiska produkcyjnego kosztują dokładnie dwukrotność kosztu pojedynczego. W tym przypadku istnieje bardzo silna motywacja do ponownego wykorzystania zasobów i upakowywania funkcjonalności razem, żeby zaoszczędzić.

Ostatnimi czasy pracowałem z klientem, u którego całe tworzenie raportów fukcjonowało na tym samym klastrze co środowisko produkcyjne. W rezultacie generowanie pojedynczego raportu spowalniało całą resztę aplikacji, które pracowały na tym samym sprzęcie. Rozwiązanie polegające na przygotowaniu środowiska dla każdego typu raportu jest nieekonomiczne, ponieważ przez większość czasu takie środowisko byłoby nieużywane. Z drugiej strony scalenie wszystkich generatorów w jeden klaster wymaga bardzo dokładnego planowania i zarezerwowania odpowiednich zasobów, a to z kolei często kończy się przepłacaniem (przeszacowanie, płacimy za niewykorzystane zasoby) lub problemami w przypadku nieco wyższego zapotrzebowania (niedoszacowanie). W przypadku AWS Lambda planowanie jest po stronie dostawcy, a my jako klienci nie mamy żadnej motywacji ani zysku z upakowywania różnych aplikacji na przygotowanych zasobach. Zamiast scalać komponenty, żeby zaoszczędzić pieniądze na wdrożeniach, co będzie powodowało dalsze problemy z konkurencją związane z zasobami oraz z wydajnością, Lambda zapewnia nam możliwość wdrażania każdego typu generatora wspomnianych raportów osobno, w izolowanej infrastrukturze. Skoro cena jest proporcjonalna do wykorzystania, a nie ilości zarezerwowanych zasobów, to koszt będzie identyczny niezależnie od sposobu, w jaki podzielimy nasze generatory. Jeżeli generatory mają różne zapotrzebowanie co do zużycia pamięci, będziemy mogli dodatkowo zaoszczędzić. W klastrze każdy z kontenerów musiałby zarezerwować tyle pamięci, ile potrzebowałby najbardziej pamięciożerny generator. W przypadku funkcji Lambda procesy są izolowane i możemy dobrać odpowiedni rozmiar pamięci do każdego generatora, a mniej pamięci oznacza niższy koszt.

Ponadto z Lambdą możemy stworzyć dowolną liczbę wersji naszej infrastruktury bez dodatkowych kosztów. Zamiast programistów i testerów, którzy kłócą się o pojedynczą kopię środowiska testowego, można stworzyć po jednej dla każdego zespołu lub nawet poszczególnej osoby. Wszystko z idealnym odwzorowaniem środowiska produkcyjnego, zwłaszcza gdy chodzi o zarezerwowane zasoby. Koszt będzie dokładnie taki sam, jak w przypadku rozwiązania z jednym współdzielonym środowiskiem, ponieważ znowu cena jest uzależniona od zużycia, a nie od zarezerwowanych zasobów czy ilości środowisk.

Wpływ modelu płatności na bezpieczeństwo

Wspomniany mechanizm ma również istotny wpływ na bezpieczeństwo aplikacji. Współczesne aplikacje serwerowe zazwyczaj posiadają wewnętrzną implementację, która odpowiada za uwierzytelnienie, kontrolę dostępu i rozdzielanie pracy. Taki proces potrzebuje dostępu do zewnętrznych usług, nierzadko również bazy danych. Zabezpieczenie całej infrastruktury jest możliwe, ale jest też problematyczne. Większość serwerów aplikacji webowych oraz bazodanowych uruchamianych jest zazwyczaj z uprawnieniami administratora. W razie włamania do systemu z wykorzystaniem takiego procesu w zasadzie nie ma żadnych innych zabezpieczeń, które mogłyby nas uchronić przed niecnymi zamiarami atakujących. W przypadku rozwiązań opartych na serverless jest znacznie łatwiej kontrolować dostęp oraz reguły bezpieczeństwa. Ponieważ nie ma motywacji do scalania zadań w większe serwisy, każda funkcja Lambda może wykonywać tylko małe zadanie, pracując w restrykcyjnych warunkach z ograniczonymi uprawnieniami. Bardzo łatwo zastosować zasadę minimalnych uprawnień (principle of least priviledge), przyznając dostęp tylko do niezbędnych zasobów. Takie zachowanie pomaga ograniczyć zasięg ewentualnego włamania do minimum.

Wraz z cennikiem skupionym na zużyciu twórcy aplikacji dążą do optymalizacji w postaci dynamicznych środowisk i infrastruktury. Dostawca może łatwo usunąć zbędne lub zastąpić istniejące zasoby bez przerywania pracy aplikacji. To ma krytyczne znaczenie w kontekście aplikowania poprawek bezpieczeństwa. Pewnego dnia, w sierpniu 2018 roku, otrzymaliśmy wiadomość e-mail od jednego z administratorów firmy naszego klienta. Pytał nas o plany wdrożenia poprawek bezpieczeństwa dotyczących dziur w procesorach Intel[5], które pojawiły się poprzedniego dnia. W trakcie wyszukiwania informacji na temat tych konkretnych błędów bezpieczeństwa natrafiłem na informację, że poprawki zostały już zaaplikowane przez Amazon na infrastrukturę, na której pracuje AWS Lambda. Nie musieliśmy z naszej strony wykonać żadnego działania w tej sprawie. To dostawca zadbał samodzielnie o to, aby stare środowiska uruchomieniowe zostały załatane, wykonując całą operację za nas, w dodatku bez zatrzymywania naszych aplikacji.

Wpływ modelu płatności na decyzje dotyczące rozwoju produktu

Cennik Lambdy wpływa również na sposób, w jaki organizacje wypuszczają nowe funkcjonalności. Czy kojarzysz sytuację, że na wymagania jednego klienta, wymagania, które notabene możesz przygotować w kilka dni, muszą czekać dodatkowe kilka tygodni, ponieważ tyle potrwa dostosowanie ich do wszystkich innych klientów i wdrożenie? Z AWS Lambda możesz wypuścić nową wersję dla tego konkretnego klienta tak szybko, jak tylko będzie ona gotowa, jednocześnie czekając na dostosowanie funkcjonalności do wszystkich. Dwa środowiska nie kosztują nas ani grosza więcej niż jedno współdzielone.

Podobnie, płatność za zużycie otwiera całkiem nowe możliwości związane z testami funkcjonalności. Jeśli chcesz przetestować, jak zmiana po stronie serwera wpłynie na konwersję, nic nie stoi na przeszkodzie, aby stworzyć drugie środowisko, na którym przetestujemy naszą zmianę, wysyłając tam 10% całkowitego ruchu. Liczba użytkowników pozostaje w dalszym ciągu taka sama, więc dwie wersje środowiska będą kosztowały dokładnie tyle co jedno środowisko. Uruchomienie testów A/B po stronie klienta jest z reguły tanie i szybkie. Organizacja tego samego po stronie serwera w klasycznych rozwiązaniach wiąże się z podwojeniem kosztów. Pięć lat temu tylko firmy z olbrzymimi zasobami, takie jak Google lub Facebook, mogły uruchamiać ciągle takie eksperymenty na swojej infrastrukturze serwerowej. Dzięki Lambdzie jest to dostępne dla wszystkich, nawet zespołów jednoosobowych. Nie kosztuje to więcej niż utrzymanie pojedynczego środowiska produkcyjnego.

Jeszcze innym interesującym aspektem analizowanego modelu cenowego jest granulacja kosztów na poziomie pojedynczego żądania. Aby poprawnie wyliczyć całkowity koszt, AWS zlicza każde żądanie i ta informacja jest dostępna również dla nas, klientów. Dysponując tymi informacjami, jesteśmy w stanie w pełni przeanalizować przepływ kapitału przez aplikację z dokładnością do pojedynczego zasobu. Możemy wyliczyć bardzo dokładnie, ile kosztuje nas obsługa pojedynczego klienta, włącznie z decyzją, czy klient jest wart obsługi, czy nie. Poza tym możemy bardzo łatwo policzyć, ile kosztuje nas utrzymanie poszczególnych funkcjonalności w naszej aplikacji, i z taką wiedzą możemy racjonalnie zadecydować, czy warto w nią dalej inwestować.

Ograniczenia techniczne na platformie AWS Lambda

Lambda jest stosunkowo młodą usługą i ewoluuje bardzo szybko. Bardzo często przygotowuję wystąpienia na konferencje i bawi mnie przeglądanie slajdów pod kątem poprawek związanych z ograniczeniami i problemami z serverless. Kwestie, takie jak czas startu aplikacji albo brak zgodności z wymaganiami PCI (Payment Card Industry), były poważnymi ograniczeniami w 2017 roku, ale zostały poprawione. Bardzo częstym zarzutem, z jakim Lambda się spotyka, jest brak umów SLA (Service Level Agreements), ale w październiku 2018 roku AWS opublikował dokument regulujący tę kwestię[6] (obecnie jest ona na poziomie 99,95%).

Wtedy, kiedy będziesz czytać tę książkę, wymienione niżej ograniczenia mogą ulec zmianie, ale w tym momencie są cztery ważne problemy, o których warto pamiętać w kontekście wykorzystania Lambdy:

- brak sesji użytkowników;

- niedeterministyczne opóźnienia;

- czas wykonania ograniczony do 15 minut;

- brak pełnej kontroli nad przydzielaniem mocy obliczeniowej.

Brak sesji użytkownikόw

Ponieważ to dostawca kontroluje proces skalowania, nie będziesz w stanie zadecydować o tym, jak wiele kontenerów przygotować do wykonywania poszczególnych funkcji. To Lambda zadecyduje, czy potrzebujesz dodatkowy zasób do obsłużenia żądania i kiedy wykorzystać ponownie poprzedni kontener lub usunąć ten aktywny. Może się tak zdarzyć, że dwa następujące po sobie żądania pochodzące od tego samego użytkownika zostaną obsłużone przez tę samą instancję funkcji lub dwie różne.

Nie ma możliwości, aby z wykorzystaniem samej Lambdy kontrolować przepływ żądań lub zapewnić w jakikolwiek inny sposób, żeby żądania od tego samego źródła trafiły w tej samej kolejności do tego samego celu. Można to uzyskać, korzystając z dodatkowych usług, takich jak Amazon Kinesis, ale to Lambda cały czas decyduje o tym, kiedy stworzyć nowy kontener, a kiedy ponownie użyć poprzedniego.

To ograniczenie, wraz z nazewnictwem dotyczącym funkcyjnego podejścia, bardzo często powoduje zamieszanie związane z tym, jak Lambda działa. Funkcje nie są bezstanowe, przynajmniej nie w znaczeniu związanym z programowaniem funkcyjnym, gdy nic nie jest ponownie wykorzystywane między poszczególnymi wywołaniami. Każde z wywołań dalej uruchomione jest na kontenerze, który ma lokalną pamięć. I jeśli platforma zadecyduje o ponownym skorzystaniu z niego, dwa kolejne żądania mogą dzielić stan przez lokalną pamięć kontenera. Ten sam kontener ma również tymczasową przestrzeń dyskową, więc jest możliwe skorzystanie z niej i zapisanie pliku. Nie mamy jednak żadnej gwarancji, dotyczącej ponownego wykorzystania tego stanu między żądaniami, oraz nie mamy wpływu na sposób rozdzielania żądań.

Najlepszym wyjściem jest założenie, że stan pamięci będzie zawsze nienaruszony między dwoma kolejnymi żądaniami obsłużonymi przez Lambdę. Należy tak projektować funkcje, aby nie współdzieliły niczego. Możemy oczywiście korzystać z pamięci podręcznej lub na początku przygotowywać pewne wyliczenia, jednakże sesja użytkownika oraz jego stan musi być składowany poza funkcjami Lambda. Mamy kilka różnych rozwiązań tego konkretnego problemu. Omówię je w rozdziale 7 oraz w rozdziale 8. W rozdziale finałowym podzielę się również typowymi rozwiązaniami, jak współdzielić stan oraz konfigurację między Lambdami.

Niedeterministyczne opόźnienia

Funkcje Lambda są zoptymalizowane, aby móc dostarczać jak najwyższą przepustowość, a nie najszybszy czas dostępu. Zatem ważniejsze jest obsłużenie wielu równoległych żądań, tak aby żadne z nich nie czekało, od tego, żeby obsłużyć każde z nich w jak najkrótszym czasie. Oznacza to, że w niektórych przypadkach żądania będą musiały poczekać, aż wystartuje nowa instancja funkcji, a w niektórych nie. Opóźnienia w przetwarzaniu będą trudne do przewidzenia.

W żargonie serverlessowym istnieje pojęcie zimnego startu (cold start), z którym mamy do czynienia wówczas, gdy nadchodzące żądanie musi poczekać na stworzenie nowej funkcji. Na początku istnienia serwisu zimny start Lambdy wynosił średnio kilka sekund. W tym czasie powstało wiele artykułów na temat, jak ominąć ten problem dzięki podtrzymywaniu ciepłych instancji na siłę. Ta sytuacja uległa jednak drastycznej poprawie i w wielu przypadkach można zignorować te porady. AWS nie publikuje żadnych informacji na temat zimnego startu, ale nasze testy sugerują, że jeśli chodzi o JavaScript lub Python, ta wartość oscyluje w granicach sekundy w pesymistycznym przypadku. Jeśli chodzi o języki, które posiadają maszynę wirtualną, takie jak Java, ten parametr będzie odpowiednio dłuższy i dodatkowo zależny od rozmiaru aplikacji. To jeszcze jeden powód, dlaczego preferujemy lekkie środowiska i języki programowania w przypadku platformy Lambda. Jeśli chodzi o bardziej drobiazgową analizę tego problemu (jednak w dalszym ciągu nieoficjalną), polecam sprawdzić artykuł autorstwa Mikhaila Shilkova[7].

Pod koniec 2019 roku (tuż po opublikowaniu pierwszej wersji książki) AWS znacząco zredukował czas startu w odniesieniu do funkcji Lambda, które korzystają z połączenia do usługi VPC (Virtual Private Cloud). W przypadku aplikacji, które nie mogą pozwolić sobie na długotrwałą inicjalizację, dostawca umożliwił rezerwację zasobów, co praktycznie rozwiązało problem w sprawie zimnego startu.

Ograniczony czas wykonania

Kolejnym znaczącym obostrzeniem jest maksymalny czas wykonania. Obecnie Lambda jest ograniczona domyślnie do 3 sekund, ale możemy ten czas wydłużyć, pamiętając jednak o tym, że górny limit wynosi 15 minut. Jeśli zadanie nie zostanie wykonane w zadanym czasie, platforma zatrzyma środowisko uruchomieniowe oraz odeśle błąd związany z przekroczeniem czasu wykonania.

Wspomniany górny limit jest na ten moment nieprzekraczalny. Dłuższe zadania muszą być podzielone na mniejsze i wykonywane w kolejnych wywołaniach lub za pomocą innego serwisu. AWS ma w swoim portfolio usługi, takie jak Fargate, które na to pozwalają w modelu serverless, wszystko jednak kosztem dłuższego zimnego startu oraz wyższego kosztu za zużycie.

Gdy znamy to ograniczenie, w wielu przypadkach, projektując aplikację, będziemy w stanie przygotować odpowiednie rozwiązanie tego problemu. Przykładem może być wykorzystanie funkcji do wystartowania zdalnego zadania z długim czasem oczekiwania oraz oczekiwanie na odpowiedź za pomocą kolejnej funkcji. W praktyce pierwsza funkcja wysyła żądanie do serwisu, który na zakończenie długotrwałego procesu wykona zupełnie inną funkcję Lambda. Można wykorzystać do koordynacji koleją usługę dostępną na platformie AWS, zwaną Step Functions. Koordynacja i oczekiwanie na odpowiedź może trwać nawet do roku, a po otrzymaniu odpowiedzi możemy wykonać kolejną funkcję. Dzięki takiej usłudze nie trzeba już nigdy więcej płacić za oczekiwanie.

Brak pełnej kontroli nad przydzielaniem mocy obliczeniowej

Ostatni problem związany jest z alokacją mocy obliczeniowej. Dzisiaj w klasycznych rozwiązaniach opartych na kontenerach możemy doprecyzować, jakimi dysponują zasobami i mocą obliczeniową, wliczając w to różne rodzaje i rozmiary CPU oraz GPU, oraz typy zasobów zoptymalizowanych pod kątem konkretnych zastosowań. Jeśli chodzi o Lambdę, nie mamy takiej możliwości. Jedyna decyzja dotyczy ilości pamięci, od 128 MB do prawie 4 GB. Nie ma sposobu, aby np. dołączyć do naszej funkcji GPU.

Dodatkowo decyzja dotycząca pamięci wpływa pośrednio na moc obliczeniową. Lambda przeznacza określoną część wirtualnego procesora proporcjonalnie do wybranej pamięci. Od wartości 1792 MB otrzymujemy na wyłączność jeden wirtualny procesor[8]. W przypadku Node.js wszystkie operacje i tak wykorzystują tylko jeden rdzeń, więc jeżeli chodzi o to środowisko, nie ma sensu przydzielać więcej niż wyżej wspomnianą wartość. W odniesieniu do innych środowisk, np. Java, możemy w pełni wykorzystać wiele rdzeni i wtedy alokacja maksymalnej dopuszczalnej wartości, jeśli chodzi o pamięć, bezpośrednio przełoży się na szybsze czasy odpowiedzi i niższy koszt wykonania zadań, które potrzebują więcej czasu od procesora.

Warto pamiętać, że część ceny związana ze zużyciem jest uzależniona od dwóch rzeczy: od zaalokowanej pamięci oraz od czasu trwania wykonania naszego kodu. Biorąc pod uwagę fakt, że więcej pamięci przekłada się proporcjonalnie na procesor, może to doprowadzić do sytuacji, w której teoretycznie droższa Lambda będzie w efekcie tańsza, co jest niezgodne z intuicją. Co prawda, wybierając funkcję z większą ilością pamięci, podrażamy opcję cenową, ale jeśli to znacząco obniży czas wykonania, możemy dzięki temu uzyskać niższy koszt całkowity.

Ponieważ nie mamy jednak bezpośredniej kontroli nad alokacją mocy obliczeniowej, najlepszą metodą na obniżenie kosztów jest eksploracja możliwych kombinacji. Na szczęście, jak wcześniej wspominałem, platforma ułatwia przygotowanie wielu środowisk testowych, nie pociągając za sobą dodatkowych kosztów, oraz sama zmiana przydzielonej pamięci jest prosta i natychmiastowa. W przypadku zadań, które stają się za wolne lub zbyt drogie, gorąco polecam eksperymenty związane z przydzielaniem odpowiedniej ilości pamięci, tak aby odnaleźć idealną wartość, jeśli chodzi o cenę oraz wydajność. Dzięki otwartemu projektowi Alexa Casalboniego AWS Lambda Power Tuning[9] będziesz w stanie porównać wydajność (i koszty) w odniesieniu do różnych konfiguracji Twoich funkcji Lambda.

Kiedy używać Lambdy?

Lambda pasuje idealnie w sytuacjach, w których przepustowość jest kluczowa, a zadania łatwo zrównoleglić. Typowym przykładem są żądania w aplikacjach webowych, które wymagają wykonania odpowiedniej logiki biznesowej w zależności od przesłanych parametrów lub stanu zapisanego w zewnętrznym serwisie, np. bazie danych. Inny bardzo dobry przykład to automatyczna obsługa przychodzących zdarzeń, np. wiadomości e-mail lub zapytań od botów. Obsługa każdego z pojedynczych żądań, które zajmuje średnio najwyżej paręset milisekund, nie będzie zauważalne przez użytkownika końcowego, a Lambda pozwoli na to, żeby każdy został odpowiednio szybko obsłużony, niezależnie od obciążenia i zmiennych wzorców związanych z ruchem.

Długotrwałe przetwarzanie danych albo zadania obliczeniowe, które będą trwać krócej niż 15 minut lub które mogą być podzielone na niezależne segmenty o wspomnianym czasie trwania, to również dobry przykład wykorzystania tej platformy. W takich przypadkach zimny start nie będzie miał praktycznie żadnego wpływu na proces przetwarzania. Dobrym przykładem takich zadań są konwersje plików do różnych formatów, generowanie miniaturek lub obrazów podglądu oraz cykliczne raporty.

Zadania, które potrzebują wysokiej dostępności oraz stabilnej infrastruktury, ale nie mają wyśrubowanych wymagań co do czasu trwania, to również dobry przykład użycia. Jednym z takich przypadków użycia jest obsługa płatności oparta na zewnętrznych usługodawcach, np. PayPal lub Stripe. Obsługa takich żądań i przychodzących notyfikacji musi być stabilna oraz relatywnie szybko reagować na zmieniające się wzorce ruchu, za to nie ma wymagań, aby czas odpowiedzi był jak najniższy.

Lambda w tym momencie nie jest przystosowana do zadań, które wymagają gwarancji najniższych czasów odpowiedzi, np. handel wysokich częstotliwości (high-frequency trading, HFT) lub systemy czasu rzeczywistego, ze sztywnymi gwarancjami co do czasu wykonania. Jeśli zadanie wymaga, aby było obsłużone zawsze poniżej pewnego progu, np. 10 lub 20 ms, dużo lepszym rozwiązaniem będzie klasyczny model, w przypadku którego zarezerwujemy odpowiednie zasoby i podłączymy go bezpośrednio do źródła żądań.

Kolejna sytuacja, w przypadku której Lambda się nie sprawdzi, dotyczy zadań wymagających dłuższego czasu wykonania niż 15 minut. Dobrym przykładem będzie transkodowanie dużych plików wideo. Innym przykładem jest wykorzystanie stanowego połączenia sieciowego i nieustanna konsumpcja danych – z powodu ograniczenia nałożonego na czas wykonania będziemy musieli rozłączać i podłączać się ponownie.

Trzecią kategorią przypadków, w odnieisieniu do których nie polecam używać AWS Lambda, jest przetwarzanie wymagające dużej mocy obliczeniowej i koordynacji, np. renderowanie wideo. Zadania tego typu lepiej wykonywać na przeznaczonej do tego i zarezerwowanej infrastrukturze, w której kontrolujemy alokację zasobów i którą możemy dostosowywać na żądanie (włączając w to CPU oraz GPU).

Jako ostatni negatywny przykład chciałbym zaprezentować przypadek, który nie wymaga żadnych operacji wykonywanych na żądanie, np. serwowanie statycznych plików stron internetowych. W teorii nic nie stoi na przeszkodzie, aby wykorzystać do tego Lambdę, jednak będzie to strata pieniędzy. Wyjdzie znacznie taniej i szybciej, jeżeli skorzystamy ze specjalizowanej usługi, np. CDN (Content Delivery Network).

Interesującym aspektem wykorzystania Lambdy jest jej integracja z pozostałymi usługami na platformie AWS. Dzięki temu, krok po kroku, jesteśmy w stanie transformować nasze aplikacje, aby lepiej korzystały z architektury typu serverless, korzystając z przeznaczonych specjalizowanych usług, które lepiej pasują do tego konkretnego przypadku niż AWS Lambda. Możesz przeczytać o takich przykładach w rozdziale 13.

Rozpoczynając od rozdziału 3, zaczniemy budować usługę, która połączy dwie wyżej wymienione kategorie zadań: przetwarzanie żądań w aplikacji webowej oraz konwersję plików w tle. Jednak najpierw skonfigurujemy lokalne środowisko, tak aby łatwiej było nam tworzyć i testować naszą aplikację.

Interesujące kwestie

- Przeanalizuj swoje aplikacje, nad którymi obecnie pracujesz, i postaraj się zidentyfikować pojedyncze zadania wykonywane przez kod aplikacji.

- Zastanów się nad czasem wykonania i przepustowością każdego z takich zadań, włączając w to wykonania równoległe, czasem trwania i ograniczeniami związanymi z sesją użytkowników. Zidentyfikuj te, które warto uruchomić jako funkcje Lambda.

- Przygotuj minimalny zestaw uprawnień wobec wybranych zadań i porównaj je z prawami dostępu, jakie mają obecnie.

- Postaraj się wyestymować koszt uruchomienia tych zadań na platformie AWS Lambda.

Przypisy

[1] https://pages.awscloud.com/Gated_IDC_Generating_Value_Through_IT_Agility.html

[2] https://www.doc.ic.ac.uk/~rbc/papers/fse-serverless-17.pdf

[3] https://get.cloudability.com/ebook-state-of-cloud-2018.html

[4] https://www.youtube.com/watch?v=b7Nc_FJiosk

[5] Chodziło o błędy Meltdown oraz Spectre (od tłumacza).

[6] https://aws.amazon.com/lambda/sla/

[7] https://mikhail.io/serverless/coldstarts/aws/

[8] https://docs.aws.amazon.com/lambda/latest/dg/resource-model.html

[9] https://github.com/alexcasalboni/aws-lambda-power-tuning