Magia kontenerów i mikroserwisów w chmurze

Magia kontenerów i mikroserwisów w chmurze

Kontenery, Docker, IoT i mikroserwisy – na słowa te możemy się coraz częściej natknąć przeglądając fora bądź blogi technologiczne. Co one jednak oznaczają? W swoim pierwszym wpisie postaram się Wam przybliżyć co wegług mnie oznaczają kontenery.

Od ogółu do szczegółu

Zaczniemy od ogółu do szczegółu. Zdobywamy kontrakt, zbieramy wymagania, tworzymy projekt – jeden z najbardziej typowych scenariuszy. Nasz super-hiper produkt wdrażamy na produkcję, wszyscy są zadowoleni :). Aplikacja nasza działa, działa, lecz pewnego pięknego poranka dostajemy zapytanie o dodanie nowych funkcjonalności, po czym ponownie analizujemy problem i dokodowujemy jego rozwiązanie. Powtarzamy całość tej interacji N razy. Ilość linijek kodu coraz bardziej nam się rozrasta, czas kompilacji, testów i wdrożenia wydłuża. Z biegiem czasu i wzrostem obciążenia aplikacji obserwujemy, że jeden z elementów naszej “apki nie wyrabia”, potrzebuje on znaczącej ilości pamięci operacyjnej. Podejmujemy decyzję: Skalujemy!. W końcu kto bogatemu zabroni…

Uruchamiamy kolejne instancje naszej aplikacji, lecz pojawia nam się świadomość, że niewykorzystujemy w pełni efektywnie dzierżawionych przez nas instancji serwerów.

I właśnie w tym miejscu, każdy z architektów projektu powinien się zastanowić i zadać sobie jedno, ważne pytanie: Czy nas stać, by dalej skalować całość aplikacji, czy dzielimiy ją na mniejsze kawałki?, a następnie wybrać odpowiednią drogę.

Zdecydowałeś? Podjąłeś męską decyzję? Jeśli wciąż to czytasz, to znak, że dobrze wybrałeś 🙂 . Na tego rodzaju dylematy i problemy jednym z ciekawszych rozwiązań jest architektura mikroserwisów (bądź mikrousług, jak to niektóre słowniki tłumaczą). Jak to działa? Działanie tego wzorca jest proste. Zamiast tworzyć jedną wielką aplikację, tworzymy kilka mniejszych “pod-aplikacji”. Zaletą takiego podziału jest możliwość skalowania nie tylko całości aplikacji, lecz również tylko tych komponentów, które potrzebujemy zeskalować.

Figure 1Porównanie monolitycznej i “mikroserwisowej” architektury aplikacji

Komunikacja

Świat zewnętrzny (internet) komunikuje się z naszymi mikroserwisami poprzez bramę API (ang. API Gateway), która przekierowuje zapytania do odpowiednich mikroserwisów. Komunikacja wewnątrz naszej aplikacji niestety nie wygląda już tak kolorowo. Poszczególne usługi zazwyczaj porozumiewają się między sobą za pomocą zapytań HTTP bądź wiadomości wrzucanych na szynę danych/kolejkę, w przeciwieństwie do bezpośrednich wywołań funkcji w kodzie.

Wybierając pierwszą opcję (HTTP) musimy być ostrożni, ponieważ zbyt rozmowne interakcje między poszczególnymi serwisami (ang. chatty conversation) mogą stać się źródłem klęski naszej superskalowalnej platformy. Dzieląc nasz istniejący system (bądź podczas tworzenia nowego) powinniśmy startować ze stosunkowo niewielką liczbą serwisów, stopniowo dodając nowe, bądź łącząc już istniejące, gdyż jak wiadomo – nie od razu Kraków zbudowano, i naszą aplikację powinniśmy rozwijać w ramach coraz to kolejnych iteracji.

Druga opcja powinna być dla nas chlebem powszednim, jeżeli chcemy mówić o jakim kolwiek wykonywaniu operacji długich, asynchronicznych, takich jak synchronizacja danych pomiędzy zewnętrznymi serwisami, przetwarzanie plików, bądź wszelkiego rodzaju analityka. Komunikacja ta polega na tworzeniu przez jedną z usług wiadomości, którą później serializujemy do najbardziej odpowiadającego nam formatu (zazwyczaj JSON), a następnie publikujemy ją do kolejki. Nie musimy się tutaj ograniczać tylko i wyłącznie kolejki – możemy również publikować ją do innych dobrodziejst tegoż mechanizmy (topic, direct, fanout, queue). Możemy wykorzystać tutaj typowe rozwiązanie on-premise, jakim jest RabbitMQ, bądź skorzystać z gotowego rozwiązania PaaS, jakim jest między innymi Azure Queue Storage bądź Azure Service Bus.

