Zaznacz stronę

Za co, poza dostarczaniem starych laptopów, najczęściej krytykuje się dostawców usług IT? Brak zdolności nadążania za zmianą skali biznesu. Zwykle oczekuje się, że IT będzie w stanie prawie natychmiast skalować swoje możliwości wraz ze zmianami skali biznesu. Każdy nowy styl architektury IT obiecuje, że będzie od teraz w stanie sprostać wyzwaniom rosnącej skali biznesu.

Zmiana skali firmy może być rozpatrywana w trzech wymiarach:

  • jako zmiana ilości danych,

  • jako zmiana liczby interakcji,

  • jako zmiana funkcjonalności.

Zmian w każdym z tych wymiarów ma odrębne cechy charakterystyczne i pojawia się w przedsiębiorstwach w różnym natężeniu.

Ilość danych

Dane są często postrzegane jako główny kapitał zapewniający firmie przewagę konkurencyjną. Powszechnie obserwowanym zjawiskiem jest gromadzenie i przetwarzanie coraz większych ilości danych. Nierzadko wzrost jest uzasadniony faktyczną potrzebą a dane są zbierane do użycia w przyszłości. Na przykład rejestr rozpatrzonych reklamacji klientów przechowuje się do późniejszej analizy statystycznej lub do scenariuszy do uczenia maszyn. Tendencja jest stała i ilość danych będzie rosła. Rewolucja IoT sprawi, że będzie ona rosła nieliniowo.

Mniej oczywisty jest fakt, że wartość poszczególnych rekordów zmienia się z czasem. Informacja na temat zamówień klientów w trakcie realizacji jest kluczowa dla wielu stron. Integralność i dostępność tych informacji bezpośrednio wpływa na doświadczenie klienta. Jednak te same informacje kilka miesięcy później nie mają prawie żadnej wartości jednak w razie potrzeby powinny być dostępne dla klienta i zespołu obsługi klienta. Tymczasem, nawet jeśli jesteśmy świadomi tego zjawiska, wszystkie rekordy przechowywane są w jednej operacyjnej i wysokodostępnej bazie danych, której wysoka dostępność wymaga ponoszenia odpowiednich kosztów. Rzadko próbuje się obniżyć koszty przenosząc takie dane innych, mniej dostępnych baz. Powód jest jasny: struktura danych w bazie dużych systemów monolitycznych zwykle obwarowana jest więzami integralności na co dzień zapewniającymi spójność danych, które w momencie potrzeby przenoszenia danych między bazami stanowią znaczną przeszkodę. Zwykle systemy te tworzono z założeniem, że wszystkie dane będą przechowywane w jednej bazie. W związku z tym podzielenie jej na dwie lub więcej części wiązałoby się ze znaczącą zmianą architektury, co pociąga za sobą wysoki koszt i ryzyko.

Liczba interakcji

Łatwość nawiązywania kontaktu z firmami za pośrednictwem internetu sprawia, że liczba interakcji użytkowników z systemami IT gwałtownie się zmienia. Część z tych zmian łatwo jest przewidzieć, zwłaszcza te stymulowane przez same firmy. W handlu detalicznym częstą praktyką jest organizacja tzw. flash sales czyli krótkoterminowe obniżki cen lub promocje. Dla wielu firm flash sales są wartościową taktyką, pozwalającą utrzymać zainteresowanie klientów. Dla innych to świetny sposób na wyprzedaż towarów po zakończeniu sezonu lub takich, które długo już są prezentowane w katalogach. Z punktu widzenia IT wadą takich rozwiązań jest dużo większe obciążenie strony e-commerce podczas flash sales niż zwykle. Wiele firm doświadcza dwudziestokrotnego wzrostu ruchu na stronie webowej tuż po tym, jak informacja o wyprzedaży trafia do klientów. Kluczowym czynnikiem powodzenia takiego wydarzenia jest zapewnienie perfekcyjnej obsługi klienta przez całą wyprzedaż. Flash sales mają znaczenie tylko wtedy, gdy wywołuje u klientów pozytywne wrażenie na temat firmy. Stworzenie infrastruktury IT, która będzie gotowa na szczyt sprzedażowy i wykorzystywanie na co dzień jej potencjału tylko w pięciu procentach jest bardzo kosztowne. Nieco innym przykładem są wydarzenia kulturalne, sportowe lub polityczne. Są one planowane z wyprzedzeniem, zajmują całe dni albo tygodnie, ale odbywają się tylko raz do roku lub rzadziej. Całkowicie nieprzewidywalne jest wystąpienie zdarzeń losowych, w rodzaju katastrof naturalnych. Liczba interakcji z witrynami serwisów informacyjnych lub rządu może gwałtownie wzrosnąć w kilka minut. Dział IT może bronić się przed odpowiedzialnością za pomocą SLA gwarantującym pewien poziom ruchu na stronie. Jest to jednak jedynie sposób, by zrzucić odpowiedzialność na kogoś innego. Nie rozwiązuje problemu, przed jakim staje firma.

