Disclosure: This article may contain affiliate links. We may earn a commission if you purchase through these links, at no extra cost to you. We only recommend products we believe in.

Poznaj jak zbudować pipeline CI/CD na Azure DevOps. Poradnik krok po kroku: continuous integration, automatyzacja wdrożeń i dobre praktyki DevOps Azure.



Problem, który znasz z autopsji

Każdy zespół deweloperski, który choć raz wdrażał aplikację ręcznie o 3:00 w nocy przed release'em, wie, dlaczego automatyzacja to nie luksus — to konieczność. Według raportu DORA (DevOps Research and Assessment) z 2023 roku, organizacje z wysokim poziomem automatyzacji wdrożeń dostarczają oknet 208 razy szybciej niż te z niskim poziomem. Ryzyko awarii spada o 2-3 razy. Jeśli więc nadal klikasz "Publish" w Visual Studio i logujesz się ręcznie na serwer produkcyjny — ten artykuł jest dla Ciebie.

W Ciro Cloud regularnie wdrażamy pipeline DevOps na Azure dla klientów z sektora finansowego, e-commerce i logistyki. Poniżej dzielę się sprawdzoną metodologią, którą wypracowaliśmy na przestrzeni setek projektów.


Co to jest Azure DevOps i dlaczego warto go używać w chmurze?

Azure DevOps (wcześniej Visual Studio Team Services) to kompletna platforma SaaS od Microsoft do zarządzania cyklem życia aplikacji. Składa się z pięciu głównych usług:

  • Azure Repos — prywatne repozytoria Git (bez limitu w planie Basic)
  • Azure Pipelines — engine CI/CD z obsługą YAML i visual designers
  • Azure Boards — tracking zadań (Kanban, Scrum)
  • Azure Test Plans — testowanie manualne i automated
  • Azure Artifacts — zarządzanie pakietami (NuGet, npm, Maven)

Dlaczego chmura? Ponieważ Azure Pipelines oferuje bezpłatne build minutes: 1,800 minut miesięcznie na publiczne projekty i 500 minut na prywatne (w planie Free). Dla małych zespołów to często wystarczające, żeby zacząć bez żadnych kosztów operacyjnych.

Cennik Azure DevOps (stan na 2024):

  • Basic: ~6 USD/użytkownika/miesiąc — obejmuje repozytoria, Azure Pipelines (5 użytkowników Basic), Boards
  • Basic + Test Plans: ~52 USD/użytkownika/miesiąc
  • Visual Studio Enterprise subscribers: dostęp Basic w cenie subskrypcji

Architektura pipeline CI/CD na Azure DevOps

Zanim napiszesz pierwszą linię YAML, musisz zrozumieć architekturę. Typowy pipeline DevOps Azure składa się z trzech warstw:

  1. Source Stage — kod w Azure Repos (lub GitHub, Bitbucket)
  2. Build Stage — kompilacja, testy jednostkowe, statyczna analiza
  3. Release Stage — wdrożenie do środowisk (Dev, Staging, Production)

Każda warstwa działa na Azure Pipeline Agents. Microsoft oferuje dwa typy:

  • Microsoft-hosted agents — maszyny w Azure, zarządzane przez Microsoft. Są resetowane po każdym zadaniu. Idealne dla prostych buildów.
  • Self-hosted agents — instalowane na Twoich maszynach (VM, kontener). Utrzymujesz je sam. Dają większą kontrolę, cache Buildkite'a, i dostęp do prywatnej sieci. Koszt: tylko koszt infrastruktury.

Rekomendacja z praktyki: Jeśli budujesz aplikacje .NET Core lub Node.js na Windows — Microsoft-hosted wystarczy. Jeśli potrzebujesz specyficznych narzędzi (np. Android SDK, специфічних bibliotek Linux), rozważ self-hosted na Azure VM Scale Set. Kosztuje to około 40-60 USD miesięcznie za maszynę (Standard B2s), ale drastycznie przyspiesza buildy dzięki caching.


Krok po kroku: tworzenie pipeline'u CI/CD na Azure DevOps

Krok 1: Utwórz projekt w Azure DevOps

  1. Przejdź do dev.azure.com
  2. Kliknij "Create New Project"
  3. Wybierz "Git" jako Version Control
  4. Ustaw Visibility: Private (chyba że open source)
  5. Kliknij "Create"

Krok 2: Skonfiguruj Azure Repos

Możesz użyć wbudowanego Azure Repos lub połączyć zewnętrzny Git (GitHub, Bitbucket). Dla GitHub integracja jest native — wystarczy zainstalować Azure Pipelines app z GitHub Marketplace.

git init
git add .
git commit -m "Initial commit with Azure DevOps pipeline"
git remote add origin https://dev.azure.com/your-org/your-project/_git/repo
git push -u origin --all

Krok 3: Utwórz plik azure-pipelines.yml

