定义容器作业 (YAML)

Azure DevOps Services | Azure DevOps Server 2022 - Azure DevOps Server 2019

默认情况下,作业在安装了代理的主机上运行。 这很方便,通常非常适合刚刚开始采用 Azure Pipelines 的项目。 随着时间的推移,你可能发现自己需要对运行任务的上下文拥有更多控制。 YAML 管道为这种控制级别提供容器作业。

在 Linux 和 Windows 代理上,作业可以在主机上容器中运行。 (在 macOS 和 Red Hat Enterprise Linux 6 上,容器作业不可用。)容器提供与主机的隔离,并允许固定特定版本的工具和依赖项。 主机作业需要维护的初始设置和基础结构较少。

容器提供对主机操作系统的轻型抽象。 可以选择生成所需的操作系统、工具和依赖项的精确版本。 在管道中指定容器时,代理将首先提取并启动容器。 然后,作业的每个步骤都将在容器中运行。 不能有嵌套容器。 当代理已在容器中运行时,不支持容器。

如果需要在单个步骤级别进行精细控制,则步骤目标允许你为每个步骤选择容器或主机。

要求

基于 Linux 的容器

Azure Pipelines 系统需要在基于 Linux 的容器中满足以下要求:

  • Bash
  • 基于 glibc
  • 可以运行代理提供的 Node.js
  • 不定义 ENTRYPOINT
  • USER 无需 sudo 即可访问 groupadd 和其他特权命令

对于代理主机,需满足以下要求:

  • 确保已安装 Docker
  • 代理必须具有访问 Docker 守护程序的权限

确保容器中的每个工具都可用。 Docker Hub 上提供的一些精简容器(尤其是基于 Alpine Linux 的容器)不满足这些最低要求。 包含 ENTRYPOINT 的容器可能无法正常工作,因为 Azure Pipelines 将分别对等待容器和一系列命令执行 docker createdocker exec,并希望容器始终处于启动和运行状态。

注意

对于基于 Windows 的 Linux 容器,必须预安装 Node.js。

Windows 容器

Azure Pipelines 还可以运行 Windows 容器。 需要 Windows Server 版本 1803 或更高版本。 必须安装 Docker。 请确保管道代理有权访问 Docker 守护程序。

Windows 容器必须支持运行 Node.js。 基本 Windows Nano Server 容器缺少运行 Node 所需的依赖项。

托管代理

windows-2019ubuntu-* 映像支持运行容器。 macOS 映像不支持运行容器。

单个作业

一个简单的示例:

pool:
  vmImage: 'ubuntu-latest'

container: ubuntu:18.04

steps:
- script: printenv

这会告知系统从 Docker Hub 提取标记为 18.04ubuntu 映像,然后启动容器。 当 printenv 命令运行时,会在 ubuntu:18.04 容器内发生。

Windows 示例:

pool:
  vmImage: 'windows-2019'

container: mcr.microsoft.com/windows/servercore:ltsc2019

steps:
- script: set

注意

Windows 要求主机和容器的内核版本匹配。 由于此示例使用 Windows 2019 映像,因此我们将对容器使用 2019 标记。

多个作业

容器还可用于在多个作业中运行相同的步骤。 在以下示例中,将在多个版本的 Ubuntu Linux 中运行相同的步骤。 (我们不必提及 jobs 关键字,因为只定义了单个作业。)

pool:
  vmImage: 'ubuntu-latest'

strategy:
  matrix:
    ubuntu16:
      containerImage: ubuntu:16.04
    ubuntu18:
      containerImage: ubuntu:18.04
    ubuntu20:
      containerImage: ubuntu:20.04

container: $[ variables['containerImage'] ]

steps:
- script: printenv

终结点

容器可以托管在公共 Docker Hub 注册表以外的注册表上。 若要在 Azure 容器注册表或其他专用容器注册表(包括专用 Docker Hub 注册表)上托管映像,请向专用注册表添加服务连接。 然后即可在容器规范中引用它:

container:
  image: registry:ubuntu1804
  endpoint: private_dockerhub_connection

steps:
- script: echo hello

container:
  image: myprivate.azurecr.io/windowsservercore:1803
  endpoint: my_acr_connection

steps:
- script: echo hello

