[Azure][Bicep]Azure Container Apps と Jobs の特徴とBicepによる構築

概要

こんにちは、サイオステクノロジーの安藤 浩です。 Bicep で Azure Container Apps と Jobs を構築したので、使い分けと実際のBicep でのパラメータとインフラを構築する方法を説明します。

Azure Container Apps とは

Azure Container Apps は、サーバーレスなコンテナプラットフォームで、コンテナー化されたアプリケーションを実行するため、保守が容易でコスト削減できます。

特徴:

主な特徴としては以下になるかと思います。

  • サーバーレス
  • マイクロサービスに最適
  • 自動スケーリング機能 (HTTP トラフィック、CPU,メモリの負荷状況、ServiceBus キューなど)

用途:

主な用途としては以下が考えられます。

  • Web アプリケーション/Web API
  • マイクロサービスの実行
  • リアルタイム処理

Azure Container Apps Jobs とは

Azure Container Apps Jobs は、タスクベースのワークロードに特化したサーバーレスコンテナサービスです。

特徴:

主な特徴としては以下になるかと思います。

  • サーバーレス
  • スケジュールトリガー、手動トリガー、イベントドリブン トリガー
  • 実行履歴管理:ジョブ実行の状況が把握できる
  • 並列実行のサポート

※Dapr、イングレスとカスタム ドメインや SSL 証明書などの関連機能 はサポートされていません。

用途:

主な用途としては以下が考えられます。

  • バッチ処理
  • データ処理パイプライン
  • 定期的なメンテナンスタスク
  • イベントドリブンな処理

Azure Container Apps と Azure Container Apps Jobs の使い分けポイント

以下の観点でAzure Container Apps と Azure Container Apps Jobs を使い分けることがポイントになると思います。

ポイント Azure Container Apps Azure Container Apps Jobs
サービス種別 継続的に実行されるサービス タスク、バッチ処理
稼働形態 常時稼働、またはリクエスト/イベントに応じて起動 手動、スケジュール、またはイベントに応じて起動・実行・終了
主なトリガー HTTP、TCP 手動、スケジュール、イベント
ユースケース Web API、Web アプリ、マイクロサービス 定期的なバッチ処理、データ処理

簡単に言うと、Azure Container AppsはWebアプリやAPIのように「継続的にリクエストされるWebアプリケーション」向け、Azure Container Apps Jobsはデータ処理やバッチ処理のような「実行して完了するタスク」向けのサービスです。

Bicep によるAzure Container Apps と Azure Container Apps Jobs の構築

ソースコードは以下に配置しています。

https://github.com/Hiroshi-Ando-sti/intro-bicep/tree/main/container-apps

上記のソースコードで利用しているサンプルとなるコンテナ イメージは以下のリポジトリで管理しています。

https://github.com/Hiroshi-Ando-sti/githubpkg-containerregistry

前提条件

以下をインストールします。

構成要素

以下の構成を構築します。

  • VNET
  • Application Insights
  • Container Environments
  • Container Apps
  • Container Apps Jobs

Bicep コード

1. メインとなるBicep コード (infra/main.bicep)

メインとなるコードの主要部分のみを記載します。 Container Apps やJobs を実行するために必要なリソースは VNET やContainer Apps Environment ですので、事前に作成します。 Container Apps と Container Apps Jobs は containerAppsSettings と containerAppJobsSettings のパラメータを利用して、複数作成できるようにしています。

module vnet './modules/network/vnet.bicep' = {
  name: 'vnet'
  params: {
    name: '${abbrs.networkVirtualNetworks}${environmentName}${suffixResourceName}${uniqueStr}'
    location: location
    tags: tags
    subnetName: 'default'
  }
}

var containerAppEnvironmentName = '${abbrs.appManagedEnvironments}${environmentName}${suffixResourceName}${uniqueStr}'
module containerAppEnvironment './modules/container/containerAppsEnv.bicep' = {
  name: 'containerAppEnvironment'
  params: {
    vnetName: vnet.outputs.name
    environmentName: containerAppEnvironmentName
    location: location
    tags: tags
    logAnalyticsWorkspaceName: logAnalyticsWorkspaceName
  }
}

module containerAppJobs './modules/container/jobs.bicep' = [for containerAppJobsSetting in containerAppJobsSettings: {
  name: 'containerAppJobs-${containerAppJobsSetting.name}'
  params: {
    containerAppEnvironmentName: containerAppEnvironmentName
    location: location
    tags: tags
    containerAppJobsSetting: containerAppJobsSetting
    appInsightsConnectionString: appInsights.outputs.connectionString
  }
}]

