3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Azure Container Apps Jobs で GitHub Actions を実行して、Private Endpoint で保護された App Service にデプロイする

Posted at

はじめに

App Service や Azure Functions においてプライベートエンドポイントが有効にし、パブリックアクセスを無効にしている場合、デプロイ時には工夫が必要となります。

この記事では、Azure Container Apps Jobs を利用して、VNet 内で GitHub Actions セルフホステッドランナーを動かすことで、プライベートエンドポイントで保護されたリソース(App Service) にデプロイすることを試してみます。

TL;DR

  • Container Apps Environment ではカスタム VNet を利用する(ターゲットとなる App Service の Private Endpoint にアクセスするため)
  • GitHub の PAT のスコープはしっかり確認しましょう
  • Container Apps Jobs 上で動かすイメージはチュートリアルのものだと物足りないので、自前で用意する必要アリ

GitHub Action Runner on Container Apps jobs

Azure Container Apps jobs is 何?

ざっくりいうとイベントドリブンで動作するコンテナ。その分コンピューティングリソースの使用量を抑えられる。

Container Apps としてデプロイできるので、VNet にアクセスすることが可能となり、GitHub Actions や、Azur DevOps などの、CI/CD パイプラインをホストすることが想定される利用シナリオとしても挙げられている。

Azure Container Apps には、アプリとジョブという 2 種類のコンピューティング リソースがあります。
アプリは継続的に実行されるサービスです。 アプリ内のコンテナーが失敗すると、自動的に再起動されます。 アプリの例としては、HTTP API、Web アプリ、入力を継続的に処理するバックグラウンド サービスなどがあります。
ジョブとは、開始され、一定時間実行され、完了すると終了されるタスクです。 1 つのジョブを実行するたびに、通常、1 つの作業単位が実行されます。 ジョブの実行は、手動、スケジュール、またはイベントに応答して開始されます。 ジョブの例としては、オンデマンドで実行されるバッチ プロセスやスケジュール タスクなどがあります。

image.png

従量課金 プランでは、Azure Container Apps ジョブによって消費されるリソースには、アクティブな料金が課金されます。 ジョブが完了すると実行によってリソースの消費が停止されるため、アイドル料金はジョブには適用されません。

手順

基本的には以下のチュートリアルに沿って構成することでOK

本記事では VNet から App Service の Private Endpoint に対してアクセスするため、Container Apps Environment にてカスタム VNet を利用する構成とした。

はまったポイント

Workflow がキューに作成されても Job コンテナが起動されない

こんな感じで Queued ステータスでとまってしまう。
image.png

原因としては、GitHub の PAT を作成する際に対象のリポジトリを選択する箇所がある。ここで今回ワークフローを実行したいリポジトリの選択を誤っていたことで、ワークフローがいつまでたっても実行されないことになっていた。

image.png

image.png

これはなぜかというと、今回チュートリアルで作成したジョブコンテナのスケーリングルールでは Github Runner Scaler を用いている、このスケーラーは何をやっているかというと、api.github.com のリポジトリ系 API をインターバルに従ってキューの有無を監視、キューにアイテムがあればスケーリング(= Self hosted runner 用のコンテナの起動)といったことを実行している。
そのため、PAT が対象のリポジトリに対するアクセス権がないと、APIの応答が適切に得られず、スケーリングが発生しないという状態だった。

ContainerAppSystemLogs_CL ではスケーリングが発生したときのログは出力されるが、何も起きていない状態ではログが残らないため、解決のためにはスケーラーのコードを見て呼び出してそうな API を確認、PAT を使って API を叩いてみるといった地道なデバッグが必要だった。

$ curl -L \
  -H "Accept: application/vnd.github+json" \
  -H "Authorization: Bearer $GHPAT" \
  -H "X-GitHub-Api-Version: 2022-11-28" \
  https://api.github.com/repos/georgeOsdDev/ghaction-on-aca-jobs/actions/runs
{
  "message": "Not Found",
  "documentation_url": "https://docs.github.com/rest/actions/workflow-runs#list-workflow-runs-for-a-repository"
}

PAT の対象リポジトリを適切に設定すると、その PAT を使った API で応答が得られるようになる。
30秒のインターバルが設定されているので、PAT の設定変更から30秒以内にジョブの開始が確認できるはず。

    --min-executions 0 \
    --max-executions 10 \
    --polling-interval 30 \
    --scale-rule-name "github-runner" \
    --scale-rule-type "github-runner" \
    --scale-rule-metadata "github-runner=https://api.github.com" "owner=$REPO_OWNER" "runnerScope=repo" "repos=$REPO_NAME" "targetWorkflowQueueLength=1" \
    --scale-rule-auth "personalAccessToken=personal-access-token" \

