🦁

Azure Container Apps を少し触ったメモ

2022/02/13に公開

先日、少し Azure Container Apps を触ってみました。その時のことを忘れないようにメモしておこうと思います。

Azure Container Apps とは

公式ドキュメントに詳細は譲りますが、サーバーレスで Docker イメージ化されたアプリをホストするための Azure のサービスで現時点では公開プレビューというステータスのサービスです。

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

試した内容

Azure Container Apps に Azure Container Registry に push したイメージのアプリをデプロイする。

Visual Studio 2022 のプレビュー版には、GitHub のリポジトリに紐づいているプロジェクトで発行をすると Azure Container Apps にデプロイするワークフローを出力してくれる機能があるので、それをベースにデプロイする方法を試してみました。あと、bicep で Azure Container Apps を作る方法も確認してみました。

Bicep で Azure Container Apps を作る

Azure Container Apps をデプロイするには最低で 3 つのリソースが必要です。コンテナイメージを置く場所も用意する場合は Azure Container Registry も必要なので 4 つになります。内訳は以下のようになります。

  • Azure Log Analytics Workspace
    • ログ出力先
  • Azure Contaienr Apps Environment
    • この中に Contaienr App をおいていくイメージ
  • Azure Container Registry
    • Docker イメージ置き場
  • Azure Container App
    • ここでアプリが動く。デプロイするイメージやメモリやCPUの設定や、スケールのルールもここに設定する。

まずは、Contaienr App をデプロイする前に上の 3 つのリソースをデプロイしないといけないので、その部分の bicep を作りました。以下のような感じです。

main.bicep
targetScope = 'resourceGroup'

param baseName string // リソースのベースの名前
param location string = resourceGroup().location

var resourceName = '${baseName}${uniqueString(resourceGroup().id)}'

// Log Analytics Workspace
resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-06-01' = {
  name: 'la-${resourceName}'
  location: location
  properties: {
    sku: {
      name: 'PerGB2018'
    }
  }
}

// Container Registry
resource containerRegistry 'Microsoft.ContainerRegistry/registries@2021-09-01' = {
  name: 'cr${resourceName}'
  location: location
  sku: {
    name: 'Standard'
  }
  properties: {
    adminUserEnabled: true
  }
}

// Container Apps 環境
resource env 'Microsoft.Web/kubeEnvironments@2021-02-01' = {
  name: 'ce-${resourceName}'
  location: location
  properties: {
    type: 'managed'
    internalLoadBalancerEnabled: false
    appLogsConfiguration: {
      // ログの出力先の設定
      destination: 'log-analytics'
      logAnalyticsConfiguration: {
        customerId: logAnalytics.properties.customerId
        sharedKey: logAnalytics.listKeys().primarySharedKey
      }
    }
  }
}

これを Azure CLI などで以下のようにデプロイします。

az group create --name rg-zenn --location "Canada Central"
az deployment group create --resource-group rg-zenn --template-file main.bicep --parameters baseName='kazukica'

次に Container App のデプロイもします。多分、環境のデプロイとアプリのデプロイは別タイミングでやることが多いと思うので別ファイルにしています。Container App 用のデフォルトのイメージをデプロイしたアプリを bicep で定義すると以下のような感じになります。

app.bicep
targetScope = 'resourceGroup'

param baseName string
param location string = resourceGroup().location

var resourceName = '${baseName}${uniqueString(resourceGroup().id)}'

resource env 'Microsoft.Web/kubeEnvironments@2021-02-01' existing = {
  name: 'ce-${resourceName}'
}

resource app1 'Microsoft.Web/containerApps@2021-03-01' = {
  name: 'app1'
  kind: 'containerapp'
  location: location
  properties: {
    kubeEnvironmentId: env.id
    configuration: {
      activeRevisionsMode: 'single'
      ingress: {
        external: true
        targetPort: 80
      }
    }
    template: {
      containers: [
        {
          name: 'simple-hello-world-container'
          image: 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
          resources: {
            cpu: '.25'
            memory: '.5Gi'
          }
        }
      ]
      scale: {
        minReplicas: 1
        maxReplicas: 3
      }
    }
  }
}

これも、先ほどと同様に az コマンドでデプロイします。

az deployment group create --resource-group rg-zenn --template-file app.bicep --parameters baseName='kazukica'

アプリのデプロイ

アプリのデプロイは適当に .NET 6 の Web アプリを Docker のサポートにチェックを入れて作ったものをデプロイしてみました。作成時に Docker のサポートにチェックを入れ忘れても、作成後にプロジェクトの右クリックメニューの追加から Docker ファイルを追加することができます。

このプロジェクトを GitHub のリポジトリと紐づけた状態で Azure に発行を行うと発行のウィザードで Azure Container Apps が選択出ます。

そして、Container App の選択画面が出てくるので先ほど作った Container App を選択します。

コンテナ名は先ほどデフォルトのイメージをデプロイした simple-hello-world-container を選択します。

続けて Docker イメージを push するリポジトリも選択します。

そして、最後に GitHub Actions を選択すると発行のウィザードは完了です。

これで GitHub の方にワークフローで使う資格情報などがシークレットに自動的に登録されます。

APP1_SPN が Azure にデプロイをするために使われる資格情報で、下の2つが Azure Container Registry のユーザー名とパスワードになります。

生成される YAML は以下のようになっていますが、このままだとちょっと動きません。まだプレビューなので、こなれてない感じですが、正式版だとちゃんと動くようになると思います。

app1.yml
name: Build and deploy .NET application to Container App app1
on:
  push:
    branches:
    - master
env:
  CONTAINER_APP_CONTAINER_NAME: simple-hello-world-container
  CONTAINER_APP_NAME: app1
  CONTAINER_APP_RESOURCE_GROUP_NAME: rg-zenn
  CONTAINER_REGISTRY_LOGIN_SERVER: crkazukicaragl6rolk7hnw.azurecr.io
  DOCKER_FILE_PATH: ./Dockerfile
  PROJECT_NAME_FOR_DOCKER: webapplication8
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout to the branch
      uses: actions/checkout@v2
    - name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v1
    - name: Log in to container registry
      uses: docker/login-action@v1
      with:
        registry: ${{ env.CONTAINER_REGISTRY_LOGIN_SERVER }}
        username: ${{ secrets.crkazukicaragl6rolk7hnw_USERNAME_FFFF }}
        password: ${{ secrets.crkazukicaragl6rolk7hnw_PASSWORD_FFFF }}
    - name: Build and push container image to registry
      uses: docker/build-push-action@v2
      with:
        push: true
        tags: ${{ env.CONTAINER_REGISTRY_LOGIN_SERVER }}/${{ env.PROJECT_NAME_FOR_DOCKER }}:${{ github.sha }}
        file: ${{ env.DOCKER_FILE_PATH }}
  deploy:
    runs-on: ubuntu-latest
    needs: build
    steps:
    - name: Azure Login
      uses: azure/login@v1
      with:
        creds: ${{ secrets.app1_SPN }}
    - name: Deploy to containerapp
      uses: azure/CLI@v1
      with:
        inlineScript: >
          echo "Installing containerapp extension"

          az extension add --source https://workerappscliextension.blob.core.windows.net/azure-cli-extension/containerapp-0.2.1-py2.py3-none-any.whl --yes

          echo "Starting Deploying"

          az containerapp update --name ${{ env.CONTAINER_APP_NAME }} --resource-group ${{ env.CONTAINER_APP_RESOURCE_GROUP_NAME }} -i ${{ env.CONTAINER_REGISTRY_LOGIN_SERVER }}/${{ env.PROJECT_NAME_FOR_DOCKER }}:${{ github.sha }} --registry-login-server ${{ env.CONTAINER_REGISTRY_LOGIN_SERVER }} --registry-username ${{ secrets.REGISTRY_USERNAME }} --registry-password ${{ secrets.REGISTRY_PASSWORD }} --debug
    - name: logout
      run: >
        az logout

上側の build のジョブで Azure Container Registry にイメージを push しているのですが、Docker ファイルへのパスが ./Dockerfile になっています。正確には ./プロジェクト名/Dockerfile になるので環境変数の DOCKER_FILE_PATH の修正をします。

そして下側の Azure Container App へのデプロイをしている deploy ジョブの az containerapp update の行で指定している --registry-username と --registry-password に指定されているシークレットのキーが間違っています。正確には GitHub Actions のシークレットの名前と合わせないといけないので build ジョブで使われているものと同じものを指定しないといけません。

この修正を行って GitHub の push するとアプリがビルドされて Azure Container Registry に登録されて、Container App にデプロイされます。

ただ、この方法だと CPU やメモリやスケールの設定を変更しようとするとポータルではまだサポートされていなさそうだし Contaienr App をデプロイした bicep をいじってデプロイすると、多分デプロイされているイメージまで上書きされちゃいそうなので、このデフォルトのスクリプトの deploy ジョブは以下のように置き換えてみました。

方針としては、ここで bicep を使って Container App に新しいイメージにデプロイしています。

