Ciągła integracja/ciągłe wdrażanie dla architektur mikrousług

Azure

Szybsze cykle wydawania są jedną z głównych zalet architektur mikrousług. Jednak bez dobrego procesu ciągłej integracji/ciągłego wdrażania nie osiągniesz elastyczności, którą obiecują mikrousługi. W tym artykule opisano wyzwania i zaleca się kilka podejść do problemu.

Co to jest ciągła integracja/ciągłe wdrażanie?

Kiedy mówimy o ciągłej integracji/ciągłego wdrażania, naprawdę mówimy o kilku powiązanych procesach: ciągłej integracji, ciągłego dostarczania i ciągłego wdrażania.

  • Ciągła integracja. Zmiany kodu są często scalane z gałęzią główną. Zautomatyzowane procesy kompilacji i testowania zapewniają, że kod w gałęzi głównej jest zawsze jakością produkcyjną.

  • Ciągłe dostarczanie. Wszelkie zmiany kodu, które przechodzą proces ciągłej integracji, są automatycznie publikowane w środowisku przypominającym środowisko produkcyjne. Wdrożenie w środowisku produkcyjnym na żywo może wymagać ręcznego zatwierdzenia, ale w przeciwnym razie jest zautomatyzowane. Celem jest to, że kod powinien być zawsze gotowy do wdrożenia w środowisku produkcyjnym.

  • Ciągłe wdrażanie. Zmiany kodu, które przechodzą poprzednie dwa kroki, są automatycznie wdrażane w środowisku produkcyjnym.

Poniżej przedstawiono kilka celów niezawodnego procesu ciągłej integracji/ciągłego wdrażania dla architektury mikrousług:

  • Każdy zespół może tworzyć i wdrażać usługi, które są jej właścicielami niezależnie, bez wpływu na inne zespoły lub zakłócania ich działania.

  • Zanim nowa wersja usługi zostanie wdrożona w środowisku produkcyjnym, zostanie wdrożona w środowiskach tworzenia i testowania/kontroli jakości w celu weryfikacji. Bramy jakości są wymuszane na każdym etapie.

  • Nową wersję usługi można wdrożyć obok poprzedniej wersji.

  • Obowiązują wystarczające zasady kontroli dostępu.

  • W przypadku obciążeń konteneryzowanych można ufać obrazom kontenerów wdrożonym w środowisku produkcyjnym.

Dlaczego niezawodny potok ciągłej integracji/ciągłego wdrażania ma znaczenie

W tradycyjnej aplikacji monolitycznej istnieje pojedynczy potok kompilacji, którego dane wyjściowe są plikiem wykonywalnym aplikacji. Wszystkie prace programistyczne są wprowadzane do tego potoku. Jeśli zostanie znaleziona usterka o wysokim priorytcie, należy zintegrować, przetestować i opublikować poprawkę, co może opóźnić wydanie nowych funkcji. Aby wyeliminować te problemy, można użyć dobrze uwzględnionych modułów i gałęzi funkcji, aby zminimalizować wpływ zmian kodu. Jednak wraz ze wzrostem złożoności aplikacji, a coraz więcej funkcji jest dodawanych, proces wydawania monolitu zwykle staje się bardziej kruchy i może ulec awarii.

Zgodnie z filozofią mikrousług nigdy nie powinno być długie szkolenie wydania, w którym każdy zespół musi dostać się w kolejce. Zespół tworzący usługę "A" może w dowolnym momencie wydać aktualizację bez oczekiwania na scalenie, przetestowanie i wdrożenie zmian w usłudze "B".

Diagram monolitu ciągłej integracji/ciągłego wdrażania

Aby osiągnąć wysoką szybkość wydawania, potok wydania musi być zautomatyzowany i wysoce niezawodny, aby zminimalizować ryzyko. W przypadku wydania do środowiska produkcyjnego co najmniej raz dziennie regresje lub przerwy w działaniu usługi muszą być rzadkie. W tym samym czasie, jeśli zła aktualizacja zostanie wdrożona, musisz mieć niezawodny sposób szybkiego wycofywania lub wycofywania do poprzedniej wersji usługi.

