Serverless, czyli architektura bezserwerowa, to rewolucja w sposobie myślenia o budowaniu i wdrażaniu aplikacji. W przeciwieństwie do tradycyjnych modeli, gdzie deweloperzy muszą zarządzać serwerami, systemami operacyjnymi i skalowaniem, w podejściu serverless cała ta odpowiedzialność spoczywa na dostawcy chmury. Programista skupia się wyłącznie na pisaniu kodu, a dostawca (jak AWS, Google Cloud czy Azure) zajmuje się resztą – skalowaniem, utrzymaniem i dostępnością. Choć nazwa „bezserwerowy” jest nieco myląca, ponieważ serwery oczywiście nadal istnieją, to odpowiedzialność za nie jest przeniesiona poza zespół deweloperski.
Kiedy Serverless ma sens?
Serverless nie jest panaceum na wszystkie problemy i nie sprawdzi się w każdym scenariuszu. Istnieją jednak konkretne przypadki użycia, w których jego zalety stają się bezkonkurencyjne.
Po pierwsze, idealnie nadaje się do aplikacji opartych na zdarzeniach (event-driven applications). Funkcje serverless, takie jak AWS Lambda, Google Cloud Functions czy Azure Functions, są projektowane do uruchamiania w odpowiedzi na konkretne zdarzenia. Może to być wrzucenie pliku do magazynu obiektów (np. AWS S3), zmiana w bazie danych, wiadomość w kolejce (np. SQS) czy wywołanie z API Gateway. Taka architektura jest niezwykle efektywna, ponieważ kod jest uruchamiany tylko wtedy, gdy jest potrzebny, co przekłada się na oszczędności. Przykładem może być funkcja, która automatycznie przetwarza obrazy po ich przesłaniu na stronę.
Po drugie, API i mikroserwisy. Tworzenie bezstanowych, skalowalnych mikroserwisów za pomocą funkcji serverless jest bardzo proste. Każda funkcja może być oddzielnym endpointem API, który przetwarza żądania. Skalowanie następuje automatycznie w zależności od liczby przychodzących zapytań, a deweloper nie musi martwić się o konfigurację serwerów czy równoważenie obciążenia. Pozwala to na szybkie prototypowanie i wdrażanie nowych funkcjonalności.
Po trzecie, zadania wsadowe i procesy w tle. Serverless świetnie sprawdza się w przypadku sporadycznie uruchamianych zadań, które przetwarzają duże ilości danych, ale nie wymagają ciągłego działania serwera. Przykładowo, można uruchomić funkcję, która raz na dobę generuje raporty, bez konieczności utrzymywania dedykowanej maszyny przez całą dobę.
Wreszcie, aplikacje z bardzo zmiennym ruchem. Jeśli Twoja aplikacja doświadcza gwałtownych wzrostów i spadków ruchu, serverless jest idealnym rozwiązaniem. Automatyczne skalowanie w górę (scale-up) i w dół (scale-down) jest praktycznie natychmiastowe, co oznacza, że płacisz tylko za czas, w którym Twoja aplikacja faktycznie działa, a nie za bezczynne serwery.
Anti-patterny, których należy unikać
Choć serverless jest potężny, istnieją pułapki, które mogą sprawić, że jego użycie będzie nieefektywne lub wręcz szkodliwe.
Anti-pattern nr 1: Długo działające, intensywne obliczeniowo zadania. Funkcje serverless mają zazwyczaj limit czasu wykonania (np. 15 minut w AWS Lambda). Ich głównym przeznaczeniem są krótkotrwałe operacje. Używanie ich do zadań, które trwają godzinami, jest kosztowne i nieefektywne. W takich przypadkach lepiej sprawdzą się rozwiązania oparte na kontenerach (np. AWS Fargate) lub tradycyjne maszyny wirtualne.
Anti-pattern nr 2: Stan w funkcjach (stateful functions). Funkcje serverless są zaprojektowane jako bezstanowe. Oznacza to, że nie ma gwarancji, że kolejne wywołanie funkcji trafi na ten sam „serwer”. Próba przechowywania stanu, takiego jak dane sesji, w pamięci funkcji jest błędem, ponieważ stan ten może zostać utracony. Wszelkie dane, które muszą przetrwać pomiędzy wywołaniami, powinny być przechowywane w zewnętrznej, trwałej bazie danych lub magazynie.
Anti-pattern nr 3: Monolit w Serverless. Próba przeniesienia dużej, monolitycznej aplikacji do jednego gigantycznego mikroserwisu serverless to częsty błąd. Taki „monolit serverless” będzie trudny w zarządzaniu, testowaniu i debugowaniu. Architektura bezserwerowa najlepiej działa, gdy aplikacja jest podzielona na małe, niezależne funkcje, z których każda ma jedno, jasno określone zadanie.
Optymalizacja kosztów w Serverless
Jedną z największych zalet serverless jest model rozliczeniowy „pay-per-use”, co oznacza, że płacisz za liczbę wywołań i czas ich trwania. Choć wydaje się to proste, można łatwo wpaść w pułapki, które znacząco zawyżą rachunki.
Przede wszystkim, monitoruj użycie i koszty. Dostawcy chmury udostępniają narzędzia do monitorowania zużycia zasobów i przewidywania kosztów. Regularne sprawdzanie tych danych pozwala na szybkie wykrycie nieoczekiwanych wzrostów. Wykorzystaj narzędzia do budżetowania i alerty, aby otrzymywać powiadomienia, gdy koszty przekroczą ustalony próg.
Dopasuj pamięć (memory) i moc obliczeniową (CPU) do potrzeb. W większości platform serverless, ilość przydzielonej pamięci RAM jest bezpośrednio powiązana z mocą obliczeniową (CPU). Mimo że automatyczne skalowanie działa świetnie, to my, jako deweloperzy, musimy zdefiniować, ile pamięci potrzebuje nasza funkcja. Zbyt duża ilość pamięci to marnotrawstwo i wyższe koszty, a zbyt mała może prowadzić do wolniejszego działania lub nawet awarii. Eksperymentuj z różnymi ustawieniami, aby znaleźć optymalny punkt.
Zmniejszaj czas wykonania funkcji. Im krócej działa funkcja, tym mniej płacisz. Optymalizuj kod, używaj asynchronicznego programowania, ograniczaj zbędne operacje wejścia-wyjścia i dbaj o to, aby zewnętrzne wywołania (np. do baz danych) były jak najszybsze. Wybieranie efektywnych algorytmów i optymalizacja logiki biznesowej bezpośrednio przekłada się na niższe koszty.
Wykorzystaj mechanizmy „cold start” z rozwagą. „Cold start” to czas, w którym funkcja uruchamia się po raz pierwszy po okresie nieaktywności. Może on trwać od kilkudziesięciu milisekund do kilku sekund. Długie czasy cold start mogą wpływać na doświadczenia użytkownika. W krytycznych aplikacjach można wykorzystać mechanizmy „provisioned concurrency” lub „pre-warming” (dostępne w niektórych platformach), które utrzymują instancje funkcji w gotowości, eliminując cold start kosztem stałej opłaty.
Architektura serverless to potężne narzędzie, które może znacznie przyspieszyć proces tworzenia aplikacji i obniżyć koszty, pod warunkiem, że jest używane z rozwagą. Idealnie sprawdza się w aplikacjach opartych na zdarzeniach, w mikroserwisach oraz w zadaniach wsadowych, które nie wymagają stałego działania serwera. Kluczowe jest unikanie pułapek, takich jak długo działające funkcje, przechowywanie stanu i tworzenie monolitycznych potworów. Właściwa optymalizacja kosztów, poprzez monitorowanie, dostosowywanie zasobów i skracanie czasu wykonania funkcji, pozwala w pełni wykorzystać potencjał tego modelu, przekładając się na realne oszczędności i zwiększoną efektywność.