环境中的 Kubernetes 资源

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

本文介绍如何在 Azure Pipelines 环境中 使用 Kubernetes 资源,这些资源可以针对部署。 可以连接到 Azure Kubernetes 服务(AKS)或其他云提供商中的公共或专用 Kubernetes 群集。

环境资源视图显示 Kubernetes 资源状态,并提供管道的可跟踪性,并返回到触发提交。 还可以创建动态 Kubernetes 环境资源,以在合并之前查看拉取请求。 有关环境资源的详细信息,请参阅 YAML 管道中的资源 和资源 安全性

注意

专用 AKS 群集不会通过公共 IP 地址公开其 API 服务器终结点,因此必须连接到群集的虚拟网络。 可在允许访问群集虚拟网络的虚拟网络中设置自托管代理,或使用托管 DevOps 池。 有关详细信息,请参阅 用于连接到专用群集的选项

Kubernetes 环境资源优势

Kubernetes 环境中的资源和资源视图提供以下优势:

  • 管道可跟踪性Kubernetes 清单部署任务添加了支持管道可跟踪性的注释。 可以看到 Azure DevOps 组织、项目和管道负责更新命名空间中的对象。

    显示 Kubernetes 群集的管道可跟踪性的屏幕截图。

  • 资源运行状况诊断。 工作负荷状态视图是调试新部署引入的错误或回归的快速方法。 例如,Pod 状态信息可以帮助你确定问题的原因,比如未配置的imagePullSecrets导致ImagePullBackOff错误。

    显示 Kubernetes 部署的工作负荷状态视图的屏幕截图。

  • 评价应用。 评审应用将 Git 存储库中的每个拉取请求部署到环境中的动态 Kubernetes 资源,并发布 GitHub 评论链接至评审应用。 审阅者可以在将更改合并到目标分支及部署到生产环境之前,查看 PR 更改的外观与功能,并与其他依赖服务配合工作。

    显示 GitHub 中的“审阅应用注释”的屏幕截图。

AKS 资源

AKS 在所选群集和命名空间中创建 ServiceAccount ,并将环境中的 Kubernetes 资源映射到指定的命名空间。 有关在环境外部设置 Kubernetes 服务连接的信息,请参阅 Kubernetes 服务连接

对于启用了 Kubernetes 角色的访问控制(RBAC)群集,还将创建 RoleBinding 以将服务帐户的范围限制为所选命名空间。 对于禁用 Kubernetes RBAC 的群集,创建的服务帐户具有跨命名空间的群集范围权限。

若要将 AKS 资源添加到 Azure Pipelines 环境,请执行以下作:

  1. “管道>环境”下的“环境”页上,选择“ 添加资源 ”,然后选择 “Kubernetes”。

  2. 在下一个屏幕上,为提供程序选择Azure Kubernetes 服务,然后选择你的Azure 订阅、AKS群集,以及新的或现有的命名空间。 对于新命名空间,请输入命名空间名称。

  3. 选择 “验证并创建”。 新资源显示在环境的 “资源 ”选项卡上,文本 “永不部署”。

    显示已添加的 Kubernetes 资源的屏幕截图。

非 AKS Kubernetes 资源

若要将 Kubernetes 资源从非 AKS 群集映射到命名空间,需要具有非 AKS 提供程序的现有服务帐户。

若要将非 AKS Kubernetes 资源添加到 Azure Pipelines 环境,请执行以下作:

  1. “管道>环境”下的“环境”页上,选择“ 添加资源 ”,然后选择 “Kubernetes”。

  2. 在下一个屏幕上,为提供方选择通用提供方(现有服务帐户)

  3. 群集凭据下,输入 群集名称命名空间服务器 URL机密

    • 若要获取服务器 URL,请在本地 shell 中运行 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 没有结果,请参阅 手动为 ServiceAccount 创建生存期较长的 API 令牌

  4. 选择 “验证并创建”。

管道中的 Kubernetes 资源

创建要部署到 AKS 的 YAML 管道的最简单方法是从 “部署到 Azure Kubernetes 服务 ”模板开始。 无需编写 YAML 代码或手动创建显式角色绑定。 根据您的配置设置,生成的流水线集会设置和使用变量及其他值。

使用评论应用

DeployPullRequest 作业将 Git 存储库中的每个拉取请求部署到环境中的动态 Kubernetes 资源。 若要将此作业添加到管道,请选中“部署到 Azure Kubernetes 服务配置”窗体中“为拉取请求启用评审应用流”复选框。

注意

若要将此作业添加到现有管道,请确保支持常规 Kubernetes 环境资源的服务连接设置为 “使用群集管理员凭据”。 否则,必须为底层服务账户创建角色绑定,以便审查应用程序命名空间。

评审应用资源在环境的“资源”列表中标记为“审阅”。

显示管道环境列表中“评审”环境的屏幕截图。

示例管道

以下示例管道基于 “部署到 Azure Kubernetes 服务 ”模板。 管道首先生成并将映像推送到 Azure 容器注册表。

然后,第一个部署作业针对分支main的任何提交运行,并针对环境中的常规 Kubernetes 资源进行部署。

第二个作业在创建或更新到 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"