サービス コンテナー

Azure DevOps Services

パイプラインで 1 つ以上のサービスのサポートが必要な場合は、多くの場合、ジョブごとに各サービスを作成、接続、およびクリーンアップする必要があります。 たとえば、パイプラインでは、データベースとメモリ キャッシュへのアクセスを必要とする統合テストを実行することがあります。 データベースとメモリ キャッシュは、パイプライン内のジョブごとに新しく作成する必要があります。

コンテナーは、パイプラインが依存するサービスを実行するための簡単で移植可能な方法を備えています。 "サービス コンテナー" を使うと、コンテナー化されたサービスのライフサイクルを自動的に作成、ネットワーク化、管理できます。 各サービス コンテナーには、それを必要とするジョブのみがアクセスできます。 サービス コンテナーはどのような種類のジョブでも動作しますが、最も一般的にはコンテナー ジョブで使われます。

必要条件

サービス コンテナーでは、CMD または ENTRYPOINT を定義する必要があります。 パイプラインでは、指定されたコンテナーに対して、追加の引数なしで docker run が実行されます。

Azure Pipelines では、Linux または Windows コンテナーを実行できます。 Linux コンテナーではホストされた Ubuntu、Windows コンテナーではホストされた Windows コンテナー プールを使用します。 (ホストされた macOS プールでは、実行中のコンテナーはサポートされていません。)

単一コンテナー ジョブ

コンテナー ジョブを使用する単純な例を次に示します。

resources:
  containers:
  - container: my_container
    image: buildpack-deps:focal
  - container: nginx
    image: nginx


pool:
  vmImage: 'ubuntu-latest'

container: my_container
services:
  nginx: nginx

steps:
- script: |
    curl nginx
  displayName: Show that nginx is running

このパイプラインは、Docker Hub から nginx コンテナーと buildpack-deps コンテナーをフェッチし、コンテナーを開始します。 コンテナーはネットワークでつながっているため、services 名で互いに接続できます。

このジョブ コンテナー内から、nginx のホスト名は Docker ネットワークを使用して正しいサービスに解決されます。 ネットワーク上のすべてのコンテナーは、自動的にすべてのポートを互いに公開します。

単一のジョブ

ジョブ コンテナーなしでサービス コンテナーを使用することもできます。 単純な例を次に示します。

resources:
  containers:
  - container: nginx
    image: nginx
    ports:
    - 8080:80
    env:
      NGINX_PORT: 80
  - container: redis
    image: redis
    ports:
    - 6379

pool:
  vmImage: 'ubuntu-latest'

services:
  nginx: nginx
  redis: redis

steps:
- script: |
    curl localhost:8080
    echo $AGENT_SERVICES_REDIS_PORTS_6379

このパイプラインでは、最新の nginx コンテナーを開始します。 ジョブはコンテナーで実行されていないため、名前の自動解決は行われません。 この例では、代わりに localhost を使用してサービスに接続する方法を示します。 上記の例では、ポートを明示的に指定します (例: 8080:80)。

また、実行時にランダムなポートを動的に割り当てる方法もあります。 その後、変数を使用してこれらの動的ポートにアクセスできます。 Bash スクリプトでは、プロセス環境を使用して変数にアクセスできます。 これらの変数は、agent.services.<serviceName>.ports.<port> の形式になります。 上記の例では、redis にはホスト上のランダムに使用可能なポートが割り当てられます。 agent.services.redis.ports.6379 変数にはポート番号が含まれています。

複数のジョブ

サービス コンテナーは、同じサービスの複数のバージョンに対して同じステップを実行する場合にも役立ちます。 次の例では、同じステップが複数のバージョンの PostgreSQL に対して実行されます。

resources:
  containers:
  - container: my_container
    image: ubuntu:22.04
  - container: pg15
    image: postgres:15
  - container: pg14
    image: postgres:14

pool:
  vmImage: 'ubuntu-latest'

strategy:
  matrix:
    postgres15:
      postgresService: pg15
    postgres14:
      postgresService: pg14

container: my_container

services:
  postgres: $[ variables['postgresService'] ]
steps:
- script: printenv

Port

コンテナー リソースまたはインライン コンテナーを指定する場合、コンテナーで公開する ports の配列を指定できます。

