Magento Open Source / Adobe Commerceの商品画像に関するアレコレ〜その3

前回まではMagento Open Source / Adobe Commerceの商品画像のリサイズとそのキャッシュ再生成について取り上げてきました。
今回はVarnishとFastly側での画像キャッシュの更新について取り上げたいと思います。

そのまえに、Magento Open Source / Adobe Commerce 2.4.5で搭載された新機能について軽く触れておきたいと思います。

Magento Open Source / Adobe Commerce 2.4.5以降ではNginx / CDNでのリサイズがサポートされた

Experience Leagueにも記事がある通り、Nginx / CDN側での画像リサイズ対応が追加されています。

この機能は、これまで説明してきたMagentoフレームワークにビルトイン実装されている画像リサイズ処理を使用せずに、NginxやCDNでの画像リサイズ・最適化を行うものです。
リサイズ処理がシステム外に出されることによって、Magentoが動作するサーバー上での処理負荷軽減が見込めます。

Nginxでリサイズをする場合

CDNを使わず、Nginxデリサイズをする場合は以下の手順で設定を行います。

https://nginx.org/en/docs/http/ngx_http_image_filter_module.html

  1. Nginxにngx_http_image_filter_module を追加する
  2. Magento付属のnginx.conf.sampleを参考に定義を追加する
  3. Magento側の設定を変更する

ngx_http_image_filter_moduleは、Nginxの公式マニュアルでは標準で付属していないとあります。
Linuxディストリビューションの提供するパッケージなどについては個別に確認が必要になるため、「Nginxを使っているから利用可能だ!」と早合点することがないように注意しましょう。

また、ngx_http_image_filter_moduleではWebP形式への変換も可能となっています。ただし、WebPが利用できない一部のブラウザ向けにはWebP以外の形式での提供が必要になるため、WebPを併用した形での配信についてはさらなる調整が必要になると思っておいたほうが良いでしょう。

なおNginxでリサイズをする場合は元になる画像のストレージとしてAmazon S3を利用可能です。この場合は更にNginxに追加モジュールが必要になりますが、一旦本稿では割愛します。

CDNでリサイズをする場合

Fastlyやその他のCDNでリサイズをする場合は、以下の手順で設定を行います。

  1. CDN側の画像リサイズ・最適化サービスを有効にする
  2. Magento側の設定を変更する

こちらの場合はサーバー設定の変更などはなく、CDN側とMagento側の設定変更だけで対応が可能です。

なお、Fastlyの場合はMagento用のエクステンション側ですでに対応が完了しています。
Image Optimizerというサービスですが、

  1. Fastly側での機能有効化(使えるかどうかはFastly側に確認が必要です)
  2. エクステンション側での設定変更
  3. 動作テスト

という手順で切り替えることができます。

VarnishとFastlyにおける画像キャッシュの問題

さて、ここからが本稿の本題です。次のような環境においてはリサイズ後の画像がなかなか差し替わらない問題があります。

  • 2.4.5以前のバージョンの場合
  • 2.4.5以降でNginxやCDN側での画像リサイズを利用しない場合

そもそもの画像自体のリサイズが行われない理由については前回までで説明したと思いますが、今回は「キャッシュが更新されない」が主題です。
まずこの問題を考える際に、Magento標準のVarnish用設定ファイルであるVCLの内容を確認しましょう。

標準のVCL定義の内容

2.4.6に付属するVarnish6用の定義では静的ファイルのキャッシュについてvcl_recvサブルーチンでは次のように定義されています。

sub vcl_recv {
〜中略〜

    # Static files caching
    if (req.url ~ "^/(media|static)/") {
        # Static files should not be cached by default
        return (pass);

        # But if you use a few locales and don't use CDN you can enable caching static files by commenting previous line (#return (pass);) and uncommenting next 3 lines
        #unset req.http.Https;
        #unset req.http.X-Forwarded-Proto;
        #unset req.http.Cookie;
    }
〜中略〜
}

初期状態では静的ファイルの配信についてはVarnishでキャッシュしない設定になっています。
Varnishでキャッシュさせたい場合は、指示に沿って定義を調整することになります。これでとりあえずVarnishが静的ファイルをキャッシュしてくれるようになります。

次にvcl_hashサブルーチンで、キャッシュを識別するためのハッシュキーを生成しています。

sub vcl_hash {
    if (req.http.cookie ~ "X-Magento-Vary=") {
        hash_data(regsub(req.http.cookie, "^.*?X-Magento-Vary=([^;]+);*.*$", "\1"));
    }

    # For multi site configurations to not cache each other's content
    if (req.http.host) {
        hash_data(req.http.host);
    } else {
        hash_data(server.ip);
    }

    # To make sure http users don't see ssl warning
    if (req.http.SSL-OFFLOADED) {
        hash_data(req.http.SSL-OFFLOADED);
    }

    if (req.url ~ "/graphql") {
        call process_graphql_headers;
    }    
}

VCLの書き方をご存じの方は、vcl_hash内に

return (lookup);

が記述されていませんが、特に問題はありません。
Varnish標準の挙動として、lookupが呼ばれない場合、暗黙的にURLやホスト名・サーバーIPなどを用いてハッシュ値を作成します。
よって、静的ファイルのURL単位でキャッシュを識別するハッシュ値が作られることになるわけです。