Wyzwania

  • Wiele małych niezależnych baz kodu. Każdy zespół jest odpowiedzialny za tworzenie własnej usługi przy użyciu własnego potoku kompilacji. W niektórych organizacjach zespoły mogą używać oddzielnych repozytoriów kodu. Oddzielne repozytoria mogą prowadzić do sytuacji, w której wiedza na temat tworzenia systemu jest rozłożona na zespoły, a nikt w organizacji nie wie, jak wdrożyć całą aplikację. Co się dzieje na przykład w scenariuszu odzyskiwania po awarii, jeśli chcesz szybko wdrożyć go w nowym klastrze?

    Środki zaradcze: ujednolicony i zautomatyzowany potok do tworzenia i wdrażania usług, dzięki czemu ta wiedza nie jest "ukryta" w każdym zespole.

  • Wiele języków i struktur. Każdy zespół korzystający z własnej kombinacji technologii może być trudny do utworzenia pojedynczego procesu kompilacji, który działa w całej organizacji. Proces kompilacji musi być wystarczająco elastyczny, aby każdy zespół mógł dostosować go do wybranego języka lub struktury.

    Środki zaradcze: Konteneryzowanie procesu kompilacji dla każdej usługi. Dzięki temu system kompilacji musi mieć możliwość uruchamiania kontenerów.

  • Integracja i testowanie obciążenia. Zespoły publikujące aktualizacje we własnym tempie mogą być trudne do zaprojektowania niezawodnego kompleksowego testowania, zwłaszcza gdy usługi mają zależności od innych usług. Ponadto uruchomienie pełnego klastra produkcyjnego może być kosztowne, więc jest mało prawdopodobne, aby każdy zespół uruchamiał własny pełny klaster w skali produkcyjnej, tylko na potrzeby testowania.

  • Zarządzanie wydaniami. Każdy zespół powinien mieć możliwość wdrożenia aktualizacji w środowisku produkcyjnym. Nie oznacza to, że każdy członek zespołu ma do tego uprawnienia. Jednak posiadanie scentralizowanej roli Menedżera wydań może zmniejszyć szybkość wdrożeń.

    Środki zaradcze: tym bardziej proces ciągłej integracji/ciągłego wdrażania jest zautomatyzowany i niezawodny, tym mniej powinno być konieczne dla urzędu centralnego. Oznacza to, że mogą istnieć różne zasady dotyczące wydawania głównych aktualizacji funkcji w porównaniu z drobnymi poprawkami błędów. Decentralizacja nie oznacza zerowego ładu.

  • Aktualizacje usługi. Podczas aktualizowania usługi do nowej wersji nie należy przerywać innych usług, które są od niej zależne.

    Środki zaradcze: Użyj technik wdrażania, takich jak niebiesko-zielony lub kanary, w przypadku zmian niepowiązanych z niezgodność. W przypadku zmian powodujących niezgodność interfejsu API wdróż nową wersję obok poprzedniej wersji. Dzięki temu usługi, które korzystają z poprzedniego interfejsu API, można zaktualizować i przetestować pod kątem nowego interfejsu API. Zobacz Aktualizowanie usług poniżej.

Monorepo a multi-repo

Przed utworzeniem przepływu pracy ciągłej integracji/ciągłego wdrażania musisz wiedzieć, jak baza kodu będzie ustrukturyzowana i zarządzana.

  • Czy zespoły pracują w oddzielnych repozytoriach lub w monorepo (pojedyncze repozytorium)?
  • Jaka jest twoja strategia rozgałęziania?
  • Kto może wypychać kod do środowiska produkcyjnego? Czy istnieje rola menedżera wersji?

Podejście monorepo zyskuje na korzyść, ale istnieją zalety i wady obu.

  Monorepo Wiele repozytoriów
Zalety Udostępnianie kodu
Łatwiejsza standaryzacja kodu i narzędzi
Łatwiejsze refaktoryzację kodu
Odnajdywanie — pojedynczy widok kodu
Wyczyść własność na zespół
Potencjalnie mniej konfliktów scalania
Pomaga wymusić oddzielenie mikrousług
Wyzwania Zmiany w kodzie udostępnionym mogą mieć wpływ na wiele mikrousług
Większy potencjał konfliktów scalania
Narzędzia muszą być skalowane do dużej bazy kodu
Kontrola dostępu
Bardziej złożony proces wdrażania
Trudniejsze do udostępnienia kodu
Trudniej wymusić standardy kodowania
Zarządzanie zależnościami
Podstawy kodu rozproszonego, słaba możliwość odnajdywania
Brak udostępnionej infrastruktury

Aktualizowanie usług

Istnieją różne strategie aktualizowania usługi, która jest już w środowisku produkcyjnym. W tym miejscu omówiono trzy typowe opcje: aktualizacja stopniowa, wdrożenie niebiesko-zielone i wydanie kanary.

Aktualizacje stopniowe

W ramach aktualizacji stopniowej wdrażasz nowe wystąpienia usługi, a nowe wystąpienia zaczynają odbierać żądania od razu. W miarę tworzenia nowych wystąpień poprzednie wystąpienia są usuwane.

Przykład. W usłudze Kubernetes aktualizacje stopniowe są zachowaniem domyślnym podczas aktualizowania specyfikacji zasobnika dla wdrożenia. Kontroler wdrażania tworzy nowy zestaw replik dla zaktualizowanych zasobników. Następnie skaluje w górę nowy zestaw replik podczas skalowania w dół starego, aby zachować żądaną liczbę replik. Nie usuwa starych zasobników, dopóki nowe nie będą gotowe. Platforma Kubernetes przechowuje historię aktualizacji, dzięki czemu w razie potrzeby można wycofać aktualizację.