module containerApps './modules/container/containerApps.bicep' = [for containerAppsSetting in containerAppsSettings: {
  name: 'containerApps-${containerAppsSetting.name}'
  params: {
    containerAppsName: '${abbrs.appContainerApps}${environmentName}-${containerAppsSetting.name}${uniqueStr}'
    location: location
    tags: tags
    containerAppEnvironmentName: containerAppEnvironmentName
    appInsightsConnectionString: appInsights.outputs.connectionString
    environmentName: environmentName
    resourceSuffixName: uniqueStrWithoutHyphen
    containerAppsSetting: containerAppsSetting
  }
}]

2. Container Apps Environment モジュール (infra/modules/container/containerAppsEnv.bicep)

VNET が必要であり、Container Apps Environmentの Log出力先としてLog analytics を指定します。

@description('VNet名')
param vnetName string

param location string = resourceGroup().location

param environmentName string

param logAnalyticsWorkspaceName string

param tags object

// Container Apps 環境の VNet を取得する。
resource vnet 'Microsoft.Network/virtualNetworks@2024-05-01' existing = {
  name: vnetName
}

resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2023-09-01' existing = {
  name: logAnalyticsWorkspaceName
}

// Container Apps Environment
//https://learn.microsoft.com/ja-jp/azure/templates/microsoft.app/2025-01-01/managedenvironments?pivots=deployment-language-bicep
resource containerAppEnvironment 'Microsoft.App/managedEnvironments@2025-01-01' = {
  name: environmentName
  location: location
  tags: tags
  properties: {
    appLogsConfiguration: {
      destination: 'log-analytics'
      logAnalyticsConfiguration: {
        customerId: logAnalyticsWorkspace.properties.customerId
        sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey
      }
    }
    vnetConfiguration: {
      infrastructureSubnetId: vnet.properties.subnets[0].id
    }
  }
}

3. Container Apps モジュール (infra/modules/container/containerApps.bicep)

Container AppsのProperties では主に以下のパラメータを指定します。

properties のパラメータ 説明
managedEnvironmentId Container Apps Environment のIDを指定
configuration.ingress Container Apps イングレスの設定
configuration.secrets Container Apps 内のシークレット値
template.containers.image コンテナイメージ
template.containers.resources CPU, メモリサイズの指定
template.containers.env コンテナの環境変数
template.scale.cooldownPeriod スケールインするまでの時間(秒)を指定
template.scale.minReplicas リビジョンごとの最小レプリカ数
template.scale.maxReplicas リビジョンあたりのレプリカの最大数
template.scale.rules レプリカを追加または削除するタイミングを決定するためにコンテナー アプリによって使用される条件。スケールルールのトリガーは3つ指定可能で HTTP, TCP, カスタムメトリックがあります。例:http として、concurrentRequests: 10 とすると同時実行された HTTP 要求数がこの値を超えると、レプリカが 1つ追加されます。(maxReplicasまで)
@description('Container Appsの名前')
param containerAppsName string

@description('すべてのリソースのロケーション')
param location string = resourceGroup().location

@description('Container Apps 環境の名前')
param containerAppEnvironmentName string

@description('Container Appsが属する環境名')
param environmentName string

@description('リソース名のサフィックス')
param resourceSuffixName string

@description('Container Appsの設定')
@secure()
param containerAppsSetting object

@description('Application Insightsの接続文字列')
param appInsightsConnectionString string

@description('リソースに付与するタグ')
param tags object = {}

resource containerAppEnvironment 'Microsoft.App/managedEnvironments@2025-01-01' existing = {
  name: containerAppEnvironmentName
}

//https://learn.microsoft.com/en-us/azure/templates/microsoft.app/2025-01-01/containerapps?pivots=deployment-language-bicep
//https://learn.microsoft.com/ja-jp/azure/container-apps/scale-app?pivots=azure-cli
resource containerApps 'Microsoft.App/containerApps@2025-01-01' = {
  name: containerAppsName
  location: location
  tags: tags
  properties: {
    managedEnvironmentId: containerAppEnvironment.id
    configuration: {
      ingress: {
        external: containerAppsSetting.ingress.external
        targetPort: containerAppsSetting.ingress.targetPort
        transport: containerAppsSetting.ingress.transport
        allowInsecure: containerAppsSetting.ingress.allowInsecure
        traffic: [
          {
            latestRevision: true
            weight: 100
          }
        ]
      }
      secrets: containerAppsSetting.secretVariables
    }
    template: {
      containers: [
        {
          name: 'c-${environmentName}-${containerAppsSetting.name}-${resourceSuffixName}'
          image: containerAppsSetting.image
          resources: {
            cpu: json(containerAppsSetting.cpu)
            memory: containerAppsSetting.memory
          }
          env: union(containerAppsSetting.environmentVariables, [
            {
              name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
              value: appInsightsConnectionString
            }
          ])
        }
      ]
      scale: {
        cooldownPeriod: containerAppsSetting.scale.cooldownPeriod
        pollingInterval: containerAppsSetting.scale.pollingInterval
        minReplicas: containerAppsSetting.scale.minReplicas
        maxReplicas: containerAppsSetting.scale.maxReplicas
        rules: containerAppsSetting.scale.rules
      }
    }
  }
}

