다음을 통해 공유


환경의 Kubernetes 리소스

Azure DevOps Services | Azure DevOps Server | Azure DevOps Server 2022

이 문서에서는 배포를 사용하여 대상으로 지정할 수 있는 Azure Pipelines 환경에서 Kubernetes 리소스를 사용하는 방법에 대해 설명합니다. AKS(Azure Kubernetes Service) 또는 다른 클라우드 공급자에서 퍼블릭 또는 프라이빗 Kubernetes 클러스터에 연결할 수 있습니다.

환경 리소스 뷰는 Kubernetes 리소스 상태를 표시하고 파이프라인에 추적 가능성을 제공하고 다시 트리거 커밋으로 돌아갑니다. 동적 Kubernetes 환경 리소스를 만들어 병합하기 전에 끌어오기 요청을 검토할 수도 있습니다. 환경 리소스에 대한 자세한 내용은 YAML 파이프라인리소스 보안의 리소스를 참조하세요.

참고

프라이빗 AKS 클러스터는 공용 IP 주소를 통해 API 서버 엔드포인트를 노출하지 않으므로 클러스터의 가상 네트워크에 연결해야 합니다. 클러스터의 가상 네트워크에 액세스하거나 Managed DevOps 풀을 사용할 수 있는 가상 네트워크 내에서 자체 호스팅 에이전트를 설정할 수 있습니다. 자세한 내용은 프라이빗 클러스터에 연결하기 위한 옵션을 참조하세요.

Kubernetes 환경 리소스의 이점

환경의 Kubernetes 환경 리소스 및 리소스 뷰는 다음과 같은 이점을 제공합니다.

  • 파이프라인 추적 가능성. Kubernetes 매니페스트 배포 태스크는 파이프라인 추적 기능을 지원하는 주석을 추가합니다. 네임스페이스 내의 개체에 대한 업데이트를 담당하는 Azure DevOps 조직, 프로젝트 및 파이프라인을 볼 수 있습니다.

    Kubernetes 클러스터의 파이프라인 추적 가능성을 보여 주는 스크린샷

  • 리소스 상태 진단. 워크로드 상태 보기는 새 배포에서 도입된 실수 또는 회귀를 빠르게 디버그하는 방법입니다. 예를 들어, Pod 상태 정보는 imagePullSecrets가 구성되지 않아 ImagePullBackOff 오류가 발생하는 문제의 원인을 식별하는 데 도움이 될 수 있습니다.

    Kubernetes 배포에 대한 워크로드 상태 보기를 보여 주는 스크린샷

  • 앱을 검토합니다. 검토 앱은 Git 리포지토리의 모든 끌어오기 요청을 환경의 동적 Kubernetes 리소스에 배포하고 검토 앱에 연결되는 GitHub 주석을 게시합니다. 검토자는 변경 내용이 대상 분기에 병합되고 프로덕션에 배포되기 전에 PR 변경 내용이 다른 종속 서비스의 모양과 작동 방식을 확인할 수 있습니다.

    GitHub에서 Review 앱의 댓글을 보여 주는 스크린샷

AKS 리소스

AKS는 선택한 클러스터 및 네임스페이스에 ServiceAccount 를 만들고 사용자 환경의 Kubernetes 리소스를 지정된 네임스페이스에 매핑합니다. 환경 외부에서 Kubernetes 서비스 연결을 설정하는 방법에 대한 자세한 내용은 Kubernetes 서비스 연결을 참조하세요.

Kubernetes RBAC(역할 기반 액세스 제어) 사용 클러스터의 경우 서비스 계정의 범위를 선택한 네임스페이스로 제한하기 위해 RoleBinding도 생성됩니다. Kubernetes RBAC 사용 안 함 클러스터의 경우 만든 서비스 계정에는 네임스페이스에서 클러스터 전체 권한이 있습니다.