To jest sedno Twojego pipeline'u. Umieść go w korzeniu repozytorium:

# azure-pipelines.yml
trigger:
  branches:
    include:
      - main
      - develop
  paths:
    include:
      - src/**
      - tests/**
      - azure-pipelines.yml

pr:
  branches:
    include:
      - main

pool:
  vmImage: 'ubuntu-latest'  # Microsoft-hosted, Ubuntu 22.04

variables:
  buildConfiguration: 'Release'
  dotnetVersion: '8.0.x'
  nodeVersion: '20.x'

stages:
  - stage: Build
    displayName: 'Build and Test'
    jobs:
      - job: BuildJob
        displayName: 'Build Application'
        steps:
          # .NET build
          - task: UseDotNet@2
            displayName: 'Use .NET $(dotnetVersion)'
            inputs:
              packageType: 'sdk'
              version: $(dotnetVersion)
          
          - script: |
              dotnet restore
              dotnet build --configuration $(buildConfiguration)
              dotnet test --configuration $(buildConfiguration) --verbosity normal
            displayName: 'Build and Test .NET'
          
          # Publish artifact
          - task: PublishBuildArtifacts@1
            displayName: 'Publish Artifact'
            inputs:
              pathtoPublish: '$(Build.ArtifactStagingDirectory)'
              artifactName: 'drop'
              publishLocation: 'Container'

  - stage: Deploy_Dev
    displayName: 'Deploy to Dev'
    dependsOn: Build
    condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
    jobs:
      - deployment: DeployDev
        displayName: 'Deploy to Azure App Service Dev'
        environment: 'dev'
        strategy:
          runOnce:
            deploy:
              steps:
                - task: AzureWebApp@1
                  displayName: 'Azure App Service Deploy: dev-api'
                  inputs:
                    azureSubscription: 'AzureServiceConnection-Dev'
                    appType: 'webApp'
                    appName: 'dev-cirocloud-api'
                    package: '$(Pipeline.Workspace)/drop/**/*.zip'
                    deploymentMethod: 'auto'

  - stage: Deploy_Prod
    displayName: 'Deploy to Production'
    dependsOn: Deploy_Dev
    condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
    jobs:
      - deployment: DeployProd
        displayName: 'Deploy to Azure App Service Production'
        environment: 'production'
        strategy:
          runOnce:
            deploy:
              steps:
                - task: AzureWebApp@1
                  displayName: 'Azure App Service Deploy: prod-api'
                  inputs:
                    azureSubscription: 'AzureServiceConnection-Prod'
                    appType: 'webApp'
                    appName: 'prod-cirocloud-api'
                    package: '$(Pipeline.Workspace)/drop/**/*.zip'
                    deploymentMethod: 'auto'

Krok 4: Skonfiguruj Service Connections

Aby Azure Pipelines mógł wdrażać do Azure, potrzebujesz Service Connection:

  1. Przejdź do Project SettingsService connections
  2. Kliknij New service connectionAzure Resource Manager
  3. Wybierz Service principal (automatic) — zalecane dla CI/CD
  4. Ustaw Scope level: Subscription lub Resource Group
  5. Nazwij połączenie (np. "AzureServiceConnection-Dev")

Uwaga bezpieczeństwa: Nigdy nie używaj Credentials użytkownika. Service principal z RBAC to podstawa. Najmniejsze uprawnienia: "Website Contributor" dla App Service, "Storage Blob Data Contributor" dla Blob Storage.

Krok 5: Skonfiguruj środowiska (Environments)

Environments w Azure DevOps to nie tylko tagging — to kontrola uprawnień i historia wdrożeń:

  1. Przejdź do PipelinesEnvironments
  2. Utwórz środowiska: "dev", "staging", "production"
  3. Dla "production" włącz Approvals and checks:
    • Approval — wymagaj akceptacji od co najmniej jednego revisera
    • Azure resource group deployment check — waliduj, że zasoby istnieją
    • Business hours gate — blokuj wdrożenia poza godzinami pracy (opcjonalne)

Automatyzacja wdrożeń — strategie zaawansowane

Wdrożenie typu Rolling vs. Canary vs. Blue-Green

W pliku YAML powyżej użyłem prostego "runOnce". Dla aplikacji krytycznych rozważ:

Canary Deployment (polecam dla mikroserwisów):

- stage: Deploy_Canary
  jobs:
    - deployment: DeployCanary
      strategy:
        canary:
          increments: 10  # 10% ruchu na nową wersję
          steps:
            - task: AzureAppServiceManage@0
              inputs:
                Action: 'Swap Slots'
                WebAppName: 'prod-cirocloud-api'
                SourceSlot: 'staging'

Blue-Green z Traffic Routing:
Azure App Service ma wbudowane Traffic Routing. W Azure Portal włącz "Production" i "Staging" slots, skonfiguruj DNS, a pipeline swapuje sloty atomowo. Czas przestoju: ~0 sekund.

