YAML 管道中的容器作业

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

本文介绍了 Azure Pipelines 中的容器作业。 容器是主机作系统中的轻型抽象,提供在特定环境中运行作业所需的所有必要元素。

默认情况下,Azure Pipelines 作业 直接在安装在主机上 的代理 上运行。 托管代理作业很方便,不需要初始设置或基础结构维护,并且非常适合基本项目。 若要更好地控制任务上下文,可以在容器中定义和运行管道作业,以获取所需的作系统、工具和依赖项的确切版本。

对于容器作业,代理首先提取并启动容器,然后在容器中运行作业的每个步骤。 如果需要对各个生成步骤进行精细控制,可以使用 步骤目标 为每个步骤选择容器或主机。

容器作业的要求

  • 基于 YAML 的管道。 经典管道不支持容器作业。
  • Windows 或 Ubuntu 托管代理。 MacOS 代理不支持容器。 若要使用非 Ubuntu Linux 代理,请参阅 基于 Nonglibc 的容器
  • 在代理上安装的 Docker,有权访问 Docker 守护程序。
  • 直接在主机上运行的代理,尚未在容器中运行。 不支持嵌套容器。

基于 Linux 的容器还具有以下要求:

  • Bash 已安装。
  • 基于 GNU C 库 (glibc)。 Nonglibc 容器需要添加设置。 有关详细信息,请参阅 基于 Nonglibc 的容器
  • ENTRYPOINT。 具有容器 ENTRYPOINT 的容器可能无法正常工作,因为 docker exec 希望容器始终运行。
  • USER 提供对 groupadd 其他特权命令的访问权限,而无需使用 sudo
  • 能够运行代理提供的 Node.js。

    注意

    必须为 Windows 主机上的 Linux 容器预安装 Node.js。

Docker Hub 上的一些精简版容器,尤其是基于 Alpine Linux 的容器,并不满足这些要求。 有关详细信息,请参阅 基于 Nonglibc 的容器

单个作业

以下示例定义 Windows 或 Linux 单作业容器。

此示例告知系统从 ubuntu提取标记18.04映像,然后启动容器。 printenv 命令在 ubuntu:18.04 容器内运行。

pool:
  vmImage: 'ubuntu-latest'

container: ubuntu:18.04

steps:
- script: printenv

多个作业

可以使用容器在多个作业中运行同一个步骤。 下面的示例在多个版本的 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 配置文件进行映像注册表授权。 此文件会在 Docker 注册表容器初始化结束时注销。

如果在代理上并行运行的另一个作业已注销 Docker 配置文件,则容器作业的注册表映像拉取可能会被拒绝进行未经授权的身份验证。 解决方案是为托管代理上运行的每个代理池设置一个调用 DOCKER_CONFIG 的 Docker 环境变量。

导出每个代理池的 DOCKER_CONFIG 脚本中的 ,如下所示:

export DOCKER_CONFIG=./.docker

启动选项

可以使用该 options 属性指定用于容器启动的选项。

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

steps:
- script: echo hello

运行 docker create --help 以获取可传递给 Docker 调用的选项列表。 并非所有这些选项都可以与 Azure Pipelines 配合使用。 首先检查是否可以将属性用于同一 container 目的。

有关详细信息,请参阅 Azure Pipelines 的 YAML 架构参考中的 docker 容器创建命令引用和 resources.containers.container 定义。

可重用容器定义

以下 YAML 示例定义节中的 resources 容器,然后通过分配的别名引用它们。 关键字 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

服务终结点

可以在公共 Docker 中心以外的注册表上托管容器。 要在 Azure 容器注册表或其他私有容器注册表(包括专用 Docker Hub 注册表)上托管映像,请添加服务连接以访问注册表。 然后,可以在容器定义中引用终结点。

专用 Docker Hub 连接:

container:
  image: registry:ubuntu1804
  endpoint: private_dockerhub_connection

Azure 容器注册表连接:

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

注意

Azure Pipelines 无法为 Amazon Elastic Container Registry (ECR)设置服务连接,因为 Amazon ECR 要求其他客户端工具将 Amazon Web Services (AWS) 凭据转换为可用于 Docker 身份验证。

基于 Nonglibc 的容器

托管的 Azure Pipelines 代理提供 Node.js,这是运行任务和脚本所必需的。 Node.js 版本针对托管云中使用的 C 运行时进行编译,通常 glibc。 有些 Linux 变体使用其他 C 语言运行时。 例如,Alpine Linux 使用 musl。 有关详细信息,请参阅 Microsoft托管代理

如果要在管道中使用基于非glibc 的容器,则必须:

  • 提供自己的 Node.js 副本。
  • 向图像添加一个标签,指向 Node.js 二进制文件的位置。
  • bash提供、sudowhichgroupadd Azure Pipelines 依赖项。

提供自己的 Node.js

如果使用基于非glibc 的容器,则必须将 Node 二进制文件添加到容器。 Node.js 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:18-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" ]