hero_picture
Cover Image for PHP OPcache によってAmazon EFS のバーストクレジットが枯渇した話

PHP OPcache によってAmazon EFS のバーストクレジットが枯渇した話

2024/05/27

クラウドソリューション事業部の原口です。

忙しい人用のまとめ

Amazon EFSにWordPressやlaravelなどのそれなりに量の多いPHPファイルを設置し共有している環境でOPcacheを利用している場合。
opcache.revalidate_freq (更新状況チェック間隔秒数)の設定値が小さいと秒数毎にEFSへファイルの更新確認を行い、メタデータI/O 負荷により最悪バーストクレジットが枯渇します。

この設定値をシステムとして可能な範囲まで大きな数字(600秒ほど)にしておく事が望ましいです。

Amazon EFSを使った冗長化

AWSにおいてはAWS Well-Architected フレームワークの信頼性の柱にもある通り、ベストプラクティスとしてはシステムは冗長化を行い、パフォーマンスと可用性の面でのメリットを受けるべきとされています。しかしながら、WEB層のシステムにおいては以下のような状況で冗長構成を取る事が難しい事があります。

  • WordPressやEC-CUBEなどのOSSでコアのカスタマイズができずステートレス化が難しい
  • 担当者が不在となったシステムがステートレスを考慮されていない

このような場合に役立つ冗長化の方法として、全てのプログラムコードを含むドキュメントルートを Amazon EFS (ファイル共有ストレージ)にぶち込む事で冗長化を可能とする手法があります。

(参考)AWS での WordPress に関するベストプラクティス | リファレンスアーキテクチャ

Best Practices for WordPress on AWS -
Best Practices for WordPress on AWS - https://docs.aws.amazon.com/whitepapers/latest/best-practices-wordpress/best-practices-wordpress.pdf

また、ECSなどのコンテナ環境においてはamazon EFSをコンテナへ直接マウントできるため、ビルドを伴わないPHPのようなスクリプト言語であれば、Amazon EFSへプログラムソースをすべて乗せてしまう事でdockerビルドをしなくてもよいデプロイフローとする事ができ、簡素化するという手法もあります。

スループットとメタデータI/O

Amazon EFSは伸縮可能なNFSマネージドサービスで、通常のNFSサービスとして比較しても、その転送のスループットは謙遜無いほど高速です。

しかしEFSにはメタデータIOというもう一つの観点があります。メタデータとはEFSに保存されたファイルの作成日(timestamp)やサイズなど、ファイルの中身ではなくファイルに対する情報ですが大きいファイルでも小さいファイルでも一定のサイズの容量(たしか4KB)が確保されています。

つまり、大きいサイズのファイルを転送する速度はスループットが消費され、小さなファイルをたくさん読み込むような操作(Linuxコマンドだとlsコマンドやrsyncコマンドなど)はメタデータIOが多く発生しがちになります。

WordPressやlaravelなどのPHPプログラムの場合は一つのファイルに接続する事で大量のファイルを読み込む事が特徴です。例えばWordPressにおいてはブラウザから「ようこそ」の画面を表示するために200を超えるPHPファイルのincludeが行われます。

Amazon EFSはこのように小さくて大量のデータを捜査するような処理が非常に苦手であり、結果EFSに設置したプログラムは非常に低速となります。

当然ですがNFSサーバというネットワークを介したやりとりというところもオーバーヘッドとなります。

具体的な例としてWordPressをAmazon EFSへ設置した場合はEBSに設置した時と比べて10倍以上の速度差が発生します。

Amazon EFSの速度が遅い対策にOPcacheを使う

さて、このようなAmazon EFSに設置したPHPプログラムの速度が遅い対策としてOPcacheを使ったパフォーマンス最適化手法があります。

(参考)Amazon EFS を使用して WordPress のパフォーマンスを最適化する

もともとPHPはスクリプト言語ですのでアクセス毎にPHPがコンパイルされる事が低速となる一つの要因です。そこでコンパイル済みのPHP中間コードをキャッシュする事で高速化するという手法を取るPHPアクセラレータと呼ばれるツールが開発されていました。

OPcacheはPHPアクセラレータの一つなのですが、PHP中間コードをメモリ上にキャッシュする事から、OPcacheにてサーバーメモリにキャッシュさせる事でそもそものEFSへの接続を減らしてしまって高速化してしまおう!というものです。

