Help us understand the problem. What is going on with this article?

EC-CUBE4系でのプラグインエラーの原因と予防対策

この記事は、 EC-CUBE4系でプラグインインストール時にエラーが出た時の対処法 の詳細版です。
主に、エラーを予防する観点から記述しています。

EC-CUBE4系では、 フレームワークとして Symfony3.4 が採用され、プラグインの管理に Composer が利用されるようになりました。
モダンな技術を取り入れた反面、動作させるために多くのリソースを消費するようになりました。

特に、プラグインのインストール/有効化/無効化/アンインストール時には、以下のような処理が実行されるため、大量のリソースを消費します。

  • Composer での依存関係解決
  • Composer の classmap 生成
  • Symfony DependencyInjection コンテナのコンパイル(キャッシュ生成)
  • Doctrine ORM のキャッシュ生成
  • Entity Proxy 生成

これらフレームワークのキャッシュを活用することで、運用時のパフォーマンス向上を実現しています。
現代のフレームワークは、キャッシュに支えられているといっても過言ではありません。
しかし、大量の細かいファイルを生成し読み込むために、 Windows 環境や Docker との相性を悪くしている原因にもなっています。

プラグインインストール時に発生するトラブルの大半が、これらキャッシュファイルの処理が失敗することによって発生します。
端的に言うと、キャッシュファイルの運用を安定化させれば、プラグインインストール時のトラブルを無くすことが可能です。

確実にプラグインインストールを成功させるためには

詳細は後述しますので、手っ取り早く解決したい方は本項のみを読んでください

プラグインインストール時のトラブルの大半は、フレームワークのキャッシュ生成に失敗することで発生しますので、以下の2点をクリアすれば防止することが可能です。

  1. 性能の良いサーバーを使用する
  2. メンテナンス画面にして、外部からのリクエストを遮断する

プラグインの不具合や競合で失敗する場合は、プラグインや本体を修正するしかありません...

性能の良いサーバーを使用する

クラウドや VPS など、潤沢なリソースのサーバーを選択することが重要です。
経験値としては、6コア/メモリ8GB 以上あれば比較的安定しています。
クラウドの場合は、プラグインインストール/有効化/無効化/アンインストール時にスケールアップすると良いと思います。(オートスケールはトラブルの原因になるためおすすめしません)

レンタルサーバーでの利用は、かなり厳しい状況なのですが、、、 さくらのレンタルサーバーは例外です。安価なスタンダードプランでも大丈夫です。
その理由は、 FreeBSD という Linux とは別物の OS を利用しているためです。

ちなみに FreeBSD を採用している、さくらのレンタルサーバーも半強制的なPHPのバージョンアップがあったり、パーミッション設定の関係から少々セキュリティに課題が残っていたりします。。。

プラグインをインストールする際、大量のリソースを消費します。
性能の低い Linux サーバーですと、サーバーダウンの危険があるため OOM Killer という OS 保護機構が働いて、プラグインインストールのプロセスを強制終了させてしまいます。
これがプラグインインストールトラブルの多くの原因になっているのですが、 FreeBSD には、この OOM Killer がありません。
OOM Killer によって、プラグインインストールのプロセスが強制終了されることは無いために、安価なスタンダードプランでも安定してインストールできます。

性能の高いサーバーや、 FreeBSD でもシステムエラーになるケースがあるのですが、次項のメンテナンス画面を併用することで、ほぼ解消できると思います。

メンテナンス画面にして、外部からのリクエストを遮断する

無事にプラグインのインストールが成功しても、有効化時にシステムエラーになる場合があります。
プラグインの有効化をする際、各種キャッシュファイルを生成し直すのですが、このときに外部からのアクセスがあると、複数のプロセスでキャッシュファイル生成が競合してしまう場合があり、システムエラーの原因になります。
これは、メンテナンス画面にして、外部からのリクエストを遮断することで解消できます。