Funkcjonalności

Ostatnim, ale nie mniej ważnym wymiarem skalowalności jest liczba i złożoność funkcji biznesowych wdrażanych w systemach. Wraz z rozwojem biznesowym systemy firmy są wzbogacane o nowe zestawy funkcji. Nowe produkty i usługi, nowe kanały i nowe procesy wymagają nowych funkcji lub modyfikacji tych istniejących. Ponadto ciągła presja ze strony konkurencji i wymagań klientów prowadzą do powstania potrzeby coraz szybszej modyfikacji systemów. Tymczasem dodawanie nowych funkcji wymaga coraz więcej czasu. W ciągu mojej kariery zawodowej często spotykałem się z uwagą, że implementacja funkcji o podobnej złożoności kilka lat wcześniej wymagała dużo mniej czasu. Niestety, te opinie są uzasadnione. Powód takiego stanu rzeczy jest niemal zawsze ten sam: dziesięć lat temu wielkość kodu źródłowego aplikacji była jedynie ułamkiem jego dzisiejszej wielkości. Dodatkowo, programiści, którzy napisali system i znali wszystkie szczegóły kodu awansowali lub pracują gdzieś indziej. Natomiast dzisiejsi programiści znają tylko część systemu, przez co zaprojektowanie, implementacja i testowanie nowej funkcjonalności wymaga znacznie więcej czasu.

Mikrousługi a możliwości skalowalności we wszystkich wymiarach

Czy jest jedna recepta na wszystkie te wyzwania? Wydaje się nią być powrót do korzeni. Skoro łatwiej jest tworzyć małe aplikacje, dlaczego nie podzielić istniejących ogromnych aplikacji na mniejsze części, łatwiejsze w zarządzaniu? Po wielu latach praktykowania go przez część zespołów IT ten styl architektoniczny doczekał się nazwy „architektura mikrousług” (ang. Microservices Architecture, MSA). Czym jest architektura mikrousług? Jest to styl architektury, w którym struktura systemu bazuje na zbiorze luźno powiązanych usług rozumianych jako oddzielnie uruchamiane programy komputerowe. Usługi powinny być drobnoziarniste i połączone ze sobą za pomocą lekkich protokołów sieciowych. Zaletą podzielenia aplikacji na mniejsze usługi jest poprawa jej modularności i ułatwienie zrozumienia każdej z nich co przekłada się łatwość ich rozwoju.

Jak to się ma do każdego z wymiarów skalowalności?

Ilość danych

Jedną z zalet MSA jest to, że dla każdej z mikrousług można stosować sposób przechowywania danych optymalny dla jej funkcjonowania. Programiści nie muszą już stosować tej samej bazy danych do wszystkich danych w systemie. Poza bazami relacyjnymi istnieje dziś wiele innych opcji przechowywania danych. Zależnie od potrzeb i wartości informacji można sięgnąć po przechowywanie danych w bazach in-memory, dokumentowych lub grafowych.

Dane przechowywane przez różne usługi nie są powiązane na poziomie baz danych przez co możliwa jest zmiana technologii bazy danych bez wpływu na działanie pozostałych usług. W przypadku systemu monolitowego nawet aktualizacja wersji bazy danych może stanowić wyzwanie, nie mówiąc już o zmianie producenta lub typu bazy.

