Platformy PaaS jako narzędzie do szybkiego prototypowania cz. 6. – Terraform

W poprzednich częściach cyklu przeszliśmy szybką drogę do stworzenia aplikacji mobilnej w oparciu o Azure App Service. Wszystkie przykłady przedstawiałem w oparciu o interfejs graficzny, którego intuicyjność pozwala poznać ogrom możliwości chmury. W pewnych okolicznościach wyklikiwanie wszystkiego może okazać się niepraktyczne, a co gorsza może prowadzić do poważnych błędów.

Terraform jest narzędziem pozwalającym wdrażać infrastrukturę jako kod (Infrastructure as a code). Opiera swoje działanie o ogromną ilość wtyczek (nazywanych provider’ami) do wszystkich popularnych dostawców chmurowych i nie tylko. W zasadzie każda usługa posiadająca swoje publiczne API może posiadać swojego provider’a. Niepełną ich listę można znaleźć pod adresem: https://www.terraform.io/docs/providers/index.html

Wszystko co chcemy zautomatyzować za pomocą terraform musi zostać zaimplementowane w języku HCL. Język ten ma charakter deklaratywny i ma wyrazić docelowy stan, a w momencie aplikacji zmian powoduje szereg wywołań API używanych providerów. Aplikacja zmian powoduje też odłożenie stanu jaki udało się osiągnąć np. w plikach terraform.tfstate, co pozwala na synchronizację i planowanie kolejnych zmian w zasobach.

Czy dałoby się jednak uniknąć przechowywania stanu? Cóż w pewnych scenariuszach byłoby to możliwe, jednak bardzo komplikowałoby to całą ideę szczególnie podczas operacji aktualizacji/zastąpienia zasobów, świadomości stworzonych metadanych i relacji między nimi oraz z powodów czysto wydajnościowych.

Praktyka

Spróbujmy przejść od tej suchej teorii do praktyki. Do utworzenia zasobów w Azure będzie nam potrzebny provider azurerm. Sprobujmy też przygotować dedykowaną grupę zasobów:

Aby zaaplikować zmiany powinniśmy wykonać:

Przetworzenie całości trochę potrwa i powinniśmy być zalogowani za pomocą lokalnego klienta Azure dostępnego na wszystkie platformy (https://docs.microsoft.com/pl-pl/cli/azure/install-azure-cli). Do tak utworzonego pliku możemy przygotować definicję planu usługi określawjącą typ i rodzaj zasobów konsumowanych przez naszą aplikację:

Proszę zwróćcie uwagę w jaki sposób połączono lokalizację oraz grupę zasobów z uprzednio utworzonym planem.

Oczywiście po edycji pliku terraform ponownie wykonujemy polecenia plan i apply. W pliku stanu oraz w logach możemy podejrzeć addytywne zachowanie terraforma.

Po zdefiniowaniu planu przyszedł czas na przygotowanie usługi, która będzie z nego korzystać i pomieści naszą aplikację. Poniższe linijki mieszczą właściwie wszystkie ekrany związane z konfiguracją naszej aplikacji, a także musiały się w niej znaleźć zmienne środowiskowe przechowujące wszystkie sekrety. O tym w jaki sposób powinniśmy zarządzać sekretami opowiemy sobie w innym artykule tego cyklu.

Kiedy podejmiemy próbę aplikacji zmian okaże się że nasz kod jest niepoprawny:

Oczywiście zmienne które zdefiniowaliśmy powinniśmy gdzieś zadeklarować. Utarło się że zmienne w terraform powinno umieścić się w plikach variables.tf lub vars.tf. Deklaracja wszystkich zmiennych wygląda w zbliżony sposób jak w innych językach programowania, a dobry opis ułatwia zarządzanie nimi.

Po dodaniu powyższych definicji przy próbie wykonania planu pojawi się ładne zapytanie o kolejne wartości:

Takie zachowania można obejść na przykład poprzez zmienne środowiskowe. Muszą one przyjąć konkretny format tj. posiadać prefiks TF_VAR_. Jeśli uda nam się zestawić pełny zestaw zmiennych, mozolnie wpisując je w konsoli utworzenie aplikacji powinno udać się bez większych przeszkód.

Podsumowanie

W tym artykule udało nam się ująć definicję naszej aplikacji w dwóch plikach. Początkowo takie usprawnienie może wydawać się nadmiarowe, jednak zarządzając większą ilością usług możemy ujrzeć zalety automatyzacji i algorytmizacji naszej pracy.

Platformy PaaS jako narzędzie do szybkiego prototypowania cz. 5. – PostgreSQL for Azure

W poprzedniej części naszego cyklu udało nam się z sukcesem przenieść naszą aplikację pod postacią obrazu Docker’owego na Azure. Jednak na Heroku wciąż pozostała baza danych, co sugeruje, że nasza migracja jest wciąż niepełna. W tym artykule chciałbym skupić się na postawieniu dedykowanej bazy danych w chmurze Microsoft’u.

W portalu po wpisaniu frazy Azure Database for PostgreSQL będziemy mogli wybrać jeden z czterech rodzajów wdrożeń. Każdy z nich różni się skalą, możliwościami i kosztami.

Dla przykładowej aplikacji zrealizowanej przez nas w tym cyklu artykułów interesuje nas pojedynczy serwer, który powinien wytrzymać obciążenie aplikacji prototypowej.

Kolejne ekrany pozwolą na dokonfigurowanie wybranej opcji.Pamiętajmy, że w naszym przykładzie zagadnienia security spłycam do minimum, zatem starajcie się chronić hasła które ustawiacie.

Aprowizacja bazy potrwa chwilę, jednak jak się można domyślić utworzona przez nas baza będzie posiadała tylko użytkownika administracyjnego. Odwiedzenie bazy danych pozwoli na lekkie poluzowanie polityki bezpieczeństwa. Zacznijmy od lekkiej liberalizacji ustawień Firewall’a poprzez zezwolenie na dostęp z innych usług Azure. Jeśli chcemy pracować z naszym klientem bazy danych(psql) powinniśmy także odblokować nasz adres IP.

Do dalszej konfiguracji konieczny już będzie klient psql, ale jeśli nie chcemy go instalować wystarczy, że skorzystamy z AzureCLI otrzymamy wygodny terminal zaszyty w przeglądarce internetowej.

Nie ma znaczenia czy dalszą konfigurację wykonamy z terminala, czy z przeglądarki, do połączenia potrzebujemy jednak klienta psql:

Następnie należy utworzyć kolejno bazę danych, rolę i odpowiednie przywileje:

Po utworzeniu nowej roli, możemy zaktualizować nasze połączenia. Przykładowy connection string będzie wyglądał następująco:

I tyle, po restarcie nasza aplikacja powinna już uruchamiać się i korzystać z bazy świeżo utworzonej w Azure.

Jeśli dotrwaliście aż dotąd, można uznać że to ostatni punkt migracji. Jedyną pozostałością jaka została po Heroku, to dług w postaci dziwnie podzielonych properties zawierających poświadczenia bazodanowe.

Platformy PaaS jako narzędzie do szybkiego prototypowania cz. 4. – Azure App Service

Zanim ruszymy dalej z artykułami chmurowymi, krótkie ostrzeżenie -korzystając z AWS, czy Azure powinniśmy pamiętać, że subskrybujemy usługi płatne – do założenia konta potrzebna jest karta debetowa lub kredytowa (i niestety wszelkiego rodzaju prepaidy odpadają). Dodatkowo wiele oferowanych usług klasy Enterprise po prostu kosztuje krocie, należy więc za każdym razem sprzątać testowane przez nas usługi i kontrolować koszty. Powinniśmy też zadbać o ochronę naszego konta i dostępu do zasobów tj. włączyć MFA, chronić tokeny, stosować zasadę minimalnych uprawnień itd.
Jakiekolwiek zaniedbanie w tej dziedzinie może narazić nas na spore koszty.
Sami dostawcy oferują na swoich stronach zestawy najlepszych praktyk dot. bezpieczeństwa https://aws.amazon.com/architecture/security-identity-compliance/ i https://azure.microsoft.com/mediahandler/files/resourcefiles/security-best-practices-for-azure-solutions/Azure%20Security%20Best%20Practices.pdf z którymi zdecydowanie warto się zapoznać.

Zarówno w Azure jak i w AWS jako nowi użytkownicy możemy liczyć na pakiet powitalny w postaci sporego zestawu darmowych usług (w pewnych granicach oczywiście). Bazowa oferta pozwoli jednak na spokojny deployment prototypowanej aplikacji w modelu PaaS za free (https://azure.microsoft.com/pl-pl/pricing/details/app-service/windows/). Darmowa oferta nie jest tak dobra w porównaniu do Heroku, ale jest to poniekąd cena za ogromną uniwersalność całej platformy. Ufff… wydaje mi się, że na tym etapie możemy zakończyć informacje wstępne i spróbować założyć konto. Nie będę opisywał tego etapu, ale posiadanie już konta w usługach Microsoft skraca drastycznie cały proces – wystarczy uregulować kwestie dot. płatności i możemy działać.

Konfiguracja

Po pierwszym zalogowaniu do portalu azure (http://portal.azure.com/) zobaczymy następujące wręcz „komiksowe” menu do zarządzania zasobami.

Azure portal – dashboard

W wyszukiwarce znajdujemy App Services i klikamy + Add. Pojawia się menu w którym możemy właściwie wyklikać całą naszą aplikację:

Gdy dopiero zaczynamy naszą przygodę z Azure zapewne będziemy musieli utworzyć pierwszą grupę zasobów. Z początku ten obiekt może wydawać się czymś nadmiarowym, bo dlaczego nie utworzyć zasobu bezpośrednio na naszej subskrybcji? Spójrzmy na konto Azure z perspektywy na przykład dużej firmy – możemy mieć w niej zdefiniowane, wiele aplikacji i środowiska, często użytkowanych przez rozmaite zespoły. W takim heterogonicznym świecie łatwo stracić możliwość zarządzania całością i jakiejkolwiek kontroli kosztów. Wydzielenie grup zasobów np. z podziałem na aplikacje pozwala uzyskać nam pełną kontrolę nad naszą chmurą i płatnościami.

Z ustawień dotyczących deploymedntu pozostaje odpowiedni dobór nazwy jak i typu samego deploymentu:

Czym różni się sposób publikacji Code vs Docker Container? Code pozwala na zdefiniowanie wdrożenia w analogiczny sposób jak w przypadku Heroku – opieralibyśmy się o zbudowanie aplikacji ze źródeł po stronie Azure’a. Przygotowaliśmy sobie obraz Dockerowy po to by maksymalnie uniezależnić się od konkretnego dostawcy, zatem skorzystamy z tego i wybierzemy Docker Container.

Kolejny ekran konfiguracji oczekuje wartości, które powinniśmy już znać z poprzedniej odsłony naszego cyklu:

Przygotowaliśmy już obraz, posiadamy rejestr w Gitlab, wypadałoby wprowadzić właściwy adres rejestru, nazwę użytkownika, hasło, a także obraz – zdobyć je można w Gitlabie w Settings -> Repository -> Deploy Tokens.

W scope tworzonego token’a interesuje nas tylko możliwość odczytu obrazów w repozytorium – z punktu widzenia deploymentu jest to zupełnie wystarczające. Hasło pojawi się jednorazowo, jeśli nie skopiujemy go pozostaje nam utworzenie kolejnego token’u.

Pełną nazwę obrazu jak i adres repozytorium, uzyskamy na zakładce Container Registry.

Po wypełnieniu formatki i kliknięciu na URL możemy zauważyć, że nasza aplikacja nie startuje.

Oglądając Container Settings możemy zobaczyć logi ze startu – brakuje nam zmiennych środowiskowych. Na szczęście odłożyliśmy potrzebne dane budując kontener.

Uzupełnienie konfiguracji powinniśmy wykonać w sekcji Configuration. Oprócz zmiennych środowiskowych, które wykorzystywaliśmy uruchamiając obraz Dockerowy powinniśmy dodać jeszcze jedną: WEBSITES_PORT. W skrócie wskazuje ona na którym porcie nasłuchuje nasza aplikacja – w tym przypadku należy ustawić ją na 8080.

W zasadzie to tyle – ponowne odwiedziny URL z naszą aplikacją sprawią, że będziemy w stanie już korzystać z naszego API.

Platformy PaaS jako narzędzie do szybkiego prototypowania cz. 3. – Azure App Service

W poprzednim artykułach opisywałem Heroku, który idealnie pokrywa przypakdi hobbystyczne, co jednak kiedy chcemy przeprowadzić pewien mniej lub bardziej zaawansowany proof-of-concept w firmie, a management nie specjalnie ma ochotę na zawieranie nowych umów i sugeruje skorzystanie z już posiadanych przez nas subskrybcji.

W środowiskach korporacyjnych dominuje głównie dwóch dostawców pokrywających większość rynku i wyznaczających standardy dla konkurencji. Pierwszy z nich – AWS zapoczątkował wielką rewolucję chmurową, która odmieniła cały świat IT, zmuszając nawet najbardziej konserwatywne firmy jak banki do opracowania swoich rozwiązań (private cloud) lub wykorzystania chmury publicznej w części prowadzonych projektów (hybrid cloud).
Microsoft Azure powstał jako odpowiedź na rosnącą popularność chmur obliczeniowych i szybko stał się faworytem korporacji ze względu na ogromną ilość oferowanych usług w modelu SaaS (jak Office 365 i Dynamics). Jak już wspominałem wcześniej nawet jeśli twoja korporacja nie posiada jeszcze projektów realizowanych w chmurze Azure – jest duża szansa, że posiada subskrybcję Office365, czy Microsoft Dynamics.

W tym artykule chciałbym kontynuować naszą zabawę z modelem PaaS. Spróbujmy przenieść aplikację, którą zbudowaliśmy pod chmurę Heroku na Azure App Service. Jeśli nie popełniliśmy zasadniczych błędów projektowych, to nie powinniśmy stać się uzależnieni od dostawcy (ang. vendor lock-in) i migracja całości nie powinna kosztować nas zbyt dużo wysiłku.

Większość serwisów PaaS można uniezależnić całkowicie od języka programowania i platformy, poprzez odpowiednią konteneryzację. Jeśli pamiętacie heroku addons jak i buildpack’i wspomniane w poprzedniej części – to uniezależnienie się od nich stanowi klucz do uwolnienia naszego serwisu od określonego dostawcy i wspieranych przez niego technologii.

Przykładowa architektura:

źródło Azure Portal

Na czym zatem polega konteneryzacja?

Jest to rodzaj wirtualizacji na poziomie systemu operacyjnego (OS-level virtualization) i wbrew powszechnej opinii nie stanowi nowego wynalazku – jej wczesnym pierwowzorem były jail’e i chroot znany z systemów uniksowych (1982). W świecie backend developerów dopiero pojawienie się Docker’a upowszechniło konteneryzację. Od tej pory tworzone usługi i oprogramowanie mogły w całości uniezależnić się stack’u systemowego czy technologicznego.

Obraz Dockerowy można złożyć z kilku warstw za pomocą kilku wbudowanych poleceń takich jak FROM, CMD, COPY, ARG. Dla przykładu:

Po utworzeniu pliku Docker file jw. zbudowanie obrazu i uruchomienie kontenera sprowadza się do dwóch poleceń:

Rozbudowa aplikacji

W przypadku naszej aplikacji moglibyśmy wyodrębnić całą logikę poprzez przekopiowanie utworzonych artefaktów w następujący sposób:

W zasadzie poza skopiowaniem artefaktów i uruchomieniem naszej aplikacji zmieniamy jeszcze kontekst użytkownika na pozbawionego przywilejów ROOT’a (dobra praktyka).
Po próbie budowy, możemy niestety zaobserwować dość nieprzyjemny wyjątek:

W dość wymowny sposób pokazuje o czym zapomnieliśmy podczas uruchamiania/tworzenia obrazu.
Do uruchomienia potrzebujemy kilku dodatkowych zmiennych środowiskowych – w końcu w Heroku użyliśmy aż pięciu (!). Powinny one zostać przekazane do kontenera. Przygotujmy zatem plik w następującym formacie:

Wartości można oczywiście zabrać z naszej wcześniej utworzonej aplikacji na Heroku. Na powyższym przykładzie wychodzi też cena pokazanej wcześniej automagii.
Czy projektując aplikację nie pod konkretnego dostawcę podjęlibyśmy identyczną decyzję, jeśli chodzi o konfigurację podłączenia do bazy danych?
Zapewne zależałoby nam na rozdzieleniu pewnych elementów, dodatkowo moglibyśmy uniknąć zaprezentowanego wcześniej hack’a.
Kolejna próba z uwzględnieniem zmiennych środowiskowych i przekierowaniem portów pozwala już na lokalne uruchomienie skonteneryzowanej aplikacji:

Teraz wypadałoby poszerzyć nasz projekt o budowanie obrazu w pipelinie. Na szczęście przy oparciu naszego amatorskiego projektu o Gitlaba uzyskaliśmy możliwość skorzystania z ichniejszego rejestru obrazów (rozmiar całego repozytorium jak i rejestru posiada ograniczenie 10GB).
Użycie pipeline’ów w Gitlab sprawia, że uzyskanie do niego dostępu również jest banalnie proste poprzez predefiniowane zmienne:

Przygotowany etap wykonuje budowanie obrazu przy założeniu, że wcześniej odłożyliśmy je:

przykładowy rejestr

Po uruchomieniu kilku pipeline’ów zobaczymy, że rejestr zapełnia się kolejnymi tagami z kolejnymi wersjami aplikacji. Jak zatem powinniśmy decydować, która z nich powinna zostać zdepluyowan’a na nasze docelowe produkcyjne środowisko?

Można dodać do pipeline’u kolejny etap, którego celem będzie wykonanie właśnie tej operacji tj. odpowiednie tagowanie ostatniego build’a.

Et voilla. Nowy stage sprawi, że pod tagiem latest zawsze będzie się ukrywał najnowszy obraz pipeline’u masterowego.

Ktoś mógłby zadać pytanie, ale gdzie w tym wszystkim Azure? Dlaczego przygotowywać aplikację w ten sposób?
W następnej części cyklu zobaczymy uzysk z tych wszystkich operacji, ponadto zastanowimy się czy dałoby się podejść do tematu nieco inaczej.

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ć 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ązania 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 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.
Jednak każdy z dodatków jakie możemy użyć na platformie ma swoją cenę i pewne ograniczenia regionalne, ale w zamian 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 można 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 jakiejkolwiek 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 zacznijmy od utworzenia skryptu 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 też 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 takich 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’u, 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.

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

Koncepcja PaaS stanowi jeden z modeli chmury obliczeniowej opierającej się o usługi związane z uruchamianiem i zarządzaniem aplikacjami w ramach określonego ekosystemu. Wśród dostawców występuje ogromne zróżnicowanie oferowanych usług i rodzajów wsparcia. W ramach krótkiej serii artykułów chciałbym pokazać jak „ugryźć” temat i jak możemy za w miarę niewielkie pieniądze lub całkowicie za darmo z naszej lokalnej aplikacji wylądować z projektem działającym gdzieś w sieci.

Heroku

Jako pierwszego dostawcę w tym cyklu chciałbym przedstawić Heroku. Nie posiadają oni własnej infrastruktury, natomiast korzystają z Amazonowej platformy EC2. Od początku Heroku posiadało doskonałe wsparcie dla języka Ruby, z czasem jednak paletę wspieranych technologii poszerzono w tym także o Javę i NodeJS. Podobnie jak większość dostawców także i w tym przypadku mamy podstawowy zestaw narzędzi zupełnie darmowy.

Wdrażana aplikacja uruchamiana jest w oparciu o skonteneryzowane środowisko Linuxowe. Kontener taki w przypadku tego dostawcy nazywany jest „dyno” i to jego użytkowanie i typ stanowi podstawę rozliczenia. Dla przykładu najtańsze „dyno” posiada ograniczenie do 512 MB RAM, wyłącza się po 30 minut nieaktywności, ale… pozwala nam ekspresowo przetestować małego POC’a lub stanowić podstawę prezentacji (jako alternatywa dla ngrok’a).

Konto można założyć za pomocą dość prostego formularza. Warto zainstalować także lokalnego klienta heroku pozwalającego na wygodne zarządzanie całą platformą

W paru krokach spróbuję przedstawić w jaki sposób można zbudować aplikację opartą o framework SpringBoot odpytującą zewnętrzne API. Dość interesujące publiczne API znajdziemy [tutaj]. W ramach małego PoC’a spróbujemy odpytać jeden z endpointów.
Na początek proponuję wykorzystać [Spring initializr] i wybrać Javę 14. Z zależności użyję Reactive Web i Lombok’a – tak przygotowany projekt można umieścić w repozytorium Gitlab’owym, co daje dostęp do dość wygodnych i bardzo zaawansowanych pipeline’ów. W podstawowej konfiguracji otrzymujemy również 2000 minut procesora – w sam raz dla mniejszych i mniej aktywnych projektów realizowanych hobbystycznie.

Przygotowujemy źródło niezbędnej konfiguracji (application.yml):

Zarówno klucz prywatny jak i publiczny możemy uzyskać rejestrując się w serwisie Marvel’a. Pamiętajcie, żeby traktować owe dane jako poufne i nie commitować ich do publicznego repozytorium. Pracując w środowisku chmurowym warto wyrabiać w sobie dobre nawyki związane z security także dla własnego bezpieczeństwa. Przykładowo uzyskanie niepowołanego dostępu do naszych kluczy AWS’owych może skutkować narażeniem na poważne koszty.

Przykładowy kod kliencki – odpytanie jednego z endpointów API:

Aby sprawdzić czy wszystko działa i czy z naszej aplikacji da się połączyć z Marvelowym API musimy przygotować najprostszy endpoint:

Przygotowaną aplikację możemy potestować lokalnie i jeśli wszystko działa jak powinno, możemy spróbować podjąć próbę deploymentu.

Wdrożenie

Gdy mamy przygotowany kod – powinniśmy się zająć miejscem na którym zdeployujemy naszą aplikację. Po zalogowaniu się do panelu Heroku możemy stworzyć nową aplikację:


Kolejna formatka pozwala na podjęcie decyzji, gdzie chcemy by fizycznie znajdował się nasz kontener z aplikacją. Z wszystkich regionów AWS do wyboru są dwa (Stany Zjednoczone i Europa). Możemy też choć nie musimy nadać naszej aplikacji nazwę, po przejściu tego kroku w odpowiedzi mamy gotową aplikację typu Hello World czekającą na zastąpienie ciekawszym (bo naszym) projektem.

Wróćmy na chwilę do naszej aplikacji. Sugerowałem użycie gitlaba, ze względu na doskonałe CI/CD, które dostarcza. Dodatkowo Gitlab Pipelines posiadają bardzo niski punkt wejścia.

Przykładowo poniższy kawałek kodu pozwala nam na zdefiniowanie etapu (stage) build z jednym zadaniem (job) o nazwie build. Gitlabowy runner w pierwszym kroku pobiera obraz dockerowy posiadający maven i Javę 14, a następnie uruchamiany jest na nim build (clean package).

Jak zatem powiązać nasz build z przygotowaną aplikacją w chmurze Heroku? Wystarczy dobrać odpowiednie narzędzie do deploymentu. Jednym z tego typu narzędzi jest dpl używany także przez Travis’a – posiadający wsparcie dla kilkunastu największych dostawców chmurowych (min. Azure i AWS).

Rozbudowa naszego pipeline’u o deployment na Heroku staje się banalnie prosta:

Cała magia dzieje się w linijce:

HEROKU_APP_NAME i HEROKU_API_KEY są zmiennymi środowiskowymi, które możemy zdefiniować w ramach naszego projektu na Gitlabie. Nazwę aplikacji na Heroku znajdziemy bez trudu po zalogowaniu do głównego dashboard. Klucz (token) można stworzyć wywołując odpowiednie polecenie z CLI.

Tutaj bardzo ważna uwaga. Powyższy klucz daje pełny dostęp do konta Heroku – nie należy go udostępniać, ani commbitować do publicznych repozytoriów. W przypadku Gitlab – możemy umieścić go w chronionej zmiennej środowiskowej:

Chroniona zmienna środowiskowa w Gitlab, sprawi że będzie ona przekazywana ona tylko i wyłącznie do pipeline’ów uruchamianych na chronionych gałęziach (np. master – stąd zadanie deploymentu zostało w zawężone właśnie do tej gałęzi). Dodatkowo dodanie maskowania sprawi, że wartość nie zostanie wypisana w logach runnerów. Oczywiście nie jest to idealne zabezpieczenie, a zatem nie traktujcie powyższego rozwiązania jako wzorca do powielenia w produkcyjnej aplikacji.

Po uzupełnieniu zmiennych środowiskowych. Nasz pipeline powinien już działać i wdrożenie aplikacji do chmury Heroku powinno się wykonać.

W logach runner’a na GitLab można podpatrzeć działanie maskowania zmiennych środowiskowych:

Ostatecznie aplikacja powinna być dostępna pod URLem https://<nazwa naszej aplikacji>.herokuapp.com/marvelheroes. Na tym etapie możemy nie być jeszcze w stanie połączyć się z API Marvel’a które definiowaliśmy na początku. Czego brakuje? Oczywiście kluczy. W ustawieniu ich dla naszej aplikacji pomoże nieoceniony Heroku CLI.

Poniższe polecenia pozwalają na wyświetlenie istniejących zmiennych środowiskowych i ustawienie interesujących nas kluczy dla aplikacji:

Teraz pozostaje nam tylko odpytać naszą aplikację za pomocą curl’a i powinniśmy uzyskać interesującą nas odpowiedź.

Podsumowanie

W tym pozornie krótkim artykule udało się nam przejść przez wszystkie kroki wymagane do postawienia prostego hobbystycznego projektu.

Od wygenerowania aplikacji, poprzez przygotowanie i skonfigurowanie miejsca w chmurze Heroku aż po zestawienie prostego pipeline’u. Warto zwrócić uwagę, że najwięcej kodu i wysiłku nawet w tak banalnym przykładzie zajęło stworzenie naszej aplikacji. To też ukazuje potęgę rozwiązań typu PaaS -> developer może skupiać się na tym co dla niego najważniejsze, czyli tworzeniu własnego projektu.

AVR + Raspberry PI = ?

Grzebanie w starociach

Ostatnio przegrzebywałem się przez moje szuflady z elektroniką w poszukiwaniu cyny. Nie bawiłem się w lutowanie od czasów pierwszych lat studiów i technikum, ot takie porzucone hobby, nie hobby. Nagle w ręce wpadł mi scalak:

Po chwilę wpadło mi ręce jeszcze kilka podobnych, lecz niestety mój programator USB zaginął w akcji jakiś czas temu, Arduino nigdy nie posiadałem, a współczesne komputery nie mają już ani RS232 ani Centronics. Czytaj dalej AVR + Raspberry PI = ?

Nie taki znów zwykły SQL Server cz. 3 – Merge czyli po prostu UPSERT

Merge, upsert – o co chodzi ?

Merge ? Nigdy tego nie używałem, z tego w ogóle się korzysta ?! Mniej więcej, taka moja była pierwsza reakcja, kiedy napotkałem kod zawierający składnię owego wyrażenia. Otóż używa się i ma wiele zastosowań, gdyż potrafi czasem zastąpić aż trzy różne operacje INSERT, UPDATE, DELETE. Czy istnieje jakieś konkretny uniwersalny przypadek użycia dla MERGE ? Szczerze mówiąc ciężko mi takowy przytoczyć. Podobnie jak w przypadku wcześniej opisywanego WITH potrafi ułatwić rozwiązywanie pewnej klasy problemów, bez angażowania rozbudowanej procedury T-SQL lub języków programowania.

Składnia wyrażenia jest dość skomplikowana na pierwszy rzut oka, jednak po parokrotnym użyciu można szybko poczuć co można uzyskać korzystając z niego, a co jest niemożliwe. Najlepiej zresztą poczuć moc na przykładzie.

Czytaj dalej Nie taki znów zwykły SQL Server cz. 3 – Merge czyli po prostu UPSERT

WeeklyJavaSnippets#2 Singletons in Java

Singleton – wzorzec czy antywzorzec?

Wśród programistów, często spotykam się z opinią, że Singleton stanowi antywzorzec. Niestety, rzadko też słyszę dobre argumenty podpierające tą tezę. Fakt, jest on jednym z najpowszechniejszej stosowanych wzorców projektowych i jednym z pierwszych jaki poznaje każdy programista. Konsekwencjami tego jest olbrzymia ilość błędnych implementacji, które krążą po repozytoriach i potrafią przyprawić o prawdziwy zawrót głowy podczas czytania kodu i poszukiwania przyczyny problemów.

Czytaj dalej WeeklyJavaSnippets#2 Singletons in Java

Nie taki znowu zwykły SQL Server cz. 2 – CTE w modelach hierarchicznych cd.

Nadszedł czas na kontynuację naszej przygody z CTE. W poprzednim odcinku pokazałem jak bardzo wspólne wyrażenia tablicowe zbliżone są do podzapytań oraz jak można z nich korzystać. Wspomniałem też na samym początku, że pozwalają na obsługę modeli hierarchicznych z poziomu języka SQL, zatem postaram się nieco przybliżyć to zagadnienie.

Czym w ogóle jest model hierarchiczny ?

Czytaj dalej Nie taki znowu zwykły SQL Server cz. 2 – CTE w modelach hierarchicznych cd.