以下のような bicep をソリューションの任意の場所に起きます。

app.bicep
targetScope = 'resourceGroup'

param projectNameForDocker string
param containerAppName string
param revision string
param containerLoginServer string
@secure()
param containerUserName string
@secure()
param containerPassword string
param baseName string
param location string = resourceGroup().location

var resourceName = '${baseName}${uniqueString(resourceGroup().id)}'

resource env 'Microsoft.Web/kubeEnvironments@2021-02-01' existing = {
  name: 'ce-${resourceName}'
}

resource app1 'Microsoft.Web/containerApps@2021-03-01' = {
  name: containerAppName
  kind: 'containerapp'
  location: location
  properties: {
    kubeEnvironmentId: env.id
    configuration: {
      secrets: [
        {
          name: 'acrpassword'
          value: containerPassword
        }
      ]
      registries: [
        {
          server: containerLoginServer
          username: containerUserName
          passwordSecretRef: 'acrpassword'
        }
      ]
      ingress: {
        external: true
        targetPort: 80
      }
    }
    template: {
      revisionSuffix: revision
      containers: [
        {
          name: containerAppName
          image: '${containerLoginServer}/${projectNameForDocker}:${revision}'
          resources: {
            cpu: '.25'
            memory: '.5Gi'
          }
        }
      ]
      scale: {
        minReplicas: 1
        maxReplicas: 3
      }
    }
  }
}

そして、この bicep を使ってデプロイするように GitHub Actions のワークフローを書き換えます。

name: Build and deploy .NET application to Container App app1
on:
  push:
    branches:
    - master
env:
  RESOURCE_BASE_NAME: kazukica
  CONTAINER_APP_CONTAINER_NAME: simple-hello-world-container
  CONTAINER_APP_NAME: app1
  CONTAINER_APP_RESOURCE_GROUP_NAME: rg-zenn
  CONTAINER_REGISTRY_LOGIN_SERVER: crkazukicaragl6rolk7hnw.azurecr.io
  DOCKER_FILE_PATH: ./WebApplication8/Dockerfile
  PROJECT_NAME_FOR_DOCKER: webapplication8
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout to the branch
      uses: actions/checkout@v2
    - name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v1
    - name: Log in to container registry
      uses: docker/login-action@v1
      with:
        registry: ${{ env.CONTAINER_REGISTRY_LOGIN_SERVER }}
        username: ${{ secrets.crkazukicaragl6rolk7hnw_USERNAME_FFFF }}
        password: ${{ secrets.crkazukicaragl6rolk7hnw_PASSWORD_FFFF }}
    - name: Build and push container image to registry
      uses: docker/build-push-action@v2
      with:
        push: true
        tags: ${{ env.CONTAINER_REGISTRY_LOGIN_SERVER }}/${{ env.PROJECT_NAME_FOR_DOCKER }}:${{ github.sha }}
        file: ${{ env.DOCKER_FILE_PATH }}
  deploy:
    runs-on: ubuntu-latest
    needs: build
    steps:
    - name: Checkout to the branch
      uses: actions/checkout@v2
    - name: Azure Login
      uses: azure/login@v1
      with:
        creds: ${{ secrets.app1_SPN }}
    - name: deploy
      uses: azure/arm-deploy@v1
      with:
        subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION }}
        resourceGroupName: ${{ env.CONTAINER_APP_RESOURCE_GROUP_NAME }}
        template: ./app.bicep
        parameters: projectNameForDocker=${{ env.PROJECT_NAME_FOR_DOCKER }} containerAppName=${{ env.CONTAINER_APP_NAME }} revision=${{ github.sha }} containerLoginServer=${{ env.CONTAINER_REGISTRY_LOGIN_SERVER }} containerUserName=${{ secrets.crkazukicaragl6rolk7hnw_USERNAME_FFFF }} containerPassword=${{ secrets.crkazukicaragl6rolk7hnw_PASSWORD_FFFF }} baseName=${{ env.RESOURCE_BASE_NAME }}
        failOnStdErr: false
    - name: logout
      run: >
        az logout

このほかにサービスプリンシパル(secrets.app1_SPN) の権限がデプロイするためにはちょっと足りないので足しておきます。今回の場合は app1 という名前のサービスプリンシパルが作られているのでリソースグループのアクセス制御から共同作成者の権限をつけておきます。

こうすると、メインのブランチに更新があるたびに、Container App にアプリがデプロイされるようになります。スケールとか変えたかったら bicep いじっただけでもこの形のパイプラインだとアプリも再デプロイなのがちょっと気になる今日この頃。

Microsoft (有志)

Discussion