Azure Pipelinesを使ってみる ~services編~

# はじめに

Azure Pipelinesの機能`services`を使おうとして、いろいろハマったことがあるので、基本的な書き方を簡単にまとめたました。

ここでは例として、nginxコンテナを外部サービスコンテナとして起動し、curlでHTTPリクエストを投げ、疎通確認をしてみます。

公式ドキュメント

* [Service Containers](https://docs.microsoft.com/en-us/azure/devops/pipelines/process/service-containers?view=azure-devops&tabs=yaml)
* [Container jobs](https://docs.microsoft.com/en-us/azure/devops/pipelines/process/container-phases?view=azure-devops&tabs=yaml)

# ホストVMからリクエストを投げる
````
ホストVM → nginxコンテナ
````
では、`localhost`宛で通信できる。
ただし、nginxコンテナのポートを指定して起動する必要がある。
(`options`でポートの設定をする)

<!-- azure-pipelines.yml -->
````yaml:azure-pipelines.yml
resources:
  containers:
  - container: nginx
    image: nginx:latest
    options: -p 8080:80

services:
  nginx-svc: nginx

pool:
  vmImage: 'ubuntu-latest'

steps:
- script: |
    curl http://localhost:8080
  displayName: 'curl'
````

ちなみに、nginxのポート設定を行わなかった場合、エラーが発生する：

````log
curl: (7) Failed to connect to localhost port 80: Connection refused
````

# コンテナからリクエストを投げる
今度は

````txt
コンテナ → nginxコンテナ
````
を**２通りの方法**でやってみる。

また、リクエスト送信元のコンテナには`curl`コマンドが必要になる。
以下では、ubuntuに`curl`をインストールしたイメージ`kazumad/my-ubuntu`([dockerhub](https://cloud.docker.com/repository/docker/kazumad/my-ubuntu/general))を使うことにする。

※ほんとはジョブのなかでインストールしたいのですが、`apt-get`でpermission deniedが発生して、インストールできませんでした...
(`Initialize containers`ジョブのログを見ると、`vsts_azpcontainer`というユーザーを作成したうえでコマンドを実行しているようで、これが原因っぽい？)

## `container`を使う
Azure Pipelinesの機能(`container`)でコンテナを立ち上げて、nginxにリクエストを投げる。
この場合、nginxへは`services`で設定した値(例だと`nginx-svc`)で通信できる。

<!-- azure-pipelines.yml -->
````yaml:azure-pipelines.yml
resources:
  containers:
  - container: nginx
    image: nginx:latest
  - container: my-ubuntu
    image: kazumad/my-ubuntu:latest

container: my-ubuntu

services:
  nginx-svc: nginx

pool:
  vmImage: 'ubuntu-latest'

steps:
- script: |
    curl nginx-svc
  displayName: 'curl'
````

### [補足] コンテナのアドレスの名前解決について

`services`や`container`を使ってコンテナを起動すると、Azure Pipelinesはコンテナ間で名前解決できるようにDockerネットワークを作成してくれている。

実際に`Initialize containers`ジョブのログの見てみると、以下のように`vsts_network`から始まる名前のネットワークを作成していることがわかる。

<!-- Initialize containersのログ -->
````log
/usr/bin/docker network create --label d2053a vsts_network_{ランダムな英数字}
````

このネットワークは、コンテナ作成時に`--network`オプションで指定されている。さらに、サービスコンテナ(nginx)のほうは、`services`で指定した値が`--network-alias`オプションで設定されている。

<!-- Initialize containersのログ -->
````log
/usr/bin/docker create --name ubuntu_kazumadmyubuntulatest_29249a --label d2053a --network vsts_network_{ランダムな英数字}  ...(省略)... kazumad/my-ubuntu:latest ...(省略)

(省略)

/usr/bin/docker create --name nginx_nginxlatest_6a890c --label d2053a --network vsts_network_{ランダムな英数字} --network-alias nginx-svc ...(省略)... nginx:latest
````

### [補足] `container`に指定できるコンテナの条件について
タイトルの通り、`container`の機能で起動できるコンテナにはいくつか条件が存在する。
(条件の詳細についてはここでは記述しないので、公式ドキュメント([Container jobs > Requirements](https://docs.microsoft.com/en-us/azure/devops/pipelines/process/container-phases?view=azure-devops&tabs=yaml#requirements))を参照)
ドキュメントによると、Alpineベースのイメージが使えないので、当記事ではubuntuベースのイメージを使用している。

## `docker run`を使う
リクエスト送信元コンテナ(`kazumad/my-ubuntu`)を`docker run`コマンドから起動して`curl`を実行する。
この方法だと、上記補足で述べたようなコンテナの制限はない。


<!-- azure-pipelines.yml -->
````yaml:azure-pipelines.yml
resources:
  containers:
  - container: nginx
    image: nginx:latest

services:
  nginx-svc: nginx

pool:
  vmImage: 'ubuntu-latest'

steps:
- script: |
    # network名を取得する
    export DOCKER_NETWORK=$(docker network ls | grep "vsts_network" | awk '{print $2}')
    # curlの実行
    docker run --net=${DOCKER_NETWORK} kazumad/my-ubuntu:latest \
    curl "nginx-svc"
  displayName: 'curl'
````

`services`で設定した値(`nginx-svc`)で通信できるようするため、Azure Pipelinesが生成するdockerネットワークを利用している。
(dockerネットワークについては上述の「コンテナのアドレスの名前解決について」を参照)

具体的には、ネットワーク名(`vsts_network_{ランダムな英数字}`)を`docker network ls`コマンドで取り出し、`docker run`の`--net`オプションに指定している。