Azure Pipelines 환경에 AKS 리소스를 추가하려면 다음을 수행합니다.

  1. 파이프라인환경>의 환경 페이지에서 리소스 추가를 선택한 다음, Kubernetes를 선택합니다.

  2. 다음 화면에서 공급자에 대한 Azure Kubernetes Service를 선택한 다음, Azure 구독, AKS 클러스터 및 신규 또는 기존 네임스페이스를 선택합니다. 새 네임스페이스의 경우 네임스페이스 이름을 입력합니다.

  3. 유효성 검사 및 만들기를 선택합니다. 새 리소스는 배포 안 됨 텍스트와 함께 환경의 리소스 탭에 나타납니다.

    추가된 Kubernetes 리소스를 보여 주는 스크린샷

AKS가 아닌 Kubernetes 리소스

AKS가 아닌 클러스터의 Kubernetes 리소스를 네임스페이스에 매핑하려면 AKS가 아닌 공급자에 대한 기존 서비스 계정이 있어야 합니다.

Azure Pipelines 환경에 AKS Kubernetes가 아닌 리소스를 추가하려면 다음을 수행합니다.

  1. 파이프라인환경>의 환경 페이지에서 리소스 추가를 선택한 다음, Kubernetes를 선택합니다.

  2. 다음 화면에서 공급자에 대한 제네릭 공급자(기존 서비스 계정)선택합니다.

  3. 클러스터 자격 증명 아래에 클러스터 이름, 네임스페이스, 서버 URL비밀을 입력합니다.

    • 서버 URL을 얻으려면 로컬 셸에서 실행 kubectl config view --minify -o jsonpath={.clusters[0].cluster.server} 합니다.

    • 비밀을 얻으려면 다음을 수행합니다.

      1. 를 실행 kubectl get serviceAccounts <service-account-name> -n <namespace> -o=jsonpath={.secrets[*].name}하여 서비스 계정 비밀 이름을 가져옵니다.
      2. 이전 명령의 출력을 사용하여 실행 kubectl get secret <service-account-secret-name> -n <namespace> -o json 합니다.

      참고

      명령에서 결과를 얻지 못하면 get ServiceAccounts.

  4. 유효성 검사 및 만들기를 선택합니다.

파이프라인의 Kubernetes 리소스

AKS에 배포할 YAML 파이프라인을 만드는 가장 쉬운 방법은 Azure Kubernetes Services 템플릿에 배포를 시작하는 것입니다 . YAML 코드를 작성하거나 명시적 역할 바인딩을 수동으로 만들 필요가 없습니다. 생성된 파이프라인은 구성 설정에 따라 변수 및 기타 값을 설정하고 사용합니다.

검토 앱 사용

작업은 DeployPullRequest Git 리포지토리의 모든 끌어오기 요청을 환경의 동적 Kubernetes 리소스에 배포합니다. 파이프라인에 이 작업을 추가하려면 Azure Kubernetes Services에 배포 구성 양식에서 끌어오기 요청에 대한 검토 앱 흐름 사용 확인란을 선택합니다.

참고

이 작업을 기존 파이프라인에 추가하려면 일반 Kubernetes 환경 리소스를 지원하는 서비스 연결이 클러스터 관리자 자격 증명을 사용하도록 설정되어 있는지 확인합니다. 그렇지 않으면 검토 앱 네임스페이스에 대한 기본 서비스 계정에 대한 역할 바인딩을 만들어야 합니다.

검토 앱 리소스는 환경의 리소스 목록에서 검토로 레이블이 지정됩니다.

파이프라인 환경 목록의 검토 환경을 보여 주는 스크린샷

예제 파이프라인

다음 예제 파이프라인은 Azure Kubernetes Services에 배포 템플릿을 기반으로 합니다. 파이프라인은 먼저 이미지를 빌드하고 Azure Container Registry에 푸시합니다.

첫 번째 배포 작업은 main 브랜치에 대한 모든 커밋에 대해 실행되며, 환경의 일반적인 Kubernetes 리소스에 배포됩니다.

두 번째 작업은 PR이 main 분기에 생성되거나 업데이트될 때 실행되며, 요청에 따라 클러스터 내에서 생성하는 동적인 리뷰 앱 리소스에 대해 배포됩니다.

# Deploy to Azure Kubernetes Service
# Build and push image to Azure Container Registry; Deploy to Azure Kubernetes Service
# https://docs.microsoft.com/azure/devops/pipelines/languages/docker

trigger:
- main

resources:
- repo: self

