环境 - Kubernetes 资源

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

Kubernetes 资源视图显示映射到资源的命名空间中对象状态。 资源视图还覆盖管道可跟踪性,以便你可以从 Kubernetes 对象回溯到管道,然后回溯到提交。

环境中使用 Kubernetes 资源以 Kubernetes 群集为目标进行部署。 使用管道从任何其他云提供商部署到Azure Kubernetes 服务 (AKS) 和群集。

可以将 Kubernetes 资源与公共或专用群集一起使用。 有关资源工作原理的详细信息,请参阅 YAML 中的资源以及 资源的安全性。

注意

如果使用专用 AKS 群集,请确保连接到群集的虚拟网络,因为 API 服务器终结点不会通过公共 IP 地址公开。

Azure Pipelines 建议在有权访问群集虚拟网络的 VNET 中设置自承载代理。 有关详细信息,请参阅 用于连接到专用群集 的选项。

概述

请了解在环境中使用 Kubernetes 资源视图的以下优点:

  • 管道可跟踪性 - 用于部署的 Kubernetes 清单任务会添加更多注释,以显示资源视图中的管道可跟踪性。 管道可跟踪性有助于识别负责对命名空间中的对象进行更新的原始 Azure DevOps 组织、项目和管道。

    管道可跟踪性

  • 诊断资源运行状况 - 工作负荷状态可用于快速调试新部署引入的错误或回归。 例如,对于导致 ImagePullBackOff 错误的未配置的 imagePullSecret,Pod 状态信息可以帮助你确定问题的根本原因。

    ImagePullBackOff

  • 审核应用 - 审核应用的工作原理是将 Git 存储库中的每个拉取请求部署到环境中的动态 Kubernetes 资源。 审阅者可以在将这些更改合并到目标分支并部署到生产环境之前,查看这些更改的外观以及如何使用其他依赖服务。

使用 Azure Kubernetes 服务

使用 Azure Kubernetes 服务 (AKS) 时,会在所选群集和命名空间中创建 ServiceAccount。 对于已启用 Kubernetes RBAC 的群集,还会创建 RoleBinding,以将创建的服务帐户的范围限制为所选命名空间。 对于禁用了 Kubernetes RBAC 的集群,创建的 ServiceAccount 具有集群范围的权限(跨命名空间)。

添加 AKS Kubernetes 资源

  1. 在环境详细信息页中,选择“添加资源”,然后选择“Kubernetes”。

  2. 在“提供程序”下拉列表中选择“Azure Kubernetes 服务”。

  3. 选择 Azure 订阅、群集和命名空间(新的/现有)。

  4. 选择“验证并创建”以创建 Kubernetes 资源。

  5. 验证你是否看到适用于你的环境的群集。 如果尚未将代码部署到群集,则会看到文本“从未部署过”。

    添加 Kubernetes 群集。

使用现有的服务帐户

Azure Kubernetes 服务将环境里的 Kubernetes 资源映射到命名空间。

有关在环境外设置 Kubernetes 服务连接的更多信息,请参阅服务连接中的 Kubernetes 服务连接一节。

提示

使用通用提供程序(现有服务帐户)将 Kubernetes 资源映射到非 AKS 集群中的命名空间。

添加非 AKS Kubernetes 资源

  1. 在环境详细信息页中,选择“添加资源”,然后选择“Kubernetes”。

  2. 为你的提供程序选择“通用提供程序(现有服务帐户)”。

  3. 添加群集名称和命名空间值。

  4. 添加服务器 URL。 可以使用以下命令获取 URL:

    kubectl config view --minify -o 'jsonpath={.clusters[0].cluster.server}'
    
  5. 如何获取机密对象。

    Kubernetes 1.22+

    service-account-name 替换为你的帐户名称。

    kubectl get secret -n <namespace>  -o jsonpath='{.items[?(@.metadata.annotations.kubernetes\.io/service-account\.name==\"service-account-name\")]}'
    

    如果未获取任何内容,请参阅手动为 ServiceAccount 创建生存期较长的 API 令牌

    Kubernetes 1.22 及更低版本:

    1. 查找服务帐户机密名称
    kubectl get serviceAccounts <service-account-name> -n <namespace> -o 'jsonpath={.secrets[*].name}'
    
    1. 在此命令中,将 <service-account-secret-name> 替换为上个命令中的值
    kubectl get secret <service-account-secret-name> -n <namespace> -o json
    
  6. 使用上一步的输出获取机密对象。

    kubectl get secret <service-account-secret-name> -n <namespace> -o json
    
  7. 复制以 JSON 格式提取的机密对象并粘贴到“机密”字段中。

  8. 选择“验证并创建”以创建 Kubernetes 资源。

在管道中引用 Kubernetes 资源

如果使用 Azure Kubernetes 服务并生成 YAML 管道,则配置管道的最简单方法是使用模板。 连接到存储库并选择以下两个 Kubernetes 服务选项之一:

模板允许你设置审核应用,而无需从头开始编写 YAML 代码,也不必手动创建显式角色绑定。

Kubernetes 模板选项。

设置审阅应用

在以下示例中,第一个部署作业针对非 PR 分支运行,并针对环境下的常规 Kubernetes 资源执行部署。 第二个作业仅针对 PR 分支运行,并针对按需生成的审查应用资源(Kubernetes 集群内的命名空间)进行部署。 在环境的资源列表视图中,资源被标记为“审核”。 定义要在管道中使用的变量。 如果你使用“部署到 Azure Kubernetes 服务”模板,则会为你定义这些变量。

# Build and push image to Azure Container Registry; Deploy to Azure Kubernetes Service
trigger:
- main

resources:
- repo: self

variables:

  # Container registry service connection established during pipeline creation
  dockerRegistryServiceConnection: '12345' # Docker service connection identifier
  envName: 'myEnv' # name of your environment
  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: 
      name: $(envName).$(resourceName)
      resourceType: Kubernetes 
    strategy:
      runOnce:
        deploy:
          steps:
          - task: KubernetesManifest@0
            displayName: Create imagePullSecret
            inputs:
              action: createSecret
              secretName: $(imagePullSecret)
              dockerRegistryEndpoint: $(dockerRegistryServiceConnection)

          - task: KubernetesManifest@0
            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: 
      name: $(envName).$(resourceName)
      resourceType: Kubernetes
    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@0
            displayName: Create imagePullSecret
            inputs:
              action: createSecret
              secretName: $(imagePullSecret)
              namespace: $(k8sNamespaceForPR)
              dockerRegistryEndpoint: $(dockerRegistryServiceConnection)

          - task: KubernetesManifest@0
            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}'

          # Getting 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.<br><br>[Learn More](https://aka.ms/testwithreviewapps) about how to test and provide feedback for the app."
              fi
              echo "##vso[task.setvariable variable=GITHUB_COMMENT]$message"

要在现有管道中使用此作业,必须将支持常规 Kubernetes 环境资源的服务连接修改为“使用集群管理员凭据”。 否则,必须为“审核应用”命名空间的基础服务帐户创建角色绑定。

后续步骤