Young Leaves

Azure Kubernetes Service にActions Runner Controller を導入しSelf-hosted runner 環境を構築する

社内やプライベートな環境でGitHub Actions の実行環境をKubernetes 上にデプロイし管理したいことがあります。今回はAzure のマネージドKubernetes サービスであるAzure Kubernetes Service を使いActions Runner Controller のデプロイからGitHub Actions のジョブ実行までを説明します。

実施環境

Azure CLI

2.68.0

Kubernetes (AKS)

1.30.7

Actions Runner Controller

0.10.1

前提条件

  • Azure のアカウントおよびサブスクリプションを持っていること
  • GitHub のアカウントおよびパブリックリポジトリがあること
  • Azure CLI でaks-preview 拡張機能がインストールされていること

Azure Kubernetes Service とは

Azure Kubernetes Service (以下AKS) はAzure 上でコンテナ化したアプリケーションをデプロイし管理できるマネージドKubernetes サービスです。AKS はマネージドサービスのため、従来の複雑なKubernetes 運用・管理の多くをAzure 側に任せることができます。そのため、運用・管理コストの削減やコンテナアプリケーションの開発に集中できます。

AKS では複数のバージョンをサポートしています。現在サポートしているKubernetes のバージョンは以下ページを参照してください。

Azure Kubernetes Service (AKS) でサポートされている Kubernetes のバージョン

AKS ではKubernetes の機能と共にAzure の各種サービスを用いた様々な機能があります。開発面では混在するOS とWindows コンテナをサポートする複数のノードプールを実行するクラスターを利用できます。また、KEDA (Kubernetes Event Driven Autoscaler) による自動スケーリングやDraft を使ったアプリケーションの準備、Helm やDapar、Istio などの開発ツールとの連携もできます。運用面ではクラスター、Pod による自動スケーリングやKubernetes の自動アップグレード、Azure Monitor + Container Insight を用いたコンテナの正常性やパフォーマンス監視があります。セキュリティ・ガバナンスではMicrosoft Entra ID を利用したユーザーアクセス制御やRBAC やAzure Policy、Microsoft Defender for Cloud による制御ができます。

AKS ではクラスター作成時に価格レベルを選択します。価格レベルはFree、Standard、Premium の3つがあります。Free は開発/テスト用のレベルであり、クラスター料金の従量課金はありませんがSLA が無い、10ノード未満を推奨、と制約があります。Standard はミッションクリティカルなワークフローや5000ノードまでのアプリケーション、AKS に対するSLA が必要な場合に利用します。Standard 以上の価格レベルはクラスターに対しても従量課金が発生します。Premium はStandard に加えKubernetes で2年間のLTS サポートやMicrosoft メンテナンスの過去のコミュニティサポートが追加されます。価格レベルの詳細は以下ページを参照してください。

Azure Kubernetes Service (AKS) クラスター管理の Free、Standard、Premium 価格レベル

AKS は主に以下のようなユースケースで利用されます。

  • ミッションクリティカルなコンテナアプリケーションの構築
  • 既存コンテナアプリケーションのリフトアンドシフト
  • Azure を利用したマイクロサービスの構築
  • セキュリティで保護されたDevOps 環境
  • 大規模な機械学習モデルのトレーニング、データストリーミング
  • Kubernetes におけるWindows コンテナの利用

AKS の料金体系はベースとなるVM サイズと価格レベルに応じたクラスターの従量課金制です。VM ベースのため、AKS では予約やSavings Plan などの割引プランを利用できます。長期的な利用を検討する場合は割引プランを利用すると料金を削減できることもあります。AKS の詳細な料金は以下ページを参照してください。

Azure Kubernetes Service (AKS) の価格

GitHub Actions Runner Controller とは

GitHub Actions Runner Controller (以下ARC) はSelf-hosted runner の調整や自動スケーリングを行うためのKubernetes オペレーターです。ARC を利用すると組織やユーザーはワークフローに応じて自動的にスケーリングできるランナースケールセットを作成できます。Self-hosted runner ではワークフローの数が増加するに伴いワークフロー実行までの待ち時間が増加する、ワークフローに応じたスペック、環境が欲しいといった状況が発生します。ARC を使うことで、これらの課題を解決し、ワークフロー実行や管理を最適化できます。

ARC は2023年6月30日にGA となった機能です。GA となってからまだ日は浅いですが、規模の大きい企業で導入されることもあります。

