【Azure】Container Registryにプッシュしたら自動でApp Serviceのコンテナを最新化する

AzureでApp Serviceでコンテナを使用したWebアプリを作成しています。リポジトリは同じくAzureのContainer Registryです。
コンテナイメージを最新化するたびにApp Serviceを手動で更新するのは面倒です。そんな問題を解決する方法として、Webhookを使用してApp Serviceのコンテナを最新化するお手軽CD(Continuous Delivery/継続的デリバリー)を紹介します。

# 前提条件

- Azure(クラウドサービス)の基本を理解している
- Azure CLIの基本を理解している
- コンテナの基本を理解している

# ターゲット

- コンテナイメージを更新するたびに、App Serviceも更新するのが手間に感じている
- 簡単でいいのでCDを構築してみたい
- Container Registry/App ServiceのWebhookを使ってみたい

# 概要

![summaryx1.5_margin30.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/591bf57c-6775-e3ee-d8b0-c7c1cea25187.png)

1. Container RegistryにコンテナイメージをPushする
1. Container RegistryがイメージのPushを検知して、設定されているApp ServiceのWebhook URLを叩く
1. Webhook URLが叩かれたことでApp Serviceが再起動して、最新のコンテナイメージをPullすることで最新化される

## App Serviceにコンテナイメージの最新化を知らせる仕組み

Container Registryのコンテナイメージに更新があった場合、そのことをApp Serviceに知らせたいです。言い換えると、App Serviceに「コンテナイメージの更新」という **イベント** を通知する仕組みが必要です。

そんなときのために、App Serviceには「コンテナイメージの更新」という **イベント** を通知する仕組みとしてWebhook URLが用意されています。Webhookは、イベントが発生したらその内容を通知する仕組みのことです。Webhook URLにリクエストを送ることで、「コンテナイメージの更新」を知らせることが出来ます。

AzureのContainer RgistoryはWebhook機能があるので、そこにApp ServiceのWebhook URLを登録すれば、コンテナイメージのPushをトリガーに自動でWebhook URLを叩いてくれます。

# 構築手順

これからApp Serviceを作成する場合は、Container Registryがパブリックの場合とプライベートの場合で若干異なりますので、環境に合わせてどちらかを行ってください。
すでにApp Serviceまで作成が完了している場合は、[Webhookの設定](#webhookの設定)から行ってください。

## Container Registryの作成

Container Registryを作成していない場合は、作成しましょう。
SKUによるWebhookの違いは登録できる数ですので、お試しならば **Basic** で十分です。
セキュリティ等の観点で、プライベートレジストリが良い場合は **Premium** です。
![create_container_registry.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/1de70ebd-927a-a199-95c7-f06d475979ed.png)
**SKUの価格表**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/27a78fde-7757-696c-d7fe-e31973890c75.png)
引用元 | [Container Registry の価格 (2022/8/8現在)](https://azure.microsoft.com/ja-jp/pricing/details/container-registry/)

## コンテナイメージのPush

コンテナイメージをPushしていない場合は、Pushしましょう。
適切な権限のユーザーまたはサービスプリンシパルで行いましょう。プライベートレジストリの場合はネットワーク設定が必要な場合もあります。
お試しで何を使おうか迷う場合は、`nginx:alpine`がおすすめです。
![container_images.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/f7235387-d01a-bd86-d41c-1c5da97595dd.png)

## Container Registryの管理者ユーザー有効化

App ServiceにContainer Registryを紐づける場合は、管理者ユーザーを有効化する必要があります。
アクセスキーの設定画面で、管理者ユーザーのトグルを有効にしてください。
![container_registry_admin.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/d545501f-0cec-5a4d-c612-25f4e951676d.png)

## App Serviceの作成

App Serviceを作成していない場合は、作成しましょう。
**基本** では各項目を入力して、**公開** は **Dockerコンテナー** を選択します。
![app_service_create_basic.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/e4c8e956-e795-4302-ac3a-a8dffa83d305.png)
**Docker** では、Container Registoryの情報とそこから取得するイメージとタグを指定します。
プライベートレジストリの場合とパブリックレジストリの場合で入力項目が違いますので注意してください。

<details><summary>必要な値の確認方法はこちら</summary><div>

**{レジストリ名}**, **{ログイン サーバー}**, **{ユーザー名}**, **{password}**
Container Registryのアクセスキーから確認できます。
![container_registry_loginserver.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/e4e36949-ad38-968e-245d-9187335c3e05.png)

**{リポジトリ}**, **{タグ}**
Container Registryのリポジトリからリポジトリを選択して確認できます。
![container_registry_imagetag.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/f813f7d1-3bd0-9cd7-f3d5-2c54dfc01875.png)


</div></details>

**プライベートレジストリの場合**
オプション | 単一コンテナー
イメージ ソース | プライベートレジストリ
サーバーURL | https://{ログイン サーバー}
ユーザー名 | {ユーザー名}
パスワード | {password}
イメージとタグ | {ログイン サーバー}/{リポジトリ}:{タグ}
![app_service_create_docker_private.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/63da50aa-f889-06dd-105f-b13f941dba83.png)
**パブリックレジストリの場合**
オプション | 単一コンテナー
イメージ ソース | Azure Container Registry
レジストリ | {レジストリ名}
イメージ | {リポジトリ}
タグ | {タグ}
![app_service_create_docker_public.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/ec6a4052-4efc-2fb6-de9f-a87c1f51658d.png)

## Webhookの設定

App Serviceのデプロイセンターから **継続的デプロイ** を **オン** に設定して保存します。

![app_service_update_webhook.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/7cbfbbf3-ea44-a516-ffc5-e3248ef636b5.png)

Container Registry側にも自動でWebhookが追加されるはずなので、確認しましょう。
![container_registry_webhook.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/dc063a98-1b8a-e9a2-2c88-5306d4584921.png)

## 確認

Container Registryのリポジトリに、新しいコンテナイメージをPushして、App Serviceが自動で更新されることを確認しましょう。

ちゃんとコンテナが更新されれば成功です！

**更新されない場合に確認すること**
- リポジトリとコンテナイメージのタグがApp Serviceの設定と同じであること
- Container Registryの管理者ユーザーが有効化されていること→([Container Registryの管理者ユーザー有効化](#container-registryの管理者ユーザー有効化))
- Container RegistryのWebhooksにWebhook URLが追加されていて、状態が有効であること→([Webhookの設定](#webhookの設定))
    - 追加されていない場合は、App Serviceのデプロイセンターの設定が正しいことを確認して、手動でContainer RegistryのWebhooksを追加する
- App Serviceの **デプロイセンター > ログ** から原因を調査
    - Container Registryがプライベートレジストリの場合でコンテナイメージのPullが失敗しているときは、App ServiceがイメージをPullするときのIPアドレスがブロックされている可能性があるので、Container Registryの **ネットワーク > ファイアウォール** でIPアドレスを許可する



## おわりに

紹介した内容は、Azureでコンテナを使うCDではもっとも簡単な方法の一つだと思います。
コンテナイメージ更新時にApp Serviceが再起動するので、ダウンタイムがあってはいけない本番環境向きではないですが、ダウンタイムが許容できるサービスや開発環境などでは十分ですね。