Programista .NET na Linuxie cz. 1

Wprowadzenie

Cześć, tu Marek z firmy IT w Chmurach. Po dłuższej nieobecności wracamy z nowym wpisem. Dziś chcę pokazać wam w jaki sposób możecie pracować z dotnetem na Linuxie. Mimo upływu lat i zmian jakie przeszedł .NET Framework, wiele osób wciąż myśli że .NET == Windows. Nic bardziej mylnego. Od kilku lat .NET nie dość że jest multi-platformowy, co oznacza, że aplikacje napisane z jego użyciem możemy tworzyć i uruchamiać na takich platformach jak Linux, Mac oraz Windows, to jeszcze jest open source.

Ale, żeby nie było tak kolorowo, to musimy pamiętać, że multi-platformowość dotneta nie dotyczy aplikacji desktopowych. Aplikacje desktopowe napisane w Windows Forms czy WPF (Windows Presentation Foundation) nawet w najnowszej multi-platformowej wersji frameworka czyli .NET 7, w dalszym ciągu będą przywiązane do Windowsa. Wynika to z tego, że cała warstwa graficzna w tym wszystkie biblioteki obsługujące GUI, opierają się o API systemu Windows, które z wiadomych przyczyn nie jest kompatybilne z API interfejsów graficznych Linuxa czy iOSa.

Niemniej dla osób takich jak ja, czyli w większości tworzących przy pomocy .NET aplikacje internetowe oraz usługi, które nie mają swoich interfejsów graficznych, a jeżeli już mają to są one napisane w takich frameworkach jak Angular, React czy Blazor, które działają na wszystkich systemach operacyjnych, desktopowe ograniczenia .NET nie stanowią już problemu. 

Narzędzia

Największym wyzwaniem dla programistów .NET związanym z przesiadką na Linuxa, będzie próba zastąpienia Visual Studio. Niestety mimo wypuszczenia przez Microsoft wersji Visual Studio dla komputerów Mac, wciąż nie ma wersji Visual Studio, która działałaby na Linuxie i nie wiadomo czy w ogóle będzie. To co możemy zrobić w tej sytuacji to: 

  • kupić licencję na IDE Rider od JetBrains
  • użyć darmowego Visual Studio Code wraz z odpowiednimi wtyczkami, które pozwalają rozszerzyć VS Code o funkcje znane z Visual Studio

Minusem Ridera jest jego cena – 149 Euro za roczną subskrypcję dla osób indywidualnych. Plusem – działa na Linuxie, Windowsie i iOS oraz ma wiele funkcjonalności znanych z Visual Studio, które ułatwiają pracę.

VS Code z kolei jest darmowy, ale ma wiele ograniczeń, które możemy próbować obejść, korzystając z darmowych rozszerzeń i stuningować VS Code tak, aby mieć chociaż część funkcjonalności, jakie oferują wspomniane dwa duże IDE czyli Rider oraz Visual Studio.

Rozszerzenia VS Code dla .NET

C#

Pierwszą rzeczą jaką musimy zrobić po zainstalowaniu runtime i SDK .NET oraz VS Code jest dodanie rozszerzenia C# od Microsoft, które dostarcza narzędzia integrujące Visual Studio Code z kompilatorem oraz narzędzia do uruchamiania oraz debugowania kodu C#. Dzięki temu możemy uruchamiać aplikacje dotnetowe za pomocą klawisza F5, otrzymujemy debugger, oraz obsługę skrótów takich jak idź do implementacji czy idź do definicji. 

Mając zainstalowane rozszerzenie C#, utwórzmy przykładowy projekt ASP.NET Core i spróbujmy go uruchomić z poziomu VS Code. W tym celu przechodzimy do folderu w którym znajdzie się nasz projekt, z poziomu terminala wydajemy polecenie:

dotnet new webapp

Po utworzeniu projektu otwieramy go w Visual Studio Code za pomocą komendy:

code .

Wciskamy klawisz F5 i czekamy aż aplikacja się zbuduje i uruchomi. W przeglądarce powinna otworzyć się strona startowa aplikacji więc pierwszy krok za nami. 

Roslynator

Roslynator to jak nazwa wskazuje rozszerzenie wykorzystujące możliwości kompilatora Roslyn. Roslyn to kompilator open-source oferujący API dla języków C# i Visual Basic (VB.NET) dostarczający takich funkcji jak IntelliSense, nawigacja po kodzie, kolorowanie składni, analiza kodu i refaktoryzacja. Dzięki temu programiści mogą tworzyć swoje narzędzia do analizy kodu i refaktoryzacji. Przykładem takiego narzędzia jest właśnie Roslynator.

Spójrzmy na działanie Roslyn w praktyce. W poniższym kawałku kodu zagnieździliśmy dyrektywę using wewnętrz innej dyrektywy using. Jak widzimy przy wewnętrznym usingu pojawiło się podkreślenie sugerujące że coś możemy poprawić. Po najechaniu kursorem myszki pojawia się komunikat z kompilatora, że to wyrażenie może zostać uproszczone. 

