単一リポジトリと複数サブモジュールを束ねた構成では、Git Submodule の再帰初期化と Git LFS のオブジェクト取得が CI の壁時間を支配します。本稿はリモート Mac 上の iOS/macOS ビルドを想定し、初期化順・並列パラメータ・キャッシュ鍵・ミラー切替・リトライを「コピペ可能な値」まで落とし込みます。関連トピックは技術ブログ一覧キャッシュ戦略跨境 Git プル加速と併読すると理解が早いです。

CI における Submodule と LFS のボトルネック分解

ボトルネックは大きく四層に分かれます。(1) 親リポジトリの履歴取得、(2) 各 submodule の git fetch 連鎖、(3) LFS ポインタ解決とオブジェクト転送、(4) ディスク I/O とキャッシュミスによる再ダウンロードです。リモート Mac CI では RTT と帯域が変動しやすいため、(2)(3) を同時に走らせると帯域競合で双方がタイムアウトしがちです。

実務では「親を浅く取る → submodule をジョブ並列で広げる → LFS はスマッジを遅延または帯域制限」と段階化すると安定します。ホームから MacPull のリモート Mac を検討する場合も、同一ノードに .git と LFS キャッシュを残す設計がヒット率に効きます。

Submodule 初期化の推奨順序(チェックリスト)
  • 1. 親を git clone --recurse-submodules=no(または既存ワークスペースで git fetch)し、対象コミットを git checkout
  • 2. git submodule sync --recursive.gitmodules の URL 改変(insteadOf 等)を各子に反映。
  • 3. git submodule update --init --recursive --depth 1 --jobs 8 のように深さと並列を明示(ネットが細いときは --jobs 2〜4)。
  • 4. LFS を使うリポでは、先に GIT_LFS_SKIP_SMUDGE=1 でポインタのみ取得し、ビルド直前に git lfs pull または git lfs checkout
  • 5. 検証:git submodule status --recursivegit lfs ls-files で欠損がないか確認。

並列プルと lockfile(固定 SHA)方針の比較表

「速さ」と「再現性」はトレードオフです。チーム規模とリリース頻度に合わせて行を選んでください。

方針 Submodule/ロック 向いているケース 主なリスク
高並列・浅い履歴 --jobs N--depth 1、親のみ固定 日次開発・フィーチャーブランチ CI 子の浮動参照があると再現性が落ちる
親コミット完全固定 親の git rev-parse HEAD を成果物キーに含める リリースビルド・ストア提出 キャッシュミス時の初回が長い
LFS 遅延スマッジ GIT_LFS_SKIP_SMUDGE=1 → 後段 lfs pull 巨大アセット・多数ポインタ pull 忘れでビルドが欠損ファイルになる
モノレポ+スパース submodule 廃止、sparse-checkout パス単位の CI が多い 移行コスト・権限設計が重い

キャッシュと増分 fetch のパラメータ一覧

CI のキャッシュ鍵は「親のロック情報」と「サブモジュール定義の変化」を分離すると無駄な無効化を減らせます。

