W przypadku rozwoju aplikacji opartej na mikroserwisach, czas testowania zmian kodu w środowisku zbliżonym do produkcyjnego jest kluczowy. Wolne cykle testowe mogą znacząco obniżyć produktywność programistów i spowolnić tempo wydawania nowych wersji oprogramowania. W miarę jak zespoły inżynierskie i architektury stają się bardziej złożone, znalezienie skalowalnego i wydajnego rozwiązania do testowania mikroserwisów staje się priorytetem.
Problemy z tworzeniem środowisk na żądanie
Wielu programistów decyduje się na tworzenie środowisk na żądanie, uruchamiając oddzielne instancje dla każdego dewelopera lub zespołu. Takie rozwiązania często opierają się na wirtualnych maszynach (VM), przestrzeniach nazw Kubernetes lub nawet na oddzielnych klastrach Kubernetes. Choć początkowo może to wydawać się logiczne, to podejście to napotyka na wiele wyzwań.
Wysokie koszty zarządzania
W miarę wzrostu złożoności systemu, każde środowisko wymaga wielu komponentów, w tym usług bezstanowych, load balancerów, bramek API, baz danych, kolejek wiadomości oraz różnorodnych zasobów chmurowych. Zarządzanie i aktualizacja tych komponentów w wielu środowiskach stają się coraz trudniejsze i czasochłonne.
Rozbieżności z produkcją
Aby zredukować koszty i złożoność, zespoły często korzystają z symulatorów lub emulatorów zamiast rzeczywistych komponentów. To prowadzi do rozbieżności w stosunku do środowiska produkcyjnego, co może obniżyć wiarygodność przeprowadzanych testów.
Złożoności związane z zarządzaniem danymi
Utrzymanie i synchronizacja danych w wielu bazach w licznych efemerycznych środowiskach to ogromne wyzwanie, zwłaszcza przy pracy z dużymi zbiorami danych lub skomplikowanymi relacjami danych.
Starość środowisk
Środowiska testowe szybko się starzeją, ponieważ główna gałąź każdego mikroserwisu jest ciągle aktualizowana. To prowadzi do sytuacji, w której testy są wykonywane na przestarzałych wersjach zależnych usług, co zmniejsza ich skuteczność.
Wydłużone czasy uruchamiania
Im bardziej skomplikowane są te środowiska, tym więcej czasu zajmuje ich uruchomienie. To bezpośrednio wpływa na produktywność programistów, spowalniając cały proces tworzenia oprogramowania.
Koszty
Koszty prowadzenia wielu pełnych środowisk są znaczące. Przykładowo, dla systemu z 50 mikroserwisami można potrzebować instancji AWS EC2 m6a.8xlarge (32 vCPU, 128 GiB RAM), której koszt wynosi około 1,30 USD za godzinę. Przy działaniu przez 24 godziny na dobę przez cały miesiąc, koszt wynosi 936 USD, a roczny koszt dla jednej instancji to około 11 232 USD. Uruchomienie 50 takich instancji to już koszt 561 600 USD rocznie — i to tylko za moc obliczeniową, bez uwzględnienia magazynowania, transferu danych czy zarządzanych usług.
Alternatywa: Wspólne środowiska i piaskownice
Aby uporać się z tymi problemami, pojawiło się podejście oparte na wspólnych środowiskach z izolacją na poziomie aplikacji, zwanych „piaskownicami” (ang. sandboxes). Ten model, podobny do tego, co Uber wdrożył w przypadku testowania end-to-end, oferuje bardziej wydajne i skalowalne rozwiązanie.
Zamiast tworzyć osobne środowiska dla każdego dewelopera lub zespołu, używa się wspólnego środowiska, w którym zapewnia się izolację na poziomie aplikacji dla każdego klienta testowego. Usługi w piaskownicach są dostępne poprzez dynamiczne kierowanie żądań na podstawie nagłówków żądań.
Taki model oferuje kilka kluczowych zalet:
1. Efektywność zasobów: Dzielenie infrastruktury bazowej znacząco redukuje zużycie zasobów i związane z tym koszty.
2. Spójność: Wszystkie testy są uruchamiane w tym samym środowisku bazowym, co eliminuje problemy typu „działało u mnie” i prowadzi do bardziej wiarygodnych wyników.
3. Mniejsze koszty utrzymania: Posiadanie jednego wspólnego środowiska ułatwia jego bieżące aktualizowanie.
4. Szybsze uruchamianie: Piaskownice można tworzyć niemal natychmiast, co pozwala programistom na szybkie rozpoczęcie testów.
5. Testowanie zbliżone do produkcji: Wspólne środowisko może bardziej przypominać środowisko produkcyjne, co zwiększa wiarygodność scenariuszy testowych.
Kluczowe aspekty wdrożenia
Choć podejście oparte na wspólnych środowiskach oferuje liczne korzyści, istnieje kilka kluczowych kwestii, które należy wziąć pod uwagę podczas jego wdrażania.
Propagacja kontekstu
Aby zapewnić odpowiednią izolację w środowisku wspólnym, konieczne jest propagowanie kontekstu przez usługi. Można to osiągnąć za pomocą instrumentacji OpenTelemetry, której standardy takie jak PLACEHOLDERdb391e0def487361 i PLACEHOLDERb9d5ce011a4477a8 są szczególnie przydatne do zachowania kontekstu na granicach między usługami.
Izolacja danych
Szczególną uwagę należy zwrócić na partycjonowanie danych, zwłaszcza gdy dane są edytowane lub usuwane. Ważną zasadą jest to, że test nie powinien móc zmodyfikować danych, których sam nie utworzył. Zapewnia to, że testy równoległe nie będą ingerować w dane innych testów, co zachowuje integralność każdej piaskownicy.
Obsługa kolejek wiadomości
Kolejki wiadomości wymagają specjalnego podejścia, aby piaskownice nie konkurowały o te same komunikaty. Może to wymagać implementacji niestandardowej logiki routingu lub użycia oddzielnych kolejek dla każdej piaskownicy.
Tradycyjne podejścia do testowania mikroserwisów są nie do utrzymania
W miarę jak architektury mikroserwisowe stają się coraz bardziej skomplikowane, tradycyjne podejście polegające na duplikowaniu całych środowisk testowych staje się coraz mniej wydajne. Model wspólnego środowiska z użyciem piaskownic oferuje bardziej efektywne, oszczędne i skalowalne rozwiązanie.
Ten nowoczesny model testowania mikroserwisów przynosi już korzyści wielu firmom, takim jak Brex, Earnest i DoorDash, które dzięki niemu zdołały usprawnić procesy testowania i zwiększyć produktywność swoich programistów. Ich doświadczenia pokazują, że nowe podejście do testowania mikroserwisów ma realne zastosowanie i przynosi wymierne korzyści w praktyce.