テスト統合でのポート衝突防止と Docker Hub レート制限回避
テスト実行時に発生していたポート・コンテナ名衝突と Docker Hub のレートリミットを回避するため、ワークツリーごとに安全な Compose プロジェクト名を生成し、プルスルーキャッシュと認証再利用を導入した。本変更はテストスイート全体の安定性を向上させ、CI 環境での競合を根本的に解決します。
背景
従来のテスト設定では固定ポート(例: 22443:443)や固定レジストリ名(registry:4443)がハードコーディングされており、同時実行や CI の並列ジョブでコンテナ名・ポートの衝突が頻発し、テストが不安定になるケースがありました。また、Docker Hub への頻繁なプルがレートリミットに達し、テストが途中で失敗することも報告されていました。これらの問題は、テスト環境が共有リソースに過度に依存していたことが根本原因です。
技術的な変更
ワークツリーごとの Compose プロジェクト名 を導入し、test/integration/integration_test.rb に COMPOSE_PROJECT 定数を追加しました。SHA256 ハッシュの先頭 8 桁を使用して "kamal-test-#{hash}" とし、同一ワークツリー内での docker compose down が正しいスタックを削除でき、別ワークツリー間での名前衝突を防ぎます。
COMPOSE_PROJECT = "kamal-test-#{Digest::SHA256.hexdigest(File.expand_path("../..", __dir__))[0, 8]}"
Docker Hub のレートリミット回避策 として、docker-compose.yml に hub‑cache サービスを追加し、プルスルーキャッシュとして registry:3 を使用します。キャッシュは匿名で起動し、DOCKERHUB_USERNAME と DOCKERHUB_TOKEN が設定されていれば認証情報を流用して上位レジストリへプルします。
hub-cache:
image: registry:3
environment:
- REGISTRY_PROXY_REMOTEURL=https://registry-1.docker.io
- REGISTRY_PROXY_USERNAME=${DOCKERHUB_USERNAME:-}
- REGISTRY_PROXY_PASSWORD=${DOCKERHUB_TOKEN:-}
volumes:
- hub-cache:/var/lib/registry
healthcheck:
test: [ "CMD", "pgrep", "registry" ]
interval: 1s
内部 Docker デーモンの設定 を変更し、boot.sh(deployer と vm 両方)で registry-mirrors と insecure-registries に hub-cache:5000 を追加しました。これにより、内部コンテナがベースイメージを直接 Docker Hub から取得せず、キャッシュ経由で取得するようになります。
cat > /etc/docker/daemon.json <<'EOF'
{
"storage-driver": "vfs",
"registry-mirrors": [ "http://hub-cache:5000" ],
"insecure-registries": [ "hub-cache:5000" ]
}
EOF
テストコードのポート参照 を動的に取得するよう改修し、IntegrationTest の setup で published_port を呼び出して実際のホストポートを変数 @http_port と @https_port に保存しました。以降の HTTP アクセスは #{@http_port} を利用し、ポート衝突のリスクを排除します。
@http_port = published_port("load_balancer", 80)
@https_port = published_port("vm1", 443)
イメージ参照の簡素化 と レジストリ設定の除去 を行い、すべての Dockerfile が registry:4443 からの取得ではなく公式イメージ(例: nginx:1-alpine-slim)を直接使用するようにしました。また、deploy.yml 系統から registry:4443 の記述を削除し、busybox 等のイメージも公式リポジトリに切り替えました。これにより、テスト実行時にプライベートレジストリへの依存がなくなり、キャッシュや認証の設定だけで済むようになっています。
-FROM registry:4443/nginx:1-alpine-slim
+FROM nginx:1-alpine-slim
- image: registry:4443/busybox:1.36.0
+ image: busybox:1.36.0
セットアップスクリプトの整理 では、レジストリへの手動プッシュロジックを除去し、代わりに basecamp/kamal-proxy を直接プル・タグ付け・プッシュしてプライベートレジストリにシードする手順に変更しました。これにより、レジストリへの不要な書き込みが減り、シークエンスが簡潔になります。
docker pull basecamp/kamal-proxy:v0.9.2
docker tag basecamp/kamal-proxy:v0.9.2 registry:4443/basecamp/kamal-proxy:v0.9.2
docker push registry:4443/basecamp/kamal-proxy:v0.9.2
設計判断
プルスルーキャッシュの導入 と ワークツリー固有の Compose プロジェクト名 という二つの設計判断は、テスト環境の分離性とリソース競合回避を最小の侵襲で実現することを目的としています。キャッシュは既存のレジストリ構成に最小限の変更で追加でき、認証情報の再利用によりレートリミット問題を根本的に緩和します。Compose プロジェクト名はハッシュに基づく決定論的生成であり、CI の並列実行時でも名前衝突が起きない保証を提供します。いずれの変更も本番コードに影響を与えず、テスト専用の安全策として完結しています。
まとめ
本 PR はテスト実行時のポート・コンテナ名衝突と Docker Hub のレートリミットという二大課題を、hub‑cache によるプルスルーキャッシュと COMPOSE_PROJECT のハッシュ化により安全なプロジェクト名生成で解決しました。結果として、テストスイートは並列実行でも安定し、外部レジストリへの過度な依存を排除したことで CI の信頼性が向上します。