Dane oryginału

Original edition copyright © 2019 by Manning Publications Co. All Rights reserved. Title of English-language original:
Microservices in Action by Morgan Bruce, Paulo A. Pereira, ISBN 9781617294457. Polish-language edition © 2019 by Polish Scientific Publishers PWN Wydawnictwo Naukowe PWN Spółka Akcyjna. All Rights reserved.

Przekład

Magdalena Rogulska i Mariusz Rogulski na zlecenie

WITKOM Witold Sikorski

Projekt okładki polskiego wydania

INT-MEDIA Dawid Mazur

Wydawca

Wioleta Szczygielska

Redaktor prowadzący

Jolanta Kowalczuk

Redaktor

Irena Puchalska

Redaktor techniczny

Maryla Broda

Koordynator produkcji

Anna Bączkowska

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

Skład wersji elektronicznej na zlecenie Wydawnictwa Naukowego PWN Monika Lipiec / Woblink

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

Copyright © for the Polish edition by Wydawnictwo Naukowe PWN SA

Warszawa 2019

ISBN 978-83-01-20781-6

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

Warszawa 2019

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

Spis treści

Przedmowa

Podziękowania

O książce

O autorach

O ilustracji na okładce

Część 1 . Stan rzeczy

1 . Projektowanie i uruchamianie mikroserwisów

1.1. Czym jest aplikacja mikroserwisowa?

1.1.1. Skalowanie przez dekompozycję

1.1.2. Kluczowe zasady

1.1.3. Kto używa mikroserwisów?

1.1.4. Dlaczego mikroserwisy to dobry wybór?

1.2. Co w mikroserwisach jest wyzwaniem?

1.2.1. Wyzwania związane z projektowaniem

1.2.2. Wyzwania związane z działaniem

1.3. Cykl rozwojowy mikroserwisu

1.3.1. Projektowanie mikroserwisów

1.3.2. Wdrażanie mikroserwisów

1.3.3. Obserwowanie mikroserwisów

1.4. Odpowiedzialna i operacyjnie świadoma kultura inżynieryjna

2 . Mikroserwisy w SimpleBanku

2.1. Czym zajmuje się SimpleBank?

2.2. Czy mikroserwisy są dobrym wyborem?

2.2.1. Ryzyko i inercja w oprogramowaniu finansowym

2.2.2. Zmniejszanie tarć i zapewnienie wartości dodanej

2.3. Budowanie nowej funkcjonalności

2.3.1. Identyfikacja mikroserwisów przez modelowanie domeny

2.3.2. Współpraca usług

2.3.3. Choreografia usługi

2.4. Udostępnianie usług na zewnątrz

2.5. Wdrażanie funkcjonalności na produkcję

2.5.1. Kontrolowane pod względem jakości i zautomatyzowane wdrożenia

2.5.2. Odporność

2.5.3. Przejrzystość

2.6. Skalowanie rozwoju mikroserwisów

2.6.1. Techniczna różnorodność

2.6.2. Izolacja

2.7. Co dalej?

Część 2 . Projekt

3 . Architektura aplikacji mikroserwisowej

3.1. Architektura jako całość

3.1.1. Od monolitów do mikroserwisu

3.1.2. Rola architekta

3.1.3. Zasady architektoniczne

3.1.4. Cztery warstwy aplikacji mikroserwisowej

3.2. Platforma mikroserwisu

3.2.1. Mapowanie platformy uruchomieniowej

3.3. Usługi

3.3.1. Zdolności

3.3.2. Agregacja i usługi wyższego szczebla

3.3.3. Ścieżki krytyczne i niekrytyczne

3.4. Komunikacja

3.4.1. Kiedy używać komunikatów synchronicznych

3.4.2. Kiedy używać komunikatów asynchronicznych

3.4.3. Wzorce komunikacji asynchronicznej

3.4.4. Lokalizowanie innych usług

3.5. Granica aplikacji

3.5.1. Bramy API

3.5.2. Backends for frontends

3.5.3. Bramy consumer-driven

3.6. Klienci

3.6.1. Monolity frontendowe

3.6.2. Mikrofrontendy

4 . Projektowanie nowych funkcjonalności

4.1. Nowa funkcjonalność w SimpleBanku

4.2. Określanie zakresu według zdolności biznesowych

4.2.1. Zdolności i modelowanie domeny

4.2.2. Tworzenie strategii inwestycyjnej

4.2.3. Zagnieżdżone konteksty i usługi

4.2.4. Wyzwania i ograniczenia

4.3. Określanie zakresu według przypadków użycia

4.3.1. Składanie zleceń związanych ze strategią inwestycyjną

4.3.2. Działania i magazyny

4.3.3. Orkiestracja i choreografia

4.4. Ograniczanie zakresu według zmienności

4.5. Zdolności techniczne

4.5.1. Wysyłanie powiadomień

4.5.2. Kiedy używać zdolności technicznych

4.6. Postępowanie z niejednoznacznością

4.6.1. Zacznijmy od gruboziarnistych usług

4.6.2. Przygotujmy się na przyszłą dekompozycję

4.6.3. Wycofywanie i migracja

4.7. Własność usług w firmach

5 . Transakcje i zapytania w mikroserwisach

5.1. Spójne transakcje w aplikacjach rozproszonych

5.1.1. Dlaczego nie użyć transakcji rozproszonych?

5.2. Komunikacja oparta na zdarzeniach

5.2.1. Zdarzenia i choreografia

5.3. Sagi

5.3.1. Choreografowane sagi

5.3.2. Orkiestrowane sagi

5.3.3. Przeplatane sagi

5.3.4. Wzorce spójności

5.3.5. Event sourcing

5.4. Zapytania w rozproszonym świecie

5.4.1. Przechowywanie kopii danych

5.4.2. Oddzielanie zapytań i poleceń

5.4.3. Wyzwania związane z CQRS

5.4.4. Analizy i raportowanie

5.5. Literatura uzupełniająca

6 . Projektowanie niezawodnych usług

6.1. Definiowanie niezawodności

6.2. Co może pójść źle?

6.2.1. Źródła awarii

6.2.2. Awarie kaskadowe

6.3. Projektowanie niezawodnej komunikacji

6.3.1. Ponowne próby

6.3.2. Plany awaryjne

6.3.3. Przeterminowania

6.3.4. Bezpieczniki

6.3.5. Komunikacja asynchroniczna

6.4. Maksymalizacja niezawodności usługi

6.4.1. Równoważenie obciążenia i stan usługi

6.4.2. Limity użycia

6.4.3. Walidacja niezawodności i odporność na błędy

6.5. Bezpieczeństwo przede wszystkim

6.5.1. Frameworki

6.5.2. Service mesh

7 . Budowanie uniwersalnego frameworka mikroserwisowego

7.1. Szkielet mikroserwisu

7.2. Jaki jest cel szkieletu mikroserwisu?

7.2.1. Zmniejszone ryzyko

7.2.2. Szybsze rozpoczynanie

7.3. Projektowanie szkieletu

7.3.1. Wykrywanie usług

7.3.2. Obserwowalność

7.3.3. Równoważenie i limitowanie

7.4. Badanie funkcjonalności zaimplementowanych z użyciem szkieletu

7.5. Czy heterogeniczność nie była jedną z obietnic mikroserwisów?

Część 3 . Wdrażanie

8 . Wdrażanie mikroserwisów

8.1. Dlaczego wdrożenie jest ważne?

8.1.1. Stabilność i dostępność

8.2. Środowisko produkcyjne mikroserwisów

8.2.1. Cechy środowiska produkcyjnego mikroserwisów

8.2.2. Automatyzacja i szybkość

8.3. Wdrażanie usługi, szybka ścieżka

8.3.1. Uruchomienie usługi

8.3.2. Budowanie maszyny wirtualnej

8.3.3. Uruchamianie wielu instancji usługi

8.3.4. Dodawanie równoważenia obciążenia

8.3.5. Czego się nauczyliśmy?

8.4. Budowanie artefaktów usługi

8.4.1. Co jest w artefakcie?

8.4.2. Niezmienialność

8.4.3. Typy artefaktów usługi

8.4.4. Konfiguracja

8.5. Modele hostowania usług

8.5.1. Jedna usługa per host

8.5.2. Wiele statycznych usług per host

8.5.3. Wiele zarządzanych usług per host

8.6. Wdrażanie usług bez przestojów

8.6.1. Wdrożenia kanarkowe i rolling deploy w GCE

9 . Wdrażanie z użyciem kontenerów i programów zarządczych

9.1. Konteneryzowanie usługi

9.1.1. Praca z obrazami

9.1.2. Budowanie naszego obrazu

9.1.3. Uruchamianie kontenerów

9.1.4. Przechowywanie obrazu

9.2. Wdrażanie do klastra

9.2.1. Projektowanie i uruchamianie kapsuł

9.2.2. Równoważenie obciążenia

