【laravel10.x】chatGPTと相談しつつスナップショットからサーバを復元させた時にviewキャッシュを消す挙動を実現させたい【config/view.php, AWS User Data】

PHP

はじめに

laravelで困ったことがありました(いつもですが)。

恐らくですがlaravelのviewキャッシュが原因の、bladeファイルで作成した画面が真っ白になるという不具合に遭遇しました。ざっくりしているのですが意図的に再現することも難しく。不具合当初に取得したスナップショットを立ち上げれば再現が可能なんですけども。

どう起こったか

AMIからEC2インスタンスを立ち上げ、そのインスタンスに対してブラウザからページアクセスした際に遭遇しました。AMIはLaravel環境として動いていた既存のインスタンスから取得したものなので、復元した時点でキャッシュデータも内部に保持しています。

# 立ち上げたインスタンスの内部には、すでに既存の各キャッシュデータが存在する
ls /laravel_project/app/storage/framework/
cache  sessions  testing  views

ベースのインスタンス稼働環境とほぼ同等の環境で立ち上げたのですが、一部ページの画面が真っ白になり動作しないという現象に遭遇しました。

AWSのオートスケーリング機能を使用してリソースを調整しているシステムの場合は、ベースとなるAMIを取得して起動テンプレートに設定→AMIに準拠したインスタンスがスケールアウトするという流れになるため、この運用を行うケースがあるかと思います。

なぜviewのキャッシュが原因だと思ったか

白く表示されたページではHTTPステータスは200が返っており、またlaravel, php-fpm, nginx等にはエラーログが特に出力されていませんでした。

あれこれ調査した結果、対象ページのbladeファイルをvimなどで開く→適当に改行して保存すると正常に表示されました。またviewキャッシュを全削除(php artisan view:clear)した場合も直りました。こちらから古いキャッシュデータが何かしら噛み合うことで白い画面の問題が発生していると考えました。

対策する

今回の現象はviewキャッシュが残っている場合に発生すると上記で仮定しましたので、AMIの取得前にviewキャッシュをクリアしておくことで対処ができそうです。ただしviewキャッシュはページへのアクセスがあった場合に自動で生成されるため、キャッシュクリアしたとしてもAMIの取得前にアクセスがあるとviewキャッシュが再生成されてしまいます。というよりそもそもAMI取得前にいちいちキャッシュを消すのも面倒なのでなんとかできないか考えます(あと可能な限りキャッシュは消さずに残したい)。

chatGPTに聞いてみる

ググりつつ、並行してchatGPTくんにも聞いてみることにしてみます。

なるほど。expireでキャッシュの期限を設定できるのであれば確かに有効かもしれません。例えばexpire を1日程度に設定しておけば、1週間前に取得したAMIからスケールアウトが発生した場合、スケールアウトの時点ではキャッシュの有効期限が切れているため、該当のキャッシュデータは破棄されつつ本来のviewファイルへとアクセスされる挙動が予想できるので今回の原因について予防できそうです。

ついでに、view.phpについてあまり知らなかったので追加で聞いてみました。

ふむふむ。ドキュメントも貰っておきましょう。

Laravel - The PHP Framework For Web Artisans
Laravel is a PHP web application framework with expressive, elegant syntax. We’ve already laid the foundation — freeing you to create without sweating the small...

資料が整ったので実際に動作確認してみます。有効期限は一旦60分で。

# app/config/view.php
<?php