EC-CUBE4.0.1以降では、 EC-CUBE をインストールしたフォルダ直下に .maintenance というファイル名のファイル(中身は何でも良い)を置くと、メンテナンス画面になります。

具体的な手順は、以下の通りです。

  1. <EC-CUBEインストールフォルダ>/.maintenance ファイルを設置する
  2. フロント画面がメンテナンス画面になったのを確認する
  3. プラグインインストール/有効化/無効化/アンインストールを実行する(コマンドラインから実行すると、より安定します。 )
  4. 管理画面を何度かリロードして、システムエラーにならないのを確認する
  5. <EC-CUBEインストールフォルダ>/.maintenance ファイルを削除する
  6. フロント画面が正常に表示されるのを確認する

4 でシステムエラーになった場合は、プラグインの不具合や、競合の可能性があります。
プラグインや本体を修正する必要がありますので、 var/log 以下にあるログのシステムエラーと表示されている行をご確認ください。

コマンドラインでのプラグインインストール

管理画面からのWebインストーラよりも、コマンドライン操作の方が格段に安定しています。
何度やってもWebインストールに失敗してしまう場合、コマンドライン操作に慣れている方はお試しください。

オーナーズストアプラグイン、独自プラグインでコマンドが異なりますのでご注意ください。
事前にメンテナンス画面に変更しておくことを忘れずに。
キャッシュ削除/キャッシュ生成もコマンドラインで実行するのがポイントです。

## 事前準備 - メンテナンス画面に変更
touch .maintenance

## オーナーズストアプラグインインストール
bin/console eccube:composer:require ec-cube/<PluginCode>
bin/console cache:clear --no-warmup # キャッシュ削除
bin/console cache:warmup # キャッシュ生成

## 独自プラグインインストール
bin/console eccube:plugin:install --code <PluginCode>
bin/console cache:clear --no-warmup # キャッシュ削除
bin/console cache:warmup # キャッシュ生成

## プラグイン有効化
bin/console eccube:plugin:enable --code <PluginCode>
bin/console cache:clear --no-warmup # キャッシュ削除
bin/console cache:warmup # キャッシュ生成

## プラグイン無効化
bin/console eccube:plugin:disable --code <PluginCode>
bin/console cache:clear --no-warmup # キャッシュ削除
bin/console cache:warmup # キャッシュ生成

## オーナーズストアプラグインアンインストール
bin/console eccube:composer:remove ec-cube/<PluginCode>
bin/console cache:clear --no-warmup # キャッシュ削除
bin/console cache:warmup # キャッシュ生成

## 独自プラグインアンインストール
bin/console eccube:plugin:uninstall --code <PluginCode>
bin/console cache:clear --no-warmup # キャッシュ削除
bin/console cache:warmup # キャッシュ生成

## オーナーズストアプラグインアップデート
bin/console eccube:composer:update ec-cube/<PluginCode>
bin/console cache:clear --no-warmup # キャッシュ削除
bin/console cache:warmup # キャッシュ生成

## 独自プラグインアップデート
bin/console eccube:plugin:update <PluginCode>
bin/console cache:clear --no-warmup # キャッシュ削除
bin/console cache:warmup # キャッシュ生成

## メンテナンス画面を解除
rm .maintenance

プラグインエラーの原因と対処法詳細

Composer での依存関係解決時のエラー

EC-CUBE4系では、 Composer を利用して、プラグインの依存関係を制御しています。
EC-CUBE に限らず Laravel で開発したプロジェクトなど、 Composer を使用するプロジェクトすべてにあてはまるのですが、 composer require コマンドを実行する際に400MB〜600MBのメモリを消費します。
これは、Composer の依存関係にある JSON ファイルをすべてメモリ上に読み込むためです。

通常、 Composer の操作はコマンドラインで実行するため、問題になることは少ないのですが、 EC-CUBEの場合は Webインターフェイスを使用するために問題になります。