ARC ではコントロールマネージャーとリスナー、エフェメラルランナーの3つがあります。以下は公式ドキュメントの画像ですが、それぞれの役割は以下となります。

  1. GitHub からARC のコントロールマネージャーにワークフローの実行を伝える
  2. ARC のコントロールマネージャーはGitHub にランナーグループのID を返す
  3. GitHub からARC のリスナーにランナーID を伝える
  4. GitHub 上でワークフローの実行がトリガーされる
  5. GitHub からジョブが実行可能なことをリスナーに伝える。リスナーはスケールアップ可能か確認する
  6. ARC のリスナーからランナースケールセットのランナー数にパッチを適用する
  7. エフェメラルランナーセットで新しいランナーポッドを作成する
  8. ランナーポッド作成後、JIT トークンを使用しジョブの詳細を受け取る
  9. GitHub でランナー登録の受信確認をし、ジョブを割り当てる
  10. エフェメラルランナーでジョブを実行する
  11. ジョブ完了後、ARC からGitHub にエフェメラルランナーを削除できるか確認する。削除可能ならエフェメラルランナーを削除する

ARC で提供されるランナーコンテナイメージはコンテナランタイムと最小限のパッケージが導入されたものとなります。追加のパッケージや独自のカスタマイズを行いたい場合、以下の要件を満たす必要があります。

  • セルフホステッド ランナー アプリケーションを実行できる基本イメージを使用している
  • ランナー バイナリを /home/runner/ の下に配置し、/home/runner/run.sh を使用して起動している
  • Kubernetes モードを使用する場合は、ランナー コンテナー フックを /home/runner/k8s の下に配置している

ARC は現時点でOpenShift ではサポートされていません。GitHub Enterprise Server は3.9 以降のみサポートされています。

今回の構成

今回はAKS のプライベートクラスターにARC を導入し、ワークフローを実行するところまで実施します。プライベートクラスターでインターネットから直接通信できないため、AKS の各種設定はAzure VM を介して設定します。

Personal Access Token (PAT) の作成

初めにランナースケールセット の登録用にGitHub 上でPersonal Access Token (PAT) を登録します。GitHub 右上のプロフィールアイコンから Settings > Developer settings > Fine-grained tokens を選択し、Generate new token を選択します。

各種項目を入力し、Generate token を選択します。今回入力する項目は以下とします。

Token name

arc-test

Resource owner

自身のGitHub アカウント

Expiration

30 days

Description

ARC テスト用トークン。

Repository access

Only select repository からARC で使うリポジトリを選択

Permission

Repository permissions からAdministration > Read and write

作成後、トークンの値が表示されるためメモします。このトークンの値は後述のランナースケールセットのインストール時に使用します。

リソースグループ、AKS クラスターの作成

GitHub のPAT を作成後、変数の定義とAKS 用のリソースグループを作成します。

# 変数を定義する
RESOURCE_GROUP_NAME="rg-arctest"
NETWORK_SECURITY_GROUP_NAME="nsg-arctest"
VIRTUAL_NETWORK_NAME="vnet-arctest"
VM_SUBNET_NAME="snet-vm"
AKS_SUBNET_NAME="snet-aks"
VM_PUBLIC_IP_ADDRESS_NAME="pip-vm"
NAT_PUBLIC_IP_ADDRESS_NAME="pip-nat"
NETWORK_INTERFACE_NAME="nic-arctest"
VIRTUAL_MACHINE_NAME="vm-arctest"
OS_DISK_NAME="osdisk-arctest"
ADMIN_USER_NAME="azureuser"
ADMIN_USER_PASSWORD="zRd7-v=sUM5p"
NAT_GATEWAY_NAME="nat-arctest"
AKS_CLUSTER_NAME="aks-arctest"
LOCATION="japaneast"

# リソースグループを作成する
az group create --name $RESOURCE_GROUP_NAME --location $LOCATION

リソースグループ作成後、AKS 設定用のAzure VM を作成します。今回はAzure VM 作成時にAzure CLI、kubectl、Helm をインストールするため、事前にcloud-init の定義ファイルを作成します。

#cloud-config
package_update: true
package_upgrade: true
packages:
  - curl
  - apt-transport-https
  - snapd

runcmd:
  - curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
  - snap install kubectl --classic
  - curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
  - chmod 700 get_helm.sh
  - ./get_helm.sh

cloud-init の定義ファイルを作成後、Azure VM を作成します。

# ネットワークセキュリティグループを作成する
az network nsg create \
    --resource-group $RESOURCE_GROUP_NAME \
    --name $NETWORK_SECURITY_GROUP_NAME