なお、--scale-rule-metadata については、github-runner=https://api.github.com という指定は間違っている気がするので以下の Issue をたてた。

ContainerAppSystemLogs_CL では下記のような出力が確認できる。

image.png

Job 実行時に必要なツールが入っていない。

GitHub Hosted Runner の場合、開発で利用されることの多い一般的な言語やツール群がすでにインストールされている状態となっている。例えば Ubuntu 22.04

そのため、workflow 上で気軽に zip とかが使えるが、詳しくは後述するがチュートリアルで用いているジョブの実行イメージは ghcr.io/actions/actions-runner 単体となり、workflow 上で必要なものが足りない場合がある。App Service によって生成された yaml に対してだと、zipunzipaz cli が足りなかった。詳しくは後述。

どういうしくみで動いているのか

Workflow Job のキュー監視

これは前述のとおり github-runner という KEDA Scaler がポーリングでキューアイテムの有無を確認している。

How does it work?
The scaler will query the GitHub API to get the number of queued jobs in the specified repositories, subject to filters. If the number of queued jobs is equal to or greater than the targetWorkflowQueueLength, the scaler will scale up.

そのため、下記の資料にあるような GitHub 側からの Webhook を必要としているわけではなく、GitHub リポジトリ側に Container Apps Jobs の情報を明示的に登録する必要はない。

起動するコンテナイメージは何?

KEDA によってスケールが発生したときに起動するイメージは何をやっているのかを見てみる。
チュートリアルに沿って利用したコンテナイメージは以下の通り

az acr build \
    --registry "$CONTAINER_REGISTRY_NAME" \
    --image "$CONTAINER_IMAGE_NAME" \
    --file "Dockerfile.github" \
    "https://github.com/Azure-Samples/container-apps-ci-cd-runner-tutorial.git"

ここで指定している Dockerfike は以下のとおり

https://github.com/Azure-Samples/container-apps-ci-cd-runner-tutorial/blob/main/Dockerfile.github
FROM ghcr.io/actions/actions-runner:2.304.0
# for latest release, see https://github.com/actions/runner/releases

USER root

# install curl and jq
RUN apt-get update && apt-get install -y curl jq && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

COPY github-actions-runner/entrypoint.sh ./entrypoint.sh
RUN chmod +x ./entrypoint.sh

USER runner

ENTRYPOINT ["./entrypoint.sh"]
https://github.com/Azure-Samples/container-apps-ci-cd-runner-tutorial/blob/main/github-actions-runner/entrypoint.sh
#!/bin/sh -l

# Retrieve a short lived runner registration token using the PAT
REGISTRATION_TOKEN="$(curl -X POST -fsSL \
  -H 'Accept: application/vnd.github.v3+json' \
  -H "Authorization: Bearer $GITHUB_PAT" \
  -H 'X-GitHub-Api-Version: 2022-11-28' \
  "$REGISTRATION_TOKEN_API_URL" \
  | jq -r '.token')"

./config.sh --url $REPO_URL --token $REGISTRATION_TOKEN --unattended --ephemeral && ./run.sh

ghcr.io/actions/actions-runner:2.304.0 をベースイメージとして ./entrypoint.sh を追加している。
ghcr.io/actions/actions-runner は何かというと、GitHub が提供している ランナーの公式イメージです。

./entrypoint.sh では、エフェメラル ランナーの登録を実行したのちに、Runner そのものを起動する ./run.sh が実行されている。

エフェメラル ランナーの確認

通常時は以下の通り、Self-hosted runner は未登録
image.png

Container Apps Jobs が動作したタイミングで、以下のように追加されている。
image.png

エフェメラルランナーとして、Container Apps Jobs で動作するコンテナが登録されることで、そのタイミングでキューにあるワークフローがそのランナーで実行されることになる。

ghcr.io/actions/actions-runner のバージョン

Dockerfile で指定されている 2.304.0 はちょっと古い(最新はv2.311.0) 。latest 指定する必要はないのかなと思ったが、自動更新をよしなにやってくれる模様。

ContainerAppConsoleLogs_CL からその様子を確認できる。

image.png

また、GitHub 上でも同じく最新版で動作したことを確認

