【テクニカル・上級編】Composerのインストール速度を劇的に改善する5つのテクニック – ビルド・パッケージ管理ツール生産性向上バイブル

Composerの限界を突破せよ:ビルド時間を極限まで削ぎ落とす5つの低レイヤ最適化ハック

幾多のプロジェクトでCI/CDパイプラインのボトルネックを解析してきた者なら、一度はComposerの「Resolving dependencies…」という沈黙の時間に絶望したことがあるはずだ。
現代のPHPアプリケーションにおいて、数千に及ぶパッケージの依存関係解決とZIPアーカイブのHTTPダウンロード、そしてローカルファイルシステムへの展開は、I/Oバウンドな処理の典型例である。特にDockerコンテナのビルド時やEphemeral(使い捨て)なCI環境において、このオーバーヘッドはビジネスのデリバリー速度を直接的に阻害する。

かつては `hirak/prestissimo` がパラレルダウンロードの救世主であったが、Composer v2の登場により、並列処理や内部アーキテクチャは劇的な進化を遂げた。しかし、デフォルト設定のまのでは真のパフォーマンスを引き出すことはできない。

本稿では、Composerの内部挙動とOS/ネットワーク層のメカニズムを解剖し、ビルド時間を限界まで短縮するための5つの極限テクニックを授ける。

—

1. Composer v2のネイティブ並列ダウンローダーの限界を超える

Composer v2は、非同期HTTPクライアント(ReactPHPベース)をコアに統合し、標準でパッケージの並列ダウンロードを実現している。しかし、これが動くのは「ZIPアーカイブのダウンロードフェーズ」に限られる。依存関係の解決(SATsolverによる計算)や、巨大な `vendor/` ディレクトリへのファイル書き込みは依然としてシングルスレッド、かつ重いI/Oを発生させる。

ここで活きるのが、環境変数による並列度の最適化と、不要なメタデータの排除だ。

実装コード:環境変数によるパイプライン最適化

CI/CDのランナーやDockerビルドの直前で、以下の環境変数をインジェクトせよ。

パラレルダウンロードの同時実行数をCPUコア数に合わせて強制拡張する(デフォルトは12だがネットワーク帯域に応じて調整)
export COMPOSER_MAX_PARALLEL=24

プログレスバーの描画やANSIカラー出力を抑制し、TTYバッファのフラッシュコストとCPU負荷を排除する
export COMPOSER_NO_INTERACTION=1

ディスクI/Oを極限まで減らすため、メモリ上にtmpfs領域があればそこにキャッシュディレクトリを向ける
export COMPOSER_CACHE_DIR=”/dev/shm/composer-cache”

> アーキテクトの知見: `/dev/shm`(共有メモリ)上にComposerキャッシュを構築することで、SSDのI/Oすらバイパスし、数万ファイルのメタデータ読み書きをRAMスピードへと昇華させられる。ただし、コンテナのメモリ制限(RAMディスクの容量)を超過しないよう、CIランナーのメモリサイジングには厳密な注意が必要だ。

—

2. 依存関係解決のキャッシュ最適化と `–no-autoloader` / `–prefer-dist` の極致

「なぜ、変更のない `composer.lock` を使っているのに毎回こんなに時間がかかるのか?」
その原因の多くは、Composerが毎回全パッケージのバージョン制約を再評価し、Autoloader(Class Map)の最適化に多大なCPU時間を費やしている点にある。

ビルドのフェーズを「依存関係の解決」「ファイルの配置」「オートローダーの生成」に分離し、それぞれを極限までチューニングする。

実装コード:多段ビルド(Multi-stage Docker build)における最適化パターン

— ステージ 1: 依存関係の解決とダウンロード —
FROM php:8.3-cli AS vendor-builder

WORKDIR /app

Composerバイナリをマルチステージの別レイヤから高速コピー
COPY –from=composer:2 /usr/bin/composer /usr/bin/composer

依存関係定義ファイルのみを先にコピー(レイヤキャッシュのヒット率を100%にするため)
COPY composer.json composer.lock ./

バイナリやソースコードを含めず、ディストリビューション(ZIP)のみを最速で取得
–no-scripts: スクリプト実行による無駄なフックを排除
–no-autoloader: クラスマップの生成をスキップし、I/Oを削減
RUN composer install \
–no-dev \
–no-scripts \
–no-autoloader \
–prefer-dist \
–ignore-platform-reqs \
–no-interaction

— ステージ 2: アプリケーション本体のビルド —
FROM php:8.3-cli AS production

WORKDIR /app

ステージ1で完全に構築されたvendorディレクトリのみを高速インポート
COPY –from=vendor-builder /app/vendor ./vendor
COPY . /app

本番用最適化オートローダーの生成(ここで初めてダンプを行う)
RUN composer dump-autoload –no-dev –classmap-authoritative –optimize

> アーキテクトの知見: `–classmap-authoritative` は、PSR-4の動的なファイルシステム走査(`file_exists` の嵐)を完全に廃止し、あらかじめ生成された静的なマップ配列のみでクラスをロードさせる。これは本番環境におけるPHPの実行レイテンシ(OpCache効率の最大化)においても絶対不可欠な設定である。

—

3. プライベートレジストリ(Satis / Packagist Enterprise)通信のレイテンシを粉砕するHTTP/2 & Keep-Aliveチューニング