Przykład. Usługa Azure Service Fabric domyślnie używa strategii aktualizacji stopniowej. Ta strategia najlepiej nadaje się do wdrażania wersji usługi z nowymi funkcjami bez konieczności zmieniania istniejących interfejsów API. Usługa Service Fabric uruchamia wdrożenie uaktualnienia, aktualizując typ aplikacji do podzestawu węzłów lub domeny aktualizacji. Następnie jest on przekazywany do następnej domeny aktualizacji do momentu uaktualnienia wszystkich domen. Jeśli aktualizacja domeny uaktualnienia nie powiedzie się, typ aplikacji zostanie przywrócony do poprzedniej wersji we wszystkich domenach. Należy pamiętać, że typ aplikacji z wieloma usługami (i jeśli wszystkie usługi są aktualizowane w ramach jednego wdrożenia uaktualnienia) jest podatny na awarię. Jeśli jedna usługa nie zostanie zaktualizowana, cała aplikacja zostanie wycofana z poprzedniej wersji, a pozostałe usługi nie zostaną zaktualizowane.

Jednym z wyzwań dotyczących aktualizacji jest to, że podczas procesu aktualizacji uruchamiane i odbierane są różne stare i nowe wersje. W tym okresie każde żądanie może zostać przekierowane do jednej z dwóch wersji.

W przypadku zmian powodujących niezgodność interfejsu API dobrym rozwiązaniem jest obsługa obu wersji jednocześnie do momentu zaktualizowania wszystkich klientów poprzedniej wersji. Zobacz Przechowywanie wersji interfejsu API.

Wdrożenie niebiesko-zielone

We wdrożeniu niebiesko-zielonym wdrażasz nową wersję wraz z poprzednią wersją. Po zweryfikowaniu nowej wersji należy przełączyć cały ruch jednocześnie z poprzedniej wersji na nową wersję. Po przełączeniu można monitorować aplikację pod kątem wszelkich problemów. Jeśli coś pójdzie nie tak, możesz wrócić do starej wersji. Zakładając, że nie ma żadnych problemów, możesz usunąć starą wersję.

W przypadku bardziej tradycyjnej aplikacji monolitycznej lub N-warstwowej wdrożenie niebiesko-zielone zwykle oznaczało aprowizowanie dwóch identycznych środowisk. Nowa wersja zostanie wdrożona w środowisku przejściowym, a następnie przekierowanie ruchu klienta do środowiska przejściowego — na przykład przez zamianę adresów VIP. W architekturze mikrousług aktualizacje są wykonywane na poziomie mikrousług, więc zwykle wdraża się aktualizację w tym samym środowisku i używa mechanizmu odnajdywania usługi do zamiany.

Przykład. W usłudze Kubernetes nie trzeba aprowizować oddzielnego klastra, aby wykonywać wdrożenia niebiesko-zielone. Zamiast tego można skorzystać z selektorów. Utwórz nowy zasób wdrożenia z nową specyfikacją zasobnika i innym zestawem etykiet. Utwórz to wdrożenie bez usuwania poprzedniego wdrożenia lub modyfikowania usługi, która ją wskazuje. Po uruchomieniu nowych zasobników możesz zaktualizować selektor usługi, aby był zgodny z nowym wdrożeniem.

Jedną z wad wdrożenia blue-green jest to, że podczas aktualizacji jest uruchomionych dwa razy więcej zasobników dla usługi (bieżący i następny). Jeśli zasobniki wymagają dużej ilości zasobów procesora CPU lub pamięci, może być konieczne tymczasowe skalowanie klastra w poziomie w celu obsługi użycia zasobów.

Wydanie Canary

W wersji kanaarnej wdrażasz zaktualizowaną wersję dla niewielkiej liczby klientów. Następnie monitorujesz zachowanie nowej usługi przed wdrożeniem jej na wszystkich klientach. Pozwala to na powolne wdrażanie w kontrolowany sposób, obserwowanie rzeczywistych danych i rozwiązywanie problemów, zanim wszyscy klienci zostaną dotknięci.

Wydanie kanary jest bardziej złożone do zarządzania niż niebiesko-zielone lub stopniowe aktualizacje, ponieważ należy dynamicznie kierować żądania do różnych wersji usługi.

Przykład. Na platformie Kubernetes można skonfigurować usługę tak, aby obejmowała dwa zestawy replik (po jednym dla każdej wersji) i ręcznie dostosować liczbę replik. Jednak takie podejście jest raczej grubsze, ze względu na sposób równoważenia obciążenia platformy Kubernetes w zasobnikach. Jeśli na przykład masz łącznie 10 replik, możesz przesunąć ruch tylko w 10% przyrostów. Jeśli używasz siatki usług, możesz użyć reguł routingu siatki usług, aby zaimplementować bardziej zaawansowaną strategię wydawania kanary.

Następne kroki