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ć […]
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”:
2) Wskazujemy platformę, na której trzymamy repozytorium kodu (Github lub Azure):
3) Wybieramy sposób integracji z GitHub’em:
4) Instalujemy na koncie GitHub’owym wtyczkę Azure Pipelines:
5) Nadajemy wtyczce niezbędne uprawnienia:
6) Wybieramy repozytorium kodu:
7) Wybieramy szablon dla nowego pipeline:
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:
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:
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)
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:
Status procesu budowania możemy podejrzeć również na GitHub’ie:
Na DockerHub pojawiły się zbudowane obrazy kontenerów:
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”:
2) Wybieramy szablon “Azure App Service deployment” i naciskamy “Apply”:
3) Określamy nazwę potoku oraz pierwszego kroku:
4) Otwieramy ekran wyboru artefaktów do instalacji i wybieramy jako źródło “Docker Hub”:
5) Konfigurujemy połączenie z kontem na DockerHub:
6) Następnie wybieramy przestrzeń nazw oraz repozytorium i zatwierdzamy przyciskiem “Add”:
7) Przechodzimy na zakładkę “Tasks”:
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”:
8) Wracamy do ekranu konfiguracji potoku i konfigurujemy wyzwalacz procesu instalacji:
Proces, który udało nam się zbudować wygląda następująco:
Pierwszą instalację zainicjujemy manualnie (Release -> Create a release), wybierając wersję artefaktu, który ma zostać wykorzystany:
Po chwili proces instalacji się zakończy:
a pod adresem docelowym pojawi się nasza aplikacja:
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