Po zastosowaniu sugerowanych zmian otrzymamy następujący kod: 

Identyczne działanie zobaczymy w przypadku zagnieżdżonych instrukcji warunkowych, automatycznego uzupełniania wyrażeń switch-case, refaktoringu i wielu innych rzeczy. 

VS Code Solution Explorer

Kolejnym krokiem będzie zainstalowanie rozszerzenia o nazwie vscode-solution-explorer. Dzięki niemu otrzymujemy w Visual Studio Code panel widoku solucji znany z Visual Studio:

Dzięki temu rozszerzeniu możemy w łatwy sposób: 

  • Tworzyć solucje
  • Dodawać nowe projekty do solucji oraz je usuwać
  • Dodawać pliki klas, interfejsów, enumów oraz kontrolery na podstawie szablonów z możliwością tworzenia swoich własnych
  • Dodawać, usuwać referencje w projektach 
  • Zarządzać paczkami z repozytorium NuGet 
  • Budować, publikować, uruchamiać testy i wiele więcej

Oczywiście wszystkie te operacje możemy również wykonać z poziomu linii poleceń, ponieważ to rozszerzenie jest niczym innym jak nakładką graficzną i pod spodem wywoływane są normalne komendy dotnet CLI, co możemy zobaczyć w trakcie dodawania z poziomu vscode explorera do solucji projektu z testami xUnit, przełączając się na widok terminala:

.NET Core Test Explorer 

Pisanie testów czy to jednostkowych, integracyjnych, wydajnościowych czy jeszcze innych jest dobrą praktyką. Nie ma co tu nawet dyskutować. Aby w VS Code uruchamiać testy napisane w MSUnit, xUnit czy NUnit będziemy potrzebować dedykowanego rozszerzenia. Ja korzystam z .NET Core Test Explorer. Po zainstalowaniu rozszerzenia w pasku bocznym pojawi się ikona kolby Erlenmayera (stożkowe szkło laboratoryjne). Po przełączeniu się na widok eksploratora testów, zostaną wykryte wszystkie testy w solucji. Testy możemy uruchamiać klikając myszką na przycisk „play” w eksploratorze albo za pomocą skrótów klawiszowych (domyślnie Alt+R Alt+A).

Jak widzimy powyższy test zakończył się błędem mówiącym, że wskazany endpoint naszego API który chcieliśmy przetestować integracyjnie nie istnieje. Informacja o wyniku testu jest prezentowana w formie dużej ikonki X tuż nad sygnaturą metody testującej.

Podsumowanie

Powyższe rozszerzenia to absolutne minimum do tego, aby rozpocząć przygodę z programowaniem w dotnecie na Linuxie. Jeżeli znacie inne warte uwagi, koniecznie wspomnijcie o nich w komentarzu, a w kolejnym poście pokażę kilka dodatkowych rozszerzeń do VS Code, które nie są związane wyłącznie z programowaniem w .NET, ale usprawniają moją codzienną pracę.

Bonus – Material Icon Theme 

Dla osób, które tak jak ja przywiązują dużą wagę do wyglądu swojego IDE polecam rozszerzenie Material Icon Theme, które pozwoli zamienić standardowe ikony VS Code w te znane z Material Design.

Cloud FinOps

Dziś w kilku słowach o tym czym jest FinOps, a w kolejnym wpisie jakie narzędzia do tego dostarcza nam Azure.

FinOps to elastyczne podejście do finansów polegające na współpracy działów finansowych z produktowymi, IT oraz z partnerami biznesowymi w celu:  

  • Zrozumienia modelu kosztowego chmury (w jaki sposób generowane są koszty w chmurze) 
  • Podejmowania decyzji finansowych w oparciu o aktualne dane (bieżące koszty infrastruktury) 
  • Planowania wydatków na podstawie danych – narzędzia  FinOps mogą prognozować przyszłe wymagane zasoby i koszty pomagając w opracowywaniu odpowiednich planów finansowych.  

Podobieństwo nazwy do DevOps nie jest przypadkowe, gdyż tak jak DevOps, podejście FinOps łączy najlepsze praktyki i kulturę pracy w celu zwiększenia świadomości organizacji w zakresie kosztów generowanych przez chmurę, efektywniejszego zarządzania oraz generowania zysków poprzez umiejętne zarządzanie finansami. 

The Six Principles of FinOps

