CI/CD dla aplikacji web z wykorzystaniem Azure DevOps

CI/CD dla aplikacji web z wykorzystaniem Azure DevOps

Pod koniec września 2018 swoją premierę miała platforma Azure DevOps. Aby sprawdzić, co oferuje nowe narzędzie Microsoftu postanowiliśmy na początek stworzyć za jego pomocą procesy ciągłej integracji oraz ciągłego wdrażania (CI/CD) dla prostej (monolitycznej) aplikacji web’owej. Na potrzeby niniejszego artykułu posłużymy się zbudowaną przez nas wcześniej aplikacją PWA do zgłaszania szkód.

Rozwiązanie, które chcemy skonstruować będzie wyglądać następująco:

Proces ciągłej integracji pobiera kod źródłowy z repozytorium GitHub, kompiluje aplikację i opakowuje ją w kontener, który następnie zapisywany jest w repozytorium obrazów DockerHub.

Proces ciągłego wdrażania pobiera obraz kontenera z DockerHub i uruchamia go w przygotowanym uprzednio środowisku. W naszym przypadku będzie to Azure App Service.

 

Przygotowanie

Zanim przystąpimy do tworzenia procesów CI/CD musimy przygotować kilka rzeczy:

  • Zakładamy konto https://github.com/ i tworzymy repozytorium, do którego wrzucamy kod naszej aplikacji. Aplikacja jest skonteneryzowana (posiada przygotowany Dockerfile).
  • Zakładamy konto na https://hub.docker.com/ i tworzymy nowy zespół wraz z przypisaną do niego przestrzenią nazw.
  • Zakładamy konto na http://dev.azure.com i tworzymy nowy projekt
  • Zakładamy konto na https://portal.azure.com, instalujemy Azure CLI, autoryzujemy się (wydając polecenie: az login) i przygotowujemy środowisko uruchomieniowe dla naszej aplikacji:
    • wybieramy nazwę naszej aplikacji, grupy zasobów Azure i repozytorium obrazów w DockerHub, np.:

RG_NAME=claim-reporter
APP_NAME=claim-reporter-app
IMAGE=altkom/claim-reporter-app

    • tworzymy grupę zasobów:

az group create \
    --location westeurope \
    --name ${RG_NAME}

    • tworzymy plan usługi:

az appservice plan create \
    --name ${APP_NAME} \
    --resource-group ${RG_NAME} \
    --is-linux

    • tworzymy nowy zasób typu “App Service”:

az webapp create \
    --plan ${APP_NAME} \
    --resource-group ${RG_NAME} \
    --name ${APP_NAME} \
    --deployment-container-image-name ${IMAGE}

 

Proces Ciągłej Integracji

Zdefiniowanie procesu ciągłej integracji w Azure DevOps wymaga 11 prostych kroków:

 

1) Na ekranie wybranego projektu przechodzimy na zakładkę “Builds” i naciskamy przycisk “New Pipeline”:

Azure DevOps

2) Wskazujemy platformę, na której trzymamy repozytorium kodu (Github lub Azure):

Azure DevOps

3) Wybieramy sposób integracji z GitHub’em:

Azure DevOps

4) Instalujemy na koncie GitHub’owym wtyczkę Azure Pipelines:

Azure DevOps

5) Nadajemy wtyczce niezbędne uprawnienia:

Azure DevOps

6) Wybieramy repozytorium kodu:

Azure DevOps

7) Wybieramy szablon dla nowego pipeline:

Azure DevOps

Dostępne szablony zawierają proste przykłady procesów budowania dla wielu popularnych stosów technologicznych: Java (ant, maven, gradle), JavaScript (npm, grunt, gulp, angular, react, vue), .NET (Full, Core), UWP, Python, Ruby, Go, PHP, Xamarin, Xcode, C/C++, Docker, etc.

8) Przeglądamy go i zapisujemy bez modyfikacji:

Azure DevOps

Definicja pipeline została zapisana w repozytorium kodu w pliku azure-pipelines.yml

Nowy commit powoduje uruchomienie procesu budowania, który (z oczywistych względów) kończy się niepowodzeniem:

Azure DevOps

9) Żeby udrożnić proces budowania musimy dostosować definicję do specyfiki naszego projektu. Rozpoczynamy od zdefiniowania trzech zmiennych w pipeline:

  • dockerId – login do konta DockerHub
  • dockerNamespace – przestrzeń nazw utworzona na DockerHub
  • dockerPass – hasło konieczne do uwierzytelniania (należy pamiętać o zaznaczeniu, że ta zmienna przechowuje hasło, aby uniknąć jego wyświetlania w konsoli i logach)

Azure DevOps

10) Dostosowujemy definicję pipeline zapisaną w pliku azure-pipelines.yml:

# azure-pipelines.yml
pool:
  vmImage: 'Ubuntu 16.04'

variables:
  app: 'claim-reporter-app'
  image: '$(dockerNamespace)/claim-reporter-app'
  tag: '$(build.buildId)'

steps:
- script: docker login -u $(dockerId) -p $(dockerPass)
displayName: 'docker login'
- script: docker build -t $(image) $(app)
displayName: 'docker build $(app)'
- script: docker tag $(image) $(image):$(tag)
displayName: 'docker tag $(image):$(tag)'
- script: docker push $(image):$(tag)
displayName: 'docker push $(image):$(tag)'
- script: docker push $(image):latest
displayName: 'docker push $(image):latest'

