Ikona strzałka
Powrót do bloga

Już jest! Multi-stage YAML Pipelines w Azure DevOps

Krystian Kucharski
Krystian Kucharski
15/03/2020

Nareszcie! Ile można czekać?!

Tak całkiem niedawno zareagowała społeczność użytkowników Azure DevOps na zapowiedź obsługi multi-stage YAML Pipelines w Azure DevOps.

Co prawda obsługa wieloetapowych potoków (pipeline’ów) YAML w chwili publikacji tego artykułu jest nadal w fazie Preview, ale wszystko wskazuje na to, że jej rozwój idzie w dobrym kierunku.

To znaczy w jakim?

Zachęcam do lektury artykułu?

Everything-as-Code

Azure zawsze stara się wychodzić naprzeciw potrzebom klientów. Aby sprostać ich oczekiwaniom, jakiś czas temu firma rozszerzyła możliwości Azure DevOps o obsługę multi-stage YAML Pipelines, pozwalającą na budowanie wielu etapów w pojedynczej definicji potoku YAML.

Do tej pory mogliśmy używać definicji YAML do budowania procesów ciągłej integracji (Continuous Integration). Teraz możemy w niej także ująć procesy ciągłego wdrażania (Continuous Deployment).

Oznacza to, że wszystkie etapy budowania, testowania oraz wdrażania kodu możemy zamknąć w jednej definicji YAML:

Przejdźmy dalej, aby przekonać się, jak ten mechanizm sprawdza się w praktyce.

Jak uruchomić multi-stage YAML Pipelines?

Aby przetestować funkcję, logujemy się do Azure DevOps i odnajdujemy w prawym górnym rogu konsoli ikonę z naszym użytkownikiem. Następnie klikamy w pozycję Preview features.

Tutaj upewniamy się, że opcja Multi-stage pipelines jest włączona.

Teraz możemy przejść do sekcji Pipelines, gdzie w lewym menu wybieramy pozycję Pipelines, po czym klikamy [Create Pipeline], aby przejść do stworzenia nowego potoku.

Jako źródło kodu dla naszego potoku wybieramy Azure Repos Git.

Następnie wybieramy dowolne repozytorium, na którym ma zostać zapisana definicja potoku.

W kolejnym kroku klikamy Starter pipeline, aby rozpocząć tworzenie definicji potoku.

Aby zbytnio się nie męczyć, wcześniej przygotowałem już definicję potoku. Wystarczy wkleić ją do edytora YAML.

Definicja ta jest symulacją procesu potoku i składa się z czterech etapów (bloków stage): budowania kodu, testów, wdrożenia na środowisko deweloperskie oraz wdrożenia na środowisko odpowiedzialne za testy:

trigger:
- master

stages:
- stage: Build
  displayName: "Build Applications"
  jobs:
  - job: run_build
    pool:
      vmImage: 'Ubuntu 16.04'
    steps:
    - script: echo Build Applications

- stage: Test
  displayName: "Run tests for apps"
  jobs:
  - job: run_tests
    pool:
      vmImage: 'Ubuntu 16.04'
    steps:
    - script: echo Run applications tests


- stage: Develop
  displayName: "Deploy to the Develop"
  jobs:
  - job: deploy_develop
    pool:
      vmImage: 'Ubuntu 16.04'
    steps:
    - script: echo create infrastructure
    - script: echo deploy develop



- stage: QA
  displayName: "Deploy to the QA"
  jobs:
  - job: deploy_qa
    pool:
      vmImage: 'Ubuntu 16.04'
    steps:
    - script: echo create infrastructure
    - script: echo deploy qa

Po poprawnym wklejeniu definicji do edytora YAML klikamy [Save and run], aby uruchomić nasz potok:

W odświeżonym graficznym interfejsie możemy zobaczyć jego status:

Kliknijmy teraz w pierwszy etap potoku, Build Applications:

Zobaczymy wówczas nową, odświeżoną wersję interfejsu logowania:

Jak widać, cały proces został uruchomiony sekwencyjnie, od momentu budowania kodu na etapie Build, poprzez testowanie kodu na etapie Test, aż do wdrożeń na środowiska oznaczone kolejno jako Develop oraz QA.

Wieloetapowe potoki YAML: dodawanie warunków

Multi-stage YAML Pipelines umożliwiają dodawanie warunków w blokach etapów, które będą decydowały o wykonywaniu bloku. Zobaczmy, jak one działają.

Najpierw musimy wejść w widok wcześniej uruchomionego potoku. W prawym górnym rogu klikamy ikonę z trzema kropkami, a następnie wybieramy pozycję Edit pipeline:

Następnie wyszukujemy blok o nazwie QA i dodajemy w nim następujący parametr:

condition: eq(variables['Build.SourceBranch'], 'refs/heads/qa')

W efekcie definicja etapu QA powinna wyglądać następująco:

- stage: QA
  displayName: "Deploy to the QA"
  condition: eq(variables['Build.SourceBranch'], 'refs/heads/qa')
  jobs:
  - job: deploy_qa
    pool:
      vmImage: 'Ubuntu 16.04'
    steps:
    - script: echo create infrastructure
    - script: echo deploy qa

Oznacza to, że ten etap zostanie wykonany, jeśli gałęzią w źródłowym repozytorium kodu, z którego będzie budowana aplikacja na etapie Build, będzie gałąź o nazwie qa.

Po wprowadzeniu zmian całość definicji potoku powinna wyglądać następująco:

trigger:
- master

stages:
- stage: Build
  displayName: "Build Applications"
  jobs:
  - job: run_build
    pool:
      vmImage: 'Ubuntu 16.04'
    steps:
    - script: echo Build Applications

- stage: Test
  displayName: "Run tests for apps"
  jobs:
  - job: run_tests
    pool:
      vmImage: 'Ubuntu 16.04'
    steps:
    - script: echo Run applications tests


- stage: Develop
  displayName: "Deploy to the Develop"
  jobs:
  - job: deploy_develop
    pool:
      vmImage: 'Ubuntu 16.04'
    steps:
    - script: echo create infrastructure
    - script: echo deploy develop



- stage: QA
  displayName: "Deploy to the QA"
  condition: eq(variables['Build.SourceBranch'], 'refs/heads/qa')
  jobs:
  - job: deploy_qa
    pool:
      vmImage: 'Ubuntu 16.04'
    steps:
    - script: echo create infrastructure
    - script: echo deploy qa

Zapisujemy stan definicji, klikając Save, a następnie klikamy Run, aby uruchomić proces.

W efekcie powinniśmy otrzymać następujący rezultat:

Jak widać, blok QA nie został wykonany, zgodnie z wytycznymi.

Status Skipped świadczy o pominięciu tego etapu, ponieważ źródłową gałęzią jest gałąź master, a nie qa.

Podsumowując, wszystko poszło zgodnie z planem.

Mutli-stage YAML Pipelines: zależności

Zależności pozwalają na budowanie potoków, w których proces zawiera układ zależnych od siebie etapów.

Aby nieco przybliżyć tę funkcję, rozpoczniemy od edycji potoku.

Na początek edytujemy w definicji potoku ostatni blok o nazwie QA zgodnie z poniższą konfiguracją:

- stage: QA1
  dependsOn: Develop
  displayName: "Deploy to the QA1"
  jobs:
  - job: deploy_qa
    pool:
      vmImage: 'Ubuntu 16.04'
    steps:
    - script: echo create infrastructure
    - script: echo deploy qa1

Zauważmy, że pojawił nam się nowy znacznik:

dependsOn: Develop

Powyższy zapis znacznika dependsOn mówi o tym, że blok QA1 jest zależny od bloku Develop i nie zostanie uruchomiony, dopóki blok Develop nie zostanie wykonany.

W następnym kroku dodamy do definicji kolejne dwa bloki z etapami:

- stage: QA2
  dependsOn: Develop
  displayName: "Deploy to the QA2"
  jobs:
  - job: deploy_qa
    pool:
      vmImage: 'Ubuntu 16.04'
    steps:
    - script: echo create infrastructure
    - script: echo deploy qa2

- stage: PreProd
  dependsOn:
  - QA1
  - QA2
  displayName: "Deploy to the Pre-Production"
  jobs:
  - job: deploy_develop
    pool:
      vmImage: 'Ubuntu 16.04'
    steps:
    - script: echo create infrastructure
    - script: echo deploy pre-production

Po wprowadzeniu zmian całość definicji potoku powinna wyglądać następująco:

trigger:
- master

stages:
- stage: Build
  displayName: "Build Applications"
  jobs:
  - job: run_build
    pool:
      vmImage: 'Ubuntu 16.04'
    steps:
    - script: echo Build Applications

- stage: Test
  displayName: "Run tests for apps"
  jobs:
  - job: run_tests
    pool:
      vmImage: 'Ubuntu 16.04'
    steps:
    - script: echo Run applications tests

- stage: Develop
  displayName: "Deploy to the Develop"
  jobs:
  - job: deploy_develop
    pool:
      vmImage: 'Ubuntu 16.04'
    steps:
    - script: echo create infrastructure
    - script: echo deploy develop

- stage: QA1
  dependsOn: Develop
  displayName: "Deploy to the QA1"
  jobs:
  - job: deploy_qa
    pool:
      vmImage: 'Ubuntu 16.04'
    steps:
    - script: echo create infrastructure
    - script: echo deploy qa1

- stage: QA2
  dependsOn: Develop
  displayName: "Deploy to the QA2"
  jobs:
  - job: deploy_qa
    pool:
      vmImage: 'Ubuntu 16.04'
    steps:
    - script: echo create infrastructure
    - script: echo deploy qa2

- stage: PreProd
  dependsOn:
  - QA1
  - QA2
  displayName: "Deploy to the Pre-Production"
  jobs:
  - job: deploy_develop
    pool:
      vmImage: 'Ubuntu 16.04'
    steps:
    - script: echo create infrastructure
    - script: echo deploy pre-production

Na koniec zapisujemy zmiany w definicji, klikając Save, a następnie klikamy Run, aby uruchomić proces.

Nasz potok powinien teraz wyglądać następująco:

Jak widzimy, bloki etapów QA1 oraz QA2 są zależne od bloku Develop, natomiast ostatni blok Pre-Prod, wykonujący wdrożenie na środowisku przedprodukcyjnym, jest zależny od bloków QA1 oraz QA2.

Dokonajmy teraz drobnej modyfikacji w bloku QA2, symulując niepowodzenie w wykonaniu potoku:

- stage: QA2
  dependsOn: Develop
  displayName: "Deploy to the QA2"
  jobs:
  - job: deploy_qa
    pool:
      vmImage: 'Ubuntu 16.04'
    steps:
    - script: exit 1

Po wprowadzeniu zmian całość definicji potoku powinna wyglądać następująco:

trigger:
- master

stages:
- stage: Build
  displayName: "Build Applications"
  jobs:
  - job: run_build
    pool:
      vmImage: 'Ubuntu 16.04'
    steps:
    - script: echo Build Applications

- stage: Test
  displayName: "Run tests for apps"
  jobs:
  - job: run_tests
    pool:
      vmImage: 'Ubuntu 16.04'
    steps:
    - script: echo Run applications tests

- stage: Develop
  displayName: "Deploy to the Develop"
  jobs:
  - job: deploy_develop
    pool:
      vmImage: 'Ubuntu 16.04'
    steps:
    - script: echo create infrastructure
    - script: echo deploy develop

- stage: QA1
  dependsOn: Develop
  displayName: "Deploy to the QA1"
  jobs:
  - job: deploy_qa
    pool:
      vmImage: 'Ubuntu 16.04'
    steps:
    - script: echo create infrastructure
    - script: echo deploy qa1

- stage: QA2
  dependsOn: Develop
  displayName: "Deploy to the QA2"
  jobs:
  - job: deploy_qa
    pool:
      vmImage: 'Ubuntu 16.04'
    steps:
    - script: exit 1

- stage: PreProd
  dependsOn:
  - QA1
  - QA2
  displayName: "Deploy to the Pre-Production"
  jobs:
  - job: deploy_develop
    pool:
      vmImage: 'Ubuntu 16.04'
    steps:
    - script: echo create infrastructure
    - script: echo deploy pre-production

Sprawdźmy, co się teraz stanie. Klikamy Save i Run, aby zapisać i uruchomić proces.

Jako wynik powinniśmy zobaczyć następujący rezultat:

W tym przypadku mechanizm ponownie zadziałał prawidłowo – blok Pre-Prod nie został uruchomiony, ponieważ jeden z etapów, od którego jest zależny, a mianowicie blok QA2, nie został ukończony poprawnie.

Podsumowanie

Azure DevOps nieustannie wzbogaca się o nowe możliwości. Obsługa multi-stage YAML Pipeline jest jedną z nich. Integracja procesów CI i CD w jednym potoku to krok w dobrym kierunku, który wyraźnie sugeruje, że implementacja Everything-as-Code w Azure DevOps jest już całkiem blisko.

AKTUALNOŚCI
13/06/20232 min.
AI w średniej firmie: Tworzenie przyszłości przy użyciu LLM.

Już 21 czerwca dowiesz się, jak możesz wykorzystać AI w Twojej firmie. Damian Mazurek i Piotr Kalinowski wprowadzą Cię w świat sztucznej inteligencji i LLM.

Zobacz wpis
AKTUALNOŚCI
14/02/20232 min
Chmurowisko łączy się z Software Mind

Przed nami nowy rozdział! Chmurowisko dokonało połączenia z polskim Software Mind – firmą, która od 20 lat tworzy rozwiązania przyczyniające się do sukcesu organizacji z całego świata…

Zobacz wpis
AKTUALNOŚCI
09/11/20225 min
Migracja systemu Dynamic Precision do Oracle Cloud

Grupa Dynamic Precision podjęła decyzję o unowocześnieniu swojej infrastruktury. Razem z Oracle Polska prowadzimy migrację aplikacji firmy do chmury OCI.

Zobacz wpis
AKTUALNOŚCI
AI w średniej firmie: Tworzenie przyszłości przy użyciu LLM.

Już 21 czerwca dowiesz się, jak możesz wykorzystać AI w Twojej firmie. Damian Mazurek i Piotr Kalinowski wprowadzą Cię w świat sztucznej inteligencji i LLM.

Zobacz wpis
Grafika przedstawiająca chmuręGrafika przedstawiająca chmurę

Zapisz się do naszego newslettera i
bądź z chmurami na bieżąco!

Zostaw nam swój e–mail a co miesiąc dostaniesz spis najważniejszych nowości
z chmur Azure, AWS i GCP, z krótkimi opisami i linkami.