Powróćmy do przykładu z rekordami zamówień klientów. Po doręczeniu powinny nadal być dostępne dla użytkownika. Ich dostępność nie musi być równie wysoka jak w trakcie procesu dostarczania i nie wymagają modyfikacji. W tym przypadku można stworzyć oddzielne usługi. Pierwsza będzie służyła do obsługi zamówień w trakcie realizacji i będzie opierać się na RDBMS ze względu na dużą liczbę modyfikacji danych i wymaganą wysoką dostępność i spójność danych. Krytyczność informacji uzasadnia koszt zapewnienia wysokiej dostępności np. poprzez utrzymywanie aktywnej repliki w zapasowym centrum przetwarzania. Z kolei informacje o zamówieniach już zrealizowanych mogą być serwowane przez oddzielną usługę przy pomocy dokumentowej bazy danych, która łatwo udźwignie ogromne ilości danych. Ten rodzaj danych pozwala na zaakceptowanie dłuższego czasu odzyskiwania po awarii. Dzięki temu unikamy kosztownych rozwiązań minimalizujących przestoje. Ponieważ dokumentowe bazy danych nie wymagają jednolitej struktury przechowywanych danych (są to bazy bezschematowe) to możliwe jest przechowywanie danych, których model ulega ewolucji. Nie ma potrzeby wykonywania operacji „alter table”, która w przypadku ogromnych ilości rekordów w tabeli powoduje znaczące przestoje podczas wdrażania zmian na produkcję.

Liczba interakcji

Ekstremalna skalowalność liczby interakcji, które może obsłużyć każda usługa, jest swego rodzaju efektem ubocznym mikrousług. Czasami jednak jest przytaczana jako główny argument za przyjęciem tej architektury.

System złożony z małych usług, z których każdą można uruchomić na niemal każdym rodzaju infrastruktury, pozwala natychmiastowo reagować na zwiększone zapotrzebowanie równolegle obsługiwanych interakcji. Dodatkowo pozwala też na uwolnienie części infrastruktury, kiedy nie będzie już potrzebna. Oczywiście istotną częścią tego rozwiązania jest możliwość zwiększania i zmniejszania liczby serwerów w zależności od potrzeb. W praktyce wymaga to korzystania z infrastruktury w chmurze obliczeniowej, ale z drugiej strony bez niewielkich komponentów softwarowych cloud computing nie może w pełni pokazać swojego potencjału.

Do osiągnięcia tych korzyści wystarczy tylko podzielenie systemu na małe elementy. Każdy komponent musi być zaprojektowany w taki sposób, by spełniać konkretne wymagania skalowalności. Ponieważ wykorzystywana infrastruktura jest „ulotna”, wszystkie usługi powinny być tworzone jako bezstanowe. Oznacza to, że stan konwersacji z użytkownikiem nie jest przechowywany w środowisku wykonawczym usługi. Kolejne wywołania usługi w ramach konwersacji z użytkownikiem mogą być kierowane do jej dowolnej instancji. Stan konwersacji powinien być przechowywany po stronie klienta lub w wysoko dostępnej rozproszonej bazie danych.

Przydatną cechą wszystkich wiodących platform służących uruchamianiu mikrousług jest autoskalowanie. Poza automatyzacją uruchamiania i zatrzymywania instancji usługi w celu zapewnienia wymaganej przepustowości, często zautomatyzowane jest również zarządzanie liczbą dostępnych serwerów w chmurze publicznej.

Funkcjonalności

Ostatnia na naszej liście jest skalowalność funkcjonalna. To ona przesądza o przestawieniu się na architekturę systemów rozproszonych. Wprowadzenie zmiany funkcjonalnej w dużym systemie z kodem źródłowym składającym się z setek tysięcy linii niechybnie wymaga czasochłonnej analizy i testów. Jeśli nowa funkcja wymaga synchronizacji zmian w kilku takich systemach oraz warstwie integracyjnej pomiędzy nimi, sytuacja tylko się pogarsza. Niestety jest to codzienność nawet w dojrzałych implementacjach SOA. Odpowiedzią jest podzielenie systemu na części, z których każda realizuje użyteczną biznesowo funkcje, ale których rozmiar nie utrudnia zarządzania nimi. Każda taka część powinna zawierać wszystkie warstwy aplikacji, od interfejsu użytkownika, jeśli go zastosowano, aż po warstwę persystencji. Jest to podobne podejście do tego proponowanego przez SOA. Podkreśla się w nim po prostu rozdzielenie kodu źródłowego i środowiska uruchomieniowego poszczególnych funkcji biznesowych.