1リクエストで400MB〜600MBのメモリを消費するWebアプリケーションというのは明らかに異常な状態です。
(通常は多くても数十MB)
CPUの利用率も高い状況が継続するため、Linux では OOM Killer という OS 保護機構が働いて、プラグインインストールのプロセスを強制終了させてしまいます。

このケースでは、エラーログが出力されない場合があり、原因の特定を困難にさせる一因となっています。

対処方法

OOM Killer は Linux 固有の機構ですので、さくらのレンタルサーバーで採用されている FreeBSD など、 Linux 以外の OS では影響を受けません。
また、クラウドや VPS などで性能の良いインスタンスを使用することで、 OOM Killer の影響を低減させることが可能です。

管理画面から実行するよりも、コマンドラインから実行した方がより安定します。

よく、 php.ini の memory_limit を増やしましょうという対処法を紹介しているブログなどがあるのですが、このケースの場合は殆ど意味がありません。
Composer は、内部的に memory_limit を 1.5GB まで自動的に増加させるためです。
(つまり、メモリ 1.5GB 未満のミニマムな環境では危険な状態になります)

Docker を使用する場合は --oom-kill-disable というオプションがありますので、試してみると良いでしょう。

まとめると、以下の2点の対応で解消できる可能性が高いです。

  • 性能の良い環境を使用する or Linux 以外の OS を使用する
  • コマンドラインから実行する

Composer の classmap 生成時のエラー

プラグインをインストールする場合、 Composer API を使用して、プラグインのPHPプログラムがオートローディング用の classmap に追加されます。
フレームワークのキャッシュファイルや、プラグインが自動生成したファイルも、 classmap に追加されるのですが、生成タイミングによっては、追加されずに漏れてしまい、不整合が発生する場合があります。
特に、 Entity 拡張に失敗して、この問題が発生する場合が多いです。

この場合、システムエラーになってしまい、画面を何度もリロードしても復旧しなくなってしまいます(途方に暮れる場合が多い)

キャッシュファイルの不整合の場合は、キャッシュクリアしたり、画面を何度かリロードすれば復旧する場合が多いのですが、 Composer の classmap の場合は自動的に再生成されないため、コマンドラインから生成し直す必要があります。

対処方法

composer dumpautoload コマンドで classmap を生成し直します。
事前に、Composer コマンド のインストールが必要です。

## 事前準備 - メンテナンス画面に変更
touch .maintenance

php composer.phar dumpautoload # classmap を生成し直す
bin/console cache:clear --no-warmup # キャッシュ削除
bin/console cache:warmup # キャッシュ生成

## メンテナンス画面を解除
rm .maintenance

Symfony DependencyInjection コンテナのコンパイル時のエラー

EC-CUBE4系では、 DIコンテナとして Symfony DependencyInjection Component を採用しています。
この DI コンテナは、リクエストを検知するとDIコンポーネントのキャッシュファイルを自動的に生成(コンパイル)してからレスポンスを返します。
キャッシュを削除した直後に、複数のリクエストを同時に受信すると、リクエストごとにキャッシュファイルの生成処理が起動してしまい、結果的にキャッシュの不整合が発生してシステムエラーの原因になります。

このエラーは、 システムエラーが発生しました。 というログに以下のような Container<ランダム文字列> が含まれているのが特徴です。

/path/to/ec-cube/var/log/prod/site-log-YYYY-MM-DD.log
[2020-05-18 14:30:45] front.ERROR [N/A] [d8c46ea] [N/A] [Eccube\Log\Logger:log:66] - システムエラーが発生しました。
... snip
#10 /path/to/ec-cube/var/cache/prod/ContainerZkkhqxc/EccubeProdProjectContainer.php(3190): ContainerZkkhqxc\\EccubeProdProjectContainer->getTwigInitializeListenerService()`
...

やっかいなことに、このエラーは大量のコアを詰んだ高性能なサーバーや、 OOM Killer
の発生しない FreeBSD でも発生します。