9.2.3. Krótkie spojrzenie pod maskę

9.2.4. Kontrole działania

9.2.5. Wdrażanie nowej wersji

9.2.6. Wycofywanie

9.2.7. Łączenie wielu usług

10 . Budowanie strumienia dostarczania dla mikroserwisów

10.1. Nudne wdrażanie

10.1.1. Strumień wdrażania

10.2. Budowanie strumienia w Jenkinsie

10.2.1. Konfigurowanie strumienia budowania

10.2.2. Budowanie obrazu

10.2.3. Uruchamianie testów

10.2.4. Publikowanie artefaktów

10.2.5. Wdrażanie do środowiska staging

10.2.6. Środowiska staging

10.2.7. Wdrażanie do produkcji

10.3. Budowanie strumienia z krokami wielokrotnego zastosowania

10.3.1. Proceduralne versus deklaratywne budowanie strumieni

10.4. Techniki wdrażania o niskim oddziaływaniu i wydawanie funkcjonalności

10.4.1. Dark launch

10.4.2. Flagi funkcjonalności

Część 4 . Obserwowalność i własność

11 . Budowanie systemu monitorowania

11.1. Kompleksowy stos monitorowania

11.1.1. Dobry monitoring jest warstwowy

11.1.2. Złote sygnały

11.1.3. Typy metryk

11.1.4. Rekomendowane praktyki

11.2. Monitorowanie SimpleBanku za pomocą Prome

11.2.1. Konfigurowanie infrastruktury zbierania metryk

11.2.2. Zbieranie metryk dotyczących infrastruktury – RabbitMQ

11.2.3. Instrumentacja składania zleceń w SimpleBanku

11.2.4. Konfigurowanie alarmów

11.3. Ustawianie sensownych i praktycznych alarmów

11.3.1. Kto potrzebuje wiedzieć, że coś nie działa?

11.3.2. Objawy, nie przyczyny

11.4. Obserwowanie całej aplikacji

12 . Używanie dzienników i śladów do zrozumienia zachowania

12.1. Zrozumienie zachowania między wieloma usługami

12.2. Generowanie spójnych, uporządkowanych dzienników czytelnych dla człowieka

12.2.1. Przydatne informacje do uwzględnienia we wpisach dziennika

12.2.2. Struktura i czytelność

12.3. Konfigurowanie infrastruktury logowania w SimpleBanku

12.3.1. Rozwiązanie oparte na ELK i Fluentd

12.3.2. Konfigurowanie systemu do rejestrowania

12.3.3. Konfigurowanie tego, co będzie zbierane w dziennikach

12.3.4. Znajdowanie igły w stogu siana

12.3.5. Rejestrowanie właściwych informacji

12.4. Śledzenie interakcji między usługami

12.4.1. Korelowanie żądań: ślady i spany

12.4.2. Konfigurowanie śledzenia w usługach

12.5. Wizualizowanie śladów

13 . Budowanie zespołów mikroserwisowych

13.1. Budowanie efektywnych zespołów

13.1.1. Prawo Conwaya

13.1.2. Zasady efektywnych zespołów

13.2. Modele zespołów

13.2.1. Grupowanie funkcyjne

13.2.2. Grupowanie przekrojowe

13.2.3. Ustalanie granic zespołu

13.2.4. Infrastruktura, platforma i produkt

13.2.5. Kto jest pod telefonem?

13.2.6. Dzielenie się wiedzą

13.3. Zalecane praktyki dla zespołów mikroserwisowych

13.3.1. Czynniki zmian w mikroserwisach

13.3.2. Rola architektury

13.3.3. Jednorodność a elastyczność techniczna

13.3.4. Model otwartego oprogramowania

13.3.5. Przeglądy projektu

13.3.6. Żywa dokumentacja

13.3.7. Odpowiadanie na pytania dotyczące aplikacji

13.4. Dodatkowe materiały

Dodatek - Instalacja Jenkinsa w Minikube’ie

Przypisy

przedmowa

W ciągu ostatnich pięciu lat styl architektury oparty na mikroserwisach – strukturyzujący aplikacje jako drobnoziarniste, luźno powiązane i niezależne usługi – stawał się coraz bardziej popularny i coraz bardziej możliwy do zastosowania przez zespoły inżynierów, niezależnie od wielkości firmy.

Dla nas praca nad projektami mikroserwisowymi w Onfido była objawieniem, a ta książka zawiera wiele rzeczy, których nauczyliśmy się po drodze. Przez podział naszego produktu mogliśmy poruszać się szybciej i przy mniejszych tarciach, nie potykać się o własne nogi w dużej, monolitycznej bazie kodu. Podejście oparte na mikroserwisach pomaga inżynierom budować aplikacje, które mogą ewoluować w miarę upływu czasu, nawet przy wzroście złożoności produktu i rozmiaru zespołu.

Pierwotnie mieliśmy napisać książkę o naszym praktycznym doświadczeniu z uruchamianiem aplikacji mikroserwisowych. W trakcie pracy nad nią misja ta ewoluowała, a my zdecydowaliśmy się przekazać nasze doświadczenie dotyczące pełnego cyklu życia aplikacji – projektowania, wdrażania i zarządzania mikroserwisami – w postaci szerokiego i praktycznego przeglądu. Wybraliśmy narzędzia do zilustrowania tych technik, takich jak Kubernetes i Docker, które są popularne i idą w parze z najlepszymi praktykami w zakresie mikroserwisów; wierzymy, że da się zastosować te lekcje niezależnie od języka i narzędzi, z których ostatecznie skorzystacie, tworząc aplikacje.

Mamy szczerą nadzieję, że ta książka zostanie uznana za cenne źródło i przewodnik, a wiedza, porady i przykłady pomogą budować wspaniałe produkty i aplikacje za pomocą mikroserwisów.

podziękowania

W ciągu ostatniego półtora roku w swojej ewolucji książka ta wyrosła z pomysłu napisania niewielkiej publikacji o wdrażaniu usług do poważnej pracy obejmującej szeroki zakres tematów rozwoju mikroserwisów – od projektowania, przez komunikację, po wdrożenie i ich działanie. To był nasz przywilej pracować z tak wieloma utalentowanymi ludźmi. Napisaliśmy książkę, która, mamy nadzieję, przyda się zarówno zaczynającym stosowanie tego rodzaju architektury, jak i tym, którzy już jej używają.

Chciałbym podziękować mojej rodzinie, w szczególności Rosie i Beatriz, mojej żonie i córce, które znosiły nieobecność męża i ojca. Chciałbym również podziękować Morganowi, mojemu współautorowi i koledze. Od samego początku był nieoceniony w udzielaniu wskazówek i zapewnieniu jasności. Dziękuję Ci!

Paulo

Nie dałbym rady napisać tej książki, gdyby nie cierpliwość i wsparcie mojej rodziny, która z wdziękiem tolerowała zbyt wiele weekendów, wieczorów i wakacji ze mną siedzącym przed laptopem. Chciałbym także podziękować moim rodzicom, Heather i Allanowi, którzy nauczyli mnie miłości do czytania – bez tego nie napisałbym tej książki. I wreszcie dzięki, Paulo! Zachęciłeś mnie do rozpoczęcia tego projektu razem z tobą i chociaż droga była czasem trudna, nauczyłem się wiele w trakcie tej podróży.

Morgan

Wspólnie chcielibyśmy podziękować:

1. ■ Karen i Danowi, redaktorom działu rozwoju, którzy niestrudzenie pracowali tydzień po tygodniu, udzielając wsparcia i porad, aby pomóc nam napisać możliwie najlepszą książkę.

2. ■ Karsten Strøbæk, naszemu redaktorowi technicznemu, za jego krytyczne oko i genialne opinie.

3. ■ Michaelowi Stephensonowi za wiarę w nas i Marjanie Bace za pomoc w ukształtowaniu naszej książki w coś, co przyciąga czytelników Manninga[1].

4. ■ Wielu innym osobom, z którymi współpracowaliśmy w Manningu, którzy stworzyli tak profesjonalny i utalentowany zespół – bez nich ta książka nigdy by nie powstała.

5. ■ Wreszcie naszym recenzentom, których opinie i pomoc w ulepszeniu książki głęboko doceniamy. Są to m.in.: Akshat Paul, Al Krinker, Andrew Miles, Andy Miles, Antonio Pessolano, Bachir Chihani, Christian Bach, Christian Thoudahl, Vittal Damaraju, Deepak Bhaskaran, Evangelos Bardis, John Guthrie, Lorenzo De Leon, Łukasz Witczak, Maciej Jurkowski, Mike Jensen, Shobha Iyer, Srihari Sridharan, Steven Parr, Thorsten Weber i Tiago Boldt Sousa.

o książce

