Claude Media
Claude Codeセルフホストで事前クローンが使われているか確かめる

Claude Codeセルフホストで事前クローンが使われているか確かめる

セルフホストRunnerに仕込んだ事前クローンが実際に再利用されているかを、debugログの2行で見分ける手順。見つからないときの3点確認と、速くならないときの切り分けまで。

Runnerのイメージや永続ボリュームに事前クローンを仕込んでも、Runnerがそれを使ってくれているとは限りません。使われているかどうかは、--log-level debugで起動したRunnerのログに出る1行で判別できます。

見るのは次の2つです。

ログの文言意味
Prefetched repo found atとクローンのパス意味仕込んだクローンを見つけて再利用している
No prefetched repo, fresh init at意味そのパスに使えるクローンが無く、新しく初期化している

後者が出たら、その行に書かれたパスを手がかりに、クローンの場所・所有ユーザー・リモート名の3点を順に見ます。前者が出ているのに起動が遅いなら、疑う先はクローンの浅さ(shallow)です。以降、この順に手順を並べます。

debugログで再利用を確かめる手順

事前クローンの再利用は、checkoutフックを設定していないRunnerが対象です。フックがあると、Runner組み込みのクローンと取得の代わりにフックが動くためです。まずフックの有無を確認してから進めます。

Runnerを--log-level debug付きで起動し、事前クローンを仕込んだリポジトリでセッションを1つ始めます。起動例は次のとおりです(環境シークレットのパスは自環境のものに置き換えます)。

self-hosted-runner \
  --environment-secret-file /run/secrets/environment-secret \
  --capacity 1 \
  --base-dir /workspace \
  --log-level debug

--log-levelが取る値はinfoとdebugの2つで、既定はinfoです。環境変数での指定はありません。コンテナで動かしている場合は、起動コマンドの引数にこのフラグを足す形になります。

セッションが始まったら、ログから該当行を探します。ログの出力先は環境ごとに違うので、次は一例です。

docker logs claude-runner 2>&1 | grep -E "Prefetched repo found at|No prefetched repo, fresh init at"

Prefetched repo found atの後ろに出るパスが、Runnerが実際に使っているクローンの場所です。事前クローンを置いたつもりの場所と、この行のパスが一致しているかを最初に見ます。

No prefetched repoが出たときの3点確認

No prefetched repo, fresh init atは、その行のパスに使えるクローンが見つからなかったことを示します。確認する点は3つです。

  1. クローンの場所: Runnerは、checkoutフックなしの場合、リポジトリごとに<base-dir>/<repo-owner>/<repo>へ正規のクローンを1つ持ちます。--base-dirの既定は/workspaceなので、/workspace/<owner>/<repo>に置きます
  2. 所有ユーザー: クローンはRunnerを動かすユーザーの所有にします。ルートでイメージをビルドしてクローンを作り、Runnerを一般ユーザーで動かす構成では、ここで外れやすくなります
  3. リモート名: リモートはoriginという名前にします。別名のリモートだけだと、使えるクローンとして扱われません

3点をまとめて確かめるコマンドの例です(<owner>と<repo>は置き換えます)。

# 1. 場所: ログに出たパスにクローンがあるか
ls -d /workspace/<owner>/<repo>/.git
 
# 2. 所有ユーザー: Runnerを動かすユーザーと一致するか
stat -c '%U' /workspace/<owner>/<repo>    # macOSでは stat -f '%Su'
 
# 3. リモート名: origin があるか
git -C /workspace/<owner>/<repo> remote -v

所有ユーザーが違っていたときは、イメージのビルド手順に所有者の変更を足します。次のように、クローンを作った直後にchownをかける形が素直です(runnerは自環境のユーザー名に置き換えます)。

RUN git clone https://github.com/<owner>/<repo>.git /workspace/<owner>/<repo> \
 && chown -R runner:runner /workspace/<owner>
USER runner

上の例は書き方の一例です。クローンをイメージに焼く方法と、永続ボリュームに置く方法のどちらでも、Runnerが見る場所はこのパスです。永続ボリュームを使うなら、--lock-to-accountで1人のアカウントに固定したRunnerに限り、--base-dirをそのボリュームに向けます。固定したRunnerはClaude Tagのチャンネルセッションを拾わないので、それらを受けるRunnerにはこの方法が使えません。

見つかったのに遅いなら、まずshallowを疑う

Prefetched repo found atが出ているのにセッションの起動が遅いときは、クローンが浅いかどうかを確かめます。次のコマンドがtrueを返せば浅いクローンです。

git -C /workspace/<owner>/<repo> rev-parse --is-shallow-repository

事前クローンは--depthなしで作るのが前提です。--single-branchの有無はどちらでも構いません。Runnerは、originリモートを持つクローンへ取得するとき、CLAUDE_RUNNER_FETCH_DEPTHを設定していても--depthを渡しません。そのため、フルクローンなら各セッションが取得するのは足りないコミットだけです。

浅いクローンでは事情が変わります。クローンの最古のコミットより前に分岐したブランチのセッションでは、そのブランチの履歴を丸ごとダウンロードします。ブランチの切り方次第で、あるセッションだけ極端に遅くなる症状になります。

gitプロキシ下でも、Runnerは浅い状態を引き継ぎます。プロキシに切り替えても浅さは解消しません。

浅いクローンを直すには、git fetch --unshallowで履歴を補うか、--depthなしで作り直します。

再利用が効いても速くならない場合

再利用が確認できても、期待したほど速くならない構成があります。Runnerの設定ごとに、何が速くなり何が残るかを表にします。

