【実務・中級編】Composerで発生する「Memory Exhausted」エラーの完全な解決策 – ビルド・パッケージ管理ツール生産性向上バイブル

開発チームのテックリードである私たちが、CI/CDパイプラインやローカル環境で最も直面したくない不意のトラブル、それが Composerによる `Memory Exhausted` エラー です。

「`Allowed memory size of 134217728 bytes exhausted`」
この絶望的なメッセージを前に、何度 `php -d memory_limit=-1 composer.update` と場当たり的に打ち込んだことでしょう。

ネットを検索すれば「`php.ini` の `memory_limit` を増やせ」という記事が無数に出てきますが、それは根本的な解決ではありません。なぜなら、Composerが消費するメモリの肥大化は、「依存関係リゾルバのグラフ探索アルゴリズムの爆発」 と 「不要な開発用パッケージ(`require-dev`)の芋づる式ロード」 という構造的な問題に起因しているからです。

今回は、このメモリ枯渇問題のメカニズムを根底から断ち切り、チーム全体の開発スピードを劇的に引き上げるための「プロの実践テクニック」を全方位から解説します。

—

1. なぜComposerはメモリを食い潰すのか?(内部挙動の真実)

Composerの内部では、インストール・アップデート時に `Sausage` や `Composer\DependencyResolver` が稼働し、全パッケージのバージョン制約(Constraint)をSAT(充足可能性問題)のソルバーにかけ、最適な依存関係の組み合わせ(SAT-solving)を計算しています。

ここで何が起きているかというと:
1. 巨大な依存関係ツリーのメモリ上での展開: `require-dev` に指定されたテストツールや静的解析ツール(PHPStan, Psalm, Infection等)は、本番環境では不要であるにもかかわらず、リゾルバにとっては「解決すべき変数」としてメモリ上にロードされます。
2. メタデータのキャッシュ効率の悪化: ベンダーごとのバージョン数が数千を超えると、依存関係の組み合わせグラフが幾何級数的に膨れ上がり、デフォルトの `128MB` や `512MB` の制限を軽々と突破します。

これを力技で `memory_limit = -1` にして逃げ続けると、コンテナ環境(Docker/Kubernetes)でOOM (Out Of Memory) Killerに発動され、Podごと強制終了するリスクを抱えることになります。スマートに解決しましょう。

—

2. 根本的解決:実用的な `composer.json` のベストプラクティス構成

まず、チーム全体で共有すべき `composer.json` の設計思想を見直します。無駄な依存関係を排除し、Composerのパフォーマンスを極限まで引き出すための構成例がこちらです。

{
“name”: “enterprise/backend-api”,
“description”: “High-performance microservice API backend”,
“type”: “project”,
“require”: {
“php”: “^8.2”,
“ext-pdo”: “”,
“ext-json”: “”,
“laravel/framework”: “^10.0”,
“guzzlehttp/guzzle”: “^7.5”
},
“require-dev”: {
“fakerphp/faker”: “^1.9.1”,
“laravel/sail”: “^1.18”,
“mockery/mockery”: “^1.4.4”,
“nunomaduro/collision”: “^7.0”,
“phpunit/phpunit”: “^10.0”,
“phpstan/phpstan”: “^1.10”
},
“config”: {
“optimize-autoloader”: true,
“preferred-install”: “dist”,
“sort-packages”: true,
“process-timeout”: 0,
“allow-plugins”: {
“pestphp/pest-plugin”: true,
“phpstan/extension-installer”: true
}
},
“minimum-stability”: “stable”,
“prefer-stable”: true
}

アーキテクトのこだわりポイント

  • `”preferred-install”: “dist”`: Gitリポジトリではなく、軽量なZipアーカイブ(dist)を強制します。これにより、`.git` ディレクトリのダウンロードや不要なメタデータの処理が省かれ、メモリとディスクI/Oを大幅に削減できます。
  • `”sort-packages”: true`: 依存パッケージ名をアルファベット順に自動ソートします。マージコンフリクトを劇的に減らすだけでなく、Composer内部のパッケージ検索インデックスの効率化にも寄与します。
  • `”allow-plugins”` の厳格化: 不審なプラグインが勝手に実行されてメモリやCPUを消費するのを防ぎます。セキュリティとパフォーマンスの両面で必須の設定です。

—

