Azure Container Apps jobsを使って低コストにコンテナ運用する

:::note info
この記事はDeNA 25 新卒 Advent Calendar 2024の12日目の記事です。25卒同期が魅力的な記事を紹介してくれると思うので、ぜひぜひチェックしてみてください！
:::

# はじめに
皆さん、バッチ処理してますか？
バッチ処理を定期実行するとなると、PCを常時起動する or クラウド上で動かすかの2択ですが、クラウドが楽ですよね！
ただ、クラウドを使うとなるとコストが気になるところ。。。
そこで、この記事ではコストを抑えてAzure上でバッチ処理を実行する方法について紹介します。

例えば、Pythonで書いたバッチ処理をAzure上で定期実行することを考えると、Azureで使う環境として
- Azure virtual machines (以下、Azure VPS)
- Azure Functions
- Azure Container Apps jobs

などが挙げられます。

Azure VPSはVPSなので自由に設定して動かせますが、VMを停止している間もストレージにコストがかかり続けます。
Azure Functions等のサーバーレスでは、バッチ処理を定期実行する場合はコストをかなり抑えられますが、Pyppeteerを使う必要があるなど、OSのパッケージに依存する処理は実行できません。

一方、Azure Container Apps jobsは任意のコンテナイメージを実行でき、従量課金のため使った分しかコストが発生しません。なので、Docker Hubを利用するなど無料のコンテナレジストリさえ確保できれば低コストに運用できます。（コンテナレジストリにAzure Container Registryを使う場合、コストはAzure VPSと大差ありません）

# Azure Container Apps jobsとは
Azure Container Appsのリソースの１つです。
Azure Container Appsの中にはappsとjobsがあり、以下の違いがあります。
- Azure Container Apps：継続的に実行されるサービス向け
    - https://learn.microsoft.com/ja-jp/azure/container-apps/overview
- Azure Container Apps jobs： 一定時間で終了するバッチ処理向け
    - https://learn.microsoft.com/ja-jp/azure/container-apps/jobs?tabs=azure-cli


# Azure Container Apps jobsでコンテナを定期実行するまで
## 構成
Docker HubにPushした[hello-world](https://hub.docker.com/_/hello-world)のDockerイメージを、Azure Container Apps jobs上で実行します。
コンテナイメージの流れは以下の通りです。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2217514/cb1990da-c013-676e-93e6-9dabee5c563e.png" width="600">


## 手順
### PublicなDocker Hubのリポジトリを利用する場合
Azure portal上の`リソースの作成`から`Container App ジョブ`を選択します。
左のタブからだと出てこないので、`container apps`などで検索する必要があります。

![スクリーンショット 2024-11-29 115534.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2217514/9ed2670d-0dcd-1ec5-de5e-75bb62c00d34.png)

今回はWest US 2リージョンに定期実行するサービスを作成します。
![スクリーンショット 2024-11-29 115637.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2217514/a80fbf22-8ae3-c8e3-a3a3-4c66acfcdcb6.png)

Docker HubのリポジトリがPublicな場合はこんな感じで作成できます。
- レジストリログインサーバ：`docker.io`
- イメージとタグ：${DockerHubUserName}/my-hello:latest

:::note warn
リポジトリがPrivateの場合はレジストリログインサーバがPublicの場合と異なります（後述）
:::
![スクリーンショット 2024-11-29 115759.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2217514/de4d38f1-5724-d411-cf6a-856860e98261.png)

正常にデプロイできると以下のような画面が表示されます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2217514/73d0212d-890f-5d8c-3d7d-8ef993e099de.png" width="400">

### PrivateなDocker Hubのリポジトリを利用する場合
PrivateなDocker Hubのリポジトリのイメージを利用する場合、レジストリログインサーバを`docker.io`にするとデプロイ時にエラーが出るので、作成手順を別途紹介します。
`docker.io`を指定するとサービスのデプロイ時に
> The following field(s) are either invalid or missing. Field *  is invalid with details: 'Invalid value: ...

のエラーが発生します。
![スクリーンショット 2024-11-29 120523.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2217514/0b634eb7-482b-1e3e-e014-637226a5e49a.png)

このエラーを回避するためには、レジストリログインサーバを`registry.hub.docker.com`に変更する必要があります。
- https://stackoverflow.com/questions/76110956/how-to-integrate-docker-hub-with-azure-container-apps-using-azure-devops-pipelin
- https://github.com/microsoft/azure-container-apps/issues/352

また、`レジストリのパスワード`には、Docker Hubで作成した、少なくともRead-onlyのアクセス権が設定された`Personal access tokens`を使います。

レジストリログインサーバとレジストリのパスワードを設定し、リソースを作成すると...
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2217514/357334f1-a7b8-9895-ddc7-c1589e63140b.png width="500" >

正常にデプロイできました！
![スクリーンショット 2024-11-29 120925.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2217514/8b517d24-794f-5c61-73a6-28c23ae6e353.png)

# 動作確認
デプロイしたリソースの`Execution history`から定期実行されていることを確認できます。
![スクリーンショット 2024-11-29 120003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2217514/157167e5-2b74-ec85-ff7c-a29e02e9feb4.png)

`Console`へ移動するとhello-worldコンテナの出力が確認でき、正常に定期実行できていることが確認できます。
![スクリーンショット 2024-11-29 115924.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2217514/bdb8ee40-4c37-c064-2ae3-ed236bcce50c.png)

# おわりに
今回はAzure Container Apps jobsを使ってバッチ処理のコストを抑える方法を紹介しました。
ライブラリで完結するような処理はAzure Functionsを利用し、OSのパッケージが別途必要な場合はAzure Container Apps jobsを利用するなど使い分けることで、Azure VPSを使わずにほとんどのバッチ処理は低コストに実行できます。

それでは良いAzureライフを！