odiak.net

KakeruにCDNを導入するぜー

作成:
更新:

KakeruにCDNを導入した記録。
結論を先に書いておくと、CDNを断念してNginxのキャッシュ機能を使うことになった。


2022-03-13

Fastlyを設定して、i.kakeru.appの画像をFastly経由で配信するようにした。
今まではAppEngineから直接画像を配信していたんだけど、リクエストが多いから料金が高くなるし、キャッシュされないから少し遅いなどの問題があった。

次はもう少しキャッシュの時間を長くして、更新されたらパージする運用にしていきたい。

設定しただけなんだけど、やたらと時間がかかった。
エラーが出て、正しいレスポンスが返ってこないのである。
結果的にはTLS関連の設定が間違っていたんだけど、何が問題なのか分からずに色々な設定を延々といじっていた。

2022-03-14

絵が更新されたらFastlyのキャッシュをパージするサーバーを作っていた。
絵を描いている最中は高頻度で更新されるので、変更のたびに処理するのではなく、
一定時間おきにまとめて処理したい。
そこで、Node.jsでリクエストをdebounceするようなやつを作った。

Kakeruの画像URLは、デフォルトのSVG以外はほとんど使っている人はいないと思うけど、
実はPNGやサイズの調整など色々な裏オプションがある。
というわけで、一つの絵に対して複数の画像が紐づいている。
そういうときに、全部まとめてキャッシュをパージするにはどうすればいい?
→Surrogate-Keyというヘッダを使うらしい。
サロゲートキーの使用方法 | Fastly ヘルプガイド

Surrogate-Keyヘッダを返すようにして、キー単位でパージを試してみた。
ところが、なんかキャッシュがおかしい。
どうやら、Last-ModifiedまたはEtagを返していないのが問題っぽい。
あと、stale-while-revalidateなどの指定も必要そう。
ソフトパージ | Fastly ヘルプガイド
Cache-Control - HTTP | MDN

2022-03-16

まあ色々やっていたんですが、Fastlyの料金表を料金表を改めて見てみたら、最低金額が$50じゃないですか。
ちょっとナメてました。出直します。

普通にVM立てて、その中でキャッシュの仕組みを作りますかのぅ。
Redisに詰めてもいいかなと思ったんだけど、メモリ分しか使えないのでどうかな。
ストレージにキャッシュした方が多少遅くても容量を詰め込めるので良い気がする。

調べていたら、Nginxのキャッシュを使うのが手軽で良さそう。
Cache-Controlヘッダーを見てキャッシュしてくれるし、stale-while-revalidateとproxy_cache_background_updateを組み合わせるといい感じに裏でキャッシュを更新してくれる。

問題はパージですな。キャッシュのファイルを読んで、先頭のヘッダー部分を読み飛ばすとプレーンテキストでレスポンスが入っている。

> buffer.slice(337).toString('utf-8')
'KEY: http://127.0.0.1:5000/foo/bar\n' +
  'HTTP/1.1 200 OK\r\n' +
  'X-Powered-By: Express\r\n' +
  'cache-control: max-age=30, stale-while-revalidate=3600\r\n' +
  'Date: Wed, 16 Mar 2022 15:31:40 GMT\r\n' +
  'Connection: close\r\n' +
  '\r\n' +
  'hello, bar, 5'

ただ、ソフトパージ的なことができない。

が、revalidateを使えば良さそう。
(キャッシュ時間を短くして、last-modifiedを見て計算を省略すれば比較的コストを抑えてキャッシュを新鮮にできるはず、という)

https://nginx.org/en/docs/http/ngx\_http\_proxy\_module.html

うーん、難しい。

2022-03-19

ソフトパージっぽいことができた。
色々試した結果だけど、キャッシュファイルのヘッダー部分をこんな感じでいじると良い

  • valid_secは現在時刻のタイムスタンプ -1
    • valid_sec < now かつ valid_sec + stale-while-revalidate > now な条件を満たせばキャッシュが更新されることがソースを読んで分かった
  • last_modifiedはゼロ
    • last_modifiedが変わらないと、キャッシュの更新処理が走ってもキャッシュが上書きされない場合があるため (コンテンツは更新されているが、オリジンの返すlast-modifiedがまだ変わっていないというレアケースだけど)

ちなみに、キャッシュファイルの変更にはnjsというNginx組み込みのJavaScriptの処理系を使ってみた。

js_import purger.js;

server {
    server_name localhost;
    listen 9000;

    location / {
        js_content purger.handler;
    }
}

Nginxの設定にこんな感じで書いておいて、内側からリクエストを受け付けてキャッシュをstaleな状態にすることができた。
purge.jsはGistにメモとして残しておく。
https://gist.github.com/odiak/4694f85783a057d812f80ea537e4dfb7