3. 開発スピードを劇的に高める「神プラグイン」とCLIテクニック

メモリ枯渇を防ぎつつ、日々の開発体験を爆発的に高めるツールチェーンを導入します。

必須プラグイン:`composer-prefetch`(または並列処理)

Composerのデフォルトの処理は直列(シーケンシャル)です。ネットワークの応答待ちとメモリ上の処理がボトルネックになります。
ネイティブの並列処理(Composer 2.2+以降の機能)を活用しつつ、高速化を図りましょう。

Composerの並列ダウンローダー機能を確実に有効化(Composer 2ではデフォルトで有効ですが確認)
composer config -g process-timeout 2000

現場で使える隠れた時短ショートカット&コマンド

1. 開発環境でメモリ制限を一時的に無制限にしつつ、不要なdevパッケージを除外してCIの速度を限界突破させる
php -d memory_limit=-1 composer install –no-dev –optimize-autoloader –no-interaction

2. 特定のパッケージだけをピンポイントで更新し、全体のリゾルバ計算コスト(メモリ)を極小化する
composer update vendor/package-name –with-dependencies

3. どのパッケージがメモリを食っているのか、依存関係の構造を可視化する
composer depends –tree vendor/heavy-package

—

4. CI/CDパイプラインおよび本番環境での鉄則(スワップ設定と–no-dev)

ローカルマシンならまだしも、GitHub ActionsやAWS CodeBuild、Dockerビルド時にメモリ不足でコケる現象は、CI/CDの信頼性を大きく損ないます。以下の対策を環境構築の標準として組み込んでください。

A. Dockerビルド時のメモリ最適化(マルチステージビルド)

本番用コンテナイメージに `require-dev` の残骸を持ち込んではいけません。マルチステージビルドを使い、ビルドステージでだけComposerを走りさせます。

— ビルドステージ —
FROM composer:2.6 AS builder

WORKDIR /app
COPY composer.json composer.lock ./

★重要: 開発用パッケージを完全に排除してリゾルバの負荷とメモリ消費を最小化
RUN composer install \
–no-dev \
–no-ansi \
–no-interaction \
–no-progress \
–no-scripts \
–optimize-autoloader

COPY . .
RUN composer dump-autoload –no-dev –optimize-autoloader

— 本番実行ステージ —
FROM php:8.2-fpm-alpine
WORKDIR /app
COPY –from=builder /app /app

ここには余計なComposerキャッシュやdevパッケージは一切存在しない

B. Linux環境(CI等)におけるスワップ領域の確保

もし低スペックなCIインスタンス(例: 1GB RAM)を使用せざるを得ない場合、OSレベルでスワップ(Swap)メモリを一時的に割り当てることで、`Memory Exhausted` のクラッシュを防げます。

2GBのスワップファイルを一時作成するCIスクリプトの例
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

これで安心して composer update が実行可能になる
composer update –no-interaction

—

5. チーム開発で絶対共有すべき設定のルール化

属人化を防ぎ、チームメンバー全員が「メモリ不足エラー」の呪縛から解放されるためのガバナンスルールを提案します。

1. グローバル設定の統一:
チームメンバー全員のローカル環境において、Composerのメモリ制限を無制限、あるいは十分なサイズ(例: 2GB)にグローバル設定させます。

composer config –global memory-limit 2G

2. ロックファイル(`composer.lock`)の厳格なバージョン管理:
`composer.update` はローカルのCPUとメモリを大量に消費するため、開発者が勝手に毎日実行するべきではありません。依存関係の更新は週に一度の定期タスク(Dependabotなど)に任せ、日常の機能開発では `composer install` のみを強制するワークフローをチーム規約とします。

—

テックリードからの総括

Composerの `Memory Exhausted` は、単なるPHPの設定ミスではありません。「プロジェクトの依存関係が肥大化しているシグナル」であり、「リソース管理の設計不足」を告げる警告です。

今回紹介した `composer.json` の最適化、`–no-dev` の徹底、そしてCI/CDにおけるマルチステージビルドの導入を実践すれば、エラーとは永遠に決別できます。ツールの内部挙動を正しく理解し、コントロールすること。それこそが、開発チームの生産性を極限まで高めるテックリードの仕事です。今日からあなたのプロジェクトでも導入し、チームを快適な開発体験へと導いてください。

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