対処方法

このエラーの場合は、管理画面からキャッシュ削除したり、ブラウザを何度かリロードすると、キャッシュが再生成されて復旧される場合があります。

予防するためには、事前にメンテナンス画面に切り替えて、外部からのアクセスを遮断することが有効です。

コマンドラインから実行する場合は、 bin/console cache:warmup コマンドを使用して、キャッシュ生成してからメンテナンス画面を解除するようにしましょう。

## 事前準備 - メンテナンス画面に変更
touch .maintenance

bin/console cache:clear --no-warmup # キャッシュ削除
bin/console cache:warmup # キャッシュ生成

## メンテナンス画面を解除
rm .maintenance

Doctrine ORM のキャッシュ生成時のエラー

Doctrine ORM は、 Symfony が採用している O/R Mapping フレームワークです。
EC-CUBE でも、データベースアクセス時に利用しています。

この Doctrine ORM は、遅延読み込み機能(Lazy-Loading)を実現するために、 自動的にキャッシュファイル(Proxy クラス)を生成します。
(後述する app/proxy 以下のファイルとは別物です)

通常は、キャッシュファイルを生成してからレスポンスを返すのですが、複数のプロセスでキャッシュファイル生成が競合してしまうと、キャッシュ生成前にレスポンスを返してしまい、システムエラーとなってしまいます。

このエラーは、 システムエラーが発生しました。 というログに以下のような __CG__ から始まる PHP ファイルが含まれているのが特徴です。

/path/to/ec-cube/var/log/prod/site-log-YYYY-MM-DD.log
[2020-05-14 17:37:57] admin.ERROR [a3b9d5e0] [214492c] [N/A] [Eccube\Log\Logger:log:64] - システムエラーが発生しました。 ["Compile Error: require(): Failed opening required '/path/to/ec-cube/var/cache/prod/doctrine/orm/Proxies/__CG__EccubeEntityMasterWork.php' (include_pa
th='.:/usr/local/php/7.3/lib/php')","/path/to/ec-cube/vendor/doctrine/common/lib/Doc
trine/Common/Proxy/Autoloader.php",74,"#0 {main}"] [GET, /ec-cube/admin/store/plugin, 147.192.

参考(英語記事です)

対処方法

このエラーはの多くは一時的なもので、再度画面をリロードすることで復旧する場合がほとんどです。
やはり予防するためには、事前にメンテナンス画面に切り替えて、外部からのアクセスを遮断することが有効です。

コマンドラインから実行する場合は、 bin/console cache:warmup コマンドを使用して、キャッシュ生成してからメンテナンス画面を解除するようにしましょう。

## 事前準備 - メンテナンス画面に変更
touch .maintenance

bin/console cache:clear --no-warmup # キャッシュ削除
bin/console cache:warmup # キャッシュ生成

## メンテナンス画面を解除
rm .maintenance

Entity Proxy 生成時のエラー

EC-CUBE4系では、Entity 拡張という機能を利用して、プラグインでエンティティの属性を追加することが可能です。
この機能を利用する際、 Entity Proxy という PHP ファイルを app/proxy 以下に生成します。
(前述の Doctrine ORM の Proxy とは別物です)

