Docker Compose で構築した Next.js アプリケーションに対し、GitHub Actions 上で Playwright による E2E テストを実行しようとしたところ、ネットワークエラーやタイムアウトに悩まされました。
試行錯誤の末に安定して動作する構成にたどり着いたので、その「解決策」を共有します。
発生していた問題
GitHub Actions で docker compose を立ち上げ、Playwright からアクセスしようとすると以下のエラーが発生しました。
-
net::ERR_ADDRESS_UNREACHABLE/net::ERR_CONNECTION_REFUSED- コンテナ間の通信がうまくできない。
-
TimeoutError(30000ms)- 画面が真っ白のまま、要素が見つからずにテストが落ちる。
-
Exit code 124 (timeout)
- フロントエンドの起動待機スクリプトが終わらない。
原因と解決策
主な原因は以下の4点でした。
- ネットワークの指定ミス(固定IPやlocalhostの使用)
- Next.js のコンテナ設定(外部アクセスの拒否)
-
コンテナ再起動による待機無効化(
docker compose runの挙動) - CI環境のスペック不足によるタイムアウト
それぞれの解決策を解説します。
1. 固定IPをやめ、サービス名でアクセスする
当初は ipv4_address で固定IPを指定していましたが、CI環境ではネットワークドライバの挙動により不安定になることがあります。また、Dockerネットワーク内では localhost は「自分自身」を指すため、Playwrightコンテナから http://localhost:3000 へは繋がりません。
解決策:
Docker Compose の DNS解決(サービスディスカバリ)を利用し、サービス名でアクセスします。
tests/sample.spec.ts
// × 修正前
// await page.goto("http://10.254.xx.xx:3000/...");
// await page.goto("http://localhost:3000/...");
// 〇 修正後:docker-compose.yml のサービス名 "frontend" を指定
await page.goto("http://frontend:3000/test");
2. Next.js を 0.0.0.0 で起動する
Next.js (v13.4以降など) はデフォルトで localhost (127.0.0.1) でリッスンします。これはコンテナ内からはアクセスできますが、コンテナ外(Playwrightコンテナ)からのアクセスを拒否してしまいます。
解決策:
環境変数 HOSTNAME=0.0.0.0 を設定して、外部からのアクセスを受け付けるようにします。また、ボリュームマウントで node_modules が消えないように保護します。
docker-compose.yml
frontend:
# ...
environment:
- HOSTNAME=0.0.0.0 # 重要: これがないと接続拒否される
volumes:
- ./frontend:/frontend
- /frontend/node_modules # 重要: コンテナ内のモジュールを守る
3. コンテナの再起動を防ぐ (--no-deps)
これが一番のハマりポイントでした。
GitHub Actions で curl コマンドを使って「フロントエンドが起動するまで待機」するステップを入れていましたが、その後のテスト実行コマンドでコンテナが再起動してしまい、待機した意味がなくなっていました。
-
docker compose run playwright ...を実行すると、依存関係にあるfrontendもデフォルトで再作成(Recreate)される場合がある。 - 再起動すると Next.js のコンパイルが最初からになり、テストのタイムアウトに間に合わない。
解決策:
--no-deps オプションをつけて、すでに起動・待機済みのコンテナをそのまま使います。
.github/workflows/test.yml
# 1. 起動して待つ
- name: Wait for Frontend
run: timeout 120s bash -c 'until curl --silent --fail http://localhost:3000; do sleep 5; done'
# 2. 再起動させずにテスト実行 (--no-deps)
- name: Run Playwright tests
run: docker compose run --rm --no-deps playwright npx playwright test
4. タイムアウト時間の延長と環境変数
CI環境(GitHub Actions)はローカルマシンよりスペックが低く、レンダリングに時間がかかります。デフォルトのタイムアウト(5秒や30秒)では失敗することがあります。
また、APIのURLなどの環境変数がCI上で設定されていないと、アプリが正しく動きません。
tests/sample.spec.ts
test("テスト", async () => {
test.setTimeout(60000); // テスト全体のタイムアウトを60秒に延長
// ...
await expect(locator).toBeVisible({ timeout: 30000 }); // 要素待機も長めに
});
最終的なコード構成
docker-compose.yml
version: "3.3"
services:
playwright:
build:
context: .
dockerfile: "./playwright/Dockerfile"
volumes:
- ./playwright:/app
- /app/node_modules
depends_on:
- frontend
frontend:
build:
context: .
dockerfile: "./frontend/Dockerfile.next"
volumes:
- ./frontend:/frontend
- /frontend/node_modules
ports:
- "3000:3000"
environment:
- HOSTNAME=0.0.0.0
- NEXT_PUBLIC_APP_API_URL_8080
- NEXT_PUBLIC_BASE_URL
GitHub Actions Workflow (.github/workflows/ci.yml)
name: E2E Test
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Start services
# 必要な環境変数をここで注入
env:
NEXT_PUBLIC_APP_API_URL_8080: "http://api-server-url"
run: docker compose up -d
- name: Wait for Frontend
run: |
echo "Waiting for frontend..."
# ホスト側からlocalhost:3000を監視
timeout 120s bash -c 'until curl --silent --fail http://localhost:3000; do echo "Retrying..."; sleep 5; done'
- name: Show Frontend Logs (on failure)
if: failure()
run: docker compose logs frontend
- name: Run Playwright tests
# --no-deps でコンテナ再起動を防止
run: docker compose run --rm --no-deps playwright npx playwright test
- name: Upload screenshots
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-screenshots
path: playwright/images/
retention-days: 5
まとめ
Docker × CI 環境での E2E テストを安定させるためのポイントは以下の通りです。
-
URLはサービス名で指定 (
http://frontend:3000)。 -
Next.js は
0.0.0.0で起動 して外部アクセスを許可。 - 起動待機 (Wait) を入れる。
-
--no-depsを使って、待機後のコンテナ再起動を防ぐ。 - CI用のタイムアウト設定 を長めにとる。
これで、「ローカルでは動くのにCIだけ落ちる」現象から脱出できました。