FinOps wyróżnia 6 pryncypiów (zasad) jakimi należy się kierować wdrażając ten proces: 

  1. Współpraca – odejście od tak zwanych silosów na rzecz bezpośredniej współpracy pomiędzy zespołami. 
  2. Myślimy o chmurze nie w kategoriach kosztów, ale wartości biznesowych, jakie chmura może nam przynieść. 
  3. Każdy jest odpowiedzialny za koszty.  
  4. Raporty powinny być aktualne i dostępne dla wszystkich. Dzięki możliwościom jakie daje chmura (rozliczanie zużycia w minutach czy nawet sekundach) raportowanie miesięczne czy kwartalne jest nieakceptowalne. 
  5. Centralny zespół odpowiedzialny za implementację FinOps w organizacji, edukację pozostałych zespołów, definiowanie standardów oraz negocjacje z dostawcami 
  6. Optymalne wykorzystanie zasobów poprzez umiejętne dobieranie mocy obliczeniowych usług. Innymi słowy kupujemy tyle mocy obliczeniowej ile potrzebujemy w danej chwili (nie mniej, nie więcej) oraz kontrolujemy wydajność systemu poprzez skalowanie pionowe i poziome, w zależności od potrzeb. 

W podejściu FinOps nie ma już jednego centralnego działu (Procurement), który identyfikuje koszty i je zatwierdza. Zamiast tego mamy wielofunkcyjny zespół tzw. Cloud Cost Center of Excellence łączący świat IT, biznesu i finansów w celu nie tylko optymalizacji kosztów, ale również negocjowania z dostawcami cen i rabatów. 

Engineering/Ops/lnfrastructure  Business/Product Owner  Executive  Finance/Procurement  Infrastructure Changes  FinOps  Team  Rate Negotiations  aws  Azure  Google  Cloud

Fazy procesu FinOps

Aby poradzić sobie ze skomplikowanym światem kosztów w chmurze fundacja FinOps zaleca iteracyjne podejście do zarządzania kosztami. To iteracyjne podejście do implementacji FinOps obejmuje trzy główne etapy: informowanie, optymalizacja i eksploatacja.

What is FinOps? How it works? Best Practices & Tools [2021]
  • Inform – zapewnia bezpośredni wgląd w koszty. Dzięki temu wszyscy mają świadomość ile i na co wydają.
  • Optimise – dzięki ciągłej analizie nie tylko generowanych kosztów, ale także utylizacji zasobów, możemy jeszcze wydajniej zarządzać budżetem, poprzez eliminowanie zbędnych wydatków i podejmowanie decyzji o zmniejszeniu mocy obliczeniowej tam, gdzie widzimy, że nie jest wykorzystywana w 100%. Do tego dochodzi możliwość rezerwowania usług na określony czas (rok, dwa, trzy) jeżeli wiemy że będziemy potrzebowali danej usługi przez tak długi czas. 
  • Operate – wykorzystanie narzędzi i platform w celu poprawy wydajności pracy. W tej fazie również wpływamy na procesy biznesowe poprzez ich ciągłe udoskonalanie.

Podsumowanie

  1. FinOps to współpraca między wszystkimi zespołami wewnątrz organizacji.
  2. Każdy członek organizacji musi być świadomy modelu kosztowego chmury.
  3. Dzięki raportowaniu w czasie rzeczywistym możemy lepiej monitorować wydatki, optymalizować koszty oraz zwiększać wartość biznesową rozwiązania w celu uzyskania przewagi nad konkurencją.
  4. Dzięki FinOps możemy szybciej podejmować ważne decyzje biznesowe dotyczące zasobów i infrastruktury w chmurze.

Mam nadzieję, że ten krótki artykuł przybliżył Wam temat tego czym jest FinOps i dlaczego jest taki ważny, gdy decydujemy się na użycie chmury obliczeniowej. Po szczegóły zapraszam do świetnej książki Cloud FinOps – Collaborative, Real-Time Cloud Financial Management dostępnej na stronie wydawnictwa O’Reilly. 

A w kolejnym artykule pokażę, jakie usługi w Azure możemy użyć, aby lepiej kontrolować koszty i nimi odpowiednio zarządzać. 

Źródła

[1] https://www.finops.org/what-is-finops  

[2] https://research.aimultiple.com/finops  

[3] https://www.cloudability.com/finops  

[4] J.R. Storment, Mike Fuller – Cloud FinOps. Collaborative, Real-Time Cloud Financial Management (O’Reilly, 2020)  

Infrastructure as Code? – Azure CLI !

W poprzednim artykule pokazałem w jaki sposób możemy użyć szablonów ARM do tworzenia infrastruktury w Azure, jako część podejścia Infrastructure as Code (IaC). 

W tym artykule chcę przedstawić kolejne narzędzie, które możemy w tym celu wykorzystać – Azure CLI.


