Serverless to jednen z większych buzzword-ów ostatnich lat. Sprawdzając w google trends to “serverless” był bliski przebicia popularnością fraze “microservices”. W dzisiejszym artykule chciałbym zagłębić się w ten temat. Zaczniemy od podstaw – wygenerujemy pierwszą funkcję Azure, a następnie wdrożymy ją za pomocą Gitlab CI.

Logowanie do Azure CLI

Żeby się uczyć trzeba mieć na czym pracować. Proponuje zacząć od stworzenia pustej funkcji azure, aby mieć gdzie wdrożyć stworzoną, w tym tutorialu, funkcję. Oczywiście jest to również do wyklikania w portalu azure ale to nie wygląda tak pro, co nie? Innym sposobem (i według mnie szybszym) jest utworzenie go za pomocą Azure CLI. Jeżeli go jeszcze nie zainstalowałeś, w referencjach pod tym artykułem znajdziesz link jak to zrobić.
Do zalogowania się w konsoli potrzebna jest tylko jedna komenda:

$ az login

Po której zostaniesz przekierowany do przeglądarki, aby Azure uwierzytelniło Twoje konto.

Następnym krokiem jest zdefiniowanie subskrybcji na której chcesz pracować. Oczywiście wybrana subskrypcja może być oznaczona jako domyślna. Osobiście uważam, że dobrą praktyką jest rozpoczęcie od zdefiniowania subskrypcji. Dwie proste komendy Azure CLI, pierwsza do pobrania listy wszystkich subskrypcji, druga natomiast do ustawienia, na której będziesz pracować.

$ az account list --output table 
$ az account set --subscription { SubscriptionId }

Utworzenie Azure Function

Skoro masz już skonfigurowaną konsole możemy przejść do kolejnych punktów programu. Utworzenie Azure Function wymaga powstania dwóch innych zasobów: Resource Group i Storage Account . Jako, że to dość proste polecenia, wszystko wstawię w jeden skrypt.

$ az group create --location westus --name bd90-blog
$ az storage account create --name bd90funcstorage --resource-group bd90-blog

Kolejna komenda Azure CLI będzie już nieco bardziej skomplikowana, ponieważ konieczne będzie podanie kilku argumentów. Oprócz standardowych jak nazwa, lokalizacja, grupa, musimy przekazać informacje na temat systemu operacyjnego, środowiska uruchomieniowego, nazwę storage account, wersji funkcji ( ma to wpływ na wersję .NET SDK, jeżeli chcesz napisać funkcję w wersji 3.1 musisz wybrać wersję funkcji 3. Niestety, na chwilę obecną .NET 5 nie jest jeszcze wspierany jako środowisko uruchomieniowe w Azure Function). Poniżej masz przykład jak utworzyć funkcję, która będzie działała na Linux-ie, w środowisku .NET 3.1.

$ az functionapp create --resource-group bd90-blog \ 
  --name bd90-test-function \ 
  --consumption-plan-location westus \
  --os-type Linux \
  --runtime dotnet \
  --storage-account bd90funcstorage \
  --functions-version 3

Utworzenie Service Principal

Kolejnym etapem jest utworzenie Service Principal-a, czyli bytu w Azure, którego użyjesz do uwierzytelnienia pipeline-u Gitlab CI. Oczywiście istnieją inne sposoby,  polecałbym jednak nie przechowywać swojego loginu i hasła do Azure w zmiennych środowiskowych CI / CD. W tym tutorialu pokaże Ci jak stworzyć bardzo podstawową i okrojoną wersję SP (Service Principal). Użyjemy do tego oczywiście Azure CLI. Utworzony SP, będzie  uwierzytelniany na podstawie trzech wartości:

  • AppId
  • Password
  • TenantId

Wszystkie te wartości otrzymany po poniższej komendzie:

$ az ad sp create-for-rbac --name MyServicePrinciple

{
  "appId": "Guid",
  "displayName": "MyServicePrinciple",
  "name": "http://MyServicePrinciple",
  "password": "password",
  "tenant": "Guid"
}

Warto je zapisać, ponieważ będą używane podczas konfiguracji Gitlab CI.

Gitlab CI – Wdrażanie Azure Function

Na tym etapie zakładam, że masz już przygotowany kod do wdrożenia. Wystarczy świeżo wygenerowana aplikacja. Ja do tego celu użyłem po prostu szablonu dostępnego po zainstalowaniu plugin-u do Rider-a. Utworzyłem funkcję, która jest uruchamiana po wykonaniu zapytania HTTP GET lub HTTP POST. Jedyną zmianą, jaką wprowadziłem, to  widoczność z AuthorizationLevel.Function na AuthorizationLevel.Anonymous. Pozwala to łatwo i szybko przetestować czy skrypt wdrażania działa poprawnie.

Teraz przejdź do konfiguracji CI / CD w swoim repozytorium, aby zdefiniować zmienne. Osobiście do zmiennych środowiskowych lubię używać konwencji SCREAMING_SNAKE_CASE.

Wszystko skonfigurowane? Super! Czas na crème de la crème – napisanie pliku gitlab-ci.yml. Sam proces wdrożenia funkcji azure możemy podzielić na 3 etapy:

  • Przygotowanie środowiska (Instalacja azure cli, node-a i paczki np. azure-functions-core-tools@3)
  • Uwierzytelnienie za pomocą az login --service-principal
  • Uruchomienie wdrażania funkcji przy użyciu wcześniej zainstalowanej paczki npm

Gotowy skrypt prezentuje się następująco.

stages:
  - deploy

deploy-func-bd90-test-function:
  stage: deploy
  image: mcr.microsoft.com/dotnet/core/sdk:3.1
  script: 
    - curl -sL https://aka.ms/InstallAzureCLIDeb | bash
    - apt-get install curl && curl -sL https://deb.nodesource.com/setup_12.x | bash -
    - apt-get install nodejs
    - npm install -g azure-functions-core-tools@3 --unsafe-perm true
    - az login --service-principal --username $SP_APP_ID --password $SP_PASSWORD --tenant $SP_TENANT
    - cd ./Bd90.TestFunction
    - func azure functionapp publish bd90-test-function --csharp
  environment:
    name: Function-bd90-test-function
  when: manual

Dodałem do niego definicje środowiska rozumianego przez Gitlab. Możesz tam dodać np. link do Twojej funkcji, aby dało się ją uruchomić / przejść do niej już z poziomu samego Gitlab-a. Na samym końcu skryptu znajdziesz definicję mówiącą, że ten krok pipeline-a może być wykonany tylko przez akcję użytkownika. Odpowiedż dlaczego tak robię znajdzie się w innym artykule 😁

Po uruchomieniu pipeline-a w logach powinieneś zobaczyć, że wszystko zakończyło się poprawnie.

Wszystko działa pięknie!

To by było na tyle w dzisiejszym artykule. Mam nadzieje że się przyda 🤟

Do Następnego!

PS. Spodobał Ci się ten artykuł?