はじめに
Claude Code で並列開発をしたいけれど、アプリケーションの動作確認はローカルサーバーのポートが競合してできない、というもどかしさを感じた方は多いのではないでしょうか?
特に Docker Compose を使っているプロジェクトでは、docker compose up を 2 つ走らせると即座にポートかコンテナ名で衝突します。
本記事ではこの問題を、git worktree + 自前スクリプト + AI 統合開発環境 Superset の組み合わせで解いた手順を残します。
なぜ並列開発したいのか
動機 1: タスクを並行して進めたい
Claude Code に作業を投げたあとの「待ち時間」は、開発内容によっては結構長いです。
この間をぼーっとお茶しているわけにもいかないので、積まれている他の作業を進めたいですよね。
動機 2: PR レビューする時の準備が面倒くさい
作業中にレビュー依頼が来たときは、以下のような作業をするかと思います。
- 自分の作業差分をスタッシュ
- PR のブランチにチェックアウトして動作確認
- PR に指摘コメントを投稿する
- 自分の作業ブランチに戻ってスタッシュを適用して、作業再開
- PR の指摘内容が修正されたため、再度スタッシュして、PR のブランチにチェックアウトして動作確認
この毎回スタッシュする工程が非常に面倒でした。
コミットすればスタッシュの工程はスキップできますが、コミットは自分のタイミングでしたいですよね。
素朴な解: git worktree 単体
最初の発想は素朴で、git worktree を使えば 1 つのリポジトリから複数の作業ディレクトリを生やせます。
ブランチを切り替えずに別ブランチを編集できるので、これだけで「checkout で作業を中断する」問題は消えるはずです。
ただしなにも対処をしていないと、作成したワークツリー内でコンテナを立ち上げると、メインツリーで起動しているサーバーのポートと衝突して起動できません。
ワークツリーを作成しただけでは、Docker Compose の世界では並列起動できる状態になっていません。
静的レビューやサーバーへのアクセスを必要としない作業であれば問題ないかもしれませんが、ほとんどの場合そうではありません。
解決策: 自動ポート割当スクリプト
全体像
ポートが競合するのであれば、ワークツリーごとにポートを変更できるようにすれば解決です。
前提として、本プロジェクトの docker compose は Rails / MySQL / Redis / memcached の 4 コンテナを 1 セットで立ち上げる構成になっています。
Rails コンテナはアプリケーション本体で、起動時に MySQL(DB 永続化)・Redis(キャッシュ)・memcached(セッション・短期キャッシュ)の 3 つにネットワーク越しにアクセスしています。
実際の docker-compose.yml(抜粋)は次のような形です。ホストポートとコンテナ名を環境変数で外から差し込めるようにしてあるのがポイントです。
「変更するポートが 1 つだけならワークツリーごとに手動で書き換えても手間にはならないのでは?」と思う方もいるかもですが、下記は例であり実際には 3 ポートあり、コンテナ名も書き換えないといけないので手動で毎回変更するのは面倒です。
services:
memcached:
container_name: memcached
image: memcached:latest
networks: [my-app-network]
redis:
container_name: redis
image: redis:latest
networks: [my-app-network]
db:
container_name: db
image: mysql:8.0
environment:
MYSQL_DATABASE: myapp
networks: [my-app-network]
my-app:
container_name: ${CONTAINER_NAME:-my-app}
build: { context: ., dockerfile: Dockerfile }
environment:
MYSQL_HOST: db # ← Rails から DB へはサービス名で参照
ports:
- "${HOST_PORT:-3013}:3000" # ← ここを .env で差し替える
networks: [my-app-network]
depends_on:
redis: { condition: service_started }
db: { condition: service_healthy }
networks:
my-app-network:
external: true # ← 共有ネットワーク(後述)注目すべきは次の 2 点です。
HOST_PORTとCONTAINER_NAMEを環境変数化している点。これを.env経由でワークツリーごとに差し替えることで、同じ compose ファイルを複数ワークツリーで使い回せます。networks.my-app-network.external: true。Compose 内で作るのではなく、外部に既に存在するネットワークに繋ぐ宣言です。これがリンクワークツリーから「メイン側の MySQL / Redis / memcached」に繋ぎに行く土台になります(詳細は後述)。
このコンテナ群をポートの衝突なしに立ち上げるには、異なるポートで立ち上げる必要があります。
ただし、毎回起動ポートを手動で変更して立ち上げるのは面倒なので自動化させたいです。
ここでまず考えなくてはいけないのは、「MySQL / Redis / memcached」をワークツリー間で共有するかどうかです。
共有しない場合、ワークツリーごとに毎回これらを起動しなおさなくてはいけません。
軽量であればいいのですが、DBのデータ量が多い場合などで毎回起動するまでに時間を要するのは避けたいです。
共有する場合、1 つのワークツリー内での変更が全ワークツリーに波及するデメリットはありますが、起動に時間を要するデメリットと比べると妥協できるレベルだったので共有する方針としました。
そのため、ポートの競合で解決しなくてはいけないのは Rails サーバー周りのポートだけになりました。
手順は以下の 3 ステップになります。
- ポートをワークツリーごとに自動で別の値に割り当てる
.envにそれを書き込む(既存の手書き設定は壊さない)- ここまでの作業を 1 コマンド で完了させる
これから登場するスクリプトの最終的な配置は次のようになります。
my-app/
├── docker-compose.yml
├── .env
└── scripts/
└── worktree/
├── port.sh # ポートを算出
├── setup.sh # .env の管理ブロックを書き換え
└── docker-up.sh # setup.sh + docker compose up を 1 コマンドにポートをパスから決定的に割り当てる
ポート割当の要件はシンプルに 2 つです。
- 衝突しない: 他のワークツリーと被らない
- メインツリーのポートは固定にしたい: 本プロジェクトでは Rails は API サーバーのため、フロント開発者も立ち上げて API 通信をします。その時にポートが固定化されていないと、API サーバーのポートに合わせてフロントの通信先を変更する必要が出てきてしまいます。そのためメインツリーは固定にする必要があります。
自動ポートの設定方法に決まりはないですが、AI が提案してきたやり方にしました。
やっていることは以下です。
- 絶対パスを取得
- メインツリーは「3013」を指定
- リンクツリー(派生したワークツリー)のポートは、パスから「10000〜19999」のいずれかのポートを算出
実装は scripts/worktree/port.sh という単機能スクリプトに切り出していて、算出したポート番号を標準出力に echo するだけにしています。.env への書き込みは後述の setup.sh が $(...) で受け取る役割分担です。
TOPLEVEL="$(git rev-parse --show-toplevel)"
# メインワークツリーは .git が「ディレクトリ」、リンクは「ファイル」
if [ -d "$TOPLEVEL/.git" ]; then
echo "3013"
else
# パスの MD5 から数字だけ抽出 → 先頭4桁 → 10000〜19999 へ写像
echo "$TOPLEVEL" | md5 | tr -d -c '0-9' | head -c 4 | \
awk '{port = ($1 % 10000) + 10000; print port}'
fi.env の管理ブロック方式
ポートが決まったら、それを .env に書き出して docker compose に読ませます。
…
記事の続きは下のURLをクリック!
https://rightcode.co.jp/blogs/55507
もっとワクワクしたいあなたへ
現在、ライトコードでは「WEBエンジニア」「モバイルエンジニア」「データエンジニア」「ゲームエンジニア」「デザイナー」「WEBディレクター」「営業」などを積極採用中です!
ライトコードは技術力に定評のある受託開発をメインにしているIT企業です。
有名WEBサービスやアプリの受託開発などの企画、開発案件が目白押しの状況です。
- もっと大きなことに挑戦したい!
- エンジニアとしてもっと成長したい!
- モダンな技術に触れたい!
現状に満足していない方は、まずは、エンジニアとしても第一線を走り続ける弊社代表と気軽にお話してみませんか?
ネット上では、ちょっとユルそうな会社に感じると思いますが(笑)、
実は技術力に定評があり、沢山の実績を残している会社ということをお伝えしたいと思っております。
- ライトコードの魅力を知っていただきたい!
- 社風や文化なども知っていただきたい!
- 技術に対して熱意のある方に入社していただきたい!
一度、【Wantedly内の弊社ページ】をのぞいてみてください。