Mikroserwisy w akcji to praktyczna książka poświęcona tworzeniu i wdrażaniu aplikacji opartych na mikroserwisach. Napisana dla programistów i architektów z solidnym naciskiem na rozwój zorientowany na usługi, podejmuje wyzwanie polegające na wprowadzeniu mikroserwisów do praktyki. Zaczniemy od dogłębnego przeglądu zasad projektowania mikroserwisów, bazując na znajomości tradycyjnych systemów. Następnie pokażemy skuteczną drogę do ich zastosowania w praktyce. Będziemy eksplorować przykłady przy użyciu Kubernetesa, Dockera i Google Container Engine, nauczymy się budować klastry i utrzymywać je po wdrożeniu.

Techniki zawarte w tej książce nadają się do tworzenia mikroserwisów w większości najpopularniejszych języków programowania. Jako podstawowego języka w tej książce postanowiliśmy użyć Pythona, ponieważ jego bezceremonialny styl i lapidarna składnia nadają się do jasnych i przejrzystych przykładów. Nie martwcie się, jeśli nie znacie Pythona – poprowadzimy Was przez kolejne przykłady.

Jak zorganizowana jest ta książka – mapa drogowa

Część 1 tej książki zawiera krótkie wprowadzenie do mikroserwisów, pokazując właściwości i zalety systemów opartych na mikroserwisach oraz wyzwania, jakie można spotkać podczas ich tworzenia.

W rozdziale 1 przedstawimy architekturę mikroserwisów. Zbadamy zalety i wady podejścia mikroserwisowego i wyjaśnimy kluczowe zasady rozwoju mikroserwisów. Na koniec przedstawimy wyzwania związane z projektowaniem i wdrażaniem, które będziemy omawiać w tej książce.

W rozdziale 2 użyjemy podejścia mikroserwisowego do przykładowego zastosowania – SimpleBanku. Zaprojektujemy nową funkcjonalność za pomocą mikroserwisów i sprawdzimy, jak przygotować ją do zastosowania na produkcji.

W części 2 omówimy architekturę i projektowanie aplikacji mikroserwisowych.

W rozdziale 3 przejdziemy przez architekturę aplikacji mikroserwisowej obejmującą cztery warstwy: platformę, usługę, granicę i klienta. Celem tego rozdziału jest przedstawienie zgrubnego modelu, którego będzie można użyć podczas pracy nad zrozumieniem dowolnego systemu mikroserwisowego.

Rozdział 4 dotyczy jednej z najtrudniejszych części projektowania mikroserwisów – określania odpowiedzialności usługi. Przedstawimy cztery podejścia do modelowania: oparte na zdolnościach biznesowych, przypadkach użycia, zdolnościach technicznych i zmienności oraz – korzystując z przykładów z SimpleBanku, zbadamy, w jaki sposób podejmować dobre decyzje projektowe, nawet, jeśli granice są niejednoznaczne.

W rozdziale 5 pokażemy, jak tworzyć logikę biznesową w systemach rozproszonych, gdzie gwarancje transakcyjne przestają obowiązywać. Wprowadzimy czytelnika w różne wzorce transakcji, takie jak sagi i wzorce zapytań, których przykładem będzie API i CQRS.

Rozdział 6 dotyczy niezawodności. Systemy rozproszone mogą być bardziej ułomne niż aplikacje monolityczne, a komunikacja między mikroserwisami wymaga starannego zaprojektowania, aby uniknąć problemów z ich dostępnością, przestojami i awariami kaskadowymi. Korzystając z przykładów w Pythonie, zbadamy popularne techniki maksymalizacji odporności aplikacji, takie jak limity użycia, bezpieczniki, kontrole działania i ponowne próby.

W rozdziale 7 powiemy, jak zaprojektować framework mikroserwisowy do wielokrotnych zastosowań. Spójne praktyki w zakresie mikroserwisów poprawiają ogólną jakość i niezawodność aplikacji oraz skracają czas opracowywania nowych usług. Przedstawimy działające przykłady w Pythonie.

W części 3 przyjrzymy się dobrym praktykom wdrażania mikroserwisów.

W rozdziale 8 podkreślimy znaczenie zautomatyzowanego ciągłego dostarczania w aplikacjach mikroserwisowych. Wdrożymy do produkcji pojedynczą usługę – w Google Compute Engine – i na tym przykładzie pokażemy znaczenie niezmienialnych artefaktów oraz plusy i minusy różnych modeli wdrażania mikroserwisów.

W rozdziale 9 przedstawimy Kubernetesa – platformę zarządzania skonteneryzowanymi aplikacjami. Kontenery w połączeniu z programem zarządzającym, takim jak Kubernetes, są naturalnym i eleganckim rozwiązaniem dla działających na dużą skalę mikroserwisów. Korzystając z Minikube’a, nauczymy się tworzyć pakiety mikroserwisów i bezproblemowo wdrażać je w Kubernetesie.

W rozdziale 10 oprzemy się na przykładzie z poprzedniego rozdziału, aby skonstruować kompleksowy strumień wdrażania za pomocą Jenkinsa. Zaprogramujemy strumień w Jenkinsie i Groovy, który szybko i niezawodnie będzie wykonywał wdrożenia produkcyjne. Dowiemy się również, jak stosować spójne praktyki wdrażania dla floty mikroserwisów.

W końcowej części książki zbadamy zachowanie i ludzką stronę mikroserwisów.

W rozdziale 11 poprowadzimy Was przez proces rozwoju systemu monitorowania mikroserwisów, używając StatsD, Prometheus i Grafana do zbierania i agregowania metryk w celu tworzenia pulpitów i alarmów. Omówimy także dobre praktyki w zakresie zarządzania alarmami i unikania niepotrzebnych ostrzeżeń.

Rozdział 12 jest oparty na rezultatach z poprzednich rozdziałów – wprowadzimy w nim dodatkowo dzienniki i ślady. Uzyskiwanie bogatych, aktualnych i możliwych do przeszukiwania informacji z naszych mikroserwisów pomoże nam je zrozumieć, zdiagnozować problemy i udoskonalić w przyszłości. Przykłady w tym rozdziale wykorzystują Elasticsearcha, Kibanę i Jaegera.

Wreszcie w rozdziale 13 wykonamy lekki ruch w przeciwną stronę, w celu przedstawienia ludzkiej strony mikroserwisów. To ludzie wdrażają oprogramowanie – budowanie dobrego oprogramowania wymaga zarówno efektywnej współpracy, jak i wyborów związanych z wdrażaniem. Zbadamy zasady, które sprawiają, że zespoły mikroserwisowe są skuteczne, a także psychologiczne i praktyczne implikacje podejścia opartego na architekturze mikroserwisowej do dobrych praktyk inżynierskich.

O kodzie

Ta książka zawiera wiele przykładów kodu źródłowego, zarówno w ponumerowanych listingach, jak i wewnątrz zwykłego tekstu. W obu przypadkach kod źródłowy jest sformatowany w ten sposób – czcionką stałej szerokości, aby oddzielić go od zwykłego tekstu. Czasem kod jest również pogrubiony, aby wyróżnić określone linie lub rozróżnić wprowadzone polecenia od uzyskanego wyniku.

W wielu przypadkach ponownie sformatowaliśmy oryginalny kod źródłowy; dodaliśmy linie podziału i przerobiliśmy wcięcia, aby uwzględnić dostępne miejsce na stronie w książce. W rzadkich przypadkach nawet to nie było wystarczające, a listingi zawierają znaczniki kontynuacji linii (). Ponadto w listingach często usuwaliśmy komentarze z kodu źródłowego, jeśli został on opisany w tekście. Wielu listingom towarzyszą adnotacje dotyczące kodu, podkreślając ważne pojęcia.

Kod źródłowy z tej publikacji jest dostępny na stronie książki pod adresem: https://www.manning.com/books/microservices-in-action oraz w repozytorium Github pod adresem https://github.com/morganjbruce/microservices-in-action.

Instrukcje dotyczące uruchamiania przykładów można znaleźć w dalszej części książki. Aby uprościć uruchamianie przykładów, zazwyczaj będziemy używać Dockera i/lub Docker Compose. Dodatek zaś dotyczy konfigurowania Jenkinsa użytego w rozdziale 10, aby działał płynnie na lokalnej instalacji Kubernetesa.

o autorach

MORGAN BRUCE ma duże doświadczenie w tworzeniu złożonych aplikacji oraz praktykę w branży finansów i weryfikacji tożsamości, gdzie dokładność, odporność i bezpieczeństwo są kluczowe. Jako lider pracował nad zakrojonymi na szeroką skalę procesami refaktoryzacji i przeprojektowywania. Ma również doświadczenie z pierwszej ręki w prowadzeniu ewolucji od monolitycznej aplikacji do architektury mikroserwisowej.

