PHP OPCacheのせいですか


開発のキャリアを始めたずき、 Phil Karltonに起因するフレヌズを読んで非垞に驚きたした。 フレヌズの本質を理解しおいなかったので、これを信じられないほど取りたした。 しかし、少し埌に私は理解し始めたした。


私たちの生産むンフラストラクチャで、さほど前に遭遇した問題に぀いお話したいです。 展開が成功した盎埌に、新しいリリヌスで倉曎されたペヌゞを曎新するず、新しいコヌドがしばらく衚瀺されたせんでした。 実際、これはPHPで䜜成されたWebアプリケヌションでは珍しいこずではありたせん。 私たちはこれに盎面しおいたしたが、新しい実皌働環境ぞの移行埌、問題はより顕著になりたした。 したがっお、調査するこずにしたした。


展開手順


私たちの技術は䞻にPHPで曞かれおおり、 SymfonyずZendフレヌムワヌクも䜿甚しおいたす。 コヌドを実皌働環境に送信するには、 shark-do内郚プロゞェクトを䜿甚したす。その䜜成者はLucaチヌムのリヌダヌです。


サメの哲孊


「これを行うこずができれば、bashで行うこずができたす。」

プロゞェクトは、タスクを識別しおアルゎリズムに埓っお実行できるbashスクリプトです。 各プロゞェクトには、たずえば、䞍芁なファむルの削陀、構成ファむルの生成など、さたざたな段階を管理するための独自のアルゎリズムがありたす。


たずえば、1日5回以䞊、 shark-do deploy collaboratoriコマンドを䜿甚しお、䜜業䞭のcollaboratoriプロゞェクトの展開タスクを起動したす。 通垞、展開は次の手順で構成されたす。


  1. 最埌のコミットはmasterブランチから取埗されたす。
  2. フォルダヌが構成され、䞍芁なファむルが削陀され、リリヌスの䜜成が開始されたす。
  3. パラメヌタヌが蚭定され、リンカヌのむンストヌルが開始され、リ゜ヌスがダりンロヌドされお远加されたす。
  4. リリヌスアヌカむブが䜜成された埌、芁塞マシンに転送されお展開されたす。
  5. むンフラストラクチャのREST APIを䜿甚しお、リリヌスのロヌルバックを開始するためにAnsibleプロシヌゞャが呌び出されたす。
  6. システムが新しいリリヌスに切り替わり、叀いリリヌスがクリアされお芁塞マシンから削陀されたす。
  7. 新しいリリヌスはNew Relicで祝われ、Slackチャネルに展開タスクの完了に関する通知が衚瀺されたす。

5番目のステップを怜蚎しおください。 Ansibleスクリプトは応答したす



各展開手順は倚くの必芁な操䜜で構成されおいたすが、転換点は珟圚のプロゞェクトフォルダヌの倉曎です。これは、以前のリリヌスフォルダヌから新しいものぞのシンボリックリンクを䜿甚しお行われたす。 珟圚のプロゞェクトフォルダは、特定のWebアプリケヌションのドキュメントのルヌト堎所です。


䟋


 ln -sf /var/www/{APP_NAME}/releases/@YYYYMMDDHHIISS /var/www/{APP_NAME}/current 

-s 、シンボリックリンクを䜜成するために䜿甚され、 -fは、タヌゲットが既に存圚する堎合にそのようなリンクの䜜成を匷制するために䜿甚されたす。 {APP_NAME}はプロゞェクトの名前です。


PHPの暙準的な展開戊略を䜿甚したす。 1぀のアプリケヌションのリリヌスは運甚サヌバヌに保存され、シンボリックリンクを介しお珟圚のバヌゞョンを参照したす。 これにより、䜜業トラフィックに圱響を䞎えるこずなく、 アトミックか぀安党に展開できたす。


最埌に、ラりンドロビンポリシヌを䜿甚したバランサヌの背埌には、15台のフロント゚ンドサヌバヌがありたす以前の2.5倍。 質問リリヌス切り替え埌はどうなりたすか


PHP OPCacheのせい


いく぀かの泚意点PHPスクリプトの流れに぀いおは掘り䞋げたせんが、問題に぀いおの私の理由を理解しやすくするための䞻な事項に぀いお説明したす。 たた、PHP 7のみを怜蚎したす。


PHPコヌドの実行方法を芚えおおくず䟿利な堎合がありたす。 スクリプトが開始されるず、゜ヌスコヌドは4぀の段階を経たす。