Continuous Integration Cloud — build matrix

Dla aplikacji cross-platform (Windows/Linux/macOS), użyj matrix strategy:

jobs:
  - job: Build
    strategy:
      matrix:
        linux:
          imageName: 'ubuntu-latest'
        windows:
          imageName: 'windows-latest'
        macos:
          imageName: 'macOS-latest'
    pool:
      vmImage: $(imageName)
    steps:
      - script: ./build.sh

Ograniczenie: macOS build minutes kosztują więcej w Microsoft-hosted. MacOS-small pool daje 6 vCPU, 14 GB RAM za ~40 USD/10 godzin. Dla React Native lub iOS builds to jedyna opcja (bez Mac Mini na biurku).

Integracja z Azure Container Instances (ACI) dla testów

Zamiast buildować na VM, możesz wdrożyć testy do kontenera:

- task: Docker@2
  displayName: 'Build and push test image'
  inputs:
    containerRegistry: 'acr-cirocloud.azurecr.io'
    repository: 'integration-tests'
    command: 'buildAndPush'
    Dockerfile: 'tests/Dockerfile'

- task: AzureContainerApps@0
  displayName: 'Run integration tests in ACA'
  inputs:
    azureSubscription: 'AzureServiceConnection-Prod'
    containerAppEnvironment: 'test-env'
    containerAppName: 'integration-tests-runner'
    imageToDeploy: 'acr-cirocloud.azurecr.io/integration-tests:$(Build.BuildId)'

Typowe problemy i jak ich unikać

Problem 1: Build minutes timeout

Symptom: "The job has timed out after 60 minutes."
Rozwiązanie:

  • Włącz caching dla NuGet/npm packages:
- task: Cache@2
  inputs:
    key: 'npm | $(Agent.OS) | package-lock.json'
    path: '$(Pipeline.Workspace)/.npm'
    restoreKeys: |
      npm | $(Agent.OS)
  • Użyj parallel jobs (dostępne w planie paid)
  • Rozważ self-hosted agents z szybszym dyskiem (Premium SSD zamiast Standard)

Problem 2: Secrets w pipeline'u

Nigdy nie hardcoduj haseł w YAML. Używaj:

  • Azure Key Vault integration:
- task: AzureKeyVault@2
  inputs:
    azureSubscription: 'AzureServiceConnection-Prod'
    KeyVaultName: 'kv-cirocloud-prod'
    SecretsFilter: '*'
    RunAsPreJob: true
  • Variable groups z "Lock" option w Library

Problem 3: Wdrożenie na niewłaściwe środowisko

Symptom: Kod z brancha "feature/fix-bug" wdrożył się na production.
Rozwiązanie:

  • Filtry w trigger i condition (jak w przykładzie YAML powyżej)
  • Environment-level approvals — wymuszają manual review przed Prod
  • Branch policies — wymagaj PR reviews i status checks przed merge do main

Problem 4: Flaky tests blokują pipeline

Rozwiązanie:

  • Oddziel testy unitowe (szybkie, uruchamiane w Build stage) od integration tests (w osobnym, opcjonalnym stage)
  • Używaj continueOnError: true z raportowaniem:
- script: npm test
  continueOnError: true
  displayName: 'Run tests (non-blocking)'
  • Monitoruj flaky tests ratio — powyżej 2% to czerwona flaga

Integracje i rozszerzenia warte uwagi

Z GitHub Marketplace możesz rozszerzyć Azure Pipelines o:

  • SonarCloud — static code analysis (bezpłatny dla open source, ~100 EUR/miesiąc dla private)
  • Snyk Security Scan — vulnerability scanning w kontenerach (~$250/miesiąc za zespół)
  • LaunchDarkly — feature flags bez redeploya
  • Slack notifications — integracja z kanałami DevOps

Podsumowanie i kolejne kroki

Pipeline CI/CD na Azure DevOps to nie jednorazowa konfiguracja — to ewoluujący system. Zacznij od prostego build → deploy pipeline'u (jak pokazano powyżej), a potem iteracyjnie dodawaj:

  1. Testy automatyczne (unit, integration, E2E)
  2. Security scanning (SAST, DAST)
  3. Infrastructure as Code (Terraform + Azure DevOps pipelines)
  4. Monitoring i alerting (Application Insights integration)
  5. Release gates (automatyczne rollback na podstawie metryk)

Jeśli potrzebujesz pomocy we wdrożeniu pipeline'u DevOps Azure w swojej organizacji — sprawdź nasze usługi na Ciro Cloud lub zostaw komentarz. Chętnie pomogę dobrać architekturę do specyfiki Twojego projektu.


Źródła i further reading:

Weekly cloud insights — free

Practical guides on cloud costs, security and strategy. No spam, ever.

Comments

Leave a comment