Azure Container Apps デプロイのあれこれ

本記事は、[Microsoft Azure Tech Advent Calendar 2022 ](https://qiita.com/advent-calendar/2022/microsoft-azure-tech) 22 日目の投稿です。

# はじめに
Azure Container Apps はサーバーレス プラットフォームでコンテナー化されたアプリケーション実行を実現するサービスです。
この記事では、Azure Container Apps のデプロイ方法に関して、現在サポートされているもの、現在はプレビュー段階のものを紹介しつつ、一部実際にデプロイを行ってみます。

# 準備
デプロイするサンプル アプリケーションとして、以下のプロジェクトを利用します。

https://learn.microsoft.com/ja-jp/azure/app-service/quickstart-nodejs?tabs=windows&pivots=development-environment-vscode

コンテナーイメージを作成する必要があるので、以下のような Dockerfile を用意しておきます。

```Dockerfile
FROM node:12.18.1
ENV NODE_ENV=production

WORKDIR /app

COPY ["package.json", "package-lock.json*", "./"]
COPY ["*.js", "public/", "routes/", "views/", "./"]

EXPOSE 3000

RUN npm install --production

CMD [ "npm", "start" ]
```


# コンテナーを自分でビルドする／既存のイメージをデプロイする
最も単純明快なデプロイ方法です。
ローカルの環境で Docker (e.g. Docker Desktop) をインストールし、ビルド (`docker build`) して ACR へ Push (`docker push`) します。

https://learn.microsoft.com/ja-jp/azure/container-apps/quickstart-code-to-cloud?tabs=bash%2Ccsharp&pivots=docker-local

主要な手順だけかいつまむと、以下のようになります。

```
# ビルドして
docker build --tag $ACR_NAME.azurecr.io/$API_NAME .
 
# ACR に Push する
az acr login $ACR_NAME
docker push $ACR_NAME.azurecr.io/$API_NAME
```

Push が完了すると、コンテナーアプリにデプロイするイメージとして選択できるようになるので、新規リビジョンを作成します。

<img width="50%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4646/66c8653d-7c9d-e5d1-931b-d04f62f8d171.png">


# ソースコードからデプロイする
必ずしもローカル環境でビルドする必要はありません。 `az containerapp up` コマンドを用いると、コンテナーのビルドは「Azure 側」で行うようになります。ローカルの環境に Docker がなくても OK です。

```
az containerapp up -n アプリケーション名 -g リソースグループ名 --source .
```

`--source` の指定が肝です。

https://learn.microsoft.com/ja-jp/azure/container-apps/containerapp-up

このとき、出力されたログから挙動を確認してみます（後で比較するのに参照します）。
```
--- ACR にログインして、
2022/12/21 10:20:55 Successfully logged into ***************.azurecr.io
--- Dockerfile に基づいてビルドし、タグをつけて、
Step 1/9 : FROM node:12.18.1
Successfully built 7a8d6c834afa
Successfully tagged ***************.azurecr.io/aca-sample-w-d:20221221192049531045
--- ACR へ Push する
1eb0c259246c: Pushed
20221221192049531045: digest: sha256:34440174e84f425e5041c346152d111e7bc0447e93bdf8067247925a4af6957c size: 3259
2022/12/21 10:21:44 Successfully pushed image: ***************.azurecr.io/aca-sample-w-d:20221221192049531045
2022/12/21 10:21:44 Step ID: build marked as successful (elapsed time in seconds: 40.101371)
--- ビルドにかかった時間は 53 秒
Run ID: caf was successful after 53s
```

上で手動で行っていた手順を自動化してくれているようです。

# GitHub Actions, Azure DevOps Pipeline を利用する
現在プレビュー段階ではありますが、GitHub Actions のワークフロー、Azure DevOps Pipeline のタスクが用意されているので、これを用いることで容易に継続的デプロイを実現することができます（上記の発展形と見ることもできます）。


https://learn.microsoft.com/ja-jp/azure/container-apps/github-actions

https://learn.microsoft.com/ja-jp/azure/container-apps/azure-pipelines

GitHub Actions では以下のようにワークフローを定義することで、ビルド、デプロイを行うことが可能です。

```yaml
name: Azure Container Apps Build and Deploy 
 
on: 
  push: 
    branches: 
      - main 
 
jobs: 
  build: 
    runs-on: ubuntu-latest 
 
    steps: 
      - uses: actions/checkout@v3 
 
      - name: Log in to Azure 
        uses: azure/login@v1 
        with: 
          creds: ${{ secrets.AZURE_CREDENTIALS }} 
 
      - name: Build and deploy Container App 
        uses: azure/container-apps-deploy-action@v0 
        with: 
          appSourcePath: ${{ github.workspace }}/src 
          acrName: myregistry 
          containerAppName: my-container-app 
          resourceGroup: my-container-app-rg 
```

これを設定した上でワークフローをトリガーすると（main ブランチへ Push すると）、`runs-on` 節で指定したランナーを用いて `docker build` されます。

# Dockerfile-less ビルド
ところで、ここまでの方法はローカルでビルドするにせよ、GitHub Actions のランナーするにせよ、Dockerfile が必要でしたが、実は **Dockerfile がなくてもコンテナーアプリを作成できるようになった** ようです。参考記事に倣って、この方式を Dockerfile-less ビルドと呼ぶことにします。

https://azure.microsoft.com/en-us/updates/public-preview-build-and-deploy-to-azure-container-apps-without-a-dockerfile-from-the-azure-cli/

> Previously, “az containerapp up” required a Dockerfile to build a container image. "az containerapp up" now supports building container images from source code without a Dockerfile. 

Dockerfile-less ビルドは、現時点では以下の場合にサポートされています。
- GitHub Actions, Azure DevOps Pipeline を利用するとき
- az containerapp up を利用するとき

また、現状 Dockerfile-less ビルドがサポートされている言語は次のとおりです。
- .NET
- Node.js
- PHP
- Python
- Ruby
- Go

上記で用いたサンプルアプリから Dockerfile を削除して、先程と同様のコマンド (`az containerapp up -n アプリケーション名 -g リソースグループ名 --source .`) を実行しても確かに正常にデプロイできることが確認できます。

ここで、Dockerfile がない状態から、プラットフォームを特定し、最終的にコンテナーイメージを作成する役割を担うのが Oryx という Microsoft 謹製のビルドツールです。
ちなみに、Container Apps だけでなく、App Service のデプロイにも Oryx が用いられています。

https://github.com/microsoft/Oryx

先程と同様に、`az containerapp up` コマンド実行時のログから追える範囲でその挙動を確認してみます。

```
--- Dockerfile が存在しないことを特定
WARNING: No dockerfile detected. Attempting to build a container directly from the provided source...
--- まず ACR にログインするのは先程と同様
2022/12/21 10:17:51 Successfully logged into cadeb505d8e6acr.azurecr.io
--- oryx/cli というイメージを利用して、
Unable to find image 'mcr.microsoft.com/oryx/cli:20220811.1' locally
20220811.1: Pulling from oryx/cli
Status: Downloaded newer image for mcr.microsoft.com/oryx/cli:20220811.1
------ Dockerfile を生成して、
Dockerfile written to '/workspace/Dockerfile'.
2022/12/21 10:18:00 Successfully executed container: acb_step_0
...
Step 1/10 : ARG RUNTIME=node:16
Step 2/10 : FROM mcr.microsoft.com/oryx/cli:stable as build
stable: Pulling from oryx/cli
Step 3/10 : WORKDIR /app
Step 4/10 : COPY . .
------ oryx build 
Step 5/10 : RUN oryx build /app --output /output
Operation performed by Microsoft Oryx, https://github.com/Microsoft/Oryx
You can report issues at https://github.com/Microsoft/Oryx/issues
...
Oryx Version: 0.2.20220906.1, Commit: 236348ca326a99972bf8cef71c0a6605fd838ef7, ReleaseTagName: 20220906.1
--------- プラットフォーム (Node.js) を特定
Detecting platforms...
Detected following platforms:
  nodejs: 14.19.1
Version '14.19.1' of platform 'nodejs' is not installed. Generating script to install it...
Detected the following frameworks: Express
...
Downloading and extracting 'nodejs' version '14.19.1' to '/opt/nodejs/14.19.1'...
--- 上で作成した成果物をコピーして、ENTRYPOINT も指定して、コンテナーイメージを作成
Step 6/10 : FROM mcr.microsoft.com/oryx/${RUNTIME}
16: Pulling from oryx/node
Status: Downloaded newer image for mcr.microsoft.com/oryx/node:16
Step 7/10 : WORKDIR /app
Step 8/10 : COPY --from=build /output .
Step 10/10 : ENTRYPOINT ["/app/run.sh"]
--- 最後に ACR に Push するのも先程と同様
2022/12/21 10:19:41 Successfully pushed image: cadeb505d8e6acr.azurecr.io/aca-sample-w-d:20221221191731147862
--- ビルドにかかった時間は 115 秒
Run ID: cae was successful after 1m55s
```

プラットフォーム特定などの処理が加わるためか、ソースコードに変化はなくともビルド時間は 2 倍程度に膨らみました。




# おわりに
本記事では Azure Container Apps のデプロイ方法に関してまとめつつ、Dockerfile-less ビルドを試してみました。
Azure Container Apps は Kubernetes 操作の複雑性を緩和できるというのが存在意義の 1 つですが、Dockerfile-less ビルドを用いることで真にアプリケーション コードにのみ注力できるのかもしれません。

別の機会に、App Service のデプロイ方法をまとめつつ、Oryx の仕組みに関してもう少し深堀りしてみたいと思います。

# 参考記事
https://techcommunity.microsoft.com/t5/apps-on-azure-blog/introducing-more-ways-to-deploy-azure-container-apps/ba-p/3678390