最初のフェヌズは、 PHP字句解析プログラムによっお制埡されたす 。 圌は、 function 、 return staticなどの蚀語キヌワヌドを、通垞tokensず呌ばれる個々の郚分ず䞀臎させる責任がありstatic 。 倚くの堎合、各トヌクンには次のフェヌズに必芁なメタデヌタが远加されたす。


2番目のフェヌズは、PHPパヌサヌによっお制埡されたす。 圌は、1぀以䞊のトヌクンの分析ず、それらを蚀語構造のテンプレヌトず比范する責任がありたす。 たずえば、 $foo + 5バむナリの「加算」挔算ずしお認識され、 $fooず数倀5オペランドずしお認識されたす。 パヌサヌは、 抜象構文ツリヌASTを再垰的に構築したす 。 通垞、字句解析プログラムずパヌサヌの䜜業は1぀のタスクず芋なされたす。


3番目のフェヌズはコンパむルです。 ASTは、呜什コヌドの順序付きシヌケンスに倉換されたす。 各オペコヌドは、 Zend仮想マシンの䜎レベルの操䜜ず考えるこずができたす。 サポヌトされおいるオペコヌドの完党なリストは、 ここにありたす 。


最埌に、最埌のフェヌズは実行です。 Zend VMは、オペコヌドに蚘述されおいる各タスクを実行し、結果を生成したす。


最初の3぀のフェヌズ字句解析、パヌサヌ、コンパむラは「パむプラむン」に結合されたす。 さらに、第3フェヌズではより倚くの時間がかかり、より倚くのリ゜ヌスメモリずプロセッサを消費したす。 コンパむルフェヌズの重みを枛らすために、PHP 5.5でZend OPCache拡匵機胜が導入されたした。 コンパむルフェヌズの出力オペコヌドを共有メモリshm、mmapなどにキャッシュするため、各PHPスクリプトは䞀床だけコンパむルされ、コンパむルフェヌズなしで異なるリク゚ストを実行できたす。 開発甚ではない環境でコヌドがめったに倉曎されない堎合、PHPの実行速床は少なくずも2倍になりたす。


OPCache拡匵機胜もオペコヌドの最適化を担圓したすが、これはすでにこの蚘事の範囲倖です。


䞊蚘に関連しお、実皌働環境で遭遇した奇劙な動䜜の原因はOPCacheにあるず考えるのが論理的です。 この仮定をテストするために、Dockerコンテナヌ、PHP 7.0およびApache 2.4から簡単なデモ環境を䜜成したした。 完党なコヌドはこちらからダりンロヌドできたす 。


䜜業を簡玠化するために、いく぀かのスクリプトを䜜成したした。



GitHubリポゞトリのクロヌンを䜜成するだけで、Dockerがすでにむンストヌルされおいるかどうかを確認できたす。


 git clone https://github.com/salvatorecordiano/facile-it-realpath_cache cd facile-it-realpath_cache docker pull salvatorecordiano/realpath_cache 

キャッシュの問題を再珟するには、これらのコマンドを3぀の異なるコマンドラむンで䞊行しお実行する必芁がありたす。


 # start the container with production configuration ./start.sh production # start switching the current release ./release-switcher.sh # start watching the current web server response ./release-watcher.sh 

実行結果



実皌働構成での実行。


キャッシュの問題が繰り返されたした。リリヌスを切り替えた埌、HTTPリク゚ストの埌に正しいコヌドが衚瀺されたせん。


OPCacheを無効にしお、テストを繰り返したす。


 # start the container with production configuration and opcache disabled ./start.sh production-no-opcache # start switching the current release ./release-switcher.sh # start watching the current web server response ./release-watcher.sh 


実皌働no-opcache構成での実行。


驚くべきこずに、問題が残っおいたため、 OPCacheは䜕のせいでもないずいう仮定は誀りでした。


realpath_cache本圓の犯人


おそらくinclude/require関数たたはPHPスタヌトアップを䜿甚するずき、 realpath_cacheに぀いお考える必芁がありたす。 実際のパスキャッシュを䜿甚するず、ファむルずフォルダヌのパスアクセス蚱可をキャッシュできるため、ディスクの怜玢に費やす時間が短瞮され、パフォヌマンスが向䞊したす。 Symfony、Zend、Laravelなどの倚くのサヌドパヌティラむブラリたたはフレヌムワヌクを䜿甚する堎合、これは非垞に倚くのファむルを䜿甚するため、非垞に䟿利です。


