2018/07/06 Frontrend Vol.12 - サービスの誕生と成長
発表者: - SSRについて @kubosho - CDNについて @ktknest
https://frontrend.connpass.com/event/90107/
AbemaTVはただのSSRじゃねぇんだよ@kubosho @ktknest2018/07/06 Frontrend Vol.12 - サービスの誕生と成長
View Slide
ここ数年のWebアプリケーションにおけるSSRとは● SSR = Special Super Rare Server Side Rendering● サーバー側で初期表示に必要なHTMLツリーを作って返すこと● 狭義では、Reactの renderToString() で作ったHTML文字列を、Expressのres.send() で返すこと
ただのSSRじゃねぇ?どういうことだ?
ありそうな話● 新規にWebサービスを立ち上げて、そこでSSR + SPA構成にしました!● SPAをやめてSSRだけにしました!● SPAをしているWebサービスの構成を変えるついでにSSRもするようにしました!
AbemaTVの場合● 新規に何かサービスを作ったわけではない● SSRに加え、従来と同様のSPAも継続している● 既存のコード設計やデータフロー、使うライブラリなどはあまり変えずにSSRした
SSR実装について
なぜAbemaTVではSSRをするようにしたか● SSRをすることにより、ユーザーにとって意味のあるコンテンツが速く表示されるようにしたかった● ページ全体をSSRすることにより、ページをよりマシンリーダブルにする○ スプラッシュ画面はSSRをしていたがページの情報量が不足している○ 検索エンジンのクローラーに、より情報を読み込ませる目的があった
ページ全体をSSRするようにした効果● サービス名や番組名で検索したとき *.abema.tv から始まるページが上位に出やすくなった○ SSRをしているページ(※1)でユーザーにとって意味があるコンテンツがより速く表示されるようになった(※2)ことが関係している?■ ※1 トップ・エピソード一覧・エピソード詳細ページが対象■ ※2 指標で言えばSpeed IndexやFirst Meaningful Paint Timeなど○ ページの内容が初期表示時に読み込まれるようになって、検索エンジンのクローラーに認識されやすくなった?
デモ
SSR実装でハマったところ
開発時にバックエンド側でエラーが多発した● 元々SSRすることを想定して作られていなかった○ ブラウザ上でしか動かないコードが多かった○ window や localStorage などのプロパティを触っている箇所でエラーが出た● モジュールをインポートする際に余計なものが読み込まれていた○ 必要なものだけを読み込むようにした
バックエンドとクライアントで描画の差異があった● バックエンド側でタイトルや説明文などのメタデータしか返してなかった○ コンポーネントを描画するためのデータが足 りてなかった● 結果 Warning: Expected server HTML to contain a matching in が多発した○ console.log() やHTMLのソースを見て、コンポーネント描画に必要なデータを返すようにした○ SSRできないものは componentDidMount() が実行された後に描画するようにした
まとめ
まとめ● SSRをするようにしたらページの表示速度が速くなって良かった○ 速くなったことによりユーザー体験が上がったと思う○ SEOという意味でも効果があった● SPA + SSR構成をやる場合は、最初からやったほうが途中で導入するより楽○ 最初からブラウザとバックエンド両方で動くように考慮できそう● 課題もまだあるけど引き続き潰していきたい○ ページの表示高速化はまだやれるところがある
CDN導入について
以前のAbemaTVとCDNCloud LBCloud CDN KubernetesEngineNginxNode.jshttps://abema.tv
SSR化にあたっての課題● デスクトップとモバイルで異なるHTMLやJS/CSSを配信したい○ Cloud CDNではVary: User-Agentによる条件分岐はできない○ SSR化するには、これが可能な CDNへの移行が必要になったデスクトップ モバイル全く異なる
Cloud LB KubernetesEngineNginxfor FastlyNode.jshttps://abema.tv現在のAbemaTVとCDNFastly
Fastlyを選んだ理由● 次世代CDNの豊富な機能○ インスタントパージでキャッシュ即削除○ Varnishベースのため、VCLで柔軟なカスタマイズができる○ HTTP/2 Server Push が使える● 社内での先行事例あり (https://www.superchoice.bet/)○ 各種設定を始め、VCLカスタムやノウハウを参考にできそう● 一度は使ってみたかった(チーム一同)○ とても大事!
Fastly移行の課題
TLS 1.0/1.1 が利用できなくなる● AbemaTVの推奨環境で影響がないか確認● Desktop / iOS 9+ / Android 5+ OK● Android 4.4○ 標準ブラウザ NG → サポート外とする○ WebView(Chromium 30) OKFYI: [Android4系端末のTLS1.1&1.2対応について](https://qiita.com/ntsk/items/9f31fc7b44c04ea45e0b)
UA別のキャッシュ振り分け● Varnish: 設定方法● Varnish: UAやファイル種類に応じて識別ヘッダーをセット● Varnish: 識別ヘッダーをVaryヘッダーにセット● Node.js: 識別ヘッダーを使って内部の分岐に利用
Varnish: 設定方法● FastlyではVCLを直接記述する方法が以下用意されている○ カスタムVCL: 独自のVCLファイルを作成することができる○ VCLスニペット: カスタムVCLを使わずに、小さなVCLロジックを挿入できる● AbemaTVではカスタムVCLを利用○ 要件上は、VCLスニペットでも表現可能○ Fastlyの全設定をTerraformを通して反映しており、 TerraformがVCLスニペット未対応○ 今後の拡張性も考慮UA別のキャッシュ振り分け
Varnish: UAやファイル種類に応じて識別ヘッダーをセット公式のガイドをほぼそのまま使用 https://docs.fastly.com/guides/vcl/delivering-different-content-to-different-devices● X-UA-Device: desktop, tablet, smartphone, native, na● X-UA-Vendor: generic, apple, android, etc...画像リソースなどはUA別にする必要がないので一律 X-UA-Device: na とするUA別のキャッシュ振り分け
Varnish: 識別ヘッダーをVaryヘッダーにセットUA別のキャッシュ振り分けVary: …, X-UA-Device, X-UA-Vendor として、別々にキャッシュさせる
Node.js: 識別ヘッダーを使って内部の分岐に利用UA別のキャッシュ振り分け- X-UA-Device, X-UA-Vendror が共にある場合- デスクトップ or モバイルや、iOS or Android の判定に利用する- SSR結果を変え、Varyヘッダーによりキャッシュもそれぞれで行われる- X-UA-Device, X-UA-Vendror のどちらかがない場合- リクエスト情報のUserAgentを見て判定を行う(従来通り)- 移行直前や切り戻しで、 Fastlyを経由しないケースにも対応するため
オリジンシールドによるヒット率の向上(未遂)● オリジンサーバーへのアクセスをシールドPOP経由にする仕組み○ エッジPOPからのリクエストはシールド POP経由になる● 但し、国内限定では目立った効果がないとのこと○ Fastly社からの設定レビューでアドバイスをもらう○ エッジサーバーが少ない故(日本では数箇所)○ 結果、無効にしたhttps://docs.fastly.com/ja/guides/performance-tuning/shielding.html
オリジンサーバーの要件● 従来のURL https://abema.tv とは別のURLで提供する○ https://abema.tv はFastly用になるため● 通常のアクセスはできないようにする○ 認証用のリクエストヘッダーを定義○ FastlyのVarnish設定で、オリジンサーバーへのリクエスト時に追加する○ オリジンサーバー側でチェックし、無効な場合は 401を返す● Cloud CDNを無効にする○ 二重CDNにならないように
稼働状態からの移行方法● Cloud LBで従来のURLとオリジンサーバー用のURLを設定○ ① 従来のURL: Cloud CDNを有効 - Nginx○ ② オリジンサーバー用の URL: Cloud CDNを無効 - Nginx for Fastly● https://abema.tv のDNS設定で、切り替え・切り戻しを行う○ GCP宛: ①で受け、Cloud CDN経由で配信する○ Fastly宛: Fastlyで受け、②からリソースを取得し、 Fastly経由で配信する
現在のAbemaTVとCDN(詳細版 - Cloud CDN)Cloud LBCloud CDNKubernetesEngineNginxNode.jsNginxfor FastlyFastlyhttps://abema.tv
現在のAbemaTVとCDN(詳細版 - Fastly)Cloud LBCloud CDNKubernetesEngineNginxNode.jshttps://abema.tvNginxfor FastlyFastly認証用のリクエストヘッダーを付与
\ 無事に移行完了 /
結果● キャッシュヒット率がAvg 90%以上○ 思ったよりも高い数値だった● 1週間ほどの検証期間も問題なく経過○ その後、SSRを有効化
今後の予定● より細かい分類によるキャッシュの最適化○ 課金プランごとに一部表記が異なる等■ 現在のSSRはどちらにも対応できる形にし、 Clientで再描画を行っている● ブラウザ毎の配信JSの最適化○ サポート状況に合わせたファイルを配信する● 切り戻し手段の再考○ 現時点では、以前の構成にすぐ戻せる形にしている○ 普段使わないファイル群が残ったままになる■ メンテが煩雑化、いざ戻した際も動作が保証しきれない
ご清聴ありがとうございました ☕