Czym kierować się rozbijając istniejące monolity na mniejsze części lub budując nowe systemy? Po pierwsze – użytecznością oddzielonej części. Mam tu na myśli użyteczność z punktu widzenia użytkownika. Znacznie mniejszą wagę należy przykładać do możliwości ponownego użycia tego samego kodu. Zwykle komponenty oprogramowania, które są bardzo reużywalne, dla użytkowników są bezużyteczne. Aby stworzyć z takich komponentów coś, co będzie miało sens z punktu widzenia użytkownika, należy połączyć działanie wielu komponentów. W konsekwencji często wprowadzenie istotnej z perspektywy użytkownika zmiany funkcjonalnej wymaga wprowadzenia jednoczesnej zmiany w wielu komponentach. Zsynchronizowanie implementacji wielu połączonych usług może być dużo trudniejsza niż implementacja monolitu. Zmiana w jednym często używanym komponencie może skutkować kaskadą zmian w komponentach od niego zależnych. Jest to znany antywzorzec, nazywany rozproszonym monolitem. Pomimo zachowania wysokiej skalowalność w innych wymiarach, zabita zostaje skalowalność funkcjonalna. Koncepcja bounded context, zaprezentowana przez Erica Evansa w jego słynnej książce „Domain-driven design”, jest bardzo pomocna w rozkładaniu złożonych systemów na niezależne i funkcjonalne części.

Innym podejściem, które zachowuje skalowalność funkcjonalną, jest opieranie się na historii zmian funkcjonalnych. Granice między komponentami powinny przebiegać w taki sposób, by typowe zmiany wymagały modyfikacji tylko jednego komponentu. Jest to szczególnie użyteczna strategia przy rozbijaniu istniejących monolitów. Istnieje więcej technik, które pomagają określić granice usług, nie koncentrują się one jednak na skróceniu czasu wprowadzenia na rynek.

Co więc otrzymujemy na koniec? Z pozoru omawiana architektura wydaje się bardziej złożona. Diagram architektury wypełnia się coraz większą liczbą połączonych obiektów. Zaczynamy widzieć mnogość funkcji zaimplementowanych w systemie oraz relacje pomiędzy nimi. Ta złożoność zwiększyła się jednak tylko pozornie. Była ona obecna, jednak ukryta w monolicie i pokazywała pazury, gdy okazywało się, że czas potrzebny na wprowadzenie zmiany się wydłuża. Teraz struktura jest zwizualizowana, dzięki czemu możesz zaplanować serię drobnych zmian, z których każda będzie łatwiejsza w kodowaniu, testowaniu i wdrażaniu. Odchudzony kod źródłowy systemu łagodzi obawy przed jego modyfikacją, a to z kolei zachęca do eksperymentowania. Wprowadzenie zmiany, której skutek nie jest pewny, jest teraz tańsze i łatwiejsze do cofnięcia lub poprawienia w następnej iteracji.

Podzielenie systemu na mniejsze komponenty pozwala wrócić do czasów, gdy zmianę wprowadzały trzy osoby w ciągu tygodnia.

Podsumowanie

Architektura mikrousługowa nie jest jest remedium na wszystkie wyzwania stawiane systemom IT. Myśląc o wprowadzeniu mikrousług powinniśmy skoncentrować się na sprostaniu wyzwaniom skalowalności.

Jeśli podstawowym wyzwaniem jest zwiększenie wydajności, to wbrew pozorom rozproszenie przetwarzania może spowodować zwiększenie czasów odpowiedzi systemu z perspektywy użytkownika o ile projektując system zapomnimy o niedoskonałościach sieci. Dodatkowo nie warto decydować się na mikrousługi jeśli rozwiązaniem problemu może być optymalizacja algorytmów. Systemy mikrousługowe powszechnie uważane są za bardziej żywotne, ale jeśli projektując taki system nie uzgodni się z odbiorcą biznesowym zasad degradacji funkcjonalnej to również odbiór użytkowników może być taki, że system jest bardziej zawodny niż monolit.

Pod kątem kosztów, mikrousługi przynoszą znaczne oszczędności dopiero w dłuższej perspektywie.  MSA wymaga nowych umiejętności i technologii co na początku może podnosić koszty. W długiej perspektywie utrzymanie oraz rozwój takich systemów jest znacznie efektywniejszy pod kątem czasu oraz wysiłku w porównaniu do wdrażania zmian w systemach monolitycznych.