PAULO A. PEREIRA jest obecnie liderem zespołu zajmującego się przekształcaniem aplikacji monolitycznych w mikroserwisy przy istniejących ograniczeniach, w których ogromne znaczenie mają bezpieczeństwo i precyzja. Entuzjastycznie nastawiony do wyboru zróżnicowanych narzędzi do pracy oraz łączenia różnych języków i paradygmatów bada obecnie programowanie funkcjonalne, głównie za pośrednictwem Elixira. Paulo jest także autorem książki Elixir Cookbook, a także jednym z recenzentów technicznych Learning Elixir oraz Mastering Elixir.

o ilustracji na okładce

Obraz na okładce Mikroserwisy w akcji jest zatytułowany Habit of a Lady of China in 1700. Ilustracja pochodzi z dzieła Thomasa Jefferysa A Collection of the Dresses of Different Nations, Ancient and Modern (cztery tomy), Londyn, opublikowanego w latach 1757–1772. Strona tytułowa głosi, że są to ręcznie kolorowane miedziane ryciny, wzmocnione gumą arabską.

Thomas Jefferys (1719–1771) został nazwany Geografem Króla Jerzego III. Był angielskim kartografem, a także wiodącym dostawcą map. Wygrawerował i wydrukował mapy dla rządu i innych oficjalnych organów, a także opracował szeroki zakres komercyjnych map i atlasów, zwłaszcza dla Ameryki Północnej. Jego praca jako twórcy map wzbudziła zainteresowanie lokalnymi zwyczajami ubioru na ziemiach, które zbadał i zmapował, co jest doskonale widoczne w tej kolekcji. Fascynacja odległymi krainami i podróże dla przyjemności były stosunkowo nowymi zjawiskami pod koniec XVIII w., a kolekcje takie jak te były popularne, wprowadzając zarówno turystów, jak i podróżujących palcem po mapie do mieszkańców innych krajów.

Różnorodność rysunków w tomach Jefferysa jaskrawo mówi o wyjątkowości i indywidualności narodów świata około 200 lat temu. Od tego czasu sposoby ubierania uległy zmianie, a znaczna różnorodność w zależności od regionu i kraju, tak bogata w tamtym czasie, zanikła. Obecnie trudno jest odróżnić mieszkańców jednego kontynentu od drugiego. Być może, próbując spojrzeć na to optymistycznie, wymieniliśmy kulturową i wizualną różnorodność na bardziej zróżnicowane życie osobiste lub bardziej zróżnicowane i interesujące życie intelektualne i techniczne.

W czasach, gdy trudno jest odróżnić jedną książkę komputerową od drugiej, Manning łączy pomysłowość i inicjatywę biznesu komputerowego z okładkami opartymi na bogatej różnorodności życia regionalnego sprzed dwóch wieków, przywróconej do życia dzięki obrazom Jefferysa.

Część 1
Stan rzeczy

Wtej części wprowadzimy architekturę mikroserwisową, przedstawimy właściwości i zalety aplikacji mikroserwisowych oraz niektóre wyzwania, z jakimi trzeba się zmierzyć przy ich tworzeniu. Wprowadzimy SimpleBank – fikcyjną firmę, w której próby zbudowania aplikacji mikroserwisowej będą wspólnym wątkiem w wielu przykładach wykorzystywanych w tej książce.

1

Projektowanie i uruchamianie mikroserwisów

Niniejszy rozdział dotyczy:

1. ■ definiowania aplikacji mikroserwisowych,

2. ■ wyzwań związanych z podejściem opartym na mikroserwisach,

3. ■ podejść do projektowania aplikacji mikroserwisowych,

4. ■ podejść do skutecznego uruchamiania mikroserwisów.

Twórcy oprogramowania starają się tworzyć efektywne i ponadczasowe rozwiązania złożonych problemów. Pierwszy problem, który zazwyczaj próbujemy rozwiązać, to: Czego chce nasz klient? Jeśli mamy umiejętności (lub szczęście), uzyskamy odpowiedź. Ale nasze wysiłki rzadko się na tym kończą. Nasza udana aplikacja nadal będzie się rozwijać: debugujemy problemy, budujemy nowe funkcjonalności, zapewniamy, aby była dostępna i działała płynnie.

Nawet najbardziej zdyscyplinowane zespoły mogą borykać się z utrzymaniem początkowego tempa i zwinności w obliczu rozwoju aplikacji. W najgorszym przypadku nasz prosty i stabilny produkt staje się trudny i wrażliwy. Nie mamy możliwości zrównoważonego dostarczania dodatkowych funkcjonalności swoim klientom. Jesteśmy zmęczeni z powodu przestojów, lęku przed zwolnieniem i zbyt powolni, aby dostarczyć nowe funkcjonalności lub poprawki. Ani nasi klienci, ani nasi programiści nie są szczęśliwi.

Mikroserwisy oferują lepszy sposób zrównoważonego wpływania na biznes. Zamiast pojedynczej monolitycznej jednostki aplikacje zbudowane przy użyciu mikroserwisów składają się z luźno powiązanych autonomicznych usług. Budując usługi, które dobrze wykonują jedną rzecz, można uniknąć bezwładności i entropii dużych aplikacji. Nawet w istniejących aplikacjach można stopniowo wyodrębniać funkcjonalności w niezależne usługi, aby cały system był łatwiejszy w utrzymaniu.

Kiedy zaczęliśmy pracować z mikroserwisami, szybko zdaliśmy sobie sprawę, że budowanie mniejszych i bardziej samodzielnych usług było tylko częścią tworzenia stabilnej i krytycznej dla biznesu aplikacji. W końcu jakakolwiek udana aplikacja spędzi więcej czasu w produkcji niż w edytorze kodu. Aby dostarczyć produkt za pomocą mikroserwisów, nasz zespół nie może skupiać się wyłącznie na tworzeniu. Musimy mieć umiejętności w zakresie wdrażania, obserwacji i diagnozowania.

1.1. Czym jest aplikacja mikroserwisowa?

Aplikacja mikroserwisowa to zbiór autonomicznych usług, z których każda wykonuje dobrze jedną rzecz, i które współpracują ze sobą w celu wykonywania bardziej skomplikowanych operacji. Zamiast pojedynczego złożonego systemu tworzymy i zarządzamy pakietem stosunkowo prostych usług, które mogą wchodzić w interakcje w złożony sposób. Usługi te współpracują ze sobą za pomocą technologicznie agnostycznych protokołów komunikacji, zarówno punkt-punkt, jak i asynchronicznie.

Może się to wydawać prostym pomysłem, ale ma istotne implikacje dla zmniejszenia tarć w rozwoju złożonych systemów. Klasyczna praktyka inżynierii oprogramowania zaleca wysoką spójność i luźne powiązania jako pożądane właściwości dobrze zaprojektowanego systemu. System, który ma te właściwości, będzie łatwiejszy w utrzymaniu i bardziej podatny na zmiany.

Spójność jest stopniem, do którego elementy danego modułu są do siebie przynależne, podczas gdy wiązanie to stopień, w jakim jeden element wie o wewnętrznym działaniu drugiego. Zasada jednej odpowiedzialności Roberta C. Martina jest użytecznym sposobem na zrozumienie tego pierwszego:

Zbierz rzeczy, które zmieniają się z tych samych powodów. Oddziel te, które zmieniają się z różnych powodów.

W monolitycznej aplikacji próbujemy zaprojektować te właściwości na poziomie klasy, modułu lub biblioteki. W aplikacji mikroserwisowej zamiast tego dążymy do osiągnięcia tych właściwości na poziomie niezależnie wdrażalnych jednostek funkcjonalności. Pojedynczy mikroserwis powinien być wysoce spójny – musi być odpowiedzialny za pewne pojedyncze zdolności wewnątrz aplikacji. Ponadto, im każda usługa mniej wie o wewnętrznych działaniach innych usług, tym łatwiej dokonać zmian w jednej usłudze lub zdolności – bez wymuszania zmian w innych.

Aby lepiej zrozumieć, w jaki sposób aplikacja mikroserwisowa się integruje, zacznijmy od rozważenia niektórych funkcjonalności internetowego narzędzia do inwestowania:

1. ■ otwieranie rachunku,

2. ■ deponowanie i wypłacanie pieniędzy,

3. ■ składanie zleceń na kupno lub sprzedaż papierów wartościowych (np. akcji),

4. ■ modelowanie ryzyka i prognozowanie finansowe.

Przeanalizujmy proces sprzedaży akcji:

1. 1 Użytkownik tworzy zlecenie sprzedaży części akcji ze swojego rachunku.

2. 2 Papiery wartościowe są rezerwowane na jego rachunku, więc nie można ich sprzedać wiele razy.

3. 3 Złożenie zlecenia na giełdzie kosztuje – rachunek jest obciążany opłatą.

4. 4 System musi przekazać to zlecenie na właściwy rynek giełdowy.

Na rysunku 1.1 pokazano, jak może wyglądać umieszczenie tego zlecenia sprzedaży w ramach aplikacji mikroserwisowej. Można na nim zaobserwować trzy kluczowe cechy mikroserwisów:

1. ■ każdy mikroserwis jest odpowiedzialny za jedną zdolność – może to być związane z biznesem lub stanowić zdolność techniczną, taką jak integracja z podmiotem zewnętrznym (np. giełdą);

2. ■ mikroserwis jest właścicielem swoich zasobów danych, jeśli je ma; zmniejsza to powiązania między usługami, ponieważ inne usługi mogą uzyskiwać dostęp do danych, które nie są ich własnością, tylko za pośrednictwem interfejsu, który zapewnia usługa;

3. ■ samodzielne mikroserwisy, a nie mechanizm komunikacyjny je łączący ani inne oprogramowanie, są odpowiedzialne za choreografię i współpracę – sekwencjonowanie komunikatów i działań w celu wykonania użytecznych czynności.

Oprócz tych trzech cech można wyróżnić jeszcze dwa podstawowe atrybuty mikroserwisów:

1. ■ każdy mikroserwis może być wdrożony niezależnie – bez tego aplikacja mikroserwisowa nadal byłaby monolityczna z perspektywy wdrażania;

2. ■ mikroserwis jest wymienny – posiadanie jednej zdolności wyznacza naturalną granicę pod względem rozmiaru; ponadto, czyni łatwą do zrozumienia indywidualną odpowiedzialność lub rolę usługi.

Pomysł, że mikroserwisy są odpowiedzialne za koordynację działań w systemie jest zasadniczą różnicą między tym podejściem a tradycyjnymi architekturami zorientowanymi na usługi (SOA). Tego typu systemy często wykorzystywały korporacyjne magistrale usług (ESB) lub bardziej złożone standardy orkiestracji, aby uzewnętrzniać komunikację i proces orkiestracji z samych aplikacji. W tym modelu usługi często nie były spójne, ponieważ logika biznesowa była dodawana do magistrali usług, a nie do samych usług.

Warto się zastanowić, w jaki sposób wydzielanie funkcjonalności w internetowym systemie inwestycyjnym pozwala na większą elastyczność w obliczu zmieniających się wymagań. Wyobraźmy sobie, że musimy zmienić sposób naliczania opłat. Możemy wprowadzić i wydać te zmiany w usłudze fees bez żadnych zmian w usługach wyższego lub niższego poziomu. Albo przyjmijmy zupełnie nowy wymóg – musimy powiadomić zespół ds. ryzyka, gdy jest składane zamówienie niepasujące do typowych schematów handlu. Łatwo byłoby zbudować nowy mikroserwis, aby wykonać tę operację, bazując na zdarzeniu wywołanym przez usługę orders bez zmiany reszty systemu.

Rysunek 1.1. Przepływ komunikacji przez mikroserwisy w aplikacji, która pozwala użytkownikom sprzedawać papiery wartościowe

1.1.1. Skalowanie przez dekompozycję

Możemy się także zastanowić, w jaki sposób mikroserwisy pozwalają na skalowanie aplikacji. W The Art of Scalability Abbott i Fisher definiują trzy wymiary skalowania jako kostkę (rys. 1.2).

Aplikacje monolityczne są zazwyczaj skalowane przez horyzontalne duplikowanie – wdrażanie wielu identycznych wystąpień aplikacji. Jest to również znane jako skalowanie cookiem cutter lub skalowanie w osi X. Aplikacje mikroserwisowe są przykładem skalowania w osi Y, w którym jest wykonywana dekompozycja systemu, aby sprostać unikalnym potrzebom skalowania różnych funkcjonalności.

UWAGA  Oś Z odnosi się do poziomego partycjonowania danych – fragmentowania. Można zastosować fragmentowanie do obu podejść, mikroserwisów lub aplikacji monolitycznych, ale nie będziemy się tym zajmować w tej książce.

Rysunek 1.2. Trzy wymiary skalowania aplikacji

Ponownie jako przykładowi przyjrzyjmy się narzędziu inwestycyjnemu o następujących cechach:

1. ■ prognozy finansowe mogą być złożone obliczeniowo i występować rzadko;

2. ■ rachunki inwestycyjne mogą być regulowane przez skomplikowane przepisy prawne i biznesowe;

3. ■ handel na giełdzie może się odbywać w bardzo dużych ilościach, a jednocześnie wymagać minimalizowania opóźnień.

Jeśli tworzymy funkcjonalności jako mikroserwisy, które spełniają pewne wymagania, możemy wybrać idealne narzędzia do rozwiązania danego problemu, zamiast próbować dopasować kwadratowe kołki do okrągłych otworów. Ponadto, autonomia i niezależność wdrażania oznacza, że możemy zarządzać potrzebnymi wymaganiami mikroserwisów osobno. Co ciekawe, oznacza to naturalny sposób na ograniczenie niepowodzeń – jeśli nasza usługa prognozowania finansowego nie zadziała, to jest mało prawdopodobne, aby ta awaria kaskadowo przeniosła się do obrotu na giełdzie lub usług obsługi rachunku inwestycyjnego.

Aplikacje mikroserwisowe mają kilka interesujących właściwości technicznych:

1. ■ budowanie usług odpowiadających pojedynczym zdolnościom stawia naturalne granice co do wielkości i odpowiedzialności;

2. ■ autonomia pozwala niezależnie rozwijać, wdrażać i skalować usługi.

1.1.2. Kluczowe zasady

Rozwój mikroserwisów jest wspierany przez pięć zasad kulturowych i architektonicznych:

1. ■ autonomię,

2. ■ odporność,

3. ■ przejrzystość,

4. ■ automatyzację,

5. ■ ukierunkowanie.

Zasady te powinny wpływać na decyzje techniczne i organizacyjne podczas budowania i uruchamiania aplikacji mikroserwisowych. Przeanalizujmy każdą z nich.

Autonomia

Ustaliliśmy, że mikroserwisy są autonomiczne – każda usługa działa i zmienia się niezależnie od innych. W celu zapewnienia autonomii nasze usługi musimy zaprojektować tak, aby były:

1. ■ luźno powiązane – dzięki interakcji za pośrednictwem jasno zdefiniowanych interfejsów lub generowanych zdarzeń każdy mikroserwis może pozostawać niezależny od wewnętrznej implementacji współpracowników; na przykład usługa obsługi zleceń, którą wprowadziliśmy wcześniej, nie powinna być świadoma sposobu implementacji usługi transakcji na rachunku – zilustrowano to na rysunku 1.3;

2. ■ niezależnie wdrażane – usługi będą tworzone równolegle, często przez wiele zespołów, gdyż konieczność ich łącznego wydawania doprowadziłaby do ryzykownych i niepewnych wdrożeń; najlepsze jest korzystanie z mniejszych usług, aby umożliwić ich szybkie, częste i małe wydania.

Autonomia jest także kulturą. Ważne jest, aby delegować odpowiedzialność i własność usług do zespołów odpowiedzialnych za wpływ na biznes. Jak ustaliliśmy, projekt organizacyjny ma wpływ na projektowanie systemu. Przejrzystość usług pozwala zespołom na iteracyjne budowanie i podejmowanie decyzji, oparte na lokalnym kontekście i celu. Ponadto, taki model jest idealny do promowania kompleksowej własności, gdy zespół jest odpowiedzialny za usługę zarówno w okresie rozwoju, jak i na produkcji.

Rysunek 1.3. Można luźno wiązać usługi komunikujące się za pomocą określonych kontraktów i ukrywające szczegóły implementacji

UWAGA  W rozdziale 13 omówimy rozwijanie odpowiedzialnych i autonomicznych zespołów inżynierskich i pokażemy, dlaczego jest to kluczowe podczas pracy z mikroserwisami.

Odporność

Mikroserwisy są naturalnym mechanizmem izolowania awarii – jeśli wdrażamy je niezależnie, awaria aplikacji lub infrastruktury może wpłynąć tylko na część naszego systemu. Ponadto, możliwość wdrażania mniejszych fragmentów funkcjonalności powinna pomóc w stopniowej zmianie systemu, nie narażać na ryzykowny Wielki Wybuch związany z nową funkcjonalnością.

Ponownie rozważmy narzędzie inwestycyjne. Jeśli usługa obsługi giełdy jest niedostępna, złożenie zlecenia na giełdzie nie będzie możliwe. Użytkownik może jednak zażądać złożenia zlecenia, a usługa może je odebrać później, gdy będzie dostępna podrzędna funkcjonalność.

Mimo że dzielenie aplikacji na wiele usług może izolować awarię, to zwiększa także liczbę punktów awarii. Ponadto musimy wziąć pod uwagę, co się stanie, gdy wystąpi awaria, aby zapobiec kaskadzie awarii. W zakresie projektowania obejmuje to zarówno preferowanie asynchronicznej interakcji tam, gdzie jest to możliwe, jak i odpowiednie stosowanie bezpieczników i limitów czasowych, natomiast w zakresie działania – stosowanie sprawdzonych technik ciągłego dostarczania i solidną kontrolę aktywności systemu.