キャッシュパージの挙動

Varnishが静的ファイルをキャッシュする部分の動きをここまで確認してきましたが、いよいよ本稿の本題中の本題である「キャッシュパージ」の解説をしていきます。
Varnishに限らず、CDNやキャッシュサーバー上のキャッシュを消去することを「パージする」と表現します。
Magento用のVCL定義では、

    if (req.method == "PURGE") {
        if (client.ip !~ purge) {
            return (synth(405, "Method not allowed"));
        }
        if (!req.http.X-Magento-Tags-Pattern && !req.http.X-Pool) {
            return (synth(400, "X-Magento-Tags-Pattern or X-Pool header required"));
        }
        if (req.http.X-Magento-Tags-Pattern) {
          ban("obj.http.X-Magento-Tags ~ " + req.http.X-Magento-Tags-Pattern);
        }
        if (req.http.X-Pool) {
          ban("obj.http.X-Pool ~ " + req.http.X-Pool);
        }
        return (synth(200, "Purged"));
    }

という部分でパージの定義がなされています。
定義内容としては、

  • HTTPリクエストがPURGEで行われること
  • パージ許可IPからのリクエストであること
  • X-Magento-Tags-PatternまたはX-Poolヘッダがあること

の3点を満たすこととなっています。
X-Magento-Tagsは

  • 商品一覧画面
  • 商品詳細画面

などの商品が表示される画面を表示する際に、Magento側からVarnishに向けてHTTPヘッダの一部として送信されます。
このときの値としては、

  • ページ内に含まれている商品のID
  • ページ内に含まれているカテゴリのID

といったものが含まれています。要は「どのキャッシュをパージすればよいかを判断するための材料」を渡しているわけです。

ところがこの情報は静的ファイルについては一切含まれていません。なぜなら「Magentoが情報を付加しないから」です。
その結果として、

  • 商品やカテゴリを更新する
  • CMSページを更新する
  • 管理画面からキャッシュをクリアする

等の操作を行った場合でも、静的ファイルのクリアは一切行われないのです。

静的ファイルのキャッシュをパージしたい場合

では、静的ファイルのキャッシュをパージしたい場合はどうすればよいのでしょうか?
一応方法はいくつかあるにはあります。VarnishとFastlyで少し異なるのでそれぞれ分けて説明します。

Varnishの場合

Varnishの場合、次の3つの方法でキャッシュをパージできます。

  • Varnish自体を再起動する
  • varnishadmコマンド経由でBANリクエストを行う
  • Magentoをカスタマイズする

順番に説明していきましょう。

Varnish自体を再起動する

最も手っ取り早い方法ではありますが、プロセスの再起動を伴います。(瞬間的なサイト停止が発生します)
加えて、静的ファイル以外のすべてのキャッシュが消去されるため、しばらくの間サイトのレスポンスが低下します。

varnishadmコマンド経由でBANリクエストを行う

リモートアクセスができる場合には、Varnishサーバー上でvarnishadmコマンドを使用し、

ban req.url ~ /path/to/image

のような形でパージを実行できます。
ただし、誰でもが実行できるわけではありませんし、運用者が毎回システム管理者に依頼するのも煩雑な話です。
更新頻度が少なければ構いませんが、普通のサイトではあまりやりたくない方法です。

Magentoをカスタマイズする

無難な手としてはやはりこの方法になるでしょう。
Magento上で画像リサイズを行う前にVarnishに対してキャッシュパージを飛ばすようにすれば、自動的に新しい画像が配信されるようになります。

Fastlyの場合

Fastlyの場合、次の3つの方法でキャッシュをパージできます。

  • 「Purge All」を実行する
  • Magentoのキャッシュ管理画面からURL指定でパージリクエストを送る
  • Magentoをカスタマイズする

順番に説明していきましょう。

「Purge All」を実行する

これはVarnishの再起動と基本的に同じです。Fastly上にあるキャッシュを全て消去します。
Varnishと異なり、瞬間的なサイト停止はありませんが、パフォーマンスの低下はあります。

操作自体はMagentoとFastlyの管理画面の両方から行えます。Fastlyの操作権限がない人でも行える点はVarnishよりも便利といえば便利です。

Magentoのキャッシュ管理画面からURL指定でパージリクエストを送る

Magento用のFastly連携エクステンションを導入すると、Magento側のキャッシュ管理画面に項目が増えます。
ここからURL指定でのキャッシュパージができます。

Fastlyのパージ

Magentoをカスタマイズする

Varnishの場合と同様に、Magento側から画像リサイズ時にパージリクエストを送信するようにカスタムすることで、自動的に新しい画像が配信されるようになります。
Varnishの場合とは多少処理に違いが出てきますが、公式のドキュメントを見ながら実装すれば問題ないでしょう。

まとめ

ここまで3回に渡って、Magento Open Source / Adobe Commerceでの商品画像に関するアレコレをご紹介しました。
基本的にもともとのアプリケーション設計が「同一ファイル名での画像入れ替え」を考慮していないので、そういった運用が前提の場合には十分注意が必要です。

最近ではコア機能としてNginxやCDN側でのリサイズが行いやすいように改良されてきてはいるものの、キャッシュの更新までは実装が及んでいないようです。
実運用の前に十分な動作検証をされることをおすすめします。