しばやん雑記

ASP.NET Core とメイドさんが大好きなフリーランスのプログラマーのブログ

Azure Pipelines を使って Web App for Containers (Windows) への自動デプロイを行う

Azure Pipelines にも Windows Server 2019 の Hosted Agent が追加されているので、これまでも ltsc2019 向けの Docker Image はビルド出来ていました。

現状の Agent Image には多少の問題*1はありますが、ビルドには問題ありません。

最近は .NET Framework 4.8 向けの Docker Image が公開され、Visual Studio 2019 へのアップデートも行われたりと順調に環境は整ってきています。

そして少し前にデプロイ用のタスクが Web App for Containers の Windows 版に対応したので、Azure CLI で頑張ったりすることなく、サクッとリリース用のパイプラインを作成出来るようになりました。

まだ Deployment Center から各パイプラインを作成は出来ないので、一般的なパイプラインを作って試しました。Windows Containers 特有の罠もありますが、思っていたよりは普通に使えます。

ビルドパイプラインを作成する

作成するビルドパイプラインでは Docker Image の作成と Azure Container Registry へのプッシュまでを行います。以前紹介したように Multi-stage builds を使っていれば一瞬で完成します。

Multi-stage builds を使っていると ACR Tasks でもビルド出来るので、寄せておいた方が良いでしょう。

作成したビルドパイプラインは 2 つのタスクで終わりです。

f:id:shiba-yan:20190423165158p:plain

Trigger も設定しておけば、リポジトリにプッシュすれば Docker Image がビルドされて、ACR に Image がプッシュされます。割と GUI で設定するのは好きです。

ベースイメージとして ltsc2019 を使っている場合には、忘れないように Windows Server 2019 の Hosted Agent を選ぶようにしておきます。今後は ltsc2019 が標準になるはずです。

リリースパイプラインを作成する

次はリリースパイプラインを作成しますが、基本的にビルドパイプラインでデプロイまで行わずに、ちゃんとリリースはパイプラインを分けておきましょう。そもそも別のものなので。

リリースパイプラインは更に簡単で、Web App for Containers 用のデプロイタスクを追加するだけです。

f:id:shiba-yan:20190423165333p:plain

Windows Containers の場合は必ず Staging 用のスロットを作成して、まずそこにデプロイを行ってからスワップで Production リリースするようにしておきます。

ビルド後に動くように設定しておけば、Docker Image のビルド完了後にデプロイが行われます。

f:id:shiba-yan:20190423152540p:plain

とりあえずテストなのでスワップまでは組んでいませんが、ちゃんと自動で App Service へのデプロイまで走ったことが確認できます。App Service はコンテナが起動後にページを確認できます。

上手くいったのでスワップを追加して、Production へのリリースまで行うようにします。タスクは Stage を追加するだけなので、特に説明は不要かと思います。

f:id:shiba-yan:20190423152552p:plain

デプロイ完了後にスワップが走るので、失敗した時も安心です。Auto-redeploy trigger を設定しておけば、失敗した場合に最後に成功したデプロイに戻せるみたいなので便利そうです。

ACR などのプライベートリポジトリを使う場合は App Settings にいくつかのキー追加が必要ですが、今は予め Azure Portal で設定しておくのが楽です。

新しい App Service 用タスクについて

さらっと流してしまいましたが、Azure Pipelines に新しい App Service 用のデプロイタスクが追加されています。現在はプレビューですが、コードベースは前のタスクとほぼ同じなので問題ないでしょう。

これまでは 1 つのタスクで全ての App Service に対応してきましたが、新しいバージョンでは以下のようにサービスごとに細分化されました。

f:id:shiba-yan:20190423231353p:plain

f:id:shiba-yan:20190423231402p:plain

それぞれのサービスにあったタスクを使うことで、設定が簡略化されて楽が出来ます。将来的には Web App / Azure Function に特化された機能などが追加されるかも知れないですね。

Swap 後は確実にコンテナを起動状態にする

話は戻ってデプロイですが、スワップが成功したとしても Windows Containers の場合はコンテナが起動しているかどうかは現時点では保証が出来ません。

スワップ前にウォームアップは行いますが、コンテナの起動に時間がかかる場合にはあまり効果がないようでした。このままだとコンテナ起動中の青画面が Production に出てしまうので、自力でコンテナが立ち上がったか確認するタスクを追加しました。

良いタスクが既にありそうですが、とりあえず PowerShell で適当に書いたので、以下のスクリプトをスワップのタスク前に実行するように設定します。

while (1) {
  try {
    Invoke-WebRequest https://***-staging.azurewebsites.net/ -UseBasicParsing -TimeoutSec 10
    break
  } catch {
    echo "waiting..."
    sleep 10
  }
}

スワップ前に実行しないと意味がないので注意してください。上のスクリプトは無限にリクエストを投げるようになっているので、タスク側のタイムアウトを設定しておきます。

f:id:shiba-yan:20190423184232p:plain

コンテナの起動中は 503 が返ってくるのを利用しています。コンテナが起動すれば 200 が返ってくるページを、前もって指定しておく必要があります。

実際に Docker Image がキャッシュされていないバージョンを使って、コンテナの起動に時間がかかる状況を用意して動作を確認しておきました。スワップに時間がかかっていますね。

f:id:shiba-yan:20190423184044p:plain

無事にコンテナが起動できればスワップが行われて、起動した状態で Production にリリースされます。

Pipeline のログを見ると、ちゃんと起動してコンテンツが返ってきているのも確認できます。

f:id:shiba-yan:20190423184622p:plain

ウォームアップに関しては App Service 側で改善される可能性もありますが、仕組み上難しいと思うのでリリースパイプライン側で対応しておいた方が柔軟でしょう。

出来ればキャッシュされている Docker Image に合わせた運用を行いたいので、キャッシュされている Docker Image の API で取れるように要望を出してみようかなとも思います。

*1:キャッシュされている Docker Image が古い少ないという点