11) Zapisujemy zmianę w repozytorium, co powoduje uruchomienie procesu budowania:

Azure DevOps

Status procesu budowania możemy podejrzeć również na GitHub’ie:

Azure DevOps

Na DockerHub pojawiły się zbudowane obrazy kontenerów:

Azure DevOps

 

Proces Ciągłego Wdrażania

W celu zdefiniowania prostego procesu Ciągłego Wdrażania musimy wykonać 8 kroków:

 

1) W oknie projektu przechodzimy na zakładkę “Releases” i wybieramy opcję “New Pipeline”:

Azure DevOps

2) Wybieramy szablon “Azure App Service deployment” i naciskamy “Apply”:

Azure DevOps

3) Określamy nazwę potoku oraz pierwszego kroku:

Azure DevOps

4) Otwieramy ekran wyboru artefaktów do instalacji i wybieramy jako źródło “Docker Hub”:

Azure DevOps

5) Konfigurujemy połączenie z kontem na DockerHub:

Azure DevOps

6) Następnie wybieramy przestrzeń nazw oraz repozytorium i zatwierdzamy przyciskiem “Add”:

Azure DevOps

7) Przechodzimy na zakładkę “Tasks”:

Azure DevOps

Wybieramy subskrypcję Azure, klikamy “Authorize” i logujemy do konta Azure (uwaga: należy zwrócić uwagę, czy w przeglądarce nie jest włączona blokada wyskakujących okien).

Następnie wybieramy miejsce docelowe instalacji wskazując utworzoną uprzednio usługę “App Service” i naciskamy “Save”:

Azure DevOps

8) Wracamy do ekranu konfiguracji potoku i konfigurujemy wyzwalacz procesu instalacji:

Azure DevOps

Proces, który udało nam się zbudować wygląda następująco:

Azure DevOps

Pierwszą instalację zainicjujemy manualnie (Release -> Create a release), wybierając wersję artefaktu, który ma zostać wykorzystany:

Azure DevOps

Po chwili proces instalacji się zakończy:

Azure DevOps

a pod adresem docelowym pojawi się nasza aplikacja:

Azure DevOps

 

Podsumowanie

Kilka eksperymentów, które przeprowadziliśmy z użyciem platformy Azure DevOps pozwoliło nam poczynić następujące obserwacje:

  • już na pierwszy rzut oka możemy stwierdzić, że nie jest to nowe rozwiązanie, lecz odświeżona graficznie usługa VSTS – główne zmiany nastąpiły w cenniku, o czym wspomnę jeszcze w kolejnych punktach;
  • platforma Azure DevOps pozwala szybko i łatwo skonstruować potoki CI/CD – dla prostych przypadków;
  • konfiguracja integracji z GitHub oraz DockerHub jest bezbolesna;
  • integracja z GitHub bardzo dobrze działa w obie strony: na GitHub’ie przy każdym commicie mamy podgląd statusu kompilacji wywołanej przez daną zmianę a z poziomu Azure DevOps mamy wygodny podgląd listy zmian, które zostały uwzględnione w danej kompilacji;
  • brak odpowiedniego CLI powoduje, że wszystkie operacje trzeba wykonywać przez GUI, co dla większości inżynierów jest dużą wadą; oficjalne CLI (w wersji 0.1.3) nie pozwala na definiowanie potoków budowania; istnieje też rozwiązanie alternatywne tworzone przez społeczność: VSTeam, jest ono jednak dostępne tylko z PowerShell’a i również posiada swoje ograniczenia;
  • platforma posiada HTTP REST API oraz biblioteki klienckie dla kilku języków programowania, jednak nie wszystkie funkcjonalności są w ten sposób wspierane;
  • narzędzie nie zachowuje się jeszcze w pełni stabilnie – zdarzało się, że commit w repozytorium kodu nie powodował automatycznego uruchomienia procesu budowania;
  • na styku pomiędzy platformami Azure DevOps oraz Azure widać pewne koncepcyjne niespójności – pomimo, że chcemy wykonać deployment za pomocą Azure DevOps, przy tworzeniu kontenera App Service i tak musimy wskazać repozytorium obrazów dockerowych (nie można utworzyć pustej instancji App Service);
  • przewodnik służący do tworzenia “build pipelines” jest niedopracowany – nie pozwala np. zdefiniować zmiennych środowiskowych, z których będzie korzystał proces budowania a którego nie można  pominąć; oznacza to, że jeśli chcemy skorzystać ze zmiennych musimy najpierw utworzyć pipeline (co spowoduje jego uruchomienie) a następnie wejść w edycję, skonfigurować zmienne i ponownie uruchomić proces budowania.
  • interfejs graficzny platformy jest w niektórych miejscach niekonsekwentny – przykładowo plik YAML, zawierający definicję procesu budowania, można edytować z poziomu interfejsu graficznego  tylko podczas jego tworzenia; ewentualne zmiany trzeba wprowadzać wykonując commity bezpośrednio do repozytorium kodu;
  • obecna taryfikacja usługi powoduje, że jest ona doskonałym rozwiązaniem dla projektów Open Source oraz małych projektów realizowanych przez niewielkie zespoły (np. typu proof-of-concept)

 

Podsumowując – platforma Azure DevOps oferuje bardzo duże możliwości. Liczymy, że te nieliczne zauważone przez nas niedociągnięcia zostaną niebawem usunięte.
 

Autor: Robert Kuśmierek, Lead Software Engineer, ASC-LAB