構成速くなるもの残るもの
--capacity 1速くなるもの取得したrefへHEADを切り離してreset --hardするだけ。変更が少なければほぼ即時残るものなし
--capacityが2以上速くなるものダウンロード残るものセッションごとのworktreeのチェックアウト
--use-anthropic-git-proxy速くなるもの既存クローンへの取得(再クローンはしない)残るものインデックスが毎回消えるため、作業ツリー全体のチェックアウト

--capacityが2以上のときは、事前クローンから別のworktreeを切り出すので、ダウンロードは省けてもチェックアウトの時間は残ります。巨大なリポジトリで--capacity 1と比べて差が出るのは、この分です。

Anthropicのgitプロキシを使うと、Runnerは各セッションの前にクローンの.git/を整えます。オブジェクトの保管場所とshallowの状態は残し、インデックスを消すため、毎回フルのチェックアウトになります。サブモジュールを事前に温める構成は、プロキシ下ではサポート外です。

プロキシ下でもう1つ落とし穴があります。.git/refs/は残す一方で.git/reftable/を消します。gitのreftable形式で事前クローンを作ると、refが消えてしまいます。プロキシを使うなら、files形式で作ります。

起動時間の変化は指標で見る

ログの1行は再利用の有無を教えますが、速くなったかは教えません。起動の長さは、Prometheusの指標claude_code_self_hosted_runner_session_init_duration_secondsで見ます。事前クローンの導入前後でこのヒストグラムを比べれば、効果が数字で分かります。

指標の一覧と読み方はClaude Codeセルフホスト環境のCLIフラグ・メトリクスリファレンスにまとめています。

Runnerが自分でクローンを作り、1セッションで終了する構成なら、別の手もあります。CLAUDE_RUNNER_FETCH_DEPTHの既定は50コミットで、これを小さくすると取得する履歴が減ります。fullか0を指定すると完全な取得になります。事前クローンがある場合の挙動は前節のとおり、この変数の影響を受けません。

再開したセッションと、事前クローンを置かない場合

空のディスクでRunnerを起動する構成では、再開するセッションも同じクローンを待ちます。Runnerが落ちるとサーバーがセッションを再度キューに戻し、同じ環境の別のRunnerが引き継ぐためです。引き継ぐ側のディスクに事前クローンが無ければ、そこでもう一度クローンが走ります。

引き継ぎ先でも作業ディレクトリを同じにするには、環境内の全Runnerで--base-dirと--capacityを揃えます。--capacity 1は--base-dirの直下、2以上はセッションごとのworktreeにチェックアウトするため、値が違うと再開後のパスが変わります。事前クローンを置くパスも--base-dirに依存するので、Runnerごとに値が違うと、一部のRunnerだけNo prefetched repoになります。

事前クローンを置けない環境で初回クローンが長引いても、すぐ失敗にはなりません。Runnerは各git操作を「120秒間進捗がない」ことと「30分の上限」で見ており、進捗を報告し続ける遅いクローンは完了します。つまり、遅いだけで失敗しない症状は、事前クローンの効果を測る材料になります。

永続ボリュームで事前クローンを使うときの後始末

永続ボリュームに--base-dirを向ける構成では、事前クローンの再利用に伴って別のものも残ります。

  • 追跡ファイルの変更: --capacity 1では、各セッションの開始時にハードリセットがかかり、前のセッションの変更は消えます
  • 未追跡ファイル: Runnerはgit cleanを実行しないので、固定されたオーナーの過去セッションが作った未追跡ファイルはツリーに残ります
  • セッション単位のディレクトリ: <base-dir>/_sessions/に、セッションごとのClaude設定ディレクトリ(会話記録のローカルコピーを含む)とアップロードされたファイルが残ります

セッションは全てRunner自身のユーザーで動くので、同じディスクを使う後続のセッションはこれらを読めます。残したくないときは--remove-session-stateでRunnerを起動します。セッション終了時にセッション単位のディレクトリを削除しますが、Runnerが強制終了されたときは削除されず、正規のクローンや一時ディレクトリなどホスト上の他のファイルも残ります。

Docker Composeのレシピは再起動で同じファイルシステムに戻るため、この蓄積の対象になります。永続運用ではボリュームの容量を、この増加分を見込んで決めます。

オンデマンドで起動するRunnerの側にも、リポジトリを温めておく考え方があります。spawn-runnerフックには、特定のリポジトリを温めたRunnerへ振り分けるためのCLAUDE_RUNNER_PRIMARY_REPO_URLが渡されます。仕組みはClaude Codeセルフホスト環境でオンデマンドRunnerを起動するで扱っています。ここでいう事前クローンの再利用とは別の話で、待機Runnerを先に立てておく意味の「pre-warming」も混ざりやすいので区別が要ります。

切り分けの順番

起動が遅いと感じたときの確認順は、次の3段で足ります。

  1. checkoutフックがあるか。あれば、このページの対象外でフックの中身を見る
  2. debugログにPrefetched repo found atが出るか。出なければ、場所・所有ユーザー・originの3点を確認する
  3. 出ているなら、rev-parse --is-shallow-repositoryで浅さを見て、次に--capacityとgitプロキシの構成を見る

本番運用全般の設計はClaude Codeセルフホスト環境の本番運用に置いてあります。事前クローンの確認だけなら、ログの1行と3点確認で決着します。debugログは確認のときだけ有効にして、通常運用はinfoに戻す運用が無難です。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. code.claude.com/docs/en/self-hosted-environments-deploy
  2. code.claude.com/docs/en/self-hosted-environments-reference
  3. code.claude.com/docs/en/self-hosted-environments-configuration
この記事を共有:XはてブLinkedIn