Platformy PaaS jako narzędzie do szybkiego prototypowania cz.2.

W poprzednim artykule cyklu zaprezentowaliśmy prostą aplikację, którą w ekspresowy sposób można wdrożyć na produkcję z wykorzystaniem Heroku. Co w przypadku, gdy oczekujemy czegoś więcej niż tylko połączenia z zewnętrznym API jak np. dodatkowej warstwy persystencji? Oczywiście dostawcy PaaS, aby dostarczyć dojrzałe rozwiązanie musieli udostępnić odpowiednie oprzyrządowanie. W przypadku Heroku w sukurs przychodzą nam Add-ony (dodatki), które można w dowolny sposób podłączać do już stworzonej aplikacji. Oczywiście jak we wszystkim w modelu PaaS nie mamy takiej dowolności w porównaniu do własnoręcznego zarządzania, ale w zamian otrzymujemy prekonfigurowane komponenty, które działają właściwie od razu.
Oczywiście każdy z dodatków jakie możemy użyć na platformie ma swój koszt i pewne ograniczenia regionalne, ale odpadają wszelkie dodatkowe koszty związane z administracją i utrzymaniem.

Przykładowo dodając bazę postgres na platformie Heroku wystarczy wywołać:

Jak widzimy utworzenie nowej bazy danych jest bajecznie proste, a podobne działanie naturalnie podjąć także z poziomu UI. Chciałbym tu zwrócić uwagę na pewną rzecz – mianowicie jak dostać się do tak skonfigurowanej bazy z poziomu naszej aplikacji?

Rąbka tajemnicy uchyla powyższa linijka – „klucz” do bazy znany również jako „connection string” ląduje w zmiennej konfiguracyjnej DATABASE_URL

Już na pierwszy rzut oka widać, że baza została posadowiona na silniku EC2 od Amazona. Dodatkowo wszystko co z nią związane zostało wygenerowane sposób pseudolosowy. Spróbujmy zatem wykorzystać tą świeżo przygotowaną bazę danych w naszej testowej aplikacji.

 

Zacznijmy od przygotowania modelu – chcielibyśmy przetrzymywać w niej użytkownika:

Aby przygotować tabelę, w tym przykładzie chciałem posłużyć się technologią wersjonowania bazy danych Flyway, która stanowi głównego konkurenta do już dość dojrzałego Liquibase. Osobiście nigdy nie miałem preferencji w kierunku którejkolwiek z tych technologii, jednak zawsze odnosiłem wrażenie, że Flyway pozwala szybciej wystartować, a o to po części chodzi w tym przykładzie. Zatem stworzymy skrypt V1__Create_new_table_user.sql:

Wydaje się, że jeśli chodzi o warstwę dostępu do danych to mamy wszystko, żeby pójść dalej z naszym przykładem. Obsłużmy zatem w kodzie zapis nowego użytkownika:

Powyższy handler należy spiąć z istniejącym routingiem w naszej aplikacji, po szybkim refactoringu otrzymujemy:

Zwróćcie proszę uwagę, że w tym przypadku podobnie jak i w poprzednim przykładzie, gdzie odpytywaliśmy Marvel’owskie API staram posługiwać się paradygmatem reaktywnym, który nieśmiało pojawia się w coraz większej ilości projektów, nie zawsze w miejscach w których rzeczywiście jest wymagany i potrzebny (jak np. tutaj :-)).

Celem przetestowania repozytorium przygotujemy prosty test oparty o TestContainers, technologia ta wymaga od was utrzymywania od was demona Docker’owego zarówno na stacji roboczej jak i na pipeline’ach, jednak w odróżnieniu od zagnieżdżonych baz danych pozwala na przeprowadzenie testów integracyjnych w otoczeniu praktycznie tożsamym z produkcją.

Metoda oznaczona @DynamicPropertySource umożliwia przeciążenie konfiguracji danymi w runtime pozyskanymi ze świeżo postawionego kontenera bazodanowego. Alternatywnie można do tego wykorzystać inicjalizator kontekstu:

Gdy nasze testy już działają należałoby spróbować połączyć wszystko z bazą danych. Uważny czytelnik zapewne zauważył, że DATABASE_URL dostarczany przez Heroku ma jednak nieco inny format niż formaty obsługiwane przez Spring’a (i JDBC), jednak i ten problem został poniekąd zaadresowany, ale po kolei…

W przypadku gdy korzystamy z aplikacji Javowej na platformie (po autodetekcji przy pierwszym deploy’u), Heroku automatycznie wzbogaca ją o „buildpack”, czyli niezbędny zbiór skryptów i narzędzi jak np. maven. Szczegóły można znaleźć w dokumentacji: https://devcenter.heroku.com/articles/java-support.

Dodatkowo buildpack automatycznie będzie próbował utworzyć zmienne środowiskowe SPRING_DATASOURCE_USERNAME, SPRING_DATASOURCE_PASSWORD, SPRING_DATASOURCE_URL.
Oczywiście nawet w przypadku gdy korzystamy w nieco inny sposób z JDBC jesteśmy w stanie sobie poradzić np. przetwarzając początkowy DATABASE_URL. https://devcenter.heroku.com/articles/connecting-to-relational-databases-on-heroku-with-java#using-the-database_url-in-plain-jdbc

Oczywiście możemy wykorzystać polecenie heroku config:get i ręcznie przeciążyć ustawienia aby osiągnąć konfigurację „pod nas”, jednak w pewnym sensie byłaby to forma tightcoupling, której raczej chcemy unikać.

Wzbogaceni o tą wiedzę spróbujmy skonfigurować Flyway, tym co daje nam platforma Heroku:

Próba przygotowania tabeli powinna zakończyć się powodzeniem, co jednak z konfiguracją naszego reaktywnego repozytorium? URL’e różnią się od tych oczekiwanych – nawet w przypadku testu integracyjnego kłuje w oczy linijka w którym podmieniłem jdbc na r2dbc. Dla uproszczenia przykładu i nie tworzenia wszystkiego manualnie po prostu nadpisałem connectionFactory i obsługę properties, co nie jest być może rozwiązaniem idealnym, ale szybkim.

Mając przygotowaną aplikację – możemy zaobserwować, że pipeline kończy się już na etapie testów:[ERROR] UserRepositoryIntegrationTest ? ContainerLaunch Container startup fai

Testcontainers które wykorzystaliśmy w projekcie wymagają Docker’a a zatem musimy wzbogacić nasz pipeline (.gitlab-ci.yml) o obraz Docker in Docker.

I voill’a 🙂 Celem potwierdzenia, że nasza baza danych już działa:

w odpowiedzi powinniśmy otrzymać naszego świeżo utworzonego użytkownika z nowo nadanym id’kiem:

Podsumowanie:
W tych dwóch krótkich artykułach opowiadających o platformie Heroku poruszyliśmy całą gamę tematów związaną z modelem PaaS i utworzyliśmy szkielet aplikacji która:

  • posiada podstawowy pipeline CI/CD
  • działa w sposób reaktywny
  • swoje testy integracyjne opiera o kontenery testowe

I to wszystko w zaledwie w dwóch krótkich artykułach – co jest całkiem niezłym wynikiem. Niestety wszystko ma swoją cenę i koszt(poza rachunkiem) – obnażyliśmy też jedną z największych słabości PaaS, czyli konieczność dostosowania się do waszego dostawcy. Planując budowę i obsługę aplikacji w tym modelu należy dokładnie przeanalizować co dostawcy oferują i czy jesteśmy w stanie poradzić sobie z ograniczeniami.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *