Azure Functions – na dzisiaj ślepa uliczka dla API

.NET Developer | I build applications from concept to production. On my blog, I share practical examples (.NET “by example”) and thoughts on software architecture and bridging technology with business.
Azure Functions – obietnica serverless, rzeczywistość dla API
Azure Functions obiecywały wiele: skalowalność, niski koszt, zero zarządzania infrastrukturą. W teorii to idealne miejsce na wystawienie prostego API, zwłaszcza przy nieregularnym ruchu. W praktyce – funkcje nie są kompletnym frameworkiem webowym do budowy interfejsów HTTP. Tworzenie API w tym modelu często kończy się rozczarowaniem. Szczególnie teraz, gdy Microsoft porzuca stary model (in-process), a nowy (out-of-process) wprowadza poważne ograniczenia.
Dwa modele: in-process vs out-of-process
Usługa Azure Functions zadebiutowała na platformie Azure w 2016 roku jako rozwiązanie serverless, które pozwala uruchamiać małe, niezależne funkcje reagujące na zdarzenia – takie jak żądania HTTP, wiadomości z kolejki czy zdarzenia czasowe – bez potrzeby zarządzania infrastrukturą. Początkowo funkcje działały wyłącznie w modelu in-process, czyli były uruchamiane w tym samym procesie, co host Azure Functions. Pozwalało to na korzystanie z naturalnego dla programistów zestawu narzędzi ASP.NET przy budowie API – takiego samego, jak w aplikacjach OnPremises, kontenerach Docker czy Azure App Service.
Z czasem jednak ten model zaczął ujawniać swoje ograniczenia. Współdzielenie procesu z hostem oznaczało brak izolacji środowisk, trudności w aktualizacji wersji .NET, konflikty zależności oraz ograniczoną możliwość niezależnego rozwoju i testowania. Problemem była też niższa niezawodność i większe ryzyko błędów przy uruchamianiu bardziej złożonych projektów.
Aby zaadresować te wyzwania, Microsoft wprowadził w 2020 roku model out-of-process (znany również jako isolated worker). Funkcje działają tu w osobnym procesie .NET, który komunikuje się z hostem za pomocą RPC. Taka separacja pozwala używać dowolnych wersji środowiska .NET niezależnie od platformy Azure Functions i unikać konfliktów wersji.
Model isolated eliminuje wiele ograniczeń technicznych modelu in-process, ale jednocześnie rezygnuje z kilku kluczowych cech. Microsoft zdecydował, że to właśnie model out-of-process będzie jedynym wspieranym w przyszłości. Wsparcie dla Azure Functions in-process zakończy się w listopadzie 2026 roku.
Problemy modelu out-of-process w budowie API
Model out-of-process rozwiązuje pewne problemy techniczne starszego podejścia, ale w kontekście budowy API wprowadza wiele ograniczeń. Trudno je zaakceptować przy pracy nad rzeczywistymi usługami HTTP. Poniżej przedstawiam najważniejsze z nich:
Brak wsparcia dla ASP.NET
Nie można użyć UseMiddleware(), nie ma dostępu do IApplicationBuilder. Oznacza to, że nie zbuduje się automatycznie pełnego pipeline’u z autoryzacją, walidacją modeli, filtrowaniem błędów. Wszystko trzeba implementować inaczej i bardziej ręcznie. W praktyce oznacza to więcej kodu do utrzymania oraz utratę możliwości szybkiego zmianu sposobu hostowania API - gdy zdecydujesz się budowac w Isolated Model, migracja to innego środowiska będzie kosztowna.
Brak możliwości użycia najlepszych narzędzi do generowania dokumentacji API
W Azure Functions Isolated model, nie da się po prostu dodać Swashbuckle/Swagger do generowania dokumentacji jak w Web API. Potrzebna jest osobna biblioteka -Microsoft.Azure.Functions.Worker.Extensions.OpenAPI. Niestety na dzisiaj ma ona ograniczenia. Nie wspiera wielu cech ASP.NET, na przykład generowania dokumentacji dla klas powiązanych dziedziczeniem należących do kontraktu. Dodatkowo konfiguracja biblioteki jest mniej intuicyjna, a dokumentacja bywa niejasna. DX (Developer Experience) wyraźnie się pogarsza.
Cold start i niestabilna wydajność
Model out-of-process wymaga uruchomienia osobnego procesu. W planie konsumpcyjnym może to oznaczać kilkadziesiąt sekund czekania na odpowiedź po dłuższej przerwie. Co gorsza, każdy endpoint HTTP traktowany jest niezależnie – jeśli Twoje API składa się z wielu funkcji, to każda z nich ma własny cold start. W modelu in-process wystarczyło rozgrzać jeden endpoint (np. /health), by uruchomić cały proces hosta i tym samym „wybudzić” wszystkie punkty dostępowe na raz. W modelu isolated to już nie działa. Cold starty są bardziej dotkliwe i trudniejsze do obejścia. W mojej ocenie to show stopper dla większości przypadków tworzenia publicznego API.
Model in-process – umiera, ale był bardziej wygodny
Mimo swoich ograniczeń, model in-process był po prostu praktyczny dla małych API. Działał jak okrojony ASP.NET Web API – wspierał kontrolery, middleware, znane mechanizmy DI, a wiele gotowych narzędzi działało od ręki. Teraz Microsoft oficjalnie każe go porzucić. Od .NET 8 wspierany jest wyłącznie isolated worker. Jeśli korzystałeś wcześniej z in-process, będziesz musiał zaplanować migrację. Jeśli dopiero zaczynasz – warto rozważyć inne podejście do hostowania API, na przykład Azure App Service.
Flex Consumption – odpowiedź na problem cold startów w Azure Functions
Flex Consumption Plan pojawił się w Azure Functions w 2024 roku jako odpowiedź na problem cold startów. Ten model łączy zalety serverless podstawowego planu Consumption z utrzymywaniem funkcji w stanie „ciepłym” podobnie jak w planie Premium. Dzięki temu w API o nieregularnym ruchu pierwsze żądania trafiają do w pełni gotowych instancji, a ryzyko cold startu jest minimalne. Przy skalowaniu Azure korzysta z puli wstępnie przygotowanych instancji, dzięki czemu kolejne instancje uruchamiają się znacznie szybciej niż w klasycznym Consumption.
Niestety korzystanie z opcji Always Ready Instances oznacza stałe koszty – tu nie ma już darmowego miliona requestów miesięcznie, a płaci się cały czas, podobnie jak w App Service. Dodatkowo usługa nie eliminuje pozostałych ograniczeń modelu isolated.
Azure App Service – stary dobry koń roboczy
Jeśli chcesz wystawić produkcyjne REST API, nawet małe, to Azure App Service z Minimal API lub Web API moim zdaniem będzie lepszym wyborem.
Dostajesz:
pełne wsparcie powszechnie znanego przez deweloperów środowiska ASP.NET,
działające Swashbuckle w 3 linijki,
middleware, DI, filtry, walidację modeli,
przewidywalne czasy odpowiedzi i brak cold startów,
bardzo dobre lokalne testowanie – zwłaszcza jeśli używasz kontenerów
możliwość prostrzej migracji rozwiązania w przyszłości na inne platformy
Azure App Service daje elastyczność. Możesz uruchomić API jako zwykłą aplikację lub jako kontener – zarówno na Windowsie, jak i Linuksie.
Choć nie ma planu płatności „pay-as-you-go”, to już dziś można mieć stale działające API produkcyjne na Linuksie za ok. 45 zł miesięcznie.
Jeśli więc chcesz hostować małe API bez obaw o wydajność i przewidywalność działania – moim zdaniem Azure App Service wygrywa jakością i ergonomią z Azure Functions Isolated Model.
Jak używam Azure App Service
Sam wykorzystuję Azure App Service do hostowania mojego pet projektu - aplikacji z żartami Jokes Portal. Jest to aplikajca mobilna, która wykorzystuje Azure App Service. Działa 24/7, obsługuje realnych użytkowników i potrzebuje stabilnego API bez cold startów. Usługa sprawdza się tu znakomicie.
Podsumowanie: Nie każde rozwiązanie to API
W tym artykule skupiłem się wyłącznie na przypadku budowy REST API. Nie twierdzę, że Azure Functions są złe – wręcz przeciwnie. Model isolated to świetne narzędzie do budowy systemów event driven w chmurze Azure. Funkcje wspierają integracje z niemal wszystkimi usługami Azure out-of-the-box i doskonale sprawdzają się w scenariuszach asynchronicznych oraz transakcjach rozproszonych (Durable Functions).
Ze względu na opisane wyżej ograniczenia uważam, że Azure Functions w modelu isolated nie nadają się dziś do budowy synchronicznych REST API. W takich przypadkach warto postawić na Azure App Service. To może nie jest modne, ale po prostu działa.