キャッシュディレクトリと鍵設計(例)
  • パス$CI_CACHE/git-lfsgit config lfs.storage で指定)、既定はリポ内 .git/lfs/objects$CI_CACHE/submodule-bundles(任意の事前 bundle)、親 .git/modules/*/objects を丸ごと載せる場合はディスク見積もり必須。
  • 鍵(推奨スロット)sha256(.gitmodules)sha256(lockfile)runner.osxcode-select 相当のツールチェーン版(iOS の場合)。親のみのジョブなら HEAD の短縮 SHA をサフィックスに。
  • 増分 fetchgit fetch --depth=1 origin <branch> のあと git submodule update --init --recursive --depth=1 --jobs Nfetch.recurseSubmodules=no をデフォルトにし、明示ステップだけで子を取るとログが追いやすいです。

LFS 並列・帯域の設定例(環境変数/git config)

  • git config lfs.concurrenttransfers 4(混雑時は 2〜4、余裕時は 6〜8)
  • git config lfs.activitytimeout 300(秒・アイドル打ち切りの目安)
  • git config lfs.dialtimeout 30(接続確立の上限)
  • 帯域上限(任意):git config lfs.basictransfersonly true でカスタム転送を避けシンプルに、プロキシ配下では http.version HTTP/1.1 の検証も有効なことがあります。
ミラーと直結のフォールバックしきい値(目安)
  • 遅延:直結の p95 RTT が 200ms 超(跨境)かつジョブ SLA に余裕がない → 近傍ミラーまたはリモート Mac リージョンの見直し。
  • スループット60〜120 秒の窓で実効 < 2〜5 Mbps が続く → ミラー試行、または --jobslfs.concurrenttransfers を下げて安定優先。
  • エラー率:同一ホストで 連続 3 回 5xx/TLS 失敗/429 → 直結とミラーを入れ替え、バックオフ後に再試行。

失敗リトライとタイムアウト閾値

Git/LFS は指数バックオフ付きのラッパー(シェルまたは CI の retry ステップ)に載せると効果が高いです。

  • GIT_HTTP_LOW_SPEED_LIMIT=1000GIT_HTTP_LOW_SPEED_TIME=600(停滞検知の一例)
  • http.postBuffer を一時的に上げる(大 push 用。pull 主体なら優先度は低め)
  • ジョブレベル:最大 3〜5 回、初期待機 5〜15 秒、上限 60〜120 秒の指数バックオフ
  • 429 応答は Retry-After を尊重し、無い場合は 30 秒以上空ける

詳細な Git/Homebrew/npm のタイムアウト一覧はプル安定性 FAQを参照してください。

リモート Mac のネットワークとディスク水位チェック

Submodule と LFS を並列に走らせると、一時的にディスクとファイル記述子を大量に使います。ジョブ前のワンライナーで水位を確認しておくと原因切り分けが速くなります。

  • df -h:ビルドルートと /tmp、キャッシュボリュームに 15〜20% 以上の空きを確保
  • pingtraceroute 相当で Git ホストへの RTT、curl -o /dev/null -w '%{time_total}\n' で HTTPS のレイテンシ目安
  • 同時実行数を上げた直後に失敗が増える場合は、帯域ではなく IOPS 不足の可能性 → --jobslfs.concurrenttransfers を下げる

FAQ(認証失敗・クォータ・ミラー)

Q. Submodule だけ 401/403 になる
A. 親用トークンが子のホストで無効なケースが典型です。URL ごとに credential を分ける、git config --global url."https://token@host/".insteadOf で揃える、SSH の場合は各リポに Deploy Key を配布してください。

Q. LFS の転送/ストレージのクォータを使い切った
A. ダッシュボードで残量を確認し、古いバージョンのオブジェクト整理、もしくは成果物をリリース添付に逃がす。自前 LFS またはキャッシュヒット率改善で転送を削ります。

Q. ミラーが最新でない
A. ミラーの同期遅延を監視し、HEAD が親の要求 SHA に届かないときは直結にフォールバックする条件を CI に入れてください。

運用面の問い合わせはヘルプセンターもご利用ください。

まとめ

Submodule は同期 → 再帰 update(深さ・jobs 明示)、LFS はスマッジ遅延+ concurrenttransfers/activitytimeout のチューニングが効きます。キャッシュ鍵に .gitmodules とツールチェーン指紋を載せ、ミラーと直結は遅延・スループット・エラー率のしきい値で切り替えると運用が楽になります。同一ノードで .git/LFS を育てられるMacPullのリモート Mac なら、ヒット率と再実行コストのバランスを取りやすいです。料金購入はログイン不要で確認でき、関連記事はブログ一覧からどうぞ。

リモート Mac で Submodule/LFS CI を安定化

並列プルとキャッシュに向いた Mac ビルドノードを用意する

iOS/macOS CI の取得時間を短縮したいチーム向け。料金・購入・ヘルプはログイン不要でご利用いただけます。