Przejrzystość

Najważniejsze, byśmy wiedzieli, kiedy wystąpiła awaria, a aplikacja mikroserwisowa – w odróżnieniu od jednego systemu – zależy od interakcji i zachowania wielu usług, prawdopodobnie zbudowanych przez różne zespoły. W każdym momencie system powinien być przejrzysty i możliwy do monitorowania, aby zapewnić nadzór i diagnozowanie problemów.

Każda usługa w aplikacji będzie generować metryki biznesowe, operacyjne i infrastrukturalne, dzienniki aplikacji oraz ślady żądań. W związku z tym musimy się liczyć z tym, że będziemy mieć ogromną ilość danych.

Automatyzacja

Może się wydawać sprzeczne z intuicją, że złagodzenie problemów związanych z rosnącą aplikacją może odbyć się dzięki budowaniu wielu usług. To prawda, że mikroserwisy są bardziej złożoną architekturą niż pojedyncza aplikacja. Wykorzystując automatyzację i dążąc do spójności infrastruktury między usługami, można znacznie obniżyć koszt zarządzania tą dodatkową złożonością. Należy użyć automatyzacji, aby zapewnić poprawność wdrożeń i działanie systemu.

To nie przypadek, że popularność architektury mikroserwisowej jest analogią do coraz powszechniejszej adaptacji technik DevOps, w szczególności infrastructure-as-code, oraz rozwoju środowisk infrastrukturalnych, które są w pełni programowalne przez interfejsy API (takie jak AWS lub Azure). Te dwa trendy sprawiły, że mikroserwisy są osiągalne dla mniejszych zespołów.

Ukierunkowanie

Wreszcie, bardzo ważne jest, aby swoje wysiłki na rzecz rozwoju ukierunkować w sposób właściwy. Powinniśmy dążyć do uporządkowania swoich usług, a więc i swoich zespołów, zgodnie z koncepcjami biznesowymi – doprowadzi to do większej spójności.

Aby zrozumieć, dlaczego jest to ważne, rozważmy alternatywę. Wiele tradycyjnych SOA wdraża warstwy techniczne aplikacji oddzielnie: interfejs użytkownika, logikę biznesową, integrację, dane. Na rysunku 1.4 porównano architekturę SOA i mikroserwisową.

Takie użycie horyzontalnej dekompozycji w SOA jest problematyczne, ponieważ spójna funkcjonalność zostaje rozproszona w wielu systemach. Nowe funkcjonalności mogą wymagać skoordynowanych wydań dla wielu usług i mogą być niedopuszczalnie połączone z innymi na tym samym abstrakcyjnym poziomie technicznym.

Z kolei architektura mikroserwisowa powinna być zorientowana na pionowy rozkład; każda usługa powinna być dostosowana do jednej funkcjonalności biznesowej, obejmując wszystkie odpowiednie warstwy techniczne.

UWAGA  W rzadkich przypadkach sensowne może być budowanie usługi implementującej zdolności techniczne, takiej jak integracja z usługą podmiotu zewnętrznego, jeśli wymaga tego wiele innych usług.

Powinniśmy także pamiętać o konsumentach swoich usług. Aby zapewnić stabilny system, musimy mieć pewność, że rozwój odbywa się spokojnie i zachowuje kompatybilność wsteczną – jawnie lub przez uruchamianie wielu wersji usługi – aby nie zmuszać innych zespołów do aktualizacji lub zerwania skomplikowanych interakcji między usługami.

Praca zgodnie z tymi pięcioma zasadami pomoże nam dobrze rozwinąć mikroserwisy, prowadząc do systemów, które są wysoce podatne na zmiany oraz skalowalne i stabilne.

Rysunek 1.4. SOA vs. architektura mikroserwisowa

1.1.3. Kto używa mikroserwisów?

Wiele firm pomyślnie zbudowało i wdrożyło mikroserwisy w różnych obszarach: w mediach (The Guardian), dystrybucji treści (SoundCloud, Netflix), transporcie i logistyce (Hailo, Uber), e-commerce (Amazon, Gilt, Zalando), bankowości (Monzo) i mediach społecznościowych (Twitter).

Większość z tych firm przyjęła podejście monolityczne[1]. Zaczęto od zbudowania pojedynczej dużej aplikacji, a następnie stopniowo przenoszono ją do postaci mikroserwisów w odpowiedzi na pojawiające się presje. Przedstawiono je w tabeli 1.1.

Tabela 1.1. Presje powodujące rozwój systemu informatycznego

Presja

Opis

Wolumen

wolumen działań wykonywanych przez system może przerastać możliwości początkowo wybranych technologii

Nowe funkcjonalności

nowe funkcjonalności mogą nie być spójne z istniejącymi funkcjonalnościami lub inne technologie mogą być lepsze w rozwiązywaniu problemów

Rozwój zespołu

w miarę powiększania się zespołu zwiększa się liczba możliwych połączeń komunikacyjnych; nowi programiści spędzają więcej czasu na poznawaniu istniejącego systemu, a mniej na wprowadzaniu wartości dodanej

Techniczne zaszłości

zwiększona złożoność systemu – w tym zaszłości wynikające z wcześniejszych decyzji dotyczących rozwoju – zwiększa trudności we wprowadzaniu zmian

Rozwój międzynarodowy

rozwój międzynarodowy wprowadza wyzwania związane ze spójnością danych, dostępnością i opóźnieniami

Na przykład firma Hailo chciała rozszerzyć działalność na skalę międzynarodową, co byłoby wyzwaniem dla ich oryginalnej architektury, ale również zwiększyłoby tempo dostarczania funkcjonalności. SoundCloud chciał być bardziej produktywny, ponieważ powstrzymywała go złożoność ich pierwotnej monolitycznej aplikacji. Czasami zmiana zbiegała się ze zmianą priorytetu biznesowego – Netflix przeszedł z fizycznej dystrybucji DVD na streaming treści. Niektóre z tych firm całkowicie wycofały już swój pierwotny monolit. Ale dla wielu jest to proces ciągły, z monolitem otoczonym konstelacją mniejszych usług.

Gdy architektura mikroserwisowa została spopularyzowana – i pierwsi użytkownicy udostępnili źródła, tworzyli blogi oraz prezentowali praktyki, które okazały się dla nich skuteczne – zespoły coraz częściej zaczęły tworzyć nowatorskie projekty przy użyciu mikroserwisów zamiast budować najpierw pojedynczą aplikację. Na przykład Monzo użył mikroserwisów jako części swojej misji budowania lepszego i bardziej skalowalnego banku.

1.1.4. Dlaczego mikroserwisy to dobry wybór?

Wiele udanych przedsięwzięć jest opartych na monolitycznym oprogramowaniu – przykładami są Basecamp[2], Stack-Overflow i Etsy. W aplikacjach monolitycznych istnieje bogactwo ortodoksyjnych, długoletnich praktyk i wiedzy dotyczącej tworzenia oprogramowania. Dlaczego warto wybrać mikroserwisy?

Techniczna heterogeniczność prowadzi do mikroserwisów

W niektórych firmach techniczna heterogeniczność sprawia, że mikroserwisy są oczywistym wyborem. W Onfido zaczęliśmy budować mikroserwisy, gdy wprowadziliśmy produkt oparty na uczeniu maszynowym – niepasujący do naszego oryginalnego Ruby! Nawet, gdy nie jesteśmy w pełni przekonani do podejścia mikroserwisowego, zastosowanie tych zasad daje większy wybór technicznych rozwiązań w celu uporania się z problemami biznesowymi. Niemniej jednak nie zawsze jest to tak jednoznaczne.

Im większy wzrost skomplikowania systemu, tym bardziej narastają tarcia w trakcie jego rozwoju

Wynika to z natury złożonych systemów. Na początku rozdziału wspomnieliśmy, że programiści starają się tworzyć efektywne i ponadczasowe rozwiązania złożonych problemów. Ale systemy oprogramowania, które tworzymy, są z natury skomplikowane. Żadna metodologia ani architektura nie może wyeliminować istotnej złożoności w sercu takiego systemu.

Ale to nie jest powód, by wpadać w przygnębienie! Możemy spowodować, że wybrane przez nas podejścia programistyczne będą skutkować dobrymi złożonymi systemami, bez przypadkowej złożoności.

Poświęćmy chwilę i zastanówmy się, co chcemy osiągnąć, będąc programistami oprogramowania dla firmy. Dobrze ujął to Dan North:

Celem rozwoju oprogramowania jest zrównoważone minimalizowanie czasu oczekiwania na pozytywny wpływ na działalność.

Najtrudniejszą częścią złożonych systemów oprogramowania jest dostarczanie trwałych wartości w obliczu zmian – tak, aby nadal je dostarczać zręcznie, w odpowiednim tempie i bezpiecznie, nawet gdy system staje się większy i bardziej złożony. Dlatego uważamy, że dobry złożony system to taki, w którym w całym cyklu jego życia są minimalizowane dwa czynniki: tarcie i ryzyko.

