明けましておめでとうございます。この記事はOpenSaaS Studio Advent Calendar 2019、24日目の記事、にしようと思っていたけど年またぎました。遅れてすいません。
昨年末に初めてCDNを触ってみて感じたことがあったので、整理も兼ねて所感をまとめました。
経緯
年末年始に向けて負荷対策を行うことになったので想定負荷を出してみたところ、それなりに高い負荷を迎え撃つ必要がありました。システムはマイクロサービス構成でKubernetes上にデプロイされており、podをスケールアウトすれば対応できる想定でしたが、障害につながる要因は排除したいですよね。ということで原因を探っていたところ、CDNのキャッシュヒット率が想定よりも低いことが分かったので、キャッシュキーの条件を見直すことにしました。
効果
負荷耐性
キャッシュキーはクエリ文字列、Cache、リクエストヘッダなど色々な条件を組み合わせて設定できる(CloudFlontの例)のですが、アプリの変更によって一部のリクエストがCDNにキャッシュされなくなっていたので、CDNの設定を見直してキャッシュされるようにしました。
これにより、例えばキャッシュヒット率が60%->90%まで向上した場合、originの負荷がリクエスト全体の40%->10%と1/4まで下がるので、負荷対策として大きな成果になりました。また、定常的に負荷が下がるため、平時のoriginのリソースを削減することができ、コストの削減にもつながりました。
スパイク耐性
キャッシュヒット率を上げて定常的な負荷を下げることはできましたが、キャッシュがない状態でCDNにリクエストが来るとその数だけoriginまで問い合わせが来てしまい、スパイク時に負荷が高くなる懸念がありました。特にtoC向けのサービスだとスパイクが非常に大きくなる傾向があるため、リソースのスケールアウトが間に合わない可能性がありました。
そこで、リクエスト集約の仕組みを有効にしました。(Fastlyだとリクエスト共有、AkamaiだとMake Public Earlyという名前らしい。)これは、キャッシュキーが同一のリクエストがCDNに届くと、一つ目のリクエストだけoriginに問い合わせを行い、二つ目以降のリクエストはCDNのキューに貯めておき一つ目の問い合わせの結果をそれらのリクエストのレスポンスとして返す、という仕組みです。
これにより、大きくスパイクしてもキャッシュがあればそのキャッシュを返し、なければCDNでoriginへの問い合わせを待ち合わせしてくれる、という状態になりました。スパイク時のoriginへの影響がほぼなくなりリソースが非常に計画しやすくなりました。
気になった点
キャッシュの設計
上で挙げたCloudFlontの例のように様々な条件をキーとして指定でき、それらを組み合わせてキャッシュのバリエーションや可否を細かく制御しようとすると複雑になるので、使いこなそうとすると大変そうです。また、キャッシュ周りの設計が変わる頻度は小さいと思うので、設計意図や知識/意識をチームに残すには意識的な施策が必要かもしれません。例えば、設計意図なら前提知識も含めたドキュメントの整備、知識や意識はCDNに関する定期的な障害対応など。後者は別の観点でカオスエンジニアリングに興味があるので、その仕組みを使って自動化できると施策の運用も楽になりそうな気がします。
CDN設定の運用
設定の変更はCDNが提供しているGUIで行っているのですが、設定自体の数が多いためキャッシュキーなど複数の設定で共通している内容を変更する場合は画面上で同じ操作を繰り返す必要があります。手間や操作ミスの可能性と設定が増えていった場合に備えて、APIなどを利用してこちらの運用に合わせたツールを用意できると良さそうです。
また、可能であれば設定をコード化したりCI/CDに組み込んで運用フローに乗せられると、CDNの運用を特別意識する必要がなくなり、反映漏れなどを防ぐことができそう。
キャッシュ漏れの検知
意図せずにキャッシュヒット率が下がってきたら検知したいです。CDNに検知する仕組みがあればそちらを使っても良いし、CDNとintegrationできるモニタリングツールを採用していればそちらでアラートを仕込んでも良さそう。今回は管理するツール/設定を増やしたくなかったのでモニタリングツールの方で仕込みました。
ただし、キャッシュされなくなると上で挙げたリクエスト集約の仕組みに乗らなくなるので、スパイクした場合にoriginがリクエストを捌けなくて503エラーなどを返すのが先になるかもしれません。
まとめ
メリットが大きいので、課題を解決しつつCDNと上手く付き合っていければと。