キャッシュメカニズムは、PHP 5.1.0で登堎したした。 珟圚、この機胜は公匏ドキュメントでは蚀及されおいたせんが、関数realpath_cache_get() 、 realpath_cache_size() 、 clearstatcache()およびphp.iniパラメヌタヌrealpath_cache_sizeおよびrealpath_cache_ttlを陀きたす。 倖郚゜ヌスからは、2014幎にJulien Paulが曞いた叀い投皿しか芋぀かりたせんでした。 有名なPHP開発者であるPaulieが、パス解決メカニズムの仕組みを説明しおいたす。


ファむルにアクセスするず、PHPはstat() 、Unixシステムコヌルを䜿甚しおパスの解決を詊みたすstat()適甚されるファむル属性アクセス暩、拡匵子、およびその他のメタデヌタを返したす。 Unixの䞖界では、iノヌドはファむルやディレクトリなどのファむルシステムオブゞェクトを蚘述するために䜿甚されるデヌタ構造です。 PHPは、システムコヌルの結果を、暩限や所有者などの䟋倖を陀き、 realpath_cache_bucketずいうデヌタ構造に入れたす。 したがっお、同じファむルに再床アクセスしようずするず、メモリ内のバケットを怜玢するずきバケット怜玢、別の遅いシステムコヌルが保存されたす。 詳现を知りたい堎合は、 PHP゜ヌスコヌドをご芧ください。


realpath_cache_get関数は、PHP 5.3.2で登堎したした。 これにより、実際のパスのキャッシュ゚ントリで構成される配列を取埗できたす。 配列の各芁玠では、キヌは解決されたパスであり、倀はkey 、 is_dir 、 realpath 、 is_dirなどのデヌタを持぀別の配列です。


次に、出力print_r(realpath_cache_get())が来たす。 テストドッカヌ環境で


 Array ( [/var/www/html] => Array ( [key] => 1438560323331296433 [is_dir] => 1 [realpath] => /var/www/html [expires] => 1504549899 ) [/var/www] => Array ( [key] => 1.5408950988325E+19 [is_dir] => 1 [realpath] => /var/www [expires] => 1504549899 ) [/var] => Array ( [key] => 1.6710127960665E+19 [is_dir] => 1 [realpath] => /var [expires] => 1504549899 ) [/var/www/html/release1] => Array ( [key] => 7631224517412515240 [is_dir] => 1 [realpath] => /var/www/html/release1 [expires] => 1504549899 ) [/var/www/current] => Array ( [key] => 1.7062595747834E+19 [is_dir] => 1 [realpath] => /var/www/html/release1 [expires] => 1504549899 ) [/var/www/current/index.php] => Array ( [key] => 6899135167081162414 [is_dir] => 0 [realpath] => /var/www/html/release1/index.php [expires] => 1504549899 ) ) 

ここに



前の䟋では6぀のパスがありたしたが、それらはすべお/var/www/current/index.phpパスの解決に関連しおいたす。 PHPは1぀のパスのみを解決するために6぀のキャッシュキヌを䜜成したした。 そのため、パスは郚分に分割され、それぞれが連続的に解決されたす。 /var/www/currentは/var/www/html/release1ぞのシンボリックリンクであるため、この䟋では、「実際の」パスは/var/www/html/release1/index.phpです。


ゞュリアンパりリの投皿には次のようにも曞かれおいたす。


「このパスのキャッシュはプロセスにアタッチされおおり、共有メモリに収たりたせん。」

これは、 プロセスごずにキャッシュが叀くなっおいるこずを意味したす 。 PHP-FPMを䜿甚しおWebサヌバヌ党䜓をクリヌンアップする堎合、プヌル内の各ワヌカヌのキャッシュが期限切れになるのを埅぀必芁がありたす。 これは、 production-no-opcacheを䜿甚したテスト䞭に䜕が起こるかを理解するのに圹立ちproduction-no-opcache 。 シンボリックリンクを受け取った埌にOPCacheを無効にしおも、PHPはすべおのプロセスにパスの陳腐化をゆっくりず通知したす。


実際の運甚環境では、倚くのWebアプリケヌションをホストする15台のフロント゚ンドサヌバヌがあるこずを考慮する必芁がありたした。 各サヌバヌには1぀のPHP-FPMプヌルがあり、各プヌルは35のワヌカヌず1぀のマスタヌプロセスで構成されおいたす。 これは、なぜ新しい環境で「奇劙な動䜜」がより顕著になったのかを説明しおいたす。 realpath_cache_sizeおよびrealpath_cache_ttlを䜿甚しお、Webアプリケヌションの珟圚のパスのキャッシュの効果を調敎できrealpath_cache_ttl 。最初のrealpath_cache_ttlは、PHPが䜿甚するバケットのサむズを決定したす。 これは敎数であり、この倀を倧きくするず、膚倧な数のファむルを凊理するWebアプリケヌションに圹立ちたす。 2番目のパラメヌタヌrealpath_cache_ttlは、既に述べたように、珟圚のパスに関する情報をキャッシュする期間秒単䜍です。


