Organization配下のリポジトリで、Self-Hosted RunnerをACA Job上で動かす

# はじめに

Azure Container Apps Jobを使って、GitHub ActionsのSelf-Hosted Runnerを動かします。
導入方法については[公式チュートリアル](https://learn.microsoft.com/ja-jp/azure/container-apps/tutorial-ci-cd-runners-jobs?tabs=bash&pivots=container-apps-jobs-self-hosted-ci-cd-github-actions)があるため、これに沿って進めることで基本的には実装可能です。
ただし、以下の点についてはチュートリアル上で言及されておらず、実装時に躓きました。
- **Organization配下**のリポジトリに実装する
- Azure Portal上の操作で実装する

そこで、本記事ではこれらをポイントとしつつ、導入方法について紹介していきます。

# 実装手順
## 前提
以下が作成されていることを前提とします。
- リソースグループ
- ワークフローを実行するためのGitHubリポジトリ

# 実装手順
## 前提
以下が作成されていることを前提とします。
- リソースグループ
- ワークフローを実行するためのGitHubリポジトリ ※Organization配下

## PATの生成
リポジトリにSelf-Hosted Runnerを登録する手順は、リポジトリの **[Setting] > [Actions] > [Runners] > [New self-hosted runner]** から確認できます。
その中で、以下のようにconfig.shを実行するのですが、この時「OrganizationにRunnerを登録するためのToken」が必要になります。

![2024-09-10_21h41_15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489251/6bfe93a8-2742-e6ec-229d-e0a5e6c4d39a.png)

Tokenの値はREST API経由で取得できます。
[GitHub REST API - Create a registration token for an organization](https://docs.github.com/ja/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-a-registration-token-for-an-organization)

この際、必要な権限を付与したPATが必要になるので、以下の流れに沿ってPATを作成します。

<details><summary><b>GitHub - PAT作成画面</b></summary>

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489251/1af24130-2a45-b850-3675-4ce95d04244b.png" width=600px>

</details>

ポイントは以下です。

**■ Resource owner**
対象リポジトリの**Organization**を指定してください。
※事前にPATがOrganizationにアクセスすることを許可する必要があります。

https://docs.github.com/ja/organizations/managing-programmatic-access-to-your-organization/setting-a-personal-access-token-policy-for-your-organization

**■ Repository access**
[Only select repositories] から対象リポジトリを選択してください。

**■ Permissions**
以下の権限を追加します。
|permission|access|
|:-|:-|
|Actions|Read-only|
|Administration|Read and write|
|Metadata|Read-only|


**■ Organization permissions**
以下の権限を追加します。

|permission|access|
|:-|:-|
|Administration|Read and write|
|Custom organization roles|Read and write|
|Self-hosted runners|Read and write|

**[Generate token]** を押下するとトークンが発行されます。
表示された文字列を控えておきます。

## ランナーのコンテナイメージのビルド

ACA Jobs上でSelf-Hosted Runnerを動かすために、コンテナレジストリの作成と、コンテナーイメージのビルド及びレジストリへのプッシュを行います。

### 1. コンテナレジストリの作成
```bash
az acr create --name "$CONTAINER_REGISTRY_NAME" --resource-group "$RESOURCE_GROUP" --location "$LOCATION" --sku Basic --admin-enabled true
```
上記のコマンドと同じことをPortal上で行います。

![2024-09-08_11h02_41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489251/2edd4b60-54ea-02f3-02c5-c11c7dbead69.png)

作成画面まで進んだら、**[基本]** タブでサブスクリプション、リソースグループ、レジストリ名、ロケーション、プラン(**Basic**)をそれぞれ指定して、作成します。

この他、作成したコンテナイメージをAzure Container Apps Jobから参照するために「管理者ユーザの有効化」が必要になります。
コマンドでは```--admin-enabled true```で有効化していますが、Portalの場合は作成時に設定ができないため、作成後に **[設定] > [アクセスキー]** から「管理者ユーザ」を有効化しておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489251/93000a60-5d45-5cc7-7671-157adc0c255f.png)

### 2. イメージのビルド
Runnerのイメージはチュートリアル内で紹介されている以下のリポジトリを使って作成できます。

https://github.com/Azure-Samples/container-apps-ci-cd-runner-tutorial

ただし今回はRunnerに一部カスタマイズを加えたいので、上記のリポジトリを参考に別途リポジトリを作成しました。
作成したら、以下のコマンドを叩いてイメージをビルドします。

```bash
az acr build `
    --registry "<作成したコンテナレジストリ名>" `
    --image github-actions-runner:1.0 `
    --file "Dockerfile.github" `
    "https://github.com/<作成したリポジトリ>.git"
```

ビルドの完了後、イメージが作成されていることをPortal上で確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489251/db6b2e8f-abff-0733-1958-6d018f83bd2f.png)