variables:

  # Container registry service connection established during pipeline creation
  dockerRegistryServiceConnection: '12345' # Docker service connection identifier
  imageRepository: 'name-of-image-repository' # name of image repository
  containerRegistry: 'mycontainer.azurecr.io' # path to container registry
  dockerfilePath: '**/Dockerfile'
  tag: '$(Build.BuildId)'
  imagePullSecret: 'my-app-secret' # image pull secret

  # Agent VM image name
  vmImageName: 'ubuntu-latest'

  # Name of the new namespace being created to deploy the PR changes.
  k8sNamespaceForPR: 'review-app-$(System.PullRequest.PullRequestId)'

stages:
- stage: Build
  displayName: Build stage
  jobs:
  - job: Build
    displayName: Build
    pool:
      vmImage: $(vmImageName)
    steps:
    - task: Docker@2
      displayName: Build and push an image to container registry
      inputs:
        command: buildAndPush
        repository: $(imageRepository)
        dockerfile: $(dockerfilePath)
        containerRegistry: $(dockerRegistryServiceConnection)
        tags: |
          $(tag)

    - upload: manifests
      artifact: manifests

- stage: Production
  displayName: Deploy stage
  dependsOn: Build

  jobs:
  - deployment: Production
    condition: and(succeeded(), not(startsWith(variables['Build.SourceBranch'], 'refs/pull/')))
    displayName: Production
    pool:
      vmImage: $(vmImageName)
    environment: 'myenvironmentname.myresourcename'
    strategy:
      runOnce:
        deploy:
          steps:
          - task: KubernetesManifest@1
            displayName: Create imagePullSecret
            inputs:
              action: createSecret
              secretName: $(imagePullSecret)
              dockerRegistryEndpoint: $(dockerRegistryServiceConnection)

          - task: KubernetesManifest@1
            displayName: Deploy to Kubernetes cluster
            inputs:
              action: deploy
              manifests: |
                $(Pipeline.Workspace)/manifests/deployment.yml
                $(Pipeline.Workspace)/manifests/service.yml
              imagePullSecrets: |
                $(imagePullSecret)
              containers: |
                $(containerRegistry)/$(imageRepository):$(tag)

  - deployment: DeployPullRequest
    displayName: Deploy Pull request
    condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/pull/'))
    pool:
      vmImage: $(vmImageName)

    environment: 'myenvironmentname.$(k8sNamespaceForPR)'
    strategy:
      runOnce:
        deploy:
          steps:
          - reviewApp: default

          - task: Kubernetes@1
            displayName: 'Create a new namespace for the pull request'
            inputs:
              command: apply
              useConfigurationFile: true
              inline: '{ "kind": "Namespace", "apiVersion": "v1", "metadata": { "name": "$(k8sNamespaceForPR)" }}'

          - task: KubernetesManifest@1
            displayName: Create imagePullSecret
            inputs:
              action: createSecret
              secretName: $(imagePullSecret)
              namespace: $(k8sNamespaceForPR)
              dockerRegistryEndpoint: $(dockerRegistryServiceConnection)

          - task: KubernetesManifest@1
            displayName: Deploy to the new namespace in the Kubernetes cluster
            inputs:
              action: deploy
              namespace: $(k8sNamespaceForPR)
              manifests: |
                $(Pipeline.Workspace)/manifests/deployment.yml
                $(Pipeline.Workspace)/manifests/service.yml
              imagePullSecrets: |
                $(imagePullSecret)
              containers: |
                $(containerRegistry)/$(imageRepository):$(tag)

          - task: Kubernetes@1
            name: get
            displayName: 'Get services in the new namespace'
            continueOnError: true
            inputs:
              command: get
              namespace: $(k8sNamespaceForPR)
              arguments: svc
              outputFormat: jsonpath='http://{.items[0].status.loadBalancer.ingress[0].ip}:{.items[0].spec.ports[0].port}'

          # Get the IP of the deployed service and writing it to a variable for posting comment
          - script: |
              url="$(get.KubectlOutput)"
              message="Your review app has been deployed"
              if [ ! -z "$url" -a "$url" != "http://:" ]
              then
                message="${message} and is available at $url."
              fi
              echo "##vso[task.setvariable variable=GITHUB_COMMENT]$message"