この手法は非常によい試みで我々の案件でも多数の高速化実績があります。

このように2回目以降のアクセスはEFSを経由しないようにできるためかなりの高速化が見込めます。

ほとんどアクセスが無いサイトなのにAmazon EFS バーストクレジットが消費されていく・・・

問題が起きた構成は、WordPressをamazon EFSに入れてEC2を2台で共有するというサイトでした。マニュアル通りEC2のPHPにはOPcacheを入れて高速化を行なっています。

このサイトはほとんどアクセスが無い状態なのですが、構築後にどんどんとEFSバーストクレジットが減っていることが確認できました。

ほとんどアクセス数が無い状況で BurstCreditBalance が 0 に。
ほとんどアクセス数が無い状況で BurstCreditBalance が 0 に。

BurstCreditが枯渇した状態だと普通のアクセスでもかなりの時間がかかる状態となっており正直運用ができるレベルではありません。

この状態の際のOPcacheの設定は以下となります。

設定名
opcache.enable1
opcache.memory_consumption1024
opcache.interned_strings_buffer8
opcache.max_accelerated_files10000
opcache.huge_code_pages1
opcache.validate_timestamps1
opcache.revalidate_freq2
opcache.fast_shutdown1
opcache.enable_cli1
opcache.jit_buffer_size128M

バーストクレジット枯渇の原因を調査する

まずはEFSのメトリクスを確認したところ、こちらの枯渇はスループットではなくメタデータIOが原因である事がわかりました。

ダントツでメタデータIOの消費が多い
ダントツでメタデータIOの消費が多い

特にアクセスが無い中でメタデータIOが多く発生する原因はどこにあるのか、、続いてはOPcacheの動きにつきましてオリジナルデータのPHPファイルが更新された事の検出はどのようになっているのか調べてみました。

OPcacheについては基本的はOPcache設定項目詳細をご確認いただくのがよいのですが、更新のチェックはタイムスタンプの確認で行っているようで、 opcache.revalidate_freq で設定されていた秒数毎に元ファイルのタイムスタンプを確認しているという事がわかりました。

EFSにおいてはタイムスタンプの確認に行われるアクセスはメタデータIOを非常に消費しますし、キャッシュされているファイルは300ほどはあると思いますので、タイムスタンプチェックアクセスによる負荷である可能性がありそうです。

OPcacheはWEBサーバー毎にされるわけですから、スケールしてWEBサーバーを増えていけばその分のタイムスタンプチェックアクセスも2倍、3倍となるわけですし、デフォルトの設定値は 2 となっていますので、2秒毎にキャッシュされているファイルのタイムスタンプを確認しに来ている結果、メタデータIO負荷となったのではないかと推測しました。

検証

さて、実際にOPcacheの設定を以下のように設定してみました。

設定名
opcache.enable1
opcache.memory_consumption1024
opcache.interned_strings_buffer8
opcache.max_accelerated_files10000
opcache.huge_code_pages1
opcache.validate_timestamps1
opcache.revalidate_freq600
opcache.fast_shutdown1
opcache.enable_cli1
opcache.jit_buffer_size128M

opcache.revalidate_freq の値をデフォルトの 2 から 600 へ変更しました。これによりメタデータIOを削減され、バーストクレジットが回復されていく事が確認できました。

ただし、 opcache.revalidate_freq の値を600とした場合は、PHPのファイルを更新しても最大10分、更新が反映されないという事に注意ください。
上記はWordPressでありPHPプログラムの更新はほとんどない事がわかっていたため600という設定でfixとして対応しました。

極端な話を言うとPHPファイルの更新がコンテナデプロイなどである場合はこの値は極端に大きな値にするのがよいという事になりそうですね。

まとめ

  • PHPプログラムをEFSに置いた場合、メタデータIOにより低速になりやすく対応としてOPcacheという手法がある
  • OPcacheはopcache.revalidate_freq の値によってメタデータIOを多く消費し、最悪の場合、EFSのバーストクレジットが枯渇する
  • opcache.revalidate_freq の値はできる限り大きな値とする事でEFS負荷を減らせるが、その秒数ぶん、PHPファイルの更新が反映されなくなる事に注意