4. Container Apps Jobs モジュール (infra/modules/container/jobs.bicep)

containerAppJobsSetting をパラメータとして渡して、設定値をContainer Apps jobs の Properties に設定してあげます。 triggerType は EventManualSchedule が選択できます。

triggerType
Event イベントドリブン ジョブをトリガーします。
例:Azure Service Bus、Kafka、RabbitMQ などのキューに新しいメッセージが追加されたときに実行されるジョブ。
ワークフローまたはパイプラインのキューに新しいジョブが格納されたときに実行されるセルフホステッド GitHub Actions ランナー または Azure DevOps エージェント。
Manual 手動ジョブでの実行をします。
例:一度限りの実行。
Schedule スケジュールされたジョブを実行します。cron 式で定義します。
例: */5 * * * * : 5分ごとの実行。

propertiesのtemplate以下は基本的にContainer Apps 同様です。

注意: replicaTimeout までに処理が終わらなければ、タイムアウトしてレプリカは停止します。

param location string = resourceGroup().location

param containerAppEnvironmentName string

param containerAppJobsSetting object

param appInsightsConnectionString string

param tags object = {}

resource containerAppEnvironment 'Microsoft.App/managedEnvironments@2025-01-01' existing = {
  name: containerAppEnvironmentName
}

// Container Apps Jobs の作成
//https://learn.microsoft.com/en-us/azure/templates/microsoft.app/2025-01-01/jobs?pivots=deployment-language-bicep
resource containerAppJobs 'Microsoft.App/jobs@2025-01-01' = {
  name: containerAppJobsSetting.name
  location: location
  tags: tags
  properties: {
    environmentId: containerAppEnvironment.id
    configuration: {
      triggerType: containerAppJobsSetting.triggerType
      replicaRetryLimit: containerAppJobsSetting.replicaRetryLimit
      replicaTimeout: containerAppJobsSetting.replicaTimeout
      eventTriggerConfig: containerAppJobsSetting.triggerType == 'Event' ? {
        parallelism: containerAppJobsSetting.parallelism
        replicaCompletionCount: containerAppJobsSetting.replicaCompletionCount
        scale: containerAppJobsSetting.jobScale == null ? null : {
          maxExecutions: containerAppJobsSetting.jobScale.maxExecutions
          minExecutions: containerAppJobsSetting.jobScale.minExecutions
          pollingInterval: containerAppJobsSetting.jobScale.pollingInterval
          rules: containerAppJobsSetting.jobScale.jobScaleRule == null ? null : [
            {
              auth: containerAppJobsSetting.jobScale.jobScaleRule == null ? null : [
                {
                  secretRef: containerAppJobsSetting.jobScale.jobScaleRule.ScaleRuleAuth.secretRef
                  triggerParameter: containerAppJobsSetting.jobScale.jobScaleRule.ScaleRuleAuth.triggerParameter
                }
              ]
              identity: containerAppJobsSetting.jobScale.jobScaleRule.identity
              metadata: containerAppJobsSetting.jobScale.jobScaleRule.metadata
              name: containerAppJobsSetting.jobScale.jobScaleRule.name
              type: containerAppJobsSetting.jobScale.jobScaleRule.type
            }
          ]
        }
      } : null
      manualTriggerConfig: containerAppJobsSetting.triggerType == 'Manual' ? {
        parallelism: containerAppJobsSetting.parallelism
        replicaCompletionCount: containerAppJobsSetting.replicaCompletionCount
      } : null
      scheduleTriggerConfig: containerAppJobsSetting.triggerType == 'Schedule' ? {
        cronExpression: containerAppJobsSetting.cronExpression
        parallelism: containerAppJobsSetting.parallelism
        replicaCompletionCount: containerAppJobsSetting.replicaCompletionCount
      } : null
    }
    template: {
      containers: [
        {
          name: containerAppJobsSetting.name
          image: containerAppJobsSetting.image
          env: union(containerAppJobsSetting.environmentVariables, [
            {
              name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
              value: appInsightsConnectionString
            }
          ])
          resources: {
            cpu: json(containerAppJobsSetting.cpu)
            memory: containerAppJobsSetting.memory
          }
        }
      ]
    }
  }
}

5. パラメータファイル (main.sbx.parameters.json)