resources:
  containers:
  - container: my_service
    image: my_service:latest
    ports:
    - 8080:80
    - 5432

services:
  redis:
    image: redis
    ports:
    - 6379/tcp

同じ Docker ネットワーク上のコンテナーでは、既定ですべてのポートが自動的に互いに公開されるため、ジョブがコンテナーで実行されている場合は、ports を指定する必要はありません。

ジョブがホストで実行されている場合は、サービスにアクセスするには ports が必要です。 ポートには、<hostPort>:<containerPort> 形式か、単純な <containerPort> という形式があり、最後にオプションで /<protocol> が付くことがあります。たとえば、ポート 6379 経由で tcp を公開する 6379/tcp は、ホスト コンピューター上のランダムなポートにバインドされます。

ホスト コンピューター上のランダムなポートにバインドされているポートについては、パイプラインでは agent.services.<serviceName>.ports.<port> の形式の変数を作成して、ジョブからアクセスできるようにします。 たとえば、agent.services.redis.ports.6379 はホスト コンピューター上のランダムに割り当てられたポートに解決されます。

ボリューム

ボリュームは、サービス間でデータを共有する場合や、ジョブの複数の実行間でデータを保持する場合に便利です。

ボリューム マウントは volumes の配列として指定できます。 名前付き Docker ボリューム、匿名 Docker ボリューム、またはホスト上のバインド マウントのいずれかのボリュームを使用できます。

services:
  my_service:
    image: myservice:latest
    volumes:
    - mydockervolume:/data/dir
    - /data/dir
    - /src/dir:/dst/dir

ボリュームは <source>:<destinationPath> の形式で、<source> はホスト コンピューター上の名前付きボリュームまたは絶対パス、<destinationPath> はコンテナー内の絶対パスです。

注意

ホスト プールを使用する場合、ジョブの完了後にホスト コンピューターがクリーンアップされるため、ジョブ間でボリュームは保持されません。

その他のオプション

サービス コンテナーは、コンテナー ジョブと同じコンテナー リソースを共有します。 つまり、同じ追加オプションを使用できます。

Healthcheck

必要に応じて、いずれかのサービス コンテナーで HEALTHCHECK が指定されている場合、エージェントはコンテナーが正常になるまで待機してからジョブを実行します。

サービスを含む複数のコンテナーの例

この例では、PostgreSQL と MySQL という 2 つのデータベース コンテナーに接続された Django Python Web コンテナーがあります。 PostgreSQL データベースはプライマリ データベースであり、そのコンテナーには db という名前が付けられています。 db コンテナーではボリューム /data/db:/var/lib/postgresql/data を使用し、env を介してコンテナーに渡される 3 つのデータベース変数があります。 mysql コンテナーではポート 3306:3306 を使用し、env を介して渡されるデータベース変数もあります。 web コンテナーはポート 8000で開いています。 このステップでは、pip で依存関係をインストールしてから、Django のテストを実行します。 作業例を設定するには、2 つのデータベースが設定された Django サイトが必要です。 この例では、manage.py ファイルがルート ディレクトリにあり、Django プロジェクトがそのディレクトリ内にあることを前提としています。 /__w/1/s/manage.py test/__w/1/s/ のパスを更新しなければならない場合があります。

resources:
  containers:
    - container: db
      image: postgres
      volumes:
          - '/data/db:/var/lib/postgresql/data'
      env:
        POSTGRES_DB: postgres
        POSTGRES_USER: postgres
        POSTGRES_PASSWORD: postgres
    - container: mysql
      image: 'mysql:5.7'
      ports:
         - '3306:3306'
      env:
        MYSQL_DATABASE: users
        MYSQL_USER: mysql
        MYSQL_PASSWORD: mysql
        MYSQL_ROOT_PASSWORD: mysql
    - container: web
      image: python
      volumes:
      - '/code'
      ports:
        - '8000:8000'

pool:
  vmImage: 'ubuntu-latest'

container: web
services:
  db: db
  mysql: mysql

steps:
    - script: |
        pip install django
        pip install psycopg2
        pip install mysqlclient
      displayName: set up django
    - script: |
          python /__w/1/s/manage.py test