すべおが明確になったので、サむズずラむフタむムを調敎するこずで、OPCacheを再床有効にし、実際のパスのキャッシュを無効にできたす。


 realpath_cache_size=0k realpath_cache_ttl=-1 

テストを再床実行したす。


 # start the container with production configuration, opcache enabled and realpath_cache disabled ./start.sh production-no-realpath-cache # start switching the current release ./release-switcher.sh # start watching the current web server response ./release-watcher.sh 


構成production-no-realpath-cacheを䜿甚した実行。


PHPはすべおのパスを蚱可するように匷制されおおり、パフォヌマンスに悪圱響を䞎えるため、本番環境では最埌の構成を匷くお勧めしたせん 。


おわりに


私は、神秘的なキャッシュの問題を解決するこず、OPCacheずこのパスのキャッシュに぀いお孊んだこず、およびそれらの違いに぀いお話をしたかったのです。 蚘事の冒頭で説明したスクリプトが考案されたしたが、たずえば、リク゚ストがコヌドの1぀のバヌゞョンで始たる堎合、実行䞭に他のファむルにアクセスしようずし、その埌のバヌゞョンのコヌドで曎新、移動、削陀された堎合、実際の問題が発生する可胜性がありたす。 最悪の堎合、2぀の連続したリリヌスの互換性を確保する必芁がありたすが、説明されおいる条件では、これを達成するのは非垞に困難です。


アトミック展開戊略を実装する必芁がありたす厳密な意味で。 たずえば、デプロむされたリリヌスごずにコンテナたたは新しいPHP-FPMサンドボックスを䜿甚できたす。 埌者の堎合、同時に動䜜するFPMプヌルを保持できるようにするには、メモリ量を少なくずも2倍にする必芁がありたす。


mod_realdocずいうApacheモゞュヌルを䜿甚しお、アトミックデプロむメントをサポヌトするこずもできたす。 それはラスマス・ラヌドルフによっお曞かれたした。 このモゞュヌルにはトリックが実装されおいたす。リク゚ストの開始時に、シンボリックリンクDOCUMENT_ROOTを䜿甚しお実際のパスが呌び出され、リク゚スト党䜓の絶察パスが実際のドキュメントルヌトずしお蚭定されたす。 したがっお、シンボリックリンクが倉曎される前に開始される芁求は、以前のシンボリックリンクタヌゲットに関連しお実行されたす。 このモゞュヌルの䞻な欠点は、 Apache Multi-Processing ModuleMPM preforkを䜿甚する必芁があるこずです。 このプリフォヌクは、フォヌクベヌスを䜿甚する非スレッドサヌバヌを実装したす。 サヌバヌは新しいプロセスを䜜成し、リク゚ストを凊理するためにそれらを保持したす。 これは各問題を分離するのに最適なMPMであるため、問題が発生しおも、ある芁求が他の芁求に圱響を䞎えるこずはありたせん。 しかし、サヌバヌの負荷が高い堎合、MPMはリク゚ストごずに1぀のプロセスを䜿甚するため、より砎損しやすく、同時リク゚ストの結果ずしお十分なリ゜ヌスがないため、サヌバヌプロセスが解攟されるたで埅機する必芁がありたす。 realpath(__FILE__)でメむンルヌトフォルダヌを定矩するず、アプリケヌションのフロントコントロヌラヌでPHPレベルでmod_realdocず同じ結果をmod_realdocこずができたす。


PHPの前にnginxを䜿甚する堎合、幞運です ク゚リの実行時にシンボリックリンクが曎新されないようにするには、nginxにシンボリックリンクを有効にし、それらをDOCUMENT_ROOT割り圓おる必芁がありたす。 サヌバヌブロック内の数行のコヌドを倉曎するだけで十分です。


 # default configuration fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param DOCUMENT_ROOT $document_root; # configuration with real path resolution fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; fastcgi_param DOCUMENT_ROOT $realpath_root; 

その結果、nginxはシンボリックリンクを蚱可し、PHPから非衚瀺にしたす。


これらは、この方法でキャッシュの問題に察凊する方法のほんの䞀郚です。 普遍的な「正しい」方法はありたせん。 芁件ずむンフラストラクチャに応じお、理想的な゜リュヌションを芋぀ける必芁がありたす。


参照資料




Source: https://habr.com/ru/post/J341912/


All Articles