## ランナーのデプロイ

CLIの場合は以下のコマンドでContainer Apps 環境とJobを作成できます。
```bash
az containerapp job create -n "$JOB_NAME" -g "$RESOURCE_GROUP" --environment "$ENVIRONMENT"
  --trigger-type Event
  --replica-timeout 1800
  --replica-retry-limit 0
  --replica-completion-count 1
  --parallelism 1
  --image "$CONTAINER_REGISTRY_NAME.azurecr.io/$CONTAINER_IMAGE_NAME"
  --min-executions 0
  --max-executions 10
  --polling-interval 30
  --scale-rule-name "github-runner"
  --scale-rule-type "github-runner"
  --scale-rule-metadata "githubAPIURL=https://api.github.com" "owner=$REPO_OWNER" "runnerScope=repo" "repos=$REPO_NAME" "targetWorkflowQueueLength=1"
  --scale-rule-auth "personalAccessToken=personal-access-token"
  --cpu "2.0"
  --memory "4Gi"
  --secrets "personal-access-token=$GITHUB_PAT"
  --env-vars "GITHUB_PAT=secretref:personal-access-token" "GH_URL=https://github.com/$REPO_OWNER/$REPO_NAME" "REGISTRATION_TOKEN_API_URL=https://api.github.com/repos/$REPO_OWNER/$REPO_NAME/actions/runners/registration-token"
  --registry-server "$CONTAINER_REGISTRY_NAME.azurecr.io"
```
こちらもPortal上の操作で実行します。

1. リソースの作成から``container App ジョブ``を選択します。
  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489251/e1faed5a-1182-ee38-412e-77d0da9db7fe.png)

2. **[基本]** タブで各情報を入力します。
  ![2024-09-08_15h21_51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489251/1bfb963d-0177-a078-d291-156d0e8d5274.png)
  **[トリガーの種類]** は **[イベントドリブン]** を選択します。
  続いて、Container Apps 環境を作成するために **[新規作成]** をクリックします。

3. Container Apps 環境の作成画面が開きます。とりあえず環境名だけ入力したら、その他は特にいじらなくて大丈夫です。
  ![2024-09-08_13h08_25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489251/553d754e-6ac2-1878-d0bd-290a03162a62.png)

