← すべての記事

act を DonD で動かす

GitHub Actions をローカルでテストできる **act** は、ワークフローの開発を効率化する強力なツールです。しかし、開発環境ごとにactをインストールするのは手間がかかります。

はじめに#

GitHub Actions を毎回 GitHub 側で動作確認をするようにしていると、ghコマンドである程度簡略化できたとしてもテストが重いなーと感じる。 調べると act というのがあってローカルでテストするためのデファクトスタンダードのツールのようなのでこちらをちょっと素振り。

act とは#

主な利点:

  • 開発サイクルの高速化: push せずにワークフローをテスト
  • コスト削減: GitHub Actions の無料枠を消費しない
  • オフライン開発: インターネット接続なしでも動作確認可能
  • デバッグの容易さ: ローカルで詳細なログを確認できる

と言われても開発環境ごとにCI開発を入れるのもしんどいし、個別にactをインストールするのも手間がかかってしまうので とりあえず今回は、Docker on Docker (DonD) で act 環境をコンテナ化し、どの環境でも簡単に使えるようにするのがよいかなと思ったので対応します

なぜコンテナ化するのか#

基本的に Mac が開発機ですが、Windowsマシンもあり、WSLを使うこともあるので、とりあえずコンテナ化するモチベーションは以下。

  • 環境の統一: チームメンバー全員が同じ環境で開発
  • 簡単な配布: Dockerさえあれば動作する
  • クリーンな環境: ホストシステムを汚さない
  • 再現性の確保: Dockerfileで環境を完全に定義

DonD or DinD: アーキテクチャの選択#

act はワークフロー実行時に Docker コンテナを起動する形式になるが、コンテナ内から Docker を使う方法は2つある

Docker in Docker (DinD)#

コンテナ内で独立した Docker デーモンを起動する方式。

メリット:

  • 完全に独立した環境
  • ホストの Docker に影響を与えない

デメリット:

  • 二重の仮想化によるパフォーマンス低下
  • リソース消費が大きい
  • 特権モード (--privileged) が必要でセキュリティリスクが高い

Docker on Docker (DonD) - 今回の選択#

コンテナ内からホストの Docker デーモンを利用する方式。

メリット:

  • パフォーマンスが良い: ホストの Docker を直接使用
  • リソース効率が高い: 二重仮想化なし
  • イメージの共有: ホストとコンテナでイメージを共有できる

デメリット:

  • Docker socket の共有による潜在的なセキュリティリスク
  • ホストの Docker に依存

今回の判断基準#

今回は以下の理由から DonD を選択。 CI/CDだとDonDが多い気はします。GitLab Runnerを利用しているときもこちらの構成を使っていたことがあるので慣れているのもあります。

  1. ターゲット環境が Linux: 安定した動作が期待できる
  2. 開発環境での使用: セキュリティリスクは許容範囲
  3. パフォーマンス重視: ワークフローの実行速度を優先
  4. シンプルな構成: 設定が簡単で保守しやすい

Dockerfile の作成#

それではまず act を動かすための Dockerfile を作成します。

FROM alpine:latest
RUN apk add --no-cache \
docker-cli \
curl \
bash \
git \
ca-certificates
RUN curl -s https://raw.githubusercontent.com/nektos/act/master/install.sh | bash
WORKDIR /workspace
CMD ["/bin/bash"]

Dockerfile の解説#

ベースイメージ:

FROM alpine:latest
  • Alpine Linux を選択: 軽量(約5MB)で高速
  • セキュリティアップデートが早い
  • パッケージマネージャー(apk)が使いやすい

必要なパッケージのインストール:

RUN apk add --no-cache \
docker-cli \ # ホストのDockerデーモンと通信するCLI
curl \ # actのインストールスクリプト取得用
bash \ # actとワークフローの実行に必要
git \ # リポジトリ操作とactions/checkout用
ca-certificates # HTTPS通信の証明書検証用
  • --no-cache: キャッシュを残さずイメージサイズを削減