其他容器注册表也可能正常工作。 Amazon ECR 目前无法正常运作,因为需要其他客户端工具才能将 AWS 凭据转换为 Docker 可用于进行身份验证的某种格式。

注意

代理的 Red Hat Enterprise Linux 6 版本不会运行容器作业。 选择其他 Linux 风格,例如 Red Hat Enterprise Linux 7 或更高版本。

选项

如果需要控制容器启动,可以指定 options

container:
  image: ubuntu:18.04
  options: --hostname container-test --ip 192.168.0.1

steps:
- script: echo hello

运行 docker create --help 将为你提供可传递给 Docker 调用的选项列表。 并非所有这些选项都能保证与 Azure DevOps 配合使用。 首先检查是否可以使用容器属性来实现相同的目标。 有关详细信息,请参阅 YAML 架构中的 resources.containers.containerdocker create 命令参考。

可重用容器定义

在以下示例中,容器在“资源”部分中进行定义。 稍后通过引用其分配的别名来引用每个容器。 (为明确起见,我们在此处显式列出了 jobs 关键字。)

resources:
  containers:
  - container: u16
    image: ubuntu:16.04

  - container: u18
    image: ubuntu:18.04

  - container: u20
    image: ubuntu:20.04

jobs:
- job: RunInContainer
  pool:
    vmImage: 'ubuntu-latest'

  strategy:
    matrix:
      ubuntu16:
        containerResource: u16
      ubuntu18:
        containerResource: u18
      ubuntu20:
        containerResource: u20

  container: $[ variables['containerResource'] ]

  steps:
  - script: printenv

非基于 glibc 的容器

Azure Pipelines 代理提供 Node.js 副本,这是运行任务和脚本所必需的。 若要了解托管代理的 Node.js 版本,请参阅 Microsoft 托管代理。 Node.js 版本是针对托管云中使用的 C 运行时(通常为 glibc)编译的。 Linux 的某些变体使用其他 C 运行时。 例如,Alpine Linux 使用 musl。

如果要使用非基于 glibc 的容器作为作业容器,则需要自行安排一些事情。 首先,必须提供自己的 Node.js 副本。 其次,必须向映像添加标签,告知代理在何处查找 Node.js 二进制文件。 最后,库存 Alpine 未附带 Azure Pipelines 依赖的其他依赖项:bash、sudo、which 和 groupadd。

自带 Node.js

你负责向容器添加 Node 二进制文件。 节点 18 是一个安全的选择。 可以从 node:18-alpine 映像开始。

告知代理有关 Node.js 的信息

代理将读取容器标签“com.azure.dev.pipelines.handler.node.path”。 如果此标签存在,则它必须是指向 Node.js 二进制文件的路径。 例如,在基于 node:18-alpine 的映像中,将以下行添加到 Dockerfile:

LABEL "com.azure.dev.pipelines.agent.handler.node.path"="/usr/local/bin/node"

添加要求

Azure Pipelines 假定基于 Bash 的系统安装了常见的管理包。 需要特别指出的是,Alpine Linux 未附带所需的多个包。 安装 bashsudoshadow 将满足基本需求。

RUN apk add bash sudo shadow

如果你依赖于任何内置任务或市场任务,则还需要提供它们所需的二进制文件。

Dockerfile 的完整示例

FROM node:10-alpine

RUN apk add --no-cache --virtual .pipeline-deps readline linux-pam \
  && apk add bash sudo shadow \
  && apk del .pipeline-deps

LABEL "com.azure.dev.pipelines.agent.handler.node.path"="/usr/local/bin/node"

CMD [ "node" ]

单个托管代理上具有代理池的多个作业

容器作业使用基础主机代理 Docker config.json 进行映像注册表授权,该授权在 Docker 注册表容器初始化结束时注销。 后续注册表映像拉取授权可能会因“未经授权的身份验证”而被拒绝,因为在系统中注册用于身份验证的 Docker config.json 文件已被并行运行的某个其他容器作业注销。

解决方案是设置特定于托管代理上运行的每个代理池服务的 Docker 环境变量 DOCKER_CONFIG。 导出每个代理池的 runsvc.sh 脚本中的 DOCKER_CONFIG

#insert anything to set up env when running as a service
export DOCKER_CONFIG=./.docker