# SSH接続用の受信ルールを作成する
az network nsg rule create \
    --resource-group $RESOURCE_GROUP_NAME \
    --nsg-name $NETWORK_SECURITY_GROUP_NAME \
    --name AllowSSHAccess \
    --priority 100 \
    --direction Inbound \
    --access Allow \
    --protocol '*' \
    --source-address-prefixes '<個人のIPアドレス>' \
    --destination-address-prefixes '*' \
    --destination-port-ranges 22

# 仮想ネットワークを作成する
# ここでは仮想マシン用のサブネットを作成する
az network vnet create \
    --name $VIRTUAL_NETWORK_NAME \
    --resource-group $RESOURCE_GROUP_NAME \
    --address-prefix 10.0.0.0/16 \
    --subnet-name $VM_SUBNET_NAME \
    --subnet-prefix 10.0.0.0/24 \
    --network-security-group $NETWORK_SECURITY_GROUP_NAME

# AKS用サブネットを作成する
az network vnet subnet create \
    --resource-group $RESOURCE_GROUP_NAME \
    --vnet-name $VIRTUAL_NETWORK_NAME \
    --name $AKS_SUBNET_NAME \
    --address-prefix 10.0.10.0/24

# パブリックIPアドレス、ネットワークインターフェースを作成する
# パブリックIPアドレスはVM、NATと2つ作成する
az network public-ip create \
    --resource-group $RESOURCE_GROUP_NAME \
    --name $VM_PUBLIC_IP_ADDRESS_NAME \
    --sku Standard \
    --allocation-method Static

az network public-ip create \
    --resource-group $RESOURCE_GROUP_NAME \
    --name $NAT_PUBLIC_IP_ADDRESS_NAME \
    --sku Standard \
    --allocation-method Static

az network nic create \
    --resource-group $RESOURCE_GROUP_NAME \
    --name $NETWORK_INTERFACE_NAME \
    --vnet-name $VIRTUAL_NETWORK_NAME \
    --subnet $VM_SUBNET_NAME \
    --public-ip-address $VM_PUBLIC_IP_ADDRESS_NAME

# 仮想マシンを作成する
# cloud-init経由でAzure CLI、kubectl、Helmをインストールする
# 検証用のため認証をパスワードとする
az vm create \
    --resource-group $RESOURCE_GROUP_NAME \
    --name $VIRTUAL_MACHINE_NAME \
    --image Canonical:ubuntu-24_04-lts:server:latest \
    --admin-username $ADMIN_USER_NAME \
    --admin-password $ADMIN_USER_PASSWORD \
    --size Standard_D2s_v3 \
    --os-disk-name $OS_DISK_NAME \
    --nics $NETWORK_INTERFACE_NAME \
    --custom-data cloud-init.txt

# AKSからGitHubへ接続するためのNAT Gatewayを作成しAKSサブネットに関連付けをする
az network nat gateway create \
    --resource-group $RESOURCE_GROUP_NAME \
    --name $NAT_GATEWAY_NAME \
    --public-ip-addresses $NAT_PUBLIC_IP_ADDRESS_NAME

az network vnet subnet update \
    --resource-group $RESOURCE_GROUP_NAME \
    --vnet-name $VIRTUAL_NETWORK_NAME \
    --name $AKS_SUBNET_NAME \
    --nat-gateway $NAT_GATEWAY_NAME

AKS 接続用Azure VM 作成後、AKS クラスターを作成します。Azure VM から接続できるようにするため、同じ仮想ネットワークを選択します。

# AKS用のサブネットIDを取得
AKS_SUBNET_ID=$(az network vnet subnet show \
    --resource-group $RESOURCE_GROUP_NAME \
    --vnet-name $VIRTUAL_NETWORK_NAME \
    --name $AKS_SUBNET_NAME --query id --output tsv)

# AKSプライベートクラスターを作成する
az aks create \
    --resource-group $RESOURCE_GROUP_NAME \
    --name $AKS_CLUSTER_NAME \
    --load-balancer-sku standard \
    --enable-private-cluster \
    --network-plugin azure \
    --vnet-subnet-id "$AKS_SUBNET_ID" \
    --dns-service-ip 10.2.0.10 \
    --service-cidr 10.2.0.0/24 \
    --generate-ssh-keys

AKS のクラスター作成後、接続用VM にSSH 接続を行います。接続語、Azure CLI を使いAKS クラスターに接続します。

# 接続用Azure VMにログインする
ssh azureuser@<Azure VMのパブリックIPアドレス>