Co to są te całe ‘kontenery’?

Była mowa o mikroserwisach, teraz czas na kontenery! Kontenery tak naprawdę są środowiskiem uruchomieniowym, dla naszych mikroserwisów. Mają one znacznie krótszy czas uruchomienia jak i wymagania dyskowe w porównaniu do maszyn wirtualnych, bądź fizycznych serwerów. Zapewniają one izolację pomiędzy sobą, przez co na jednym hoście możemy uruchomić wiele instancji nawet tej samej aplikacji. Usprawniają one również proces DevOps’owy poprzez pewnego rodzaju standardyzację, jaką są kontenery Docker. Co więcej, w kontenerze takim możemy uruchomić niemal wszystko! Głównym magazynem obrazów kontenerów jest Docker Hub, z którego możemy pobrać wiele ciekawych obrazów, między innymi MySQL, SQL Server, MongoDB czy Nginx. Kontenery powoli stają się nowym sposobem dostarczania oprogramowania i usług, dając nam – programistom, administratorom czy architektom znaczną oszczędność czasu przy powoływaniu nowych środowisk bądź wdrożeniach. Kontenery Dockerowe dzielą się na Windowsowe oraz Linuxowe. Kontery Windowsowe mogą uruchamiać pełen .NET Framework, jednak obecny rozmiar obrazu 9GB nie brzmi już tak kusząco…  Definiujemy je w dość prosty sposób poprzez tajemniczy plik o nazwie Dockerfile:

  FROM microsoft/aspnetcore:1.1
  ARG source
  WORKDIR /app
  ENV ASPNETCORE_URLS http://+:5103
  EXPOSE 5103
  COPY ${source:-obj/Docker/publish} .
  ENTRYPOINT ["dotnet", "Vote.API.dll"]

Pliki Dockerfile mają pewnego rodzaju strukturę cebuli, w której definiujemy poszczególne warstwy wymagane do zbudowania obrazu kontenera (środowiska uruchomieniowego, podobnie jak maszyny wirtualnej). Dokładny opis jak stworzyć plik Dockerfile znajdziecie tutaj. Kontenery możemy łączyć w gotowe solucje, poprzez narzędzie Docker Compose, nad którym z uwagi na jego złożoność nie będę się dalej zagłębiał.

Gdzie uruchamiać?

Zarówno mikroserwisy, jak i kontenery najlepiej jest uruchamiać w chmurze, która gwarantuje nam wysoką dostępność, jak i niemal nieograniczone możliwości skalowania naszego rozwiązania.

Jedną z opcji jest skorzystanie z gotowej usługi hostingowej kontenerów wraz z orchestratorem (Kubernetes, DC/OS, Docker Swarm), jaką jest Azure Container Service.

Inną opcją jest budowa naszego rozwiązania w oparciu o framework Azure Service Fabric, które zarówno jak i w powyższym przypadku możemy wdrożyć zarówno na środowisko chmurowe, jak i nasze on-premisowe.
Możliwości wdrożenia jest wiele, jednak jeżeli zależy nam na gwarancji wydajności, dostępności, jak i skali, naszym numerem jeden powinna być właśnie chmura!

Demo

Na potrzeby tego postu stworzyłem przykładowe API do przeprowadzania głosowania, korzystające z kontenerów Dockera. Kod solucji znajdziecie tutaj. Do uruchomienia potrzebujemy Visual Studio 2017 wraz z zainstalowanym wsparciem dla .NET Core oraz Docker dla systemu Windows. Projekt składa się z kontenera zawierającego naszą aplikację webową, kontenera zawierającego system kolejkowy RabbitMQ oraz kontenera zawierającego Redisa. Aplikacja webowa korzysta z Redisa jako bazy danych o głosowaniach oraz RabbitMQ, na który publikuje eventy mówiące o dodaniu głosu bądź nowego głosowania. Projekt Vote.API jest przykładem mikroserwisu, który w całości odpowiada za przechowywanie swojego stanu i danych, a komunikacja z nim jest możliwa za pomocą interfejsu HTTP bądź kolejki.

Co dalej?

Możliwości implementacji mikroserwisów jest wiele. Możemy skorzystać z kontenerów, nad którymi będzie czuwał
Polecam Wam przyjrzeć się serii artykułów Martina Fowlera, który w szczegółowy sposób opisuje detale, jak i wady i zalety mikroserwisów, jak i serię postów na blogu NGINX.

Dodatkowo chciałbym w tym miejscu podziękować Michałowi Furmakiewiczowi oraz Markowi Grabarzowi, za okazanie mi zarówno wsparcia merytorycznego, jak i inspiracji. 🙂

One thought on “Magia kontenerów i mikroserwisów w chmurze

Leave a Reply

Your email address will not be published. Required fields are marked *