プラグイン有効化/無効化時に、このファイルを生成するのですが、 app/proxy/entity/ 以下の読み込みと src/Eccube/Entity/ の読み込みが同時に発生するタイミングがあり、 通常の運用時にも PHP Fatal error: Cannot declare class というエラーが内部で発生します。(参考 EC-CUBE/ec-cube#4173)

/path/to/ec-cube/var/log/prod/site-log-YYYY-MM-DD.log
[php7:error] [pid 22690] [client 203.x.x.x:52619] PHP Fatal error: Cannot declare class Eccube\Entity\ProductClass, because the name is already in use in /path/to/ec-cube/app/proxy/entity/ProductClass.php on line 28, referer: https://example.com/store/plugin/api/upgrade/1/confirm

通常は、システムエラー画面にならないよう制御されており、エラーメッセージがログに残るだけなのですが、プラグインの不具合であったり、キャッシュファイルの不整合が引き金となって、システムエラー画面になってしまう場合があります。

以下のように、エラーログに app/entity/proxy が含まれており、かつ PHP Fatal error: Cannot declare class ではないエラーメッセージの場合は、システムエラー画面になってしまい、キャッシュクリアしても復旧しない場合があります。

/path/to/ec-cube/var/log/prod/site-log-YYYY-MM-DD.log
システムエラーが発生しました。 ["Compile Error: require(): Failed opening required '/path/to/ec-cube/app/entity/proxy...

場合によっては、システムエラーの影響で、管理画面に入れなくなったり、メンテナンス画面への切替もできないケースもあります。

対処方法

この場合は、 Entity proxy クラスを再生成した後に Composer classmap を再生成する必要があります。

## 事前準備 - メンテナンス画面に変更
#(メンテナンス画面に切り替わらなくてもファイルは生成しておく)
touch .maintenance

# bin/console コマンドが動作しない場合もあるので、 composer install を実行して不整合を解消する
php composer.phar install

bin/console eccube:generate:proxies # Entity proxy を再生成
php composer.phar dumpautoload # classmap を生成し直す
bin/console cache:clear --no-warmup # キャッシュ削除
bin/console cache:warmup # キャッシュ生成

## メンテナンス画面を解除
rm .maintenance

CGIモードでのサーバーエラー

さくらのレンタルサーバーなど、一部のレンタルサーバーでは、 PHPの実行モードを選択できます。
CGIモードと、モジュールモードがあり、通常はモジュールモードで動作します。
しかし古い契約プランでは、CGIモードしか選択できない場合があります。
(php-fpm を使用する FastCGI とは異なります)

CGIモードの PHP では EC-CUBEは動作保証されておらず、プラグイン有効化時などにサーバーエラーが発生します。
EC-CUBEのエラー画面では無いエラー画面が表示されるのが特徴です。

また、このエラーが発生した場合は、 var/log 以下にあるEC-CUBEのログにエラーは記録されません。

サーバーのエラーログに、以下のような内容が記録される場合があります。

/var/log/httpd/error.log
[Thu Jun 11 15:57:46.492977 2020] [cgi:error] [pid 10793] [client 217.178.90.58:0] malformed header from script 'index.php': Bad header: gen -> /tmp//proxy_aceBBaJq9tJ, referer: https://example.com/path/to/ec-cube/admin/store/plugin

対処方法

PHPをモジュールモードに変更します。(さくらのレンタルサーバーでの変更方法はこちら)
CGIモード以外に選択できない場合は、サーバー移転をご検討ください。

参考

max_execution_time タイムアウト

max_execution_time はプログラムの暴走を止めるためのタイムアウトです。
デフォルトでは30秒に設定されています。
プラグインインストールは、30秒以上の時間がかかる場合が多く、インストールや有効化に失敗した場合は、この設定を疑われる方が多いです。
公式のよくある質問にも、 max_execution_timeが30→max_execution_timeを180に設定 と書かれています。

実は、この設定が問題になる場合は少ないです。
この設定を変更される方が多いですが、ほとんどの場合意味無いです。

PHP公式ドキュメントには、最大実行時間は、システムコール、ストリーム操作等の影響を受けません とあります。
プラグインインストールや有効化の処理は、ストリーム操作などが大半なのが理由です。

エラーログに、以下のようなエラーメッセージを見つけた場合のみ、この設定を変更するようにしましょう。

Fatal error: Maximum execution time of 30 seconds exceeded

また、このエラーが発生した場合は、 var/log 以下にあるEC-CUBEのログにエラーは記録されず、 Webサーバーのエラーログ(/var/log/httpd/error_log 等)に記録される場合もあるため、ご注意ください。

FastCGI のタイムアウト

Ngnix や Apache event MPM などの Webサーバーでは FastCGI という実行モードが使用されます。
(前出のCGIモードとは別物です)
php-fpm という実装が利用されるのですが、いくつかタイムアウトの設定があります。

プラグインインストール/有効化/無効化/アンインストール時に、これらのタイムアウトに抵触した場合、エラー画面にならずに長い間応答が返ってこない場合が多いです。
php-fpm を使用していて、長い間応答が返ってこない場合はこれらのエラーである疑いが強いです。

php-fpm には request_terminate_timeout という設定項目があります。
この項目は、前出の max_execution_timeオプションが何らかの理由でスクリプトの実行を止められなかった場合に利用されます。
デフォルト値は 0 (OFF)です。

/etc/php-fpm.d/php-fpm.conf
request_terminate_timeout = 0

Nginx には fastcgi_read_timeout という設定項目があります。php-fpm がこの時間内に何も送信しない場合は接続が切断されます。
デフォルト値は60秒です。

/etc/nginx/nginx.conf
fastcgi_read_timeout 60

これらエラーが発生した場合は、 var/log 以下にあるEC-CUBEのログにエラーは記録されず、 Webサーバーのエラーログ(/var/log/nginx/error.log 等)や PHPのエラーログ(/var/log/php_error.log 等)に記録されます。

参考

インストール中のDBエラーによるロールバック

  • プラグイン一覧に、プラグインが表示されない
  • app/Plugin/<Pluginコード> にはプラグインファイルが存在しているのに
  • dtb_plugin テーブルには該当のプラグインが存在しない

上記のような状況になってしまった場合は、インストール中に何らかのエラーが発生し、プラグインのインストール処理の途中でデータベースのみがロールバックしてしまった可能性があります。

この原因は様々ですが、プラグイン自体の不具合や、プラグイン同士の競合である可能性が高いです。
また、プラグイン自体は インストール完了しました と表示される場合もあり、原因の特定を困難にしているケースもあります。

対処方法

原因となっているプラグインを特定する必要がありますので、プラグインを1つずつアンインストールして確認するのが良いでしょう。

プラグイン一覧に、プラグインが表示されない場合は、コマンドラインからアンインストールを試みると良いと思います。

## オーナーズストアプラグインアンインストール
bin/console eccube:composer:remove ec-cube/<PluginCode>
bin/console cache:clear --no-warmup # キャッシュ削除
bin/console cache:warmup # キャッシュ生成

## 独自プラグインアンインストール
bin/console eccube:plugin:uninstall --code <PluginCode>
bin/console cache:clear --no-warmup # キャッシュ削除
bin/console cache:warmup # キャッシュ生成

根本的には、エラーの発生原因(プラグイン自体の不具合, 競合など)を特定し、解消する必要があります。

後程詳細を追記します...

  • composer.json の不整合
  • ブラウザのタイムアウト
  • レンサバのプロセス実行タイムアウト
  • プラグインの競合、不具合
  • インストール中の DBエラーによるロールバック
  • リソース(メモリ、CPU)不足

参考(メモ)

composer.json の不整合

アンインストールしてくれと出るやつ

 [RuntimeException]
  You can not uninstall the EC-CUBE plugin via `composer` command.
  Please use the `bin/console eccube:composer:remove ec-cube/<Plugin code>` instead.

エラーメッセージとは逆に、 bin/console eccube:composer:remove ec-cube/<Plugin code> で一旦インストールすると復旧するケースが多い

どうしても解決できないときは

この投稿のコメント覧でお問い合わせください

nanasess
Emacs のアイコンを作った人です
https://skirnir.co.jp
ec-cube
日本No.1ECオープンソースのEC-CUBEのコミッターやユーザーのコミュニティです。
http://www.ec-cube.net
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした