CMSでの画像操作とCloudFrontのファイル無効化の連動

PowerCMS X Advent Calendar 2021」の19日目です。

Next.jsを採用した当サイトでは9月に画像のリサイズについて検討・スクリプトを開発するとともにAmazon CloudFrontで配信するようにしました。画像のリクエストに幅の指定があればそのサイズにリサイズしてクライアントに送る・CloudFrontにキャッシュする仕様です。

サイトの運用にあたってはファイル名の命名規則を定めているほか、細心の注意を払って記事を執筆しているため画像を誤ってアップロードすることはほぼありませんでしたが、この4ヶ月で1度だけ何か失敗をしAWSのコンソールからCloudFrontのファイル無効化リクエストを送信したことがあります。先日開催された「PowerCMS X ver.3 開発者ミーティング」で長内さんのお話を伺うなどし、PowerCMS X Ver.3で新たに提供開始となった「AWS_CloudFrontプラグイン」を試してみることにしました。

当サイトにおける画像配信の構成は次の図のようになっています。
ブラウザとPowerCMS Xの間にCloudFrontが入っており、画像のリクエストに応じてCloudFrontのキャッシュもしくはCMSサーバーから画像ファイルが送信される。CMSサーバーからCloudFrontにはファイル無効化リクエストが送信できる。

ファイル無効化(キャッシュのパージ)を試す

AWS_CloudFrontプラグインの準備は簡単で、Composerを利用してAWS SDK for PHPをインストール後以下の項目を入力しました。詳細はマニュアルをご覧ください。

  • アクセスキー
  • シークレットキー
  • ディストリビューションID
  • 無効化のトリガー

動作確認をします。アセットをアップロードした後で何度かcurlを実行します。「[小ネタ] CloudFrontでクエリ文字列を転送しているときのInvalidationにはクエリ文字列を忘れないように注意しよう | DevelopersIO」を参考にしました。レスポンスヘッダーには「x-cache: Hit from cloudfront」と記録されています。

$ date; curl --head "https://assets.rd.powercmsx.anothersky.jp/sites1/assets/images/cloudfront_test_01.png"
2021年 12月14日 火曜日 17時58分54秒 JST
HTTP/2 200 
content-type: image/png
date: Tue, 14 Dec 2021 08:47:19 GMT
last-modified: Tue, 14 Dec 2021 08:46:55 GMT
x-cache: Hit from cloudfront

$ date; curl --head "https://assets.rd.powercmsx.anothersky.jp/sites1/assets/images/cloudfront_test_01.png?w=240"
2021年 12月14日 火曜日 17時58分57秒 JST
HTTP/2 200 
content-type: image/png
date: Tue, 14 Dec 2021 08:47:32 GMT
x-cache: Hit from cloudfront

PowerCMS Xの管理画面で画像(アセット)を削除すると、ファイル無効化のためのキューが作成されます。ファイルのパスに「*」が含まれる点は後ほど解説します。
ファイル無効化リクエストの情報が収められたキューの画面

その後cronによりAmazon CloudFrontにファイル無効化リクエストが送信されました。
AWSコンソールでファイル無効化リクエストの詳細を確認した画面

curlを実行するとレスポンスヘッダーには「x-cache: Error from cloudfront」と記録され、キャッシュがヒットしないことが確認できます。

$ date; curl --head "https://assets.rd.powercmsx.anothersky.jp/sites1/assets/images/cloudfront_test_01.png"
2021年 12月14日 火曜日 18時01分16秒 JST
HTTP/2 404 
date: Tue, 14 Dec 2021 09:01:17 GMT
x-cache: Error from cloudfront

$ date; curl --head "https://assets.rd.powercmsx.anothersky.jp/sites1/assets/images/cloudfront_test_01.png?w=240"
2021年 12月14日 火曜日 18時01分20秒 JST
HTTP/2 404 
date: Tue, 14 Dec 2021 09:01:20 GMT
x-cache: Error from cloudfront

当サイトでは画像(アセット)の更新時と削除時にファイル無効化リクエストを送信するよう設定することとしました。

クエリストリングを含む画像もCloudFrontのファイル無効化をしたい

普通にMTMLでビューを書いて出力するサイトには関係のないことですが、記事冒頭に書いた画像リサイズの仕様がありこのサイトでは画像のリクエストにクエリストリングが含まれます。

  • /sites1/assets/images/cloudfront_test_01.png (原寸)
  • /sites1/assets/images/cloudfront_test_01.png?w=810 (幅810pxにリサイズ)
  • /sites1/assets/images/cloudfront_test_01.png?w=810&q=75 (幅810pxにリサイズ・画質75)

できればこれらをまとめてファイル無効化したいのですが、公式ドキュメントでは『パラメタ付きURLについては「ルート相対パスとパージ対象パスのマッピング」設定によりクリアできる場合を除き無効化対象とはなりません。』とあります。そこで、プラグインのソースコードや実行時の挙動を確認しPADOのsave_filterコールバック(Prototype = CMSのコールバックではないことに注意)で無効化リクエストのオブジェクトのパス末尾に「*」を付けるようにカスタマイズを実施しました。よって、先のキャプチャのようなパスやcurlの実行結果になりました。元の画像を削除するだけでCMSサーバーにあるリサイズした画像が削除されるとともにファイル無効化リクエストが送信されるのは運用が大変楽です。

/**
 * PADOのsave_filterコールバックでの処理(モデル:queue)
 *
 * @access public
 * @param array $cb
 * @param PADO $pado
 * @param PADOMySQL $obj
 * @return bool
 */
public function db_save_filter_queue( $cb, $pado, &$obj ) {
    if ( $obj->method === 'queue_purge' ) {
        if ( strpos( $obj->metadata, 'assets/images' ) !== false ) {
            $obj->metadata( $obj->metadata . '*' );
        }
    }
    return true;
}

まとめ

「AWS_CloudFrontプラグイン」により、AWSに不慣れな方でもCloudFrontのファイル無効化が可能な環境を整えることができました。また、プラグインによりファイル無効化リクエストを調整できることが分かりました。高速配信と使いやすさが両立できるPowerCMS Xをぜひお試しください。