Ograniczają one szybkość i zwinność, a tym samym zdolność do wpływania na biznes. Gdy monolit rośnie, następujące czynniki mogą prowadzić do tarć:

1. ■ cykle zmian są ze sobą powiązane, co prowadzi do większych barier w koordynacji i wyższego ryzyka regresji;

2. ■ niejasne wzorce i granice kontekstu wprowadzają chaos w niezdyscyplinowanych zespołach, prowadząc do ścisłych lub nieoczekiwanych sprzężeń między komponentami;

3. ■ sam rozmiar także może być bolesny – ciągłe zadania związane z integracją i tworzeniem nowych wersji, a także lokalne uruchamianie aplikacji stają się wolniejsze.

Te cechy nie są prawdziwe dla wszystkich monolitów, ale niestety są prawdziwe dla większości, z którymi się zetknęliśmy. Ponadto tego typu wyzwania są typowym wątkiem w historiach o firmach, o których wspomnieliśmy.

Mikroserwisy zmniejszają tarcie i ryzyko

Mikroserwisy pomagają zmniejszyć tarcie i ryzyko na trzy sposoby:

1. ■ izolując i minimalizując zależności w czasie kompilacji;

2. ■ umożliwiając programistom generowanie spójnych pojedynczych komponentów zamiast całego systemu;

3. ■ umożliwiając ciągłe dostarczanie małych, niezależnych zmian.

Izolowanie i minimalizowanie zależności podczas kompilacji – czy to między zespołami, czy w istniejącym kodzie – pozwala programistom na szybsze działanie. Rozwój może się odbywać równolegle, przy mniejszej długoterminowej zależności od wcześniejszych decyzji podejmowanych dla monolitycznej aplikacji. Zaszłości technologiczne w naturalny sposób ograniczają się do granic usług.

Mikroserwisy są pojedynczo łatwiejsze do zbudowania i bardziej zrozumiałe niż aplikacje monolityczne. Jest to korzystne dla produktywności w rozwijającej się firmie. Zapewnia także przekonujący i elastyczny paradygmat do radzenia sobie z coraz większą skalą lub łatwym wprowadzaniem nowych technologii.

Małe usługi są również doskonałym sposobem na wprowadzanie częstych zmian. Wdrożenia w dużych aplikacjach mogą być ryzykowne i obejmować cykle regresji i weryfikacji. Wdrażając mniejsze fragmenty funkcjonalności, łatwiej można izolować zmiany w działającym systemie, zmniejszając potencjalne ryzyko wynikające z wdrożenia.

W tym momencie dochodzimy do dwóch wniosków:

1. ■ rozwój małych, autonomicznych usług może zmniejszyć tarcie w rozwoju długo istniejących, złożonych systemów;

2. ■ dostarczając spójne i niezależne elementy funkcjonalności, można zbudować system, który jest elastyczny i odporny na zmiany, co pomaga uzyskać zrównoważony wpływ na biznes przy zmniejszonym ryzyku.

To nie znaczy, że każdy powinien budować mikroserwisy. Byłoby cudownie, gdyby istniała obiektywna odpowiedź na pytanie „Czy potrzebuję mikroserwisów?”, ale niestety można tylko odpowiedzieć „To zależy” – od naszego zespołu, od naszej firmy i od charakteru budowanego systemu. Jeśli zakres naszego systemu jest trywialny, to jest mało prawdopodobne, że uzyskamy korzyści, które przewyższą dodatkową złożoność wynikającą z budowania i obsługi tego typu aplikacji. Ale jeśli napotkamy którekolwiek z wyzwań, o których wspominaliśmy wcześniej w tej części, to mikroserwisy są przekonującym rozwiązaniem.

Opowieść ku przestrodze

Słyszeliśmy kiedyś historię o nieudanej implementacji mikroserwisów. Istniejący proces zaczął się rozrastać, CTO zdecydował zatem, że jedynym rozwiązaniem jest przebudowanie aplikacji na postać mikroserwisów. Jeśli nie przejmujesz się tym stwierdzeniem, to lepiej zacznij!

Zespół inżynierów rozpoczął przebudowę aplikacji. Zajęło im to pięć miesięcy, podczas których nie wydali żadnych nowych funkcjonalności ani żadnego mikroserwisu do produkcji. Zespół zaczął uruchamianie nowej aplikacji mikroserwisowej w najbardziej krytycznym miesiącu dla biznesu, powodując absolutny chaos i konieczność wycofania się do pierwotnego monolitu.

Ten rodzaj migracji przysparza mikroserwisom złej sławy. Niewiele firm może sobie pozwolić na luksus zamrożenia funkcjonalności na kilka miesięcy, a także na uruchomienie nowej architektury. Mimo że zestaw przypadków jest niewielki, najbardziej udane migracje do mikroserwisów, które obserwowaliśmy, były fragmentaryczne, równoważąc wizję architektoniczną z potrzebami biznesowymi, priorytetami i ograniczeniami zasobowymi. Chociaż potrwa to dłużej i będzie wymagać więcej wysiłku inżynierskiego, miejmy nadzieję, że nigdy się nie zdarzy, że nasz zespół będzie wspominany w opowieściach ku przestrodze!

1.2. Co w mikroserwisach jest wyzwaniem?

Zagłębmy się nieco w temat i zbadajmy koszty oraz złożoność projektowania i uruchamiania mikroserwisów. Mikroserwisy nie są jedyną architekturą, która obiecała nirwanę dzięki dekompozycji i rozproszeniu, ale ostatnie próby, takie jak SOA[3], są powszechnie uważane za nieskuteczne. Żadna technika nie jest magicznym narzędziem. Na przykład, jak już wspomnieliśmy, mikroserwisy drastycznie zwiększają liczbę odrębnych elementów w systemie. Rozdzielając funkcjonalności i odpowiedzialność za dane na wiele autonomicznych usług, jest również rozdzielana odpowiedzialność za stabilność i właściwedziałanie aplikacji.

Podczas projektowania i uruchamiania aplikacji mikroserwisowych napotkamy wiele wyzwań:

1. ■ określanie zakresu i identyfikowanie mikroserwisów wymaga znacznej wiedzy na temat domeny ich zastosowania;

2. ■ właściwe granice i kontrakty między usługami są trudne do zidentyfikowania i, po ich ustaleniu, ich zmiany mogą być czasochłonne;

3. ■ mikroserwisy są systemami rozproszonymi i dlatego wymagają wielu założeń dotyczących stanu, spójności i niezawodności sieci;

4. ■ przez rozproszenie komponentów systemu w sieci i zwiększenie heterogeniczności technicznej mikroserwisy wprowadzają nowe typy awarii;

5. ■ trudniej jest zrozumieć i zweryfikować, co powinno się zdarzyć podczas normalnej pracy.

1.2.1. Wyzwania związane z projektowaniem

W jaki sposób te wyzwania wpływają na fazę projektowania i uruchamiania mikroserwisów? Wcześniej wprowadziliśmy pięć podstawowych zasad tworzenia mikroserwisów. Pierwszą z nich była autonomia. Aby nasze usługi były autonomiczne, musimy je tak zaprojektować, aby razem były luźno powiązane i każda zawierała wysoce spójne elementy funkcjonalności. Jest to proces ewolucyjny. Zakres naszych usług może się zmieniać w miarę upływu czasu, a my będziemy często się decydować na wyodrębnienie nowej funkcjonalności z usług, a nawet wycofanie już istniejących.

Dokonywanie tych wyborów jest wyzwaniem, szczególnie istotnym na początku opracowywania aplikacji! Głównym źródłem luźnych powiązań są granice między usługami; zły wybór doprowadzi do usług, które są trudne do zmiany i, ogólnie, mniej plastyczne i elastyczne do zastosowania.

Określanie zakresu mikroserwisów wymaga wiedzy na temat domeny ich zastosowania

Każdy mikroserwis jest odpowiedzialny za pojedynczą zdolność. Identyfikacja tych zdolności wymaga znajomości domeny biznesowej aplikacji. W początkowej fazie życia aplikacji nasza wiedza o domenie może być w najlepszym razie niekompletna lub, w najgorszym, niepoprawna.

Nieodpowiednie zrozumienie domeny problemu może skutkować złymi wyborami projektowymi. W aplikacji mikroserwisowej zwiększona sztywność granicy usługi w porównaniu z modułem w aplikacji monolitycznej oznacza, że koszt złych decyzji dotyczących zakresu może być wyższy:

1. ■ być może trzeba będzie refaktoryzować wiele różnych baz kodów;

2. ■ konieczna może być migracja danych z bazy danych jednej usługi do drugiej;

3. ■ możliwe, że nie zostaną zidentyfikowane niejawne zależności między usługami, co może prowadzić do błędów lub niezgodności podczas wdrażania.

