YAML パイプライン内のコンテナー ジョブ

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

この記事では、Azure Pipelines のコンテナー ジョブについて説明します。

既定では、Azure Pipelines jobs エージェントはインストールされているホスト マシン上で直接実行されます。 ホストされるエージェント ジョブは便利で、初期セットアップと保守のためのインフラストラクチャはほとんど必要なく、基本的なプロジェクトに適しています。

タスク コンテキストをより詳細に制御する場合は、コンテナーでジョブを定義して実行できます。 コンテナーは、ホストからの分離を実現する、ホスト オペレーティング システム上の軽量な抽象化です。 コンテナー内でジョブを実行する場合、ビルドに必要なオペレーティング システム、ツール、依存関係の正確なバージョンを選択できます。

Linux および Windows エージェント は、ホストまたはコンテナーでパイプライン ジョブを直接実行できます。 コンテナー ジョブは macOS では使用できません。

コンテナー ジョブの場合、エージェントは最初にコンテナーをフェッチして開始します。 その後、ジョブの各ステップがコンテナー内で稼働します。

個々のビルド ステップ レベルできめ細かな制御が必要な場合は、ステップ ターゲットを使用すると、各ステップのコンテナーまたはホストを選択できます。

前提条件

  • YAML パイプラインを使用します。 クラシック パイプラインでは、コンテナー ジョブはサポートされていません。
  • ホストされている Windows または Ubuntu エージェントを使用します。 windows-*ubuntu-* のエージェントのみがコンテナーの実行をサポートします。 macos-* エージェントは、コンテナーの実行をサポートしていません。
  • エージェントはコンテナー ジョブ用に設定されています。
    • Windows および Linux エージェントには Docker がインストールされていて、Docker デーモンにアクセスするためのアクセス許可が必要です。
    • エージェントがコンテナー内で既に稼働している場合、コンテナーはサポートされません。 入れ子になったコンテナーを使用することはできません。

追加のコンテナー要件

Linux ベースのコンテナーには、次の要件があります。 回避策については、「Nonglibc ベースのコンテナー」を参照してください。

  • Bash がインストールされている
  • GNU C ライブラリ (glibc) ベース
  • ENTRYPOINT なし
  • sudo を使用せずに、USER やその他の特権コマンドへのアクセスを groupadd に提供する
  • (エージェントが提供する) Node.js を実行できる

    Note

    Windows ホスト上の Linux コンテナーには、Node.js が事前にインストールされている必要があります。

Docker Hub で利用できる一部の簡素化されたコンテナ、特に Alpine Linux ベースのコンテナは、これらの要件を満たしていません。 Azure Pipelines docker createdocker exec はコンテナーが常に稼働していることを想定しているため、ENTRYPOINT を持つコンテナーが機能しない可能性があります。

単一ジョブの例

次の例では、1 つのジョブの Windows または Linux コンテナーを定義します。

次の簡単な例では、Linux コンテナーを定義します。

pool:
  vmImage: 'ubuntu-latest'

container: ubuntu:18.04

steps:
- script: printenv

前の例では、Docker Hub からタグ付けされた ubuntu イメージ 18.04 をフェッチし、コンテナーを起動するようにシステムに指示します。 printenv コマンドは ubuntu:18.04 コンテナー内で実行されます。

複数のジョブ

コンテナーを使用して、複数のジョブで同じ手順を実行できます。 次の例では、同じ手順が複数のバージョンの Ubuntu Linux で稼働します。 1 つのジョブのみが定義されているため、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

1 つのホステッド エージェント上のエージェント プールに対する複数のジョブ

コンテナー ジョブは、基になるホスト エージェントの Docker 構成ファイルを使用して、イメージ レジストリの承認を行います。 このファイルは、Docker レジストリ コンテナーの初期化の最後にサインアウトします。 並列で実行されている別のジョブが既に Docker 構成ファイルをサインアウトしているため、後続のコンテナー ジョブのレジストリ イメージ プルが unauthorized authentication に対して拒否される可能性があります。

解決策は、ホステッド エージェントで稼働している各エージェント プールに固有の Docker 環境変数 DOCKER_CONFIG を設定することです。 各エージェント プールの runsvc.sh スクリプトで 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 DevOps で動作することが保証されているわけではありません。 最初に、container プロパティを使用して同じ目標を達成できるかどうかを確認します。

詳細については、Azure DevOps YAML スキーマ リファレンスの docker create コマンド リファレンスと resources.containers.container 定義を参照してください。

再利用可能なコンテナー定義

次の例では、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 Hub 以外のレジストリでコンテナーをホストできます。 Azure Container Registry または別のプライベート コンテナー レジストリ (プライベート Docker Hub レジストリを含む) でイメージをホストするには、サービス接続を追加してレジストリにアクセスします。 その後、コンテナー定義内のエンドポイントを参照できます。

プライベート Docker Hub 接続:

container:
  image: registry:ubuntu1804
  endpoint: private_dockerhub_connection

Azure Container Registry 接続:

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

Note

Azure Pipelines は Amazon Elastic Container Registry (ECR) のサービス接続を設定できません。Amazon ECR では、AWS 認証情報を Docker が認証に使用できるものに変換するための他のクライアント ツールが必要になるためです。

glibc ベースでないコンテナー

Azure Pipelines エージェントは、タスクとスクリプトを実行するために必要な Node.js のコピーを提供します。 ホステッド エージェントの Node.js のバージョンを確認するには、「Microsoft ホステッド エージェント」を参照してください。

Node.js のバージョンは、ホステッド クラウド (通常は glibc) で使用する C ランタイムに対してコンパイルされます。 Linux の一部のバリアントはその他の C ランタイムを使用します。 たとえば、Alpine Linux は musl を使用します。

glibc ベースではないのコンテナーを使用する場合は、次の手順を実行する必要があります。

  • Node.js の独自のコピーを指定します。
  • Node.js バイナリを検索する場所をエージェントに伝えるラベルをイメージに追加します。
  • Azure Pipelines が依存するその他の依存関係 (bashsudowhich、および groupadd) を提供します。

独自の 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

インボックスタ スクまたは Marketplace タスクに依存している場合は、必要なバイナリも提供します。

完全な 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" ]