# Azureにログインする
az login

# AKSクラスターに接続する
az aks get-credentials \
    --resource-group $RESOURCE_GROUP_NAME \
    --name $AKS_CLUSTER_NAME

ARC、スケールセットのインストール

AKS クラスター接続後、Helm を使いARC をインストールします。

# ARCをインストールする
NAMESPACE="arc-systems"
helm install arc \
    --namespace "${NAMESPACE}" \
    --create-namespace \
    oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set-controller

ARC のインストール後、スケールセットをインストールします。GitHub アカウント (組織名) とリポジトリ名は個人で利用する内容を設定してください。PAT の値は「PAT の作成」でメモした値を設定してください。

# スケールセットをインストールする
INSTALLATION_NAME="arc-runner-set"
NAMESPACE="arc-runners"
GITHUB_CONFIG_URL="https://github.com/<アカウント名 or 組織名>/<リポジトリ名>"
GITHUB_PAT="<PATの値>"
helm install "${INSTALLATION_NAME}" \
    --namespace "${NAMESPACE}" \
    --create-namespace \
    --set githubConfigUrl="${GITHUB_CONFIG_URL}" \
    --set githubConfigSecret.github_token="${GITHUB_PAT}" \
    oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set

ワークフローの動作確認

ARC、ランナーセットのインストール後、GitHub リポジトリでテスト用のワークフローを作成します。リポジトリのフォルダ構成は以下とします。

.
└ .github/workflows
    └ main.yaml

ワークフローファイルはecho を実行する簡単なジョブとします。

name: Actions Runner Controller Demo
on:
  push:

jobs:
  Explore-GitHub-Actions:
    # You need to use the INSTALLATION_NAME from the previous step
    runs-on: arc-runner-set
    steps:
    - run: echo "🎉 This job uses runner scale set runners!"

ワークフローをリポジトリへプッシュする前に、接続用VM から以下コマンドを実行しておきます。リアルタイムでどのように作成されるか状態を確認できます。

# Runnerの状態を監視する
# 確認後はCtrl+Cで終了する
kubectl get pods -n arc-runners -w

コマンド実行後、ワークフローファイルをリポジトリにプッシュします。プッシュ後、GitHub 上ではコンテナ作成後にジョブが実行されたことを確認します。

VM 上ではRunner が作成され終了するまでの流れを確認できます。

azureuser@vm-arctest:~$ kubectl get pods -n arc-runners -w
NAME                                READY   STATUS    RESTARTS   AGE
arc-runner-set-8xvb9-runner-86lnq   0/1     Pending   0          0s
arc-runner-set-8xvb9-runner-86lnq   0/1     Pending   0          0s
arc-runner-set-8xvb9-runner-86lnq   0/1     ContainerCreating   0          0s
arc-runner-set-8xvb9-runner-86lnq   1/1     Running             0          21s
arc-runner-set-8xvb9-runner-86lnq   0/1     Completed           0          42s
arc-runner-set-8xvb9-runner-86lnq   0/1     Terminating         0          42s
arc-runner-set-8xvb9-runner-dqhh2   0/1     Pending             0          0s
arc-runner-set-8xvb9-runner-dqhh2   0/1     Pending             0          0s
arc-runner-set-8xvb9-runner-dqhh2   0/1     ContainerCreating   0          0s
arc-runner-set-8xvb9-runner-86lnq   0/1     Terminating         0          43s
arc-runner-set-8xvb9-runner-86lnq   0/1     Terminating         0          44s
arc-runner-set-8xvb9-runner-86lnq   0/1     Terminating         0          44s

もしジョブが実行されず待機中から進まない場合はAKS からGitHub への通信が正常にできていない可能性があります。その場合はネットワーク周りの設定ミスが無いか再度確認してください。

リソースのクリーンアップ

動作確認完了後、Azure リソースをクリーンアップします。

# リソースをクリーンアップする
az group delete --name rg-arctest

Azure リソースのクリーンアップ後、必要に応じてGitHub のPAT を削除してください。

まとめ

  • Azure Kubernetes Service はAzure で利用可能なマネージドKubernetes サービス。Kubernetes の運用・管理に関する負担を減らしKubernetes を利用できる。
  • GitHub Actions Runner Controller はワークフローの実行に応じてランナーをスケールできるKubernetes オペレーター
  • AKS のプライベートクラスターからActions Runner Controller に接続する場合はNAT Gateway のような外部通信できる手段が必要となる

参考資料