dotnet restore がタイムアウト・浮動バージョン・ソース散在に悩むチーム向けです。NuGet.Config のミラー連鎖、MaxHttpRequestsPerSource、globalPackagesFolder、中央パッケージ管理(CPM)と RestoreLockedMode/packages.lock.json を一枚の判断表に落とし、検証コマンドまで繋ぎます。入口はホーム、一覧は技術ブログ。関連:Gradle/Maven の CI キャッシュマトリクス、Git・npm・Homebrew のミラー最適化、Go modules/GOPROXY マトリクス。
課題:ソース順・並列 HTTP・ロック運用の三層
- packageSources の列挙順と packageSourceMapping がズレると、意図せず
nuget.org直叩きが残り、国境越え RTT が支配的になります。 - MaxHttpRequestsPerSource を上げたまま同時パイプラインを積むと、429 やソケット枯渇で
restoreがフラップします。 - RestoreLockedMode を main で必須にしたのに packages.lock.json 更新フローが無いと、PR ごとに還元が赤くなり疲弊します。
決定マトリクス(シナリオ別のおすすめ組合せ)
| シナリオ | ソース連鎖 | MaxHttp/ソース | globalPackagesFolder | RestoreLockedMode |
|---|---|---|---|---|
| 社内プロキシ完結 | clear 後に社内 v3 → 必要なら public ミラー | 8〜12 から | 共有 SSD(ジョブ ID サブフォルダ可) | main true/topic branch は段階導入 |
| 国境越え+キャッシュ重視 | 地域ミラー v3 index を先頭、nuget.org をフォールバック | 帯域次第で 12→16、429 出たら 8 | runner ローカル+ CI アーティファクト同期 | CPM+ lock コミット後に true |
| マルチリポ短期検証 | ユーザー単位 Config で一時 source を上書き | --disable-parallel で切り分け時は 4 | 既定のまま(混乱時は明示パス) | 検証中は false、合流時に true |
| オフラインに近い帯域 | 全パッケージを社内フィードへプロモートし一本化 | 低め(4〜8)で安定優先 | NFS 共有は競合に注意、推奨はローカル | true で浮動を拒否 |
中央パッケージ管理と MSBuild フラグの対応
Directory.Packages.props で ManagePackageVersionsCentrally を有効化し、バージョンは中央のみに置きます。ロックファイル運用では RestorePackagesWithLockFile を有効にして packages.lock.json を生成し、CI では /p:RestoreLockedMode=true で浮動更新を弾きます。
| キー/プロパティ | 役割 | メモ |
|---|---|---|
ManagePackageVersionsCentrally | バージョンの単一情報源 | 各 csproj から Version を外す移行が前提 |
RestorePackagesWithLockFile | ロック生成 | 初回は false→生成→コミットの運用が扱いやすい |
RestoreLockedMode | ロック不一致をエラー | ブランチ戦略と更新 PR をセットで決める |
CentralPackageTransitivePinningEnabled | 推移的依存のピン留め | ロックの見え方が変わるため段階導入を推奨 |
NuGet.Config 断片(コピペ用)
packageSourceMapping はチーム標準の id プレフィックスに合わせて増減してください。実 URL は自社ミラーに読み替えます。
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<config>
<add key="globalPackagesFolder" value="/var/ci/nuget-global" />
<add key="maxHttpRequestsPerSource" value="12" />
</config>
<packageSources>
<clear />
<add key="corp" value="https://nuget.corp.example/v3/index.json" />
<add key="nugetorg" value="https://api.nuget.org/v3/index.json" />
</packageSources>
<packageSourceMapping>
<packageSource key="corp"><package pattern="Corp.*" /></packageSource>
<packageSource key="nugetorg"><package pattern="*" /></packageSource>
</packageSourceMapping>
</configuration>
再現手順(五ステップ+検証)
リポジトリ直下に NuGet.Config を置き、packageSources の順と packageSourceMapping をレビューに回します。
globalPackagesFolder を self-hosted 全台で揃え、ディスククォータと CI キャッシュキー(lock ハッシュ)を一致させます。
MaxHttpRequestsPerSource を 8 から起動し、ログで同時接続と 429 を見て 12〜16 へ段階的に上げます。
CPM 化後に packages.lock.json を生成しコミット、dotnet restore /p:RestoreLockedMode=true を CI に固定します。
異常時は dotnet restore --disable-parallel -v:n でボトルネック URL を特定し、ソースを一つずつ無効化して切り分けます。
- 数値メモ:同時パイプライン p、ソースあたり HTTP h のとき、ピーク接続の目安は パッケジ数×h に比例。出口 NAT のセッション上限と突き合わせます。
- 運用メモ:lock 更新は専用 PR に限定し、レビューでバージョン差分だけを見える化します。
- 環境メモ:HTTPS_PROXY/NO_PROXY は対話シェル・launchd・CI YAML の三経路で一致させ、
curl -Iで v3 index の応答を先に確認します。
FAQ(本文と JSON-LD の両方に対応)
MaxHttpRequestsPerSource を上げると速くなりますか?
帯域と相手サーバの制限次第です。429 やソケットエラーが増えるなら下げ、パイプライン本数との積で調整します。
RestoreLockedMode で CI が頻繁に落ちる
lock がブランチと不一致です。ロック更新 PR の運用にするか、開発線だけ一時的に false に戻してから段階的に true へ寄せます。
globalPackagesFolder を NFS で共有してよいか
小規模なら可ですが競合リスクがあります。ローカル SSD+CI キャッシュの方が無難なことが多いです。
まとめ:国境越え restore は「ソース順・並列・ロック」の三本柱
2026 年の リモート Mac CI では、NuGet.Config で外向き経路を制御し、MaxHttpRequestsPerSource で同時取得を抑え、CPM+packages.lock.json で再現性を固定するのが実務的な黄金パターンです。検証は restore と build --no-restore を分離してログを読み切ると早いです。
パッケージ取得のボトルネックを 地理的に近いリージョンのリモート Mac ノードへ寄せると、dotnet restore の RTT 短縮とキャッシュヒット率向上を同時に狙いやすいです。次の一手:MacPull ホームで概要を確認し、料金プランや購入ページ(ログイン不要で閲覧できる範囲)からプランを選び、ヘルプセンターで SSH 接続を押さえてください。技術ブログ一覧の CI/依存記事もあわせてどうぞ。