Czynności te zilustrowano na rysunku 1.5.

Jednak podejmowanie decyzji projektowych, przy niewystarczającej wiedzy o danej dziedzinie, nie jest rzadkością w przypadku mikroserwisów! Różne są skutki tych decyzji.

UWAGA  W rozdziałach 2 i 4 za pomocą przykładowej aplikacji omówimy dobre praktyki dotyczące identyfikowania i określania zakresów usług.

Zarządzanie kontraktami między usługami

Każdy mikroserwis powinien być niezależny od sposobu implementacji innych usług. Pozwala to na techniczną heterogeniczność i autonomię. Aby to działało, każdy mikroserwis powinien wystawiać kontrakt – analogicznie do interfejsu w projektowaniu obiektowym – określający komunikaty, które spodziewa się otrzymywać i na które będzie udzielać odpowiedzi. Dobry kontrakt powinien być:

1. ■ kompletny – definiuje pełny zakres interakcji;

2. ■ zwięzły – nie wymaga więcej informacji niż jest to konieczne, tak, aby konsumenci mogli konstruować komunikaty w rozsądnych granicach;

3. ■ przewidywalny – dokładnie odzwierciedla rzeczywiste zachowanie implementacji.

Rysunek 1.5. Nieprawidłowe decyzje dotyczące zakresu usług mogą wymagać złożonej i kosztownej refaktoryzacji w obrębie granic usług

Każdy kto projektował API, wie, jak trudno osiągnąć takie cechy. Kontrakty są spoiwem między usługami. Z czasem kontrakty mogą ewoluować, ale jednocześnie muszą zachować wsteczną kompatybilność dla istniejących współpracowników. Te sprzeczne dążenia – między stabilnością a zmianą – są trudne do zarządzania.

Aplikacje mikroserwisowe są projektowane przez zespoły

W większych firmach prawdopodobnie wiele zespołów będzie budować i uruchamiać aplikację mikroserwisową, z których każdy będzie odpowiedzialny za różne mikroserwisy. Każdy zespół może mieć własne cele, sposób pracy i cykl życia dostarczania. Projektowanie spójnego systemu może być trudne, gdy trzeba pogodzić harmonogramy i priorytety wielu niezależnych zespołów. Koordynacja rozwoju poważniejszych aplikacji mikroserwisowych będzie zatem wymagać porozumienia i uzgodnienia priorytetów oraz praktyk w wielu zespołach.

Aplikacje mikroserwisowe są systemami rozproszonymi

Projektowanie aplikacji mikroserwisowych oznacza projektowanie systemów rozproszonych. W projektowaniu systemów rozproszonych pojawia się wiele błędnych założeń, takich jak:

1. ■ sieć jest niezawodna;

2. ■ opóźnienie jest zerowe;

3. ■ przepustowość jest nieograniczona;

4. ■ koszt transportu jest zerowy.

Założenia, które można zastosować do systemów scentralizowanych, takie jak szybkość i niezawodność wywołań metod, w tych przypadkach są oczywiście nieodpowiednie i mogą prowadzić do złej, niestabilnej implementacji. Należy uwzględnić opóźnienia, niezawodność i spójność stanów w całym obszarze aplikacji.

W aplikacji rozproszonej, gdzie dane o stanie aplikacji są rozmieszczone w wielu miejscach, spójność staje się wyzwaniem. Możemy nie mieć gwarancji kolejności operacji. Nie będzie można zapewnić gwarancji transakcyjnych podobnych do ACID, gdy działania mają miejsce w wielu usługach. Wpłynie to na projektowanie na poziomie aplikacji – należy się zastanowić, w jaki sposób usługa może działać w niespójnym stanie i jak wycofać się w przypadku niepowodzenia transakcji.

1.2.2. Wyzwania związane z działaniem

Podejście mikroserwisowe z natury mnoży możliwe miejsca awarii w systemie. Aby to zilustrować, wróćmy do narzędzia inwestycyjnego, o którym wspominaliśmy wcześniej. Na rysunku 1.6 są wskazane możliwe punkty awarii w tej aplikacji. Łatwo zauważyć, że w wielu miejscach coś może pójść nie tak i może mieć wpływ na normalne przetwarzanie zlecenia.

Zastanówmy się, na jakie pytania należy odpowiedzieć, gdy aplikacja jest na etapie produkcji:

1. ■ Jeśli coś pójdzie nie tak i zlecenie użytkownika nie zostanie złożone, to w jaki sposób ustalimy, gdzie doszło do błędu?

2. ■ Jak wdrożyć nową wersję usługi bez wpływu na składanie zleceń?

3. ■ Skąd wiadomo, które usługi miały zostać wywołane?

4. ■ Jak sprawdzić, czy dane zachowanie działa poprawnie między wieloma usługami?

5. ■ Co się stanie, gdy usługa będzie niedostępna?

Zamiast eliminować ryzyko, mikroserwisy przenoszą te koszty na późniejszy etap cyklu życia systemu – zmniejszają tarcia w rozwoju, ale zwiększają złożoność sposobu wdrażania, weryfikacji i obserwacji działania aplikacji.

Rysunek 1.6. Możliwe punkty awarii przy składaniu zlecenia sprzedaży

Podejście mikroserwisowe sugeruje ewolucyjne podejście do projektowania systemu – umożliwia dodawanie nowych funkcjonalności niezależnie, bez zmiany istniejących usług. Minimalizuje to koszty i ryzyko zmian.

Ale w samodzielnym systemie, który ciągle się zmienia, może być niezwykle trudno śledzić obraz całości, co sprawia, że diagnoza problemu i wsparcie są bardziej kłopotliwe. Kiedy coś pójdzie nie tak, musimy mieć jakiś sposób śledzenia zachowania się systemu (jakie usługi wywołał, w jakiej kolejności, jaki był wynik), ale potrzebujemy również sposobu, aby dowiedzieć się, jak system powinien się zachowywać.

W mikroserwisach zatem napotykamy dwa operacyjne wyzwania: obserwowalność i wiele punktów awarii. Skupmy się na każdym z nich.

Obserwowalność jest trudna do osiągnięcia

Na znaczenie przejrzystości zwróciliśmy uwagę w punkcie 1.1.2. Ale dlaczego jest to trudniejsze w aplikacjach mikroserwisowych? Jest tak, ponieważ musimy zrozumieć szerszy kontekst. Musimy go zebrać z wielu elementów układanki po to, żeby skorelować i połączyć dane, które generuje każda usługa, aby móc zrozumieć, co każda usługa robi w szerszym kontekście dostarczania wyników biznesowych. Poszczególne dzienniki usług zapewniają częściowy widok na działanie systemu, co jest pomocne, ale do pełnego zrozumienia całości potrzebne są zarówno mikroskop, jak i obiektyw szerokokątny.

Ponadto, ponieważ uruchamiamy wiele aplikacji, w zależności od tego, jak zostały wdrożone, powiązania między podstawowymi parametrami infrastruktury – takimi jak wykorzystanie pamięci i procesora – a aplikacją mogą być mniej oczywiste. Te parametry nadal mogą być użyteczne, ale są mniej przydatne niż mogłyby być w systemie monolitycznym.

Zwiększanie liczby mikroserwisów zwiększa liczbę punktów awarii

Prawdopodobnie nie będziemy zbytnimi pesymistami, jeśli powiemy, że wszystko, co może zawieść, zawiedzie. Ważne jest, żeby tak o tym myśleć – jeśli założymy słabość i niestabilność usług tworzących nasz system, to ułatwi nam to jego projektowanie, wdrażanie i monitorowanie i nie będziemy zaskoczeni, gdy coś pójdzie nie tak.

Musimy się zastanowić, jak system będzie działał mimo awarii poszczególnych komponentów. Oznacza to, że indywidualne usługi będą musiały być bardziej niezawodne – pod względem sprawdzania błędów, działania w sytuacjach awaryjnych i powrotu do normalnej pracy – ale także to, że cały system powinien działać niezawodnie, nawet jeśli poszczególne komponenty nigdy nie są w 100% niezawodne.

Rysunek 1.7. Kluczowe powtarzane etapy – projekt, wdrażanie i obserwacja – w cyklu życia mikroserwisu

Przypisy

Podziękowania

1 Wydawca książki w USA (przyp. tłum.).

1 Martin Fowler rozwinął ten wzorzec w: MonolithFirst, June 3, 2015, http://martinfowler.com/bliki/MonolithFirst.html.

2 David Heinemeier Hansson ukuł termin „Majestic Monolith”, aby opisać, jak 37signals zbudowało Basecamp: Signal v. Noise, 29 lutego 2016 r., http://mng.bz/1p3I.

3 SOA jest mętnym terminem. Mimo że wiele zasad SOA jest podobnych do tych dotyczących mikroserwisów, definicja tego pierwszego jest nierozerwalnie związana z dużymi narzędziami dla przedsiębiorstw, takimi jak ESB.