GitHub ActionsのSelf-Hosted RunnerをECSでオートスケーリングにより実装


# はじめに
今回は、副業先でのプロジェクトにおいて、
Github Actionsのself-hosted-runnerをAmazon ECS上で実装した経験についてお話しします。
この記事が、同様の課題に取り組む方々の一助となれば幸いです。

# self-hosted-runnerの必要性
我々のプロジェクトはGithub Enterpriseを使用していたため、標準のhosted-runnerではなく、self-hosted-runnerの導入が必要となりました。これにより、よりコントロールとカスタマイズが可能となり、プロジェクトの特定の要件を満たすことができました。

# アーキテクチャ選定のプロセスについて
プロジェクトではAWSを活用していたため、self-hosted-runnerを実行する基盤としてEKSとECSの二つが候補に挙がりました。EKSはHelmを通じて公式サポートされている一方で、ECSはより自由度の高いカスタマイズが必要でした。以下の点で両者を比較しました。

1. 設定の複雑さ: EKSとECSのセットアップと設定の複雑さを比較し、どちらが簡単かを検討します。
1. スケーラビリティ: クラウドリソースのスケーラビリティと拡張性を比較し、大規模なワークロードにどちらが1. 適しているかを評価します。
1. コスト: EKSとECSのランナーの運用コストを比較し、どちらが予算に適しているかを検討します。
1. セキュリティ: セキュリティ要件を満たすために、EKSとECSのどちらが適しているかを検討します。
1. 運用とメンテナンス: ランナーの運用とメンテナンスコスト、タスクのデバッグなどの観点から比較します。
1. GHESのアーキテクチャの親和性: GHESとして公式から手順が公開されていたり、推奨さされているかどうか検討します。
1. 社内のナレッジ:社内で使っているアーキテクチャ: 社内の開発者や運用チームがEKSやECSに関して持っている知識や経験を評価し、どちらのサービスが既存のスキルセットにマッチするかを検討します。

これらの比較を基に、ECSを選定しました。ECSはシンプルなセットアップとコスト効率の良さが魅力で、Kubernetesのようなクラスターの破壊的なアップデートの心配も少ないためです。

# ECSでの実装について
私たちの要件は、マルチアーキテクチャビルドに対応し、WorkFlowごとに異なる認証情報を使用してセキュリティを保つことでした。

# マルチアーキテクチャビルドについて
現代のソフトウェア開発では、異なるプラットフォームやアーキテクチャへの対応が不可欠です。
これを実現するために、我々のプロジェクトではApp Runnerを使用して、GitHub Actionsのワークフローからの要求に応じて異なるアーキテクチャのFargateタスクを動的に起動する仕組みを採用しました。

具体的には、ワークフローで指定されたラベルに基づいて、必要なアーキテクチャ（例えばx86_64やARM64）のFargateタスク定義を選択し、それに応じた環境でビルドやテストを実行することができます。この柔軟性により、様々な環境に対応したソフトウェアの開発が可能になります。

さらに、Docker in Dockerが必要なビルド環境に対しては、現在のFargateの機能では対応が難しいため、CodeBuildを利用しています。CodeBuildはDocker in Dockerに対応しており、これによりより複雑なビルド要件にも柔軟に対応することが可能になります。

このようなマルチアーキテクチャビルドへの対応は、プロジェクトの適応性を大幅に高め、多様な顧客のニーズに応えることを可能にします。

また、セルフホストランナーはEPHEMERALモードで動作し、ジョブ完了後に自動的にシャットダウンします。これにより、App Runner側ではランナーの起動制御のみを行い、終了はランナー自身が担います。

# 全体処理の流れ

1. **GitHub Enterprise Server (GHES) からのワークフローキック**
   - GHESにてGitHub Actionsワークフローがトリガーされます（例えば、プッシュイベント、プルリクエストイベントなどによって）。

2. **GitHub App経由の通信**
   - ワークフローイベントは、設定されたGitHub Appを通じてApp Runnerに送信されます。GitHub Appは、特定のイベント（例えば、新しいジョブがキューに入れられたとき）を検知し、それをApp Runnerに通知します。

3. **App RunnerからのFargate ECS or CodeBuild への指示**
   - App Runner上で動作するアプリケーション（例えば、Golangで書かれたWebhookイベントハンドラー）は、受信したイベントに基づいて、適切なアクションを行います。この場合、ECS Fargate上でセルフホストランナーを起動し、GitHub Actionsワークフローのジョブを実行します。

![スクリーンショット 2023-11-13 9.49.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626780/94ade1ac-fb7e-42aa-0421-339d30552128.png)

# App Runnerでの処理の説明
この部分は、今回の実装の中心となるプロセスです。ここでは、GitHub Actionsワークフローに定義されたruns-on属性を基に、最適なセルフホスト型ランナーを動的に起動します。ワークフローで指定されたラベルは、WorkflowJob.Labelsから取得し、これらをDockerコンテナの起動時の環境変数として活用します。

1. **ランナーのラベル付与**
   - ペイロードから得られたラベルは、起動するGitHub Actionsランナーのラベルとして設定されます。
   - ワークフローは指定されたラベルに合致するランナーで実行されます。

2. **ランナーの種類とアーキテクチャ**
   - `runs-on` 属性で指定されるラベルは、ランナーを起動する環境（Fargate, Fargate Spot, CodeBuild）やアーキテクチャ（x86_64, ARM64）を指定します。
   - マルチアーキテクチャビルドに対応するため、最大で4種類の異なる設定でコンテナが起動する可能性があります。

3. **ランナーの環境設定**
   - 各ランナーの設定は、ワークフローの**runs-on**属性によって異なります。
   - 特定の環境やアーキテクチャに特化したジョブの実行が可能になります。

4. **自動スケーリングのサポート**
   - ワークフローの起動ごとにエフェメラルモードでランナーが起動され、ジョブの処理完了後に自動的にシャットダウンします。
   - これにより、リソースの効率的な利用とスケーラビリティが実現されます。

# さいごに
この記事を通じて、App RunnerとGitHub Actionsを用いたマルチアーキテクチャビルドの実装についての理解が深まったことを願っています。App Runnerのコードは公開していませんが、本記事で説明したロジックが核心部分の理解に役立ったことを期待しています。この経験が、同様の課題に取り組む皆様の参考になれば幸いです。