act のインストール:

RUN curl -s https://raw.githubusercontent.com/nektos/act/master/install.sh | bash
  • 公式のインストールスクリプトを使用
  • /usr/local/bin/act にバイナリが配置される
  • 最新版が自動的にインストールされる

作業ディレクトリの設定:

WORKDIR /workspace
  • リポジトリがマウントされるディレクトリ
  • compose.yamlの設定と一致させる

デフォルトコマンド:

CMD ["/bin/bash"]
  • コンテナ起動時にbashシェルを起動
  • インタラクティブな操作を可能にする

Docker Compose 設定の作成#

次に compose.yaml を作成します。

services:
act:
build: .
container_name: act-runner
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- .:/workspace
working_dir: /workspace
stdin_open: true
tty: true

compose.yaml の解説#

サービスの基本設定:

services:
act:
build: . # カレントディレクトリのDockerfileを使用
container_name: act-runner # コンテナに固定名を付与(識別しやすくする)

最も重要な設定 - Docker socket の共有:

volumes:
- /var/run/docker.sock:/var/run/docker.sock

DonD なのでこうなります。ホストの Docker socket をコンテナにマウントすることで、コンテナ内から ホストの Docker デーモン に直接アクセスできます。

リポジトリのマウント:

- .:/workspace
  • カレントディレクトリ(リポジトリルート)をコンテナの /workspace にマウント
  • ワークフローファイルやソースコードにアクセスできるようにする

作業ディレクトリ:

working_dir: /workspace
  • コンテナ起動時のカレントディレクトリを指定
  • Dockerfileの WORKDIR と一致

インタラクティブモード:

stdin_open: true # 標準入力を開いたままにする (-i フラグ相当)
tty: true # 疑似TTYを割り当てる (-t フラグ相当)
  • シェルの対話的な操作を可能にする
  • docker compose exec でコンテナにアクセスする際に必要

これで DonD 環境の準備が完了しました。

動作確認#

実際に環境を起動してテストしてみます。

コンテナの起動#

Terminal window
docker compose up -d

バックグラウンドでコンテナを起動します。初回は Dockerfile からイメージをビルドするため数分かかります。

コンテナへのアクセス#

Terminal window
docker compose exec act bash

コンテナ内のシェルに入ります:

c813419d3e2a:/workspace#

act のイメージサイズ選択#

初回実行時に act が使用するベースイメージを選択するよう求められます:

Terminal window
$ docker compose exec act bash
c813419d3e2a:/workspace# act -n
INFO[0000] Using docker host 'unix:///var/run/docker.sock', and daemon socket 'unix:///var/run/docker.sock'
? Please choose the default image you want to use with act:
- Large size image: ca. 17GB download + 53.1GB storage, you will need 75GB of free disk space, snapshots of GitHub Hosted Runners without snap and pulled docker images
- Medium size image: ~500MB, includes only necessary tools to bootstrap actions and aims to be compatible with most actions
- Micro size image: <200MB, contains only NodeJS required to bootstrap actions, doesn't work with all actions
Default image and other options can be changed manually in /root/.config/act/actrc (please refer to https://nektosact.com/usage/index.html?highlight=configur#configuration-file for additional information about file structure) [Use arrows to move, type to filter, ? for more help]
Large
> Medium
Micro

イメージサイズの選択基準:

サイズダウンロードディスク使用特徴推奨用途
Large17GB53GBGitHub Hosted Runners とほぼ同じ環境本番環境との完全な互換性が必要な場合
Medium500MB数GB大半のアクションが動作する一般的な開発に推奨
Micro<200MB最小Node.js のみシンプルなワークフローのみ

今回は Medium を選択します。ほとんどのワークフローで十分に動作します。(使っているマシンに空きディスクがあんまりなかったとは恥ずかしくて言えない)

テストワークフローの準備#

.github/workflows/test.yml を用意します:

name: Test Workflow
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Run a simple test
run: |
echo "Hello from GitHub Actions!"
echo "Running on: $(uname -a)"
echo "Current directory: $(pwd)"
- name: List files
run: ls -la

このワークフローは以下をテストします:

  • リポジトリのチェックアウト (actions/checkout@v4)
  • シェルコマンドの実行
  • ファイルシステムへのアクセス

Dry-run での実行計画確認#

まずは dry-run モード (-n) で実行計画を確認します:

Terminal window
c813419d3e2a:/workspace# act -n -W .github/workflows/test.yml
INFO[0000] Using docker host 'unix:///var/run/docker.sock', and daemon socket 'unix:///var/run/docker.sock'
*DRYRUN* [Test Workflow/test] ⭐ Run Set up job
*DRYRUN* [Test Workflow/test] 🚀 Start image=catthehacker/ubuntu:act-latest
*DRYRUN* [Test Workflow/test] 🐳 docker pull image=catthehacker/ubuntu:act-latest platform= username= forcePull=true
*DRYRUN* [Test Workflow/test] 🐳 docker create image=catthehacker/ubuntu:act-latest platform= entrypoint=["tail" "-f" "/dev/null"] cmd=[] network="host"
*DRYRUN* [Test Workflow/test] 🐳 docker run image=catthehacker/ubuntu:act-latest platform= entrypoint=["tail" "-f" "/dev/null"] cmd=[] network="host"
*DRYRUN* [Test Workflow/test] ✅ Success - Set up job
*DRYRUN* [Test Workflow/test] ⭐ Run Main Checkout code
*DRYRUN* [Test Workflow/test] ✅ Success - Main Checkout code [10.669541ms]
*DRYRUN* [Test Workflow/test] ⭐ Run Main Run a simple test
*DRYRUN* [Test Workflow/test] ✅ Success - Main Run a simple test [28.75825ms]
*DRYRUN* [Test Workflow/test] ⭐ Run Main List files
*DRYRUN* [Test Workflow/test] ✅ Success - Main List files [28.826916ms]
*DRYRUN* [Test Workflow/test] ⭐ Run Complete job
*DRYRUN* [Test Workflow/test] Cleaning up container for job test
*DRYRUN* [Test Workflow/test] ✅ Success - Complete job
*DRYRUN* [Test Workflow/test] 🏁 Job succeeded

Dry-run の出力から分かること:

  1. Docker 接続確認: Using docker host 'unix:///var/run/docker.sock' - DonD が正しく動作している
  2. 実行計画: 各ステップが順番に表示される
  3. 使用イメージ: catthehacker/ubuntu:act-latest が使われる
  4. エラーの事前検出: 実際に実行する前に問題を発見できる

すべてのステップが ✅ Success となり、🏁 Job succeeded と表示されました。問題なさそうです。

実際のワークフロー実行#

次に -n フラグを外して実際に実行します:

Terminal window
c813419d3e2a:/workspace# act -W .github/workflows/test.yml
INFO[0000] Using docker host 'unix:///var/run/docker.sock', and daemon socket 'unix:///var/run/docker.sock'
[Test Workflow/test] ⭐ Run Set up job
[Test Workflow/test] 🚀 Start image=catthehacker/ubuntu:act-latest
[Test Workflow/test] 🐳 docker pull image=catthehacker/ubuntu:act-latest platform= username= forcePull=true
[Test Workflow/test] 🐳 docker create image=catthehacker/ubuntu:act-latest platform= entrypoint=["tail" "-f" "/dev/null"] cmd=[] network="host"
[Test Workflow/test] 🐳 docker run image=catthehacker/ubuntu:act-latest platform= entrypoint=["tail" "-f" "/dev/null"] cmd=[] network="host"
[Test Workflow/test] 🐳 docker exec cmd=[node --no-warnings -e console.log(process.execPath)] user= workdir=
[Test Workflow/test] ✅ Success - Set up job
[Test Workflow/test] ⭐ Run Main Checkout code
[Test Workflow/test] 🐳 docker cp src=/workspace/. dst=/workspace
[Test Workflow/test] ✅ Success - Main Checkout code [151.338375ms]
[Test Workflow/test] ⭐ Run Main Run a simple test
[Test Workflow/test] 🐳 docker exec cmd=[bash -e /var/run/act/workflow/1] user= workdir=
| Hello from GitHub Actions!
| Running on: Linux docker-desktop 6.10.14-linuxkit #1 SMP Sat May 17 08:28:57 UTC 2025 aarch64 aarch64 aarch64 GNU/Linux
| Current directory: /workspace
[Test Workflow/test] ✅ Success - Main Run a simple test [137.776875ms]
[Test Workflow/test] ⭐ Run Main List files
[Test Workflow/test] 🐳 docker exec cmd=[bash -e /var/run/act/workflow/2] user= workdir=
| total 52
| drwxr-xr-x 6 root root 4096 Oct 5 16:26 .
| drwxr-xr-x 1 root root 4096 Oct 5 16:26 ..
| drwxr-xr-x 7 root root 4096 Oct 5 16:26 .git
| drwxr-xr-x 3 root root 4096 Oct 5 16:26 .github
| -rw-r--r-- 1 root root 467 Oct 5 15:51 .gitignore
| -rw-r--r-- 1 root root 377 Oct 5 15:45 Dockerfile
| -rw-r--r-- 1 root root 5055 Oct 5 16:22 README.md
| -rw-r--r-- 1 root root 933 Oct 5 15:47 build.js
| -rw-r--r-- 1 root root 383 Oct 5 15:56 docker-compose.yml
| -rw-r--r-- 1 root root 349 Oct 5 15:47 package.json
| drwxr-xr-x 2 root root 4096 Oct 5 16:26 src
| drwxr-xr-x 2 root root 4096 Oct 5 16:26 terraform
[Test Workflow/test] ✅ Success - Main List files [133.259625ms]
[Test Workflow/test] ⭐ Run Complete job
[Test Workflow/test] Cleaning up container for job test
[Test Workflow/test] ✅ Success - Complete job
[Test Workflow/test] 🏁 Job succeeded

実行結果のポイント:

  1. イメージのダウンロード: 初回は docker pull でイメージを取得(Medium: 約500MB)
  2. コンテナの作成と起動: ワークフロー実行用の一時コンテナが起動
  3. コードのチェックアウト: docker cp でリポジトリがコンテナにコピー
  4. コマンドの実行: 各ステップのコマンドが実際に実行される
  5. 出力の表示: | プレフィックスで実行結果が表示される
    | Hello from GitHub Actions!
    | Running on: Linux docker-desktop 6.10.14-linuxkit ...
  6. クリーンアップ: ジョブ完了後、一時コンテナが自動削除される

すべて成功し、🏁 Job succeeded が表示されました!

まとめ#

構築した環境の利点#

どこでも動作: Docker さえあればOK 高速: DonD によるオーバーヘッド最小化 使いやすい: docker compose exec act bash で即座にアクセス チーム共有可能: Dockerfile と compose.yaml を共有するだけ

できるようになったこと#

  • GitHub Actions ワークフローのローカルテスト
  • Dry-run による事前検証
  • push 前のワークフロー確認
  • CI/CD パイプラインの高速な開発サイクル

次のステップ#

この基本環境を使って、以下のような活用ができます:

  • 複数ワークフローの並列テスト: -j オプションで特定ジョブを指定
  • 環境変数やシークレットの設定: .secrets ファイルで機密情報を管理
  • カスタムイベントのテスト: pull_requestworkflow_dispatch のシミュレート
  • イメージのカスタマイズ: プロジェクト固有のツールを追加