image.png

通信経路

以下のとおり、GitHub から何かを受信するものではなく、セルフホステッドランナー側からアウトバウンド通信が発生する仕組みとなっている。

セルフホステッド ランナーが GitHub.com への接続を開くので、ユーザーはセルフホステッド ランナーへのインバウンド接続を行うことを GitHub に許可する必要はありません。

セルフホステッドランナーからのアウトバウンド通信は、上記ドキュメントに記載のように、github.comapi.github.com*.actions.githubusercontent.com などセルフホステッドランナーが通信できる必要がある FQDN が定義されている。すなわち、Container Apps Jobs をデプロイするサブネットの NSG アウトバウンドルールでは、Internet 向けの通信を許可しておく必要がある。

Private Endpoint で保護された App Service AppService のデプロイに使ってみる

前置きが長くなりましたが、実際に Private Endpoint で保護された App Service へのデプロイに利用してみます。

本題とは関係ないですが今ホットな Hono アプリをデプロイしてみました。

前提

以下の通り、パブリックアクセス無効でプライベートエンドポイントが有効。プライベートエンドポイントは先に作った Container Apps Environment の統合 VNet からアクセス可能な状態。

image.png

yaml ファイルは Azure ポータルのデプロイセンター経由で自動生成されたものを利用します。

main_webapp-deploy-test2023.yml
# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
# More GitHub Actions for Azure: https://github.com/Azure/actions

name: Build and deploy Node.js app to Azure Web App - webapp-deploy-test2023

on:
  push:
    branches:
      - main
  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Set up Node.js version
        uses: actions/setup-node@v3
        with:
          node-version: '20.x'

      - name: npm install, build, and test
        run: |
          npm install
          npm run build --if-present
          npm run test --if-present

      - name: Zip artifact for deployment
        run: zip release.zip ./* -r

      - name: Upload artifact for deployment job
        uses: actions/upload-artifact@v3
        with:
          name: node-app
          path: release.zip

  deploy:
    runs-on: self-hosted
    needs: build
    environment:
      name: 'Production'
      url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}
    permissions:
      id-token: write #This is required for requesting the JWT

    steps:
      - name: Download artifact from build job
        uses: actions/download-artifact@v3
        with:
          name: node-app

      - name: Unzip artifact for deployment
        run: unzip release.zip

      - name: Login to Azure
        uses: azure/login@v1
        with:
          client-id: ${{ secrets.AZUREAPPSERVICE_CLIENTID_070C1CDA7B1343E4AD320E57FBF78D5C }}
          tenant-id: ${{ secrets.AZUREAPPSERVICE_TENANTID_82953456381644C1B354BD1E088C79BB }}
          subscription-id: ${{ secrets.AZUREAPPSERVICE_SUBSCRIPTIONID_994B5092010F4FC7AE0F13F292786610 }}

      - name: 'Deploy to Azure Web App'
        id: deploy-to-webapp
        uses: azure/webapps-deploy@v2
        with:
          app-name: 'webapp-deploy-test2023'
          slot-name: 'Production'
          package: .

まずは、GitHub Hosted Runner で実行してみる。
想定通り 403 エラーでデプロイできない。

image.png

Container Apps Jobs 上の Self Hosted Runner から実行されるようにワークフローを変更する。

image.png

zip コマンドがなくてエラー

image.png

しょうがないので zipunzip を含むイメージを作成して 1.1 として ACR にプッシュ、ACA 側で参照タグを 1.1 に変更。

az コマンドがなくてエラー

image.png

しょうがないので az を含むイメージを作成して 1.2 として ACR にプッシュ、ACA 側で参照タグを 1.2 に変更

最終的に

最終的に以下のような Dockerfileとなった。

FROM ghcr.io/actions/actions-runner:2.304.0
# for latest release, see https://github.com/actions/runner/releases

USER root

# install curl and jq zip unzip, az-cli
RUN apt-get update && apt-get install -y curl jq zip unzip && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/* && \
    curl -sL https://aka.ms/InstallAzureCLIDeb | bash

COPY entrypoint.sh ./entrypoint.sh
RUN chmod +x ./entrypoint.sh

USER runner

ENTRYPOINT ["./entrypoint.sh"]

無事デプロイ成功

image.png

ターゲットとなる App Service のデプロイセンターのログは以下の通り。

image.png

Private Endpoint につながる別環境からもHonoアプリがデプロイされていること確認
image.png

めでたしめでたし。

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?