return [
    'expire' => 3600, // 追加
     ...

書き込んだのちphp artisan config:cacheで設定を適用し、php artisan view:clearでviewキャッシュを削除しました。これでstorage/framework/viewsへと溜まるキャッシュデータに期限が付与され、60分(3600秒)後には消えるはずです。

消えず。定義した値が読まれているかをtinkerで確認しましたが認識されています。

php artisan tinker

> config('view.expire')
= "3600"

次にview.phpが実際に動作しているかを確認しました。viewのキャッシュデータの配置先を設定できるcompiledという設定値があるのですが、こちらを適当に編集したのち再びconfig:cacheで読ませました。するとデータ配置先についてのエラー( Please provide a valid cache path. )が出たので動作してはいるようです。

そこでchatGPTくんが指したexpireという設定値がそもそも存在するのか確認しました。とりあえず先ほど出力してもらったドキュメントを確認します。

Laravel - The PHP Framework For Web Artisans
Laravel is a PHP web application framework with expressive, elegant syntax. We’ve already laid the foundation — freeing you to create without sweating the small...

全然見当たらない。というかview.phpについての情報が少ない。ちなみにgithubのlaravel10.xブランチのview.phpは以下です。

laravel/config/view.php at 10.x · laravel/laravel
Laravel is a web application framework with expressive, elegant syntax. We’ve already laid the foundation for your next big idea — freeing you to create without...

なんなら、最新のlaravel11.x及びmasterブランチではview.php自体が消えています。

File not found · laravel/laravel
Laravel is a web application framework with expressive, elegant syntax. We’ve already laid the foundation for your next big idea — freeing you to create without...

使われなかったんでしょうか… 10.x版のドキュメントを軽く眺めたのですがview.phpに定義できる値の一覧などは見つからず。というか#view-configurationなんて箇所無いやないかい。configに定義できる値の一覧ってコード内から頑張れば追いかけられそうなのですがどうなんでしょうか。

動作しなかったけどexpireって定義は存在しそうなんですよね。別の設定ファイルで上書きされてたパターンでしょうか。分かったら追記します。

色々あってviewキャッシュのデータをconfig下で管理することは諦めました。今回やりたいことは「インスタンスを立ち上げた時にviewキャッシュを削除する」という内容なので別のツールで対応できないか改めて調べます。

cronで実行する

初心者向けcronの使い方 - Qiita
unix系OSでは定期的にコマンドを実行するためにcronと呼ばれるデーモンプロセスがあります。決まった時間にログを集計してメールで送るなどの使い方ができて非常に便利です。ここではクーロンの設定方法…

定期的にphp artisan view:cacheを実行させてみてはどうか考えました。ただ以下の理由からもう少し良い手法がないかを探すことにしました。

  • 通常通り稼働しているインスタンスにも設定するので(こちらからベースのAMIを取得するため)、そちらでもキャッシュデータを定時で消してしまう
  • 結局スケールアウトしたインスタンスはcronが実行されるまでviewキャッシュが残ったまま

仮にcronで毎日0時に実行するならこんな感じかと。

0 0 * * * php /laravel_project/artisan view:clear >> /dev/null 2>&1

constructで制御する

caching — Laravelビューキャッシュを無効にするにはどうすればよいですか?
私の見解の1つに例外があります。ただし、ビューの名前を教えてそれを見つけて修正できるようにする代わりに、laravelはapp/storage/views/110a3ecc0aa5ab7e6f7f50ef35a67a8b、これは無意味です。larave...
public function __construct()
{
    exec('php /full/path/to/artisan view:clear');
}

問題が起こりそうなページを管理するコントローラクラスのコンストラクタに定義することで確かに実現できます。今回は採用しませんでしたが、こういった発想は持っておきたいですね。

インスタンス起動時に設定できるUser Dataで対応する

chatGPTくんに泣きついたところ、こちらも教えて貰えました。

上記だと1と3の手法について関心を持ち、特に自分の中の要件と適しているのが1かなと思ったので調べました。

GCPでは起動スクリプト(Startup Scripts), Azureではカスタムスクリプト拡張(Custom Script Extension)と呼ぶようです。

AWSのUser Dataについては下記。

起動時に Linux インスタンスでコマンドを実行する - Amazon Elastic Compute Cloud
起動時に Amazon Linux インスタンスにユーザーデータを渡します。

設定することでインスタンスの起動時に決めた通りのコマンドを実行することができます。

場所はEC2 > インスタンス > インスタンスを起動から、設定の一番下あたりの高度な設計 > ユーザーデータで設定が可能です。起動テンプレートを使っているのであれば起動テンプレート > テンプレートを変更(新しいバージョンを作成) > 高度な設計 > ユーザーデータで同じ箇所に辿り着けます。

#!/bin/bash
# shell operation check
# echo "User Data executed at $(date)" >> /var/log/user-data.log

php /laravel_project/artisan view:clear >> /dev/null 2>&1

こちらで期待していた挙動である「インスタンスを立ち上げた時にviewキャッシュを削除する」ことを実現できました。User Dataの動作確認をしたい場合はechoの部分のコメントを外してから起動させることで、インスタンス内部の/var/log/user-data.logへと出力する内容が書き込まれているかどうかで確認できます。

おわりに

特殊な対応が必要だったにも関わらず対応できました。AWSなどのクラウドサービスの対応可能な範囲にも感謝ですが、最近はchatGPTの凄さに驚くばかりです。

要件を噛み砕いてシンプルな質問として作文できる力と、出力された内容を保証できる資料を自分で特定する力の2つがあれば非常に力強い味方になってくれます。プログラマの仕事が無くなるかもというのもあながち間違いではないかもしれません。そうならないように技術知識だけでなく、普遍的に必要であるコミュニケーション等のスキルも磨いていきたいところです。

おまけ:laravel11とview.phpに関する記事など

自分みたいに探す人がいて、この記事に辿り着く方がいるかもしれないので参考に。

ちなみに今回の現象はlaravel10で遭遇しました。

Laravel11をインストールしてLaravel10との違いを見てみよう | アールエフェクト
Laravelの新しいバージョンであるLaravel11が2024年3月12日にリリースされたのでmacOSを利用してインストールと簡単な動作を行っていました。以前のバージョンに比べてファイルなどがスリム化して更新頻度が少ないものや開発の要件によって必要のない機能は後からインストールして設定できる形になっています。
Laravel11へのアップグレード(スリム化版)
Laravel 11でのconfig周りの変更|Laravel|PHP|開発ブログ|株式会社Nextat(ネクスタット)
開発ブログ|こんにちは、ナカエです。 本日は最近リリースされたLaravel 11の記事です。Laravel 1

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