メインとなるBicep コードの箇所で説明した Container Apps と Container Apps Jobs のパラメータ: containerAppsSettings と containerAppJobsSettings のみ以下に記載しています。

Jobs のContainer Image はバッチ処理のサンプルとして 開始時にログ出力して、 SLEEP_SECONDS (秒)待機、終了するバッチです。 Container Apps の Container Image は GET / と GET /test へリクエストできるサンプルです。

今回はいずれも公開されたImageですが、プライベートの場合も指定可能です。

    "containerAppJobsSettings": {
      "value": [
        {
          "name": "schedule-container-app-jobs",
          "image": "ghcr.io/hiroshi-ando-sti/githubpkg-containerregistry/container-app-jobs:main",
          "replicaTimeout": 4000,
          "replicaRetryLimit": 2,
          "parallelism": 1,
          "replicaCompletionCount": 1,
          "triggerType": "Schedule",
          "cronExpression": "0 */10 * * *",
          "cpu": "0.25",
          "memory": "0.5Gi",
          "environmentVariables": [
            {
              "name": "RETENTION_DAYS",
              "value": "30"
            },
            {
              "name": "SLEEP_SECONDS",
              "value": "3800"
            }
          ],
          "jobScale": {
            "maxExecutions": 1,
            "minExecutions": 1,
            "pollingInterval": "PT5M",
            "maxExecutionTime": "PT30M",
            "jobScaleRule": null
          }
        }
      ]
    },
    "containerAppsSettings": {
      "value": [
        {
          "name": "webapi",
          "image": "ghcr.io/hiroshi-ando-sti/githubpkg-containerregistry/webapi:main",
          "ingress": {
            "external": true,
            "targetPort": 8000,
            "transport": "auto",
            "allowInsecure": false
          },
          "secretVariables": [
            {
              "name": "app-private-key",
              "value": "app-private-key-value"
            }
          ],
          "cpu": "0.25",
          "memory": "0.5Gi",
          "environmentVariables": [
            {
              "name": "BATCH_SIZE",
              "value": "100"
            },
            {
              "name": "LOG_LEVEL",
              "value": "INFO"
            },
            {
              "name": "SLEEP_SECONDS",
              "value": "3800"
            }
          ],
          "scale": {
            "cooldownPeriod": 65,
            "pollingInterval": 30,
            "minReplicas": 0,
            "maxReplicas": 2,
            "rules": [
              {
                "name": "http-rule",
                "http":{
                  "metadata": {
                    "concurrentRequests": "2"
                  }
                }
              }
            ]
          }
        }
      ]
    }

デプロイ手順

1. リソースグループの作成

az group create --name rg-container-apps --location "Japan East"

2. Bicepコードによるデプロイ

cd infra
az deployment group create \
  --name rg-container-apps-deploy \
  --mode Complete \
  --resource-group rg-container-apps \
  --confirm-with-what-if \
  --template-file ./main.bicep \
  --parameters ./main.parameters.sbx.json

3. デプロイの確認

デプロイの確認は以下で行います。

# デプロイ状況を確認
az deployment group list --resource-group rg-container-apps --output table

# Container Appsの状況を確認
az containerapp list --resource-group rg-container-apps --output table

# Container Jobsの状況を確認
az containerapp job list --resource-group rg-container-apps --output table

Azure Portal 上の Container Apps Enviroment からも Container Apps が作成されていることが確認できます。

まとめ

Azure Container Apps と Jobs は、それぞれ異なる用途に最適化されたサーバーレスコンテナサービスです。

1. Azure Container Apps と Jobs の使い分けのポイント

Azure Container Apps は継続的に実行されるWebアプリケーションやAPI向けのサービスで、Azure Container Apps Jobs はバッチ処理や定期的なタスク向けのサービスです。

2. Container Apps と Jobs 実装時の考慮点

Container Apps と Jobs を実装する際は、以下の点を考慮することが重要です:

  • リソース配分:CPU とメモリの適切な設定
  • スケーリング戦略:トラフィックパターンに応じたスケールルールの設定
  • セキュリティ:シークレット管理
  • 監視とログ:Log analytics, Application Insights による可観測性の確保
  • コスト最適化:最小レプリカ数の調整とスケールイン時間の設定
  • トリガー:適切なトリガーの選択

Bicep による Infrastructure as Code と組み合わせて、スケーラブルで保守性の高いコンテナベースのアプリケーション基盤を構築してはいかがでしょうか。

参考URL

Azure Container Apps の概要

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

Azure Container Apps のコンテナー

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

Azure Container Apps のジョブ

https://learn.microsoft.com/ja-jp/azure/container-apps/jobs?tabs=azure-cli

 

ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

0人がこの投稿は役に立ったと言っています。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です