4. Jobの作成画面に戻ってきたら、 **[コンテナー]** タブで各種設定を行います。
    - **コンテナ―の詳細**
      ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489251/248fd375-4cbd-0266-4cc5-a4d5c7635425.png)
      先ほど作成したコンテナレジストリとビルドしたイメージを指定します。
      この時、「管理者ユーザの有効化」を行っていない場合はエラーになります。
      ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489251/9100d863-de12-4961-7996-2a8079da3ff0.png)

    - **コンテナリソースの割り当て**
      ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489251/45cf17d8-e5b1-dc09-f726-41df0fa37aa4.png)
      CPUとメモリを指定します。チュートリアルのコマンドでは「2コア」「4GBメモリ」を指定していたので、その通りに指定しています。
    - **環境変数**
      ```bash
      --env-vars "GITHUB_PAT=secretref:personal-access-token" "GH_URL=https://github.com/$REPO_OWNER/$REPO_NAME" "REGISTRATION_TOKEN_API_URL=https://api.github.com/repos/$REPO_OWNER/$REPO_NAME/actions/runners/registration-token"
      ```
      上記の環境変数を画面から設定します。
      ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489251/30f92481-0ed6-ab9c-aa56-30c4d6e3ddd3.png)
      |名前|値|
      |:-|:-|
      |GH_URL|`https://github.com/{Oranization名}`|
      |REGISTRATION_TOKEN_API_URL|`https://api.github.com/orgs/{Organization名}/actions/runners/registration-token`|

      今回はリポジトリではなくOrganizationにRunnerを登録するため、チュートリアルとは少し設定値が異なっています。
      `GITHUB_PAT`は最初に作成したPATを指定しますが、直接入力するのではなく、「シークレット」に保存した上でその値を参照します。ただし、この時点では「シークレット」への保存がPortalからは行えないため、リソースの作成後に設定します。
    - **スケールルールの設定**
      ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489251/2de404ed-c5a1-1c75-a12c-898be36443f4.png)
      Runnerの同時実行数と、GitHubに対するポーリングの間隔を指定します。
    - **スケールルール**
      **[+ 追加]** ボタンからスケールルールを追加できます。
      ```bash
      --scale-rule-name "github-runner"
      --scale-rule-type "github-runner"
      --scale-rule-metadata "githubAPIURL=https://api.github.com" "owner=$REPO_OWNER" "runnerScope=repo" "repos=$REPO_NAME" "targetWorkflowQueueLength=1"
      --scale-rule-auth "personalAccessToken=personal-access-token"
      ```
      上記のスケールルールを画面上から設定します。
      ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489251/a8548d90-3561-10c4-d25c-860238ec196b.png)

      |名前|値|
      |:-|:-|
      |githubAPIURL|`https://api.github.com`|
      |owner|`Organization名`|
      |runnerScope|`org`|
      |targetWorkflowQueueLength|1|

      各メタデータの意味は[github-runner スケールルールのドキュメント](https://keda.sh/docs/2.15/scalers/github-runner/)から確認できます。
      OrganizationにRunnerを登録するため、`owner`と`runnerScope`の設定値がチュートリアルとは異なっています。また、`repos`は設定不要です。
      `--scale-rule-auth`についてはシークレットに保存したPATを参照する形にしたいのですが、先ほどと同様この時点で設定できないため、リソースの作成後に設定します。
5. 以上が設定できたらリソースを作成します。
6. リソースの作成後、PATをシークレットに保存します。
  ![2024-09-10_23h13_47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489251/ecfcb007-5248-c223-e44c-59179ec129fe.png)
7. スケールルールの「認証」からシークレットに保存したPATを割り当てます。
  ![2024-09-10_23h14_41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489251/86dc3bc6-35a7-dbeb-5d98-4470549cd7fb.png)
8. コンテナアプリの環境変数`GITHUB_PAT`に、シークレットに保存したPATを割り当てます。
  ![2024-09-10_23h15_19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489251/d3157aa7-b751-b221-b7ad-b30c278e8c57.png)

以上でAzure側の設定は完了です。

## ワークフロー
GitHub ActionsではJobを実行するランナーを指定することができます。

https://docs.github.com/ja/actions/writing-workflows/choosing-where-your-workflow-runs/choosing-the-runner-for-a-job

今回実装したSelf-Hosted Runnerを利用する場合は、`runs-on`を以下の通り指定します。
```yml
runs-on: self-hosted
```

実装内容に問題なければActionをスタートするとAzure側でRunnerコンテナが起動して、30秒程度でJobの処理が開始します。