Ponieważ celem artykułu jest przybliżenie możliwości narzędzia, a nie kurs jego nauki, po szczegółowe informacje i samouczki zapraszam do oficjalnej dokumentacji.(https://docs.microsoft.com/en-us/cli/azure/get-started-with-azure-cli?view=azure-cli-latest).  

Jeżeli prowadzisz firmę i chciałbyś wykorzystać podejście Infrastructure as Code w swoich projektach, a Tobie i Twojemu zespołowi brakuje wiedzy z tego obszaru to mam w swojej ofercie szkoleniowej 2-dniowe warsztaty Infrastruktura jako kod.


Azure CLI (Azure Command Line Interface), jak sama nazwa wskazuje to narzędzie linii poleceń, więc nie spodziewajmy się tutaj udogodnień graficznych, podobnych do tego co oferuje Visual Studio dla szablonów ARM. W przeciwieństwie do deklaratywnego sposobu tworzenia infrastruktury oferowanego przez ARM, Azure CLI wykorzystuje podejście imperatywne, czyli polegające na wydawaniu poleceń. Na przykład:  

az group create –l westeurope –n MyResourceGroup

Polecenie to nakazuje utworzenie grupy zasobów o nazwie MyResourceGroup w lokalizacji westeurope.  

Ta sama operacja z użyciem szablonów ARM wyglądałaby następująco: 

{ 
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", 
    "contentVersion": "1.0.0.0", 
    "parameters": {}, 
    "functions": [], 
    "variables": {}, 
    "resources": [ 
        { 
            "name": "MyResourceGroup", 
            "type": "Microsoft.Resources/resourceGroups", 
            "apiVersion": "2019-10-01", 
            "location": "westeurope", 
            "dependsOn": [ 
            ], 
            "tags": { 
            } 
        } 
    ], 
    "outputs": {} 
} 

Składnia poleceń Azure CLI jest typowa dla tego typu narzędzi i składa się z ciągu poleceń i argumentów. Wszystkie instrukcje zaczynamy od polecenia az <grupa_poleceń> po którym następuje właściwe polecenie, takie jak create czy list i przekazanie argumentów do polecenia. 

Porównując tworzenie tak prostego zasobu jak Resource Group za pomocą szablonów ARM do tej samej operacji wykonanej za pomocą Azure CLI widzimy, że w Azure CLI jest to jedna komenda, a w szablonie ARM cały zestaw atrybutów i ich wartości. Na pierwszy rzut oka może się wydawać, że szablony ARM dają większą kontrolę i możliwości, co nie do końca jest prawdą, gdyż w Azure CLI mamy do dyspozycji wszystkie udogodnienia znane z konsoli bash, takie jak: 

  • formatowanie odpowiedzi (wyjścia), tutaj domyślnie używany jest JSON, ale możemy także wyświetlić wynik polecenia w formie tabeli (table) lub w formacie TSV (tab separated values), gdzie wartości oddzielone są od siebie tabulatorem
  • narzędzia takie jak grep 
    • az vm list --query "[?powerState=='VM running'].name" | grep my_vm 
  • Przypisywanie wartości do zmiennych. Tak utworzoną zmienną o nazwie runnin_vm_ids możemy wykorzystać w innym poleceniu:
    • running_vm_ids=$(az vm list -d -g my_rg --query "[?powerState=='VM running'].id" -o tsv) 

Jeżeli po przeczytaniu tego artykułu masz wątpliwości, które narzędzie jest lepsze, ARM Templates czy Azure CLI, to niestety nie mam dla Ciebie dobrej wiadomości, gdyż nie ma jednoznacznej odpowiedzi na to pytanie. Tak jak z każdym narzędziem, powinniśmy używać tego co jest najlepsze do rozwiązania danego problemu. Przykładowo jedne usługi łatwiej będzie utworzyć za pomocą Azure CLI aniżeli przy pomocy szablonów ARM. Tak samo w drugą stronę. Ja osobiście używam obydwu tych podejść, i tak samo robi większość osób z mojego otoczenia, a trzeba pamiętać, ze nie są to jedyne narzędzia tego typu bo mamy do dyspozycji jeszcze Azure PowerShell i Azure SDKs, którym niedługo poświęcę osobne artykuły.

Infrastructure as Code? – to proste!

Wstęp

Czasy w których infrastrukturę IT tworzyło się ręcznie odchodzą do lamusa. Wraz z rozpowszechnieniem się wirtualizacji i praktyki DevOps specjaliści IT dostali szereg narzędzi pozwalających na optymalizację ich pracy. Jednym z nich jest Infrastructure as Code (IaC).

IaC to podejście w którym infrastrukturę IT oraz jej konfigurację opisujemy za pomocą kodu. Daje nam to wiele zalet:

  • automatyzacja wdrożeń – koniec z monotonnym i czasochłonnym klikaniem w GUI czy przepychaniem komend w CLI, żeby stworzyć pożądane środowisko,
  • powtarzalność wdrożeń – możemy tworzyć wiele środowisk i mieć pewność, że zawsze będą takie same,
  • wersjonowanie – tak jak w przypadku kodu aplikacji, kod infrastruktury przechowujemy w repozytorium dzięki czemu mamy możliwość:
    • powrotu do dowolnego momentu w historii
    • znamy historię zmian
    • kod odzwierciedla stan faktyczny infrastruktury
  • oszczędność czasu i pieniędzy*

*Oszczędność wynikająca z wdrożenia podejścia IaC nie jest widoczna od razu. IaC to nie tylko filozofia pracy, ale przede wszystkim zestaw narzędzi i bibliotek, które wymagają czasu potrzebnego na ich opanowanie.

Pomimo niewątpliwych zalet wynikających z użycia IaC spotykam się z oporem niektórych programistów i architektów oprogramowania do wykorzystania tego podejścia w swoich projektach? Dlaczego? Nie do końca wiem. Wydawać by się mogło, że takie podejście będzie naturalne dla programistów, ponieważ mają do czynienia z kodem na co dzień, więc ten sposób pracy z infrastrukturą powinien być dla nich chlebem powszednim, ale nie zawsze jest i z jakiegoś powodu tworząc kolejne środowisko wolą klikać next, next, next…

Jeden z powodów upatruję w przeciążeniu mentalnym. Programiści i tak mają już tyle technologii do opanowania, że na samą myśl o nauce kolejnego narzędzia (lub narzędzi!) do automatyzacji infrastruktury (Terraform, Vargant, Puppet, Chef, Ansible, CloudFormation, ARM Templates), żeby wymienić pierwsze z brzegu, zwyczajnie brakuje im chęci. A w kolejce do nauki stoi kolejny framework JS. W końcu większość z nas ma rodziny, hobby i zainteresowania. Żeby nie było, rozwój w branży IT jest ważny i jeżeli chce się być dobrym to nie da się go całkowicie wyeliminować z życia, niemniej trzeba zachować umiar i nie podążać ślepo za nowinkami, rozsądnie dobierając narzędzia i biblioteki do nauki, by znaleźć równowagę pomiędzy rozwojem zawodowym, a życiem osobistym.

Tym wpisem chcę rozpocząć serię artykułów pokazujących podejście IaC do tworzenia środowisk w Azure, bez obaw o przeciążenie mentalne naszych umysłów w najgorszym wypadku skutkujące wypaleniem zawodowym.

Na początek na warsztat weźmiemy szablony ARM (ang. ARM Templates).

Azure Resource Manager Templates

ARM (Azure Resource Manager) Template to plik zawierający opis infrastruktury Azure w postaci JSON. Przykład takiego pliku możemy znaleźć np. tutaj: https://github.com/Azure/azure-quickstart-templates/blob/master/101-webapp-linux-managed-postgresql/azuredeploy.json. Ten szablon utworzy aplikację NodeJS hostowaną za pomocą usługi AppService oraz bazę danych PostgreSQL. Opisywanie infrastruktury za pomocą składni JSON może wydawać się nieco skomplikowane, w końcu trzeba pamiętać te wszystkie atrybuty i ich składnię, a jest co bo to co widzimy w podlinkowanym szablonie to tylko wycinek atrybutów jakimi możemy opisać AppService. Pełna składnia i specyfikacja zasobu aplikacji dostępna jest tutaj: https://docs.microsoft.com/en-us/azure/templates/microsoft.web/sites a gdzie jeszcze specyfikacja bazy danych?

Na szczęście Visual Studio dostarcza nam zestaw szablonów projektów, które możemy wykorzystać do pracy z Azure. VS Code ma podobne udogodnienia, niemniej VS Code jest nastawiony na pracę z edytorem tekstowym i terminalem, dlatego temat VS Code i ARM Templates poruszę w jednym z kolejnych wpisów. Ponieważ chcemy zbudować infrastrukturę wybieramy szablon projektu Azure Resource Group.

Po podaniu informacji o projekcie takich jak nazwa i lokalizacja zostaniemy poproszeni o wybór szablonu ARM. Możemy zacząć od gotowca lub zacząć od zera i wybrać pusty.

W szablonie ARM wyróżniamy następujące sekcje:

  • parameters
  • variables
  • resources
  • outputs

Parameters

W sekcji parameters możemy zdefiniować parametry, z których będziemy korzystać w dalszej części szablonu, takie jak nazwa aplikacji, lokalizacja, konto administratora, rozmiar bazy danych, pricing plan (darmowy, płatny), itp.

Jak widzimy do utworzenia wartości parametru, możemy wykorzystać wbudowane funkcje takie jak concat() czy uniqueString(). Pełną listę funkcji możemy znaleźć w dokumentacji https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/template-functions. Do parametrów możemy następnie odwoływać z pozostałych miejsc szablonu.

Variables

Sekcja variables służy do definiowana zmiennych, czyli wartości do których możemy odwołać się w takich sekcjach jak resources czy outputs. W sekcji variables, podobnie jak w parameters możemy korzystać z wbudowanych funkcji. Co bardziej doświadczeni czytelnicy zauważą, że obydwie sekcje mogą realizować te same funkcjonalności i zadać pytanie jaka jest różnica i kiedy używać jednego lub drugiego? Odpowiedź nie jest prosta. W skrócie moglibyśmy powiedzieć, że sekcja parameters może przyjmować wartości spoza szablonu. To znaczy, że jeżeli szablon używa wartości przyjmowanych z zewnątrz, takie których nie chcemy umieszczać w szablonie bo zależą od konfiguracji czy środowiska: url-e, nazwy użytkowników, hasła, klucze dostępowe, itp, to powinniśmy je zdefiniować w sekcji parameters, gdyż wartości te są dostarczanie dynamicznie w trakcie wdrażania szablonu.

Z kolei sekcji variables powinniśmy używać do przechowywania wartości generowanych na podstawie parametrów i gdy chcemy skorzystać z funkcji, aby wykonać jakąś logikę. Spójrzmy na przykładowy szablon, żeby rozwiać wszelkie wątpliwości:

{
  "parameters": {
    "application": { ... },
    "businessUnit": { ... },
    "environment": { ... }
  },
  "variables": {
    "webApp": {
      "name": "[concat('app-', parameters('application'), '-', parameters(businessUnit), '-', parameters('environment'))]"
    }
  },
  "resources": [
    {
      "type": "Microsoft.Web/sites",
      "name": "[variables('webApp').name]",
      ...
    }
  ]
}

W powyższym szablonie wywołanie funkcji [variables('webApp').name spowoduje utworzenie nazwy aplikacji o następującym formacie:

app-application-businessUnit-environment

Przykładowa nazwa aplikacji mogłaby wyglądać wtedy tak:

app-wordpress-marketing-dev

Resources

Ostatnią sekcją szablonów ARM którą chciałbym pokrótce omówić to resources, czyli serce naszego szablonu. Jak sama nazwa wskazuje to w tym miejscu definiujemy zasoby Azure do stworzenia oraz ich pożądaną konfigurację (ang. Desired State Configuration).

Jak zauważyłem wcześniej ilość atrybutów, którymi możemy opisać zasoby Azure może przyprawiać o ból głowy i powodować trudności w odpowiedzi na pytanie, czy w swoim szablonie powinienem użyć wszystkich atrybutów, czy może wystarczy tylko kilka z nich? Czy muszę mieć pod ręką link do dokumentacji każdego zasobu, żeby wiedzieć jakie atrybuty posiada? Otóż nie do końca. Visual Studio dostarcza okno o nazwie JSON Outline. Jak możemy zobaczyć poniżej wyświetla ono strukturę szablonu w nieco bardziej przystępny sposób.

Dzięki temu oknu nie tylko możemy w łatwy sposób nawigować po szablonie czy widzieć z jakich elementów się składa, ale również dodawać nowe zasoby. W tym celu klikamy prawym klawiszem myszki na węzeł resources i wybieramy Add New Resource.

Wybieramy zasób z listy, np Redis Cache i klikamy Add.

Po kliknięciu Add zobaczymy, że w szablonie pojawił się nowy zasób:

Co bardziej dociekliwi czytelnicy zauważą, że wraz z pojawieniem się nowego zasobu, pojawiły się również odniesienia do parametrów reprezentujących wartości dla niektórych atrybutów. Jak możemy się domyśleć Visual Studio dodał odpowiednie wpisy w sekcji parameters za nas:

Nam jedynie pozostaje przekazać odpowiednie wartości do parametrów i voila – infrastruktura potrzebna do zbudowania cache aplikacji opartego o Redis gotowa.

ARM Template Deployment

Po przygotowaniu szablonu nie pozostaje nam nic innego jak wdrożyć go na środowisko w chmurze. W tym celu w Solution Explorer klikamy prawym klawiszem myszki i wybieramy Deploy > New. W kolejnym oknie wybieramy subskrypcję, resource group oraz szablon i klikamy Deploy.

W kolejnym oknie mamy możliwość przekazania parametrów do szablonu:

Po kliknięciu Save rozpocznie się proces deploymentu, czyli tworzenia zdefiniowanej infrastruktury w Azure wraz z pożądaną konfiguracją.

Podsumowanie

Mam nadzieję, że tym wpisem udało mi się pokazać, że mając pod ręką odpowiednie narzędzia, tworzenie szablonów ARM nie jest takie trudne jak może się z początku wydawać. Oczywiście zaprezentowane przykłady stanowią niewielki wycinek możliwości jakie dają szablony ARM (pełna specyfikacja znajduje się pod adresem https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/) niemniej chciałem Cię zachęcić Drogi Czytelniku do spróbowania tego podejścia i poeksperymentowania we własnych zakresie.

Dla Firm

Jeżeli prowadzisz firmę i chciałbyś wykorzystać podejście Infrastructure as Code w swoich projektach, a Tobie i Twojemu zespołowi brakuje wiedzy z tego obszaru to mam w swojej ofercie szkoleniowej 2-dniowe warsztaty Infrastruktura jako kod w których uczestnicy poznają teorię, narzędzia wspierające IaC oraz samodzielnie piszą kod wdrażający przykładowe rozwiązania w Azure – od małych środowisk obejmujących aplikację i bazę danych do średniej wielkości systemów składających się z takich usług jak: AzureSQL, kontenery, Azure Search, App Service, CDN, Computer Vision, Storage Account, Azure Front Door, Azure Functions).

Ze względu na obecną sytuację z COVID-19 szkolenia prowadzone są online.

Raporty w systemach rozproszonych

Wstęp

Coraz więcej firm decyduje się na implementację bądź migrację swoich systemów do chmury, często w oparciu o architektury rozproszone jak popularne mikroserwisy. Czy zasadnie czy nie i z jakim skutkiem to opowieść na inny wpis. Niemniej jednak, takie sytuacje mają miejsce i jako konsultant oraz architekt, gdy rozmawiam z innymi programistami czy osobami odpowiedzialnymi za biznesy w firmach dla których pracuję, zauważam, że o ile większość moich rozmówców ma świadomość tego jak zbudować taki system z kilku mniejszych usług biznesowych, będących odzwierciedleniem kontekstów ograniczonych w organizacji (ang. Bounded Contexts) i jak połączyć je w całość, o tyle gdy przychodzi temat raportów i szeroko pojętego Business Intelligence to sprawy mają się różnie. W tym wpisie chciałbym pokazać z jakimi najczęściej wdrażanymi rozwiązaniami się spotykam, jakie mają wady/zalety oraz jak można to zrobić lepiej używając oczywiście chmury 😊. 

Podejście nr 1 – serwis agregujący (ang. Aggregation Service) 

Jak sama wskazuje to rozwiązanie polega na wprowadzeniu do systemu nowej usługi raportowej, której zadaniem jest wyciąganie danych z pozostałych usług i łączenie ich (agregacja) w jeden model danych, który zostanie użyty do wygenerowania raportu. Przykładowy proces został przedstawiony na poniższym diagramie. 

Frontend 
Manager 
Generate report 
Rep ort 
Reporting 
Service 
Create repo N 
Get Orders Data 
Orders 
Service 
Customers 
Service 
Get Customers Data 
Aggregate Data 
Generate Report

Pomimo prostoty tego rozwiązania, największą jego wadą jest komunikacja synchroniczna. Serwis raportowy musi odpytać API każdej usługi, której dane są potrzebne do wygenerowania raportu. W naszym przypadku są to dwie usługi: Orders i Customers, ale w rzeczywistym systemie z pewnością będzie ich więcej. Ponieważ są to żądania synchroniczne (tutaj HTTP, ale może to też być gRPC lub SOAP zwłaszcza gdy organizacja używa systemów legacy), to musimy czekać na zakończenie każdego z nich i następnie poskładać wszystkie dane w jedną całość co będzie miało duży wpływ na wydajność i responsywność systemu. Raz, że będziemy tracić czas na oczekiwanie na pobranie danych, transformację do modelu raportowego i generowanie wynikowego raportu, a dwa, że będziemy niepotrzebnie obciążać inne usługi poprzez każdorazowe odpytywanie ich API. Oczywiście w niedużych systemach, gdzie raporty nie są złożone i nie są generowane zbyt często to rozwiązanie się sprawdzi bardzo dobrze. Niestety wraz z rozwojem systemu, przyrostem użytkowników i wzrostem złożoności raportów będziemy coraz bardziej obserwować spadek wydajności. Jedni w takiej sytuacji radzą sobie poprzez skalowanie systemu w górę lub wszerz, inni decydują się na zmianę architektury i wdrożenie kolejnych usług, tym razem odpowiedzialnych za wyciąganie danych i zapisywanie ich w dedykowanej bazie raportowej.

Podejście nr 2 – Data Workers

To podejście zmienia sposób komunikacji usługi raportowej z pozostałymi poprzez wprowadzenie dodatkowych komponentów, tak zwanych data workerów, których zadaniem jest wyciąganie danych z innych usług i zapisywanie ich w dedykowanej bazie, skąd są później odczytywane przez usługę raportową przy generowaniu raportu. Dzięki takiemu rozluźnieniu komunikacji, usługa raportowa nie musi tracić czasu na odpytywanie innych usług i agregację danych tylko może się skupić na generowaniu raportu na podstawie jednego źródła, zawierającego wszystkie potrzebne informacje. 

Data workery pozwalają również na filtrowanie, oczyszczanie i normalizację danych, przez co usługa raportowa nie musi robić później skomplikowanych zapytań. Jeżeli komuś skojarzyło się to z procesem ETL (Extract, Transform, Load) to bardzo dobrze bo koncepcyjnie jest to uproszczona implementacja tego procesu.  

Podejście nr 3 – zdarzenia (ang. Events) 

Innym podejściem do tematu raportów jest architektura oparta o zdarzenia. Tutaj nie mamy usług, które odpytują inne usługi o dane, tylko usługi same powiadamiają siebie nawzajem o tym, że coś się wydarzyło. Koncepcyjnie rozwiązanie możemy zaprezentować następująco:

Zacznijmy od tego czym jest zdarzenie. Zdarzenie modeluje sytuację która miała miejsce w przeszłości i się zakończyła – jest faktem. Przykładowymi zdarzeniami w systemie są sytuacje gdy: 

  • Utworzono użytkownika 
  • Zatwierdzono zamówienie 
  • Opłacono zamówienie,  
  • Anulowano rezerwację, itd 

Takie zdarzenia niosą ze sobą ogrom informacji. Odpowiednio zaprojektowane usługi zorientowane na zdarzenia pozwalają na analizowanie historii działania usługi (event log), śledzenie aktywności, czy nawet przywracanie stanu usługi do momentu z przeszłości. Architektura oparta o zdarzenia (ang. Event Driven Architecture) jest szeroko stosowana w wysokowydajnych systemach o architekturze mikrousługowej do komunikacji usług między sobą. Usługi zamiast komunikować się synchronicznie poprzez HTTP, SOAP lub gRPC, publikują i subskrybują zdarzenia (Publish/Subscribe Pattern). W tym celu wymagany jest komponent, który będzie obsługiwał wiadomości, zwykle jest to message broker (RabbitMQ, ActiveMQ, Azure Service Bus itd.), ale może też to być magazyn zdarzeń (ang. Event Store) taki jak Kafka.  

Implementacja architektury opartej o zdarzenia nie jest łatwa. Wymaga bezpiecznej i niezawodnej infrastruktury – awaria Message Brokera lub Event Store uziemia cały system. Bardzo łatwo o duplikację zdarzeń np. w wyniku awarii którejś z usług. Przykładowo usługa reagująca na zdarzenie obciążające użytkownika finansowo, może otrzymać to samo zdarzenie dwa razy i naliczyć opłaty podwójnie. Do tego dochodzą problemy związane ze spójnością danych (Eventual Consistency), transakcyjnością, czy orkiestracją lub choreografią zdarzeń w procesy biznesowe.  

Oczywiście nikt z nas nie zdejmie odpowiedzialności za nieprawidłowe zaprojektowanie systemu w wyniku którego może dojść do błędów w systemie, niemniej jednak możemy część obowiązków związanych z utrzymaniem i zarządzaniem infrastrukturą przerzucić na kogoś innego, a mianowicie dostawcę chmury. Obecnie każda wiodąca chmura posiada usługi pozwalające na implementację rozwiązań w architekturze opartej o zdarzenia przy użyciu bezpiecznej, odpornej na awarie i skalowalnej infrastruktury. My w tym czasie zamiast skupiać się na zapewnieniu ciągłości działania i niezawodności infrastruktury możemy skupić się na właściwym modelowaniu i implementacji procesów biznesowych, wykorzystując takie narzędzia jak Event Storming, Domain Driven Design czy Test Driven Development.

Przykładowe wdrożenie usługi raportowej do systemu uruchomionego w MS Azure i opartego o architekturę mikrousługową mogłoby wyglądać następująco:

W tym rozwiązaniu usługi komunikują się ze sobą za pomocą usługi Event Grid w modelu Publish/Subscribe. Dzięki temu, wykorzystujemy istniejącą infrastrukturę komunikacyjną i podpinamy kolejną usługę – Reporting Service, której głównym zadaniem będzie nasłuchiwanie na zdarzenia z pozostałych usług i aktualizowanie danych raportowych na podstawie przychodzących zdarzeń z pozostałych usług. Dane te mogą potem zostać wykorzystane przez narzędzia do wizualizacji danych jak SQL Server Reporting Services czy PowerBI. Dzięki zastosowaniu komunikacji asynchronicznej w modelu Publish/Subscribe pomiędzy usługami, jest to stosunkowo mało inwazyjne rozwiązanie, które w niewielkim stopniu wymaga modyfikacji pozostałych usług.

Rozwiązanie to, mimo, że relatywnie proste w implementacji ma swoje ograniczenia, mianowicie usługa raportująca jest odpowiedzialna za przetwarzanie napływających zdarzeń, to znaczy filtrowanie, walidację, transformację i zapis danych. W jednym z kolejnym wpisów zobaczymy w jaki sposób możemy wykorzystać usługi analityczne i big data MS Azure do budowy wysokowydajnego silnika raportowego wykorzystującego strumienie zdarzeń, analizę i wizualizację danych w trybie rzeczywistym.

Do usłyszenia!