社内製ライブラリを大量に抱える企業において、ComposerのボトルネックはPackagist.orgではなく「社内のプライベートComposerレジストリ」とのTCPハンドシェイクおよびTLSネゴシエーションの回数にある。数百のパッケージを個別、あるいは順次リクエストする場合、TCPの接続確立コストが全体の数割を占めるようになる。

これを解決するのが、HTTP/2 multiplexing(多重化) と、Composer内部のHTTPクライアントが利用するcURLハンドルの最適化だ。

実装コード:`COMPOSER_HOME/config.json` による高度ネットワーク設定

グローバル、またはCI環境の環境変数として以下を定義し、持続的接続(Keep-Alive)を強制する。

{
“config”: {
“preferred-install”: “dist”,
“github-protocols”: [“https”],
“sec-security”: false,
“http-basic”: {
“repo.internal.company.com”: {
“username”: “ci-bot”,
“password”: “secret-token-or-app-password”
}
}
},
“repositories”: [
{
“type”: “composer”,
“url”: “https://repo.internal.company.com”
},
{
“packagist.org”: false
}
]
}

さらに、PHP自体のcURL拡張の挙動を最適化するため、以下の環境変数をCIスクリプトの先頭に挿入せよ。

cURLのDNSキャッシュの生存期間を引き上げ、名前解決のオーバーヘッドを消滅させる
export CURL_CA_BUNDLE=”/etc/ssl/certs/ca-certificates.crt”

—

4. プロキシサーバーとComposerプロキシキャッシュ(Composer Proxy / Repositorrent)のアーキテクティング

分散拠点や厳格なセキュリティポリシーを持つエンタープライズ環境では、すべての外部トラフィックがプロキシを経由するため、SSLインスペクション(復号・再暗号化)によってComposerのダウンロードが致命的に遅くなる。

この障壁を打ち破るには、ローカルプロキシキャッシュ(例: Repositorrent や Artifactory, Nexus等を用いたComposerリポジトリプロキシ)を同一ネットワーク(同一データセンター内)に配置することが唯一にして最善の解となる。

構成イメージと設計思想

1. キャッシュヒット率の最大化: 社内の複数プロジェクトが同じバージョンのライブラリ(例: `monolog/monolog v3.0.0`)を要求した場合、外部のPackagistにリクエストを飛ばさず、ローカルプロキシが保持するZIPをローカルネットワーク(10Gbps環境など)で高速配信する。
2. SSLインターセプションの回避: プロキシサーバー側でPackagistのメタデータとZIPファイルを一度キャッシュするため、開発者やCIランナーが直接外部とTLSハンドシェイクを行う回数が激減する。

これをComposerに認識させるには、リポジトリのミラーリング機能(`mirrors`)を利用する。

実装コード:`composer.json` におけるミラーリング設定

{
“repositories”: [
{
“packagist.org”: {
“type”: “composer”,
“url”: “https://composer-proxy.internal.net”
}
}
]
}

> アーキテクトの知見: ミラー機能を使うことで、Composerはオリジナルの `packagist.org` の代わりに指定した内部プロキシのURLからメタデータ(`packages.json` や各種p2 JSON)を取得する。これにより、外部ネットワークのパケットロスや遅延の影響を完全に排除し、ダウンロード速度を数倍から十数倍に跳ね上げることが可能となる。

—

5. PHPランタイムとOPcache/JITのプリロードを活用した「Composer自体の高速化」

忘れられがちだが、Composer自体も「巨大なPHPスクリプトの集合体」である。
数千のクラスを読み込み、数万行のJSON/PHARをパースするComposerの実行中、PHPのインタプリタは大量のファイルオープンとバイトコードコンパイルを行っている。

つまり、Composerを実行するPHPランタイム環境自体を最適化することが、究極のビルド高速化につながる。

実装コード:CLI用 `php.ini` の極限チューニング

Composer実行専用の `php.ini` プロファイルを作成し、CI上で適用せよ。

[PHP]
memory_limit = 2G

[opcache]
; CLI環境であってもOPcacheを有効化し、Composerのコンパイル済みバイトコードをメモリに常駐させる
opcache.enable_cli=1
opcache.memory_consumption=512
opcache.interned_strings_buffer=64
opcache.max_accelerated_files=32768
opcache.validate_timestamps=0
opcache.jit=1255
opcache.jit_buffer_size=128M

> アーキテクトの知見: `opcache.validate_timestamps=0` を設定することで、PHPはスクリプトファイルの更新日時(`stat` システムコール)のチェックを一切行わなくなる。Composerのような単発のCLIコマンド実行において、この数千回に及ぶ `stat` コールを消去する効果は計り知れない。

—

結語:ビルド時間は、エンジニアリングの誇りである

ここに提示した5つのテクニック――メモリ上のtmpfs活用、マルチステージによるI/O分離、HTTP/2とローカルプロキシによるネットワークの最適化、そしてPHPランタイムの底上げ――は、単なる「小手先のテクニック」ではない。

システムの内部構造(メモリ、ファイルシステム、ネットワークスタック、言語処理系)を深く理解し、ボトルネックがどこに存在するかを正確に突き止めた者だけが到達できる、エンジニアリングの極致である。

CIのビルド時間が短縮されるたびに、デプロイの心理的ハードルが下がり、チームのイテレーション速度は加速する。
あなたのパイプラインに今すぐこれらのハックを組み込み、圧倒的なスピードを手に入れろ。

タイトルとURLをコピーしました