Docker Composeを使用してGitHub Actionsのセルフホステッドランナーをローカル環境で一括管理する構成です。
REPLACE_EXISTING_RUNNERにより、Docker Desktopのアップデート・再起動後も自動復旧します。
| リポジトリ | CI用 | Dependabot用 | パッケージマネージャー | 備考 |
|---|---|---|---|---|
| 223n/vehicle-management | 3 | 1 | npm | CI + deploy |
| 223n/CatPro-Cloudflare | 2 | 1 | pnpm | CI + E2E |
| 223n/devcontainer-base | 1 | - | - | 手動実行のみ |
| 223n-tech/haru.223n.tech | 2 | - | pnpm | deploy + scheduled-build |
| 223n/FursuitWeather_iMac | 2 | - | npm | CI + deploy |
| 223n/npo-tool | 1 | 1 | composer | CI + Dependabot |
| 223n/sleep-diary-php | 1 | 1 | composer | CI + Dependabot |
| 223n/FursuitWeather_iOS | - | 1 | npm | Dependabotのみ |
| 223n/NightDream | 2 | 1 | npm | CI (node 22/24 matrix) |
| 223n/MyHealth | - | 1 | npm | CIは別ホストのmacOS |
合計: 17台(CI用12台 + Dependabot用5台)と、CI用の共有MySQL 1台
kigurumi-event-hubとokusuri.223n.techのランナーは、リポジトリがアーカイブされたため削除しました。
npo-toolとsleep-diary-phpのCIはMySQLを必要とします。
しかしGitHub Actionsのservices:は、この構成では使えません。
services:で起動したコンテナーのポートはDockerホスト側に公開されます。
一方、ジョブはランナーコンテナーの中で動くため、ジョブから見た127.0.0.1は
ランナーコンテナー自身を指します。両者がつながりません。
docker.sockをマウントしてホストのDockerを操作する構成(docker-out-of-docker)に
共通する制約です。
そのため、常設のMySQLを同じcomposeネットワークへ置いています。
composeの既定ネットワークではサービス名で名前解決できるため、
ワークフローからは**ホスト名mysql**で参照します。
データベースはmysql-init/01-databases.sqlで作成します。
ジョブを分離したい場合はここへ足して、ワークフロー側の接続先を向け替えてください。
| データベース | 用途 |
|---|---|
npo_tool_test |
npo-tool |
sleep_diary_test |
sleep-diary-php |
sleep_diary_test_lowest / _highest |
sleep-diary-php のマトリクス用 |
sleep_diary_test_coverage |
sleep-diary-php のカバレッジ用 |
CIランナーを各1台に絞っているのは、共有MySQLを使うためです。 同一リポジトリのジョブが並走するとデータベースを取り合います。 台数を増やす場合は、ジョブごとにデータベースを分けてください。
ランナーを用意しただけでは動きません。各リポジトリのワークフローで次を行います。
runs-onを[self-hosted, ci]にする (CatPro-Cloudflareのように${{ vars.RUNNER_LABEL || 'ubuntu-latest' }}と 書けば、リポジトリ変数の切り替えだけで戻せます)services:のブロックを削除する- 接続先のホストを
127.0.0.1からmysqlへ変える
Dependabot on self-hosted runnersを有効にしたリポジトリでは、Dependabotのジョブが
dependabotラベルを持つランナーだけを探します。通常のワークフローと違いruns-onを
指定できないため、このラベルを持つランナーが1台も無いとジョブはキューに滞留したまま
24時間後にキャンセルされます。失敗として通知されないため気付きにくく、
依存更新もセキュリティ更新も止まったままになります。
CI用ランナーとは別のコンテナーに分けています。CI実行中に依存更新が待たされるのを避けるためです。
ただしコンテナーを分けるだけでは不十分です。ランナー登録時にself-hosted・
Linux・X64はGitHubが自動で付与するため、Dependabot専用ランナーからこれらを
外せません。ワークフローにruns-on: self-hostedと書くと、Dependabot専用ランナーも
候補に入ります。
区別するため、CI用ランナーにはciラベルを付けています。ワークフロー側は
runs-on: [self-hosted, ci]と書いてください。
| ランナー | ラベル | 拾うジョブ |
|---|---|---|
| CI用 | self-hosted Linux X64 ci |
runs-on: [self-hosted, ci] および runs-on: self-hosted |
| Dependabot用 | self-hosted Linux X64 dependabot |
Dependabotのジョブ、および runs-on: self-hosted |
runs-on: self-hostedのままでも動きますが、Dependabot用ランナーを占有しえます。
# ジョブが要求しているラベルを確認する
gh api "repos/<owner>/<repo>/actions/runs?per_page=20" --jq '.workflow_runs[] | select(.name | startswith("npm_and_yarn")) | "\(.created_at) \(.conclusion)"'
# 登録済みランナーのラベルを確認する
gh api "repos/<owner>/<repo>/actions/runners" --jq '.runners[] | "\(.name) \(.labels | map(.name) | join(","))"'ジョブ側がdependabotラベルを要求しているのにランナー側に無ければ、この構成に追加します。
セルフホストで動かす必要が無いリポジトリなら、リポジトリのSettings > Code security >
Dependabot on self-hosted runnersを無効にしてGitHubホステッドランナーへ戻す方法もあります。
- Docker / Docker Compose
- GitHub Personal Access Token(PAT)
- Fine-grained token(推奨)
- Repository permissions: Administration: Read and write
# 1. .env ファイルを作成
cp .env.example .env
# 2. .env を編集して GitHub PAT を設定
# GITHUB_PAT=ghp_xxxxxxxxxxxxxxxxxxxx
# 3. ランナーを起動
docker compose up -d管理スクリプト(runner.ps1)を使用します。Docker Desktopが未起動の場合は自動で起動します。
.\runner.ps1 start # ランナーを起動(Docker Desktop自動起動)
.\runner.ps1 stop # ランナーを停止(GitHub登録自動解除)
.\runner.ps1 restart # ランナーを再起動
.\runner.ps1 status # ランナーの状態を表示
.\runner.ps1 logs # 全ランナーのログを表示
.\runner.ps1 logs runner-sleep-diary # 特定ランナーのログを表示
.\runner.ps1 clean # 停止 + キャッシュボリューム全削除docker composeコマンドを直接使用することもできます。
docker compose up -d
docker compose ps
docker compose downリポジトリごとにtoolcacheとパッケージマネージャーのキャッシュをDockerボリュームで永続化しています。
# キャッシュボリューム一覧
docker volume ls | grep github-runnersmyoung34/github-runner:ubuntu-noble(Ubuntu 24.04 / glibc 2.39)を使用します。
latestタグはUbuntu 20.04ベース(glibc 2.31)で、@cloudflare/workerd-linux-64が要求する
GLIBC_2.32〜GLIBC_2.35を満たしません。この状態ではvitest-pool-workersを使うテストが
workerdを起動できず、Test Files: no testsのまま失敗します。
イメージを更新する場合は明示的にpullします。docker compose up -dだけでは
取得済みイメージが再利用され、タグを変えても反映されません。
docker compose pull
docker compose up -dDocker Desktopのアップデートや強制再起動でランナーが停止した場合、以下のコマンドで復旧できます。
docker compose up -dREPLACE_EXISTING_RUNNER: "true"により、古い設定ファイルが残っていても既存登録を上書きして再起動します。
.
├── .env.example # 環境変数テンプレート
├── .env # 環境変数(Git管理外)
├── .gitattributes # 改行コード設定
├── .gitignore
├── docker-compose.yml # ランナー定義
├── mysql-init/ # CI用MySQLの初期化SQL
├── runner.ps1 # 管理スクリプト(PowerShell)
└── README.md