Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Web配信の技術 #187

Open
hysryt opened this issue Sep 8, 2021 · 5 comments
Open

Web配信の技術 #187

hysryt opened this issue Sep 8, 2021 · 5 comments
Labels

Comments

@hysryt
Copy link
Owner

hysryt commented Sep 8, 2021

No description provided.

@hysryt hysryt added the label Sep 8, 2021
@hysryt
Copy link
Owner Author

hysryt commented Sep 8, 2021

2章

  • RTT(Round Trip Time)
    • リクエストを投げてからレスポンスが帰ってくるまでの時間
  • 物理的な距離を近づける
    • CDN
  • キャッシュの分類
    • ローカルキャッシュ
      • ブラウザキャッシュ
    • 配信経路上のキャッシュ
      • CDN
    • ゲートウェイキャッシュ
      • Proxy
  • CDNのエッジサーバーの振り分け方式
    • DNS方式
      • EDNS Client Subnet(RFC7871)
    • IP Anycast方式
  • スループット
    • 通信速度。時間当たりにどれだけデータを通信できたか。
  • レイテンシ
    • 送信してから相手に届くまでの時間。送信してからレスポンスを受け取るまでの時間のことを往復レイテンシという。

@hysryt
Copy link
Owner Author

hysryt commented Sep 12, 2021

3章

  • HTTPヘッダーでローカルキャッシュを活用する
    • cache-control ヘッダ
      • max-age=<seconds> - キャッシュの有効期間。
      • s-maxage=<seconds> - 共有キャッシュ用のキャッシュの有効期間。max-age より優先される。
      • no-cache - 名前が紛らわしいがキャッシュはされる。ただしキャッシュされたデータを使用する前にデータが有効かどうかをサーバーに問い合わせる必要がある。キャッシュをさせない場合は no-store を使用する。
      • no-store - キャッシュしてはならないことを表す。
      • must-revalidate - キャッシュが古くなったらキャッシュが有効かどうかをサーバーに問い合わせる必要があることを表す。
    • age ヘッダ
      • プロキシのキャッシュに入ってからの経過時間。
  • コンテンツを改善して転送量を減らす

  • Apacheでヘッダーを操作する場合は mod_headers を使用する。
    • header set - ヘッダーを設定する。同名のヘッダーがある場合は上書き。
    • header append - 同名のヘッダーにヘッダー値をコンマ区切りで追加する。同名のヘッダーがない場合は新規追加。
    • header add - ヘッダーを追加する。同名のヘッダーがあっても新規追加。
    • header unset - ヘッダーを削除する。

  • HTTP
    • RFCには存在しないが、プロキシやCDNの中にはHTTPメソッドとしてPURGEを使用できるものがある
    • HTTP2ではリクエストの開始行は擬似ヘッダーに置き換わっている。バージョン指定は無くなった。

  • HTTPレスポンス
    • 206 Partial Content - Range付きのリクエストが成功して部分的なレスポンスボディを返すときのステータスコード。動画配信など。
    • 502 Bad Gateway - CDNやProxyとオリジンの間に問題があることを表すステータスコード。名前に反して、オリジンの方に問題があることが多いらしい。
    • 504 Gateway Timeout - CDNからオリジンの間のレスポンスが遅い場合などに返されるステータスコード。

  • HTTPキャッシュ
    • RFC7234
    • HTTPのレスポンスはデフォルトでキャッシュされる仕組みになっている(cache-controlの設定が無かったともしても)
    • キャッシュされる条件は78ページの図がわかりやすい
    • キャッシュを使用する条件は80ページの図がわかりやすい
    • キャッシュ可能なメソッドはGET、HEAD、POST
      • POSTもRFC的にはキャッシュ可能だが基本的にはキャッシュされない。(前にお問い合わせフォームの確認画面がキャッシュでおかしいことになったけどこれのせい?.htaccessとかで no-store を指定すれば回避できたかも)
      • キャッシュ可能なステータスコードは 200, 203, 204, 206, 300, 301, 404, 405, 410, 414, 421, 451, 501
        • 404 もキャッシュ可能なことに注意
        • 実装によってはこれら以外の場合でもキャッシュされることがある。

  • リクエストの cache-control ヘッダで max-age=0 を送信すると、「指定された秒数を超えるキャッシュを受け付けない」という指定になるらしい(意味がよくわからない)
    • 指定した秒数(つまりこの場合は0)以上経過しているキャッシュは受け付けない、ということっぽい?
      • ブラウザは通常のリロードの場合に max-age=0 を送信する
        • ページに埋め込まれたcssやjsの読み込みには使わないっぽい?
      • ただし解釈しないCDNが多いらしい

  • キャッシュの分類
    • shared - 複数のクライアントで共有するキャッシュ(経路上のキャッシュ)
    • private - 共有しない(してはいけない)キャッシュ(ローカルキャッシュ)
  • publicまたはprivateを指定するとキャッシュできない場合(キャッシュできないステータスコードなど)でも強制的にキャッシュを行う
    • 403はキャッシュできないステータスコードだが、publicやprivateの指定があればキャッシュできる
      • public を指定すると shared にキャッシュ、private を指定すると private にキャッシュ
    • 未指定、public、private のそれぞれの動作の違いは84ページの図がわかりやすい
    • 前述のとおり、HTTPは基本的にキャッシュを行うので未指定であってもキャッシュできない場合を除いてキャッシュは行われる
    • sharedキャッシュとするためだけにpublicを指定するのは間違い
    • privateキャッシュに格納する場合はprivateの指定が必要(ただし通常キャッシュできない場合もキャッシュされることに注意)
    • Google Cloud CDN は明示的にpublicを指定しないとキャッシュされないらしい

  • キャッシュの使われ方の分類
    • no-store - キャッシュを格納してはならないという指定
    • no-cache - キャッシュを使用する際はオリジンへ問い合わせを行い、そのキャッシュが有効な場合のみ使用できる

  • キャッシュの更新の方法の分類
    • must-revalidate - 期限切れキャッシュの場合は再検証を強制し、検証できなければキャッシュは使用できない。
    • proxy-revalidate - 期限切れキャッシュの場合は再検証を強制し、検証できなければキャッシュは使用できない。ただしprivateキャッシュには適用されない。
    • immutable - キャッシュは更新できない。キャッシュを使わないようにしたい場合はURLを変更するしかない。
  • no-cacheは毎回検証が必要だが、must-revalidateとproxy-revalidateは期限が切れた時に再検証が必要。

  • no-transform - 経路の中間でオブジェクトに対しての操作を許可しない
    • httpsを使うことでも回避できる(というよりhttpsの方が確実?)

  • キャッシュの期限指定
    • キャッシュの有効期限のことをTTL(Time to live)と呼ぶ
    • キャッシュには2種類の状態がある
      • Fresh - TTLの期限内のキャッシュ(検証が成功してキャッシュが有効な状態)
      • Stale - 期限切れだが条件付きで使えるキャッシュ(検証が無効な状態)
      • 英単語としては、Freshは「新鮮」、Staleは「新鮮でない」という意味
      • Stale から Fresh にするには再検証に成功して有効な状態にする必要がある
    • 有効期限はその間は「必ず保存されていること」を保証するものではない
    • 有効期限が切れても即座に削除されるわけではない
      • 期限切れのキャッシュとして保持され、場合によってはそれが使用される
    • ディレクティブ
      • max-age - レスポンスを生成した時点からの残りの有効期間の秒数。実際の実装ではクライアントがリクエストを開始してからの秒数として使用されることが多いらしい。
      • s-maxage - 経路上のキャッシュのみに有効なmax-age
      • stale-while-revalidate - 再検証を行なっている間に古いキャッシュを使うことを許容する期限
      • stale-if-error - 再検証でエラー(オリジンがダウンしている時など)が起きた際に古いキャッシュを使うことを許容する期限。現時点では対応しているブラウザは少ないらしい。

  • Expires
    • 期限切れの時刻を指定する
    • GMT以外は使用できない
    • Cache-Control: max-age の方が優先される。
    • そもそもExpiresはCache-Control: max-age を理解しないクライアントようらしい
      • 現在はほとんどのブラウザやProxy/CDNで Cache-Control: max-age を理解する

  • TTLを指定しなかった場合
    • TTL = (Date - Last-Modified) / ブラウザ実装による定数(一般的には10)

  • キャッシュさせたくない場合の指定
    • キャッシュでの事故でありがちなのが、個人データがCDNでキャッシュされてしまうこと
    • そのため、キャッシュを使用しない no-store に加え、経路上にキャッシュさせないための private 、もしキャッシュがあっても再検証を必要とさせる no-cache を指定すると良い。この本の中では must-revalidate も推奨している。
    • MDNなどでは no-store 以外を指定するのは悪い例と書かれているが、現状のCDNの実装などを見ると no-store だけでは心許ないらしい。
    • Last-Modifiedヘッダを消すとTTL未指定の時の動きを排除できるらしい。

  • 条件付きリクエスト
    • 再検証を効率化するための仕組み
      • If-Modified-Since ヘッダ - キャッシュのLast-Modifiedヘッダを値として使用する。更新がある場合はコンテンツを送るよう指示するヘッダ
      • If-None-Match - キャッシュのETagヘッダを値として使用する。一致しなかったらコンテンツを送るよう指示する。
    • If-Modified-SinceIf-None-Match を両方指定した場合は If-None-Match が優先される。
    • コンテンツを送る必要がない場合は 304 Not Modified となる。
    • 304 として返す場合にもキャッシュの指定が必要。

  • Varyヘッダ
    • リクエストの特定のヘッダをキャッシュのセカンダリキーとして使用する。
      • プライマリキーはURL
    • たとえば Accept-Encoding や User-Agent などが指定されることが多い
      • ただしUser-Agentはブラウザによってかなり異なるため無数のパターンが生まれてしまう

  • ETag
    • ETagの生成方法にinodeが含まれる場合は注意が必要
      • ロードバランサは以下に複数のWebサーバーがある場合、Webサーバーによってinodeが変わってしまう
        • 結果、ファイルが変わっていないのにETagが変わってしまう
      • 過去のApacheではデフォルトでinodeが含まれていたらしい

  • 既存の設定の改善
    • ゲートウェイに設定を集約する
      • ProxyやWebサーバーなど、構成によって異なる
      • 3つに分類して深掘りする
        • キャッシュさせたくない
        • ローカルのみにキャッシュさせたい
        • ローカル+経路上にキャッシュさせたい

  • コンテンツサイズの削減
    • 圧縮
      • gzip、brotli

  • ありがちな問題
    • JSONが圧縮されてない
    • 複数サーバーを使っている時にデプロイの時間によってLast-Modifiedが変わってしまっている
      • デプロイ時にタイムスタンプを保持する設定を行う

@hysryt
Copy link
Owner Author

hysryt commented Sep 25, 2021

4章

  • 経路上のキャッシュ
    • Proxy や CDN
    • クライアント数増加の際に有効
    • 複数のユーザー間でキャッシュを共有するため注意が必要

  • Proxy
    • ゲートウェイキャッシュ
    • Varnish(https://varnish-cache.org/)
      • 同じ立ち位置のソフトウェアはNGINX、Squidなど?
    • オリジンに一番近いキャッシュ

  • 負荷分散時に考慮すべきリソースの一例
    • CPU
    • メモリ
    • ロードバランサのキャパシティ
    • Appのコネクション数
    • DBのコネクション数
    • ログの出力先のディスク容量
    • エフェメラルポート数
      • 通信の際、一時的に使用されるポート。Linuxではデフォルトで32768~61000。

  • AWS Lambda は高スケーラビリティらしい
  • 予めリソースを準備しておき、予期しない流入に備えてオートスケールされるようにしておく

  • スケールアウトは即時ではない
    • 実際にスケールアウトされるまでの間どう凌ぐか?
      • キャッシュがあれば負荷の増加が緩やかになる

  • Appサーバーがキャッシュを配信できないのか?
    • Appサーバーはアプリケーションを動かすのが主な役目でありアクセスを捌くのが主ではない
    • キャッシュの仕組み自体も複雑
    • Appサーバーのスケールアウトよりキャッシュサーバーのスケールアウトの方が楽
    • そのため別途キャッシュの機能を担うサーバーを用意した方が良い

  • キャッシュ事故例
    • Set-Cookieをキャッシュしてしまい、ログイン情報が漏洩した
    • Cache-Controleの不備やCDNの解釈によって意図せずにキャッシュされてしまい、漏洩した
    • 特定パス以下のレスポンスが全て同じになってしまった
  • CDNやオリジンを信用しすぎないようにする
    • CDNが必ずRFC通りに実装されているという保証はない

  • キャッシュキー戦略
    • キャッシュの事故はキャッシュキーに何を採用するかの設定ミスが多い
      • キャッシュキーにユーザーIDを含めないと各ユーザーでキャッシュが共有されてしまう
    • キャッシュキーとして何を使用するか?
      • 個人情報を含むページであればユーザーIDなどを含める必要あり
  • CDNはオリジンへ送るヘッダーを制限できる
    • 個人情報が必要ないページの場合はAuthorizationやCookieヘッダを削除してリクエストを送るなど
  • キャッシュキー(プライマリキー)とセカンダリキー
    • キャッシュキーはクライアントのリクエストの情報のみで構築する
    • セカンダリキーはクライアントのリクエストとオリジンからVaryを組み合わせて構築する

  • TTLを決める基準
    • ステップ1:セッション中にTTLが切れないようにする
    • ステップ2:次のセッション時にTTLが切れていないようにする
    • ステップ3:不要なボディ転送を減らす
      • 304を返して不要なボディ転送を減らす → 再検証が成功すれば古いキャッシュを新しいキャッシュとしてそのまま使用できる

  • ローカルキャッシュと経路上のキャッシュのTTLの考え方の違い
    • 経路上のキャッシュは次のリクエストが来るまでキャッシュが残っていれば良い
      • 秒間10回のアクセス(複数のクライアントからのアクセス)があるのであればTTLが1秒でも効果が出る
      • ただしTTLを長くする子にもメリットはある
        • TTLが長ければ再検証のリクエストを減らせる

  • キャッシュの消去
    • URLを変更する cache busting
      • 以前のキャッシュは残る
    • キャッシュの消去(purge)には無効化(invalidate)と削除(delete)の2つがある
      • 無効化はキャッシュをstale状態にすること
      • 削除はキャッシュを完全に削除する
      • どちらが行われているか判断できない場合は再度リクエストを送ればわかる。再検証のリクエストが送信されていれば無効化、通常の初回リクエストが送られていれば削除されている。
  • キャッシュを消去すると一時的にリクエストが増大する可能性がある
    • 消去を複数回に分けることで対策できる
  • キャッシュを消す際は上流から(オリジンに違い方から)消していく必要がある
  • キャッシュの消去を正常系の運用に組み込むべきかは慎重に考える必要がある
    • 多くの場合TTLの調整で対応可能

@hysryt
Copy link
Owner Author

hysryt commented Oct 3, 2021

5章

  • キャッシュヒット率
    • キャッシュを使用できた割合。高いほど良い。
    • オフロード率ともいう。
    • ヒット率は高いが一部の負荷の高いリソースはキャッシュできておらず、その結果サービス全体の負荷は軽減できていないというケースもあるため、ヒット率だけを盲信してはならない。

  • 多くのProxyやCDNでは各イベント時にヘッダの操作などが行えるようになっている
    • イベントにはいくつかある。以下はその一例。
      • RxReq:クライアントからのリクエストの受信時
      • TxReq:オリジンへのリクエストの送信時
      • RxResp:オリジンからのレスポンスの受信時
      • TxResp:クライアントへのレスポンスの送信時

  • RxReqで行う処理
    • アクセス制御
    • キャッシュキーおよびセカンダリキーの操作
      • Varyで指定されているヘッダー値の正規化など
    • 不要なcookieの削除
    • キャッシュするかどうかの判定
    • クエリ文字列の正規化

  • キャッシュがあるかどうかの判定
    • キャッシュキーが一致するキャッシュを取得する
      • キャッシュに Vary ヘッダが含まれている場合は、指定されているリクエストのヘッダがセカンダリーキーと一致するキャッシュを取得する
    • 一致するキャッシュが存在すればキャッシュあり、なければキャッシュなし

  • TxReqで行う処理
    • オリジンが複数ある場合などにオリジンの切り替えを行う。
    • キャッシュキーに影響を与えたくないリライト
      • Cache lookup後にリライトすることでキャッシュキーに影響を与えずに済む

  • RxRespで行う処理
    • オリジンからのレスポンスをもとにキャッシュを行うかどうかの判定
    • ttlの上書き
    • キャッシュする内容の加工
      • Set-Cookieの削除など
    • Varyヘッダの書き換え

  • TxRespで行う処理
    • クライアントに応じてヘッダを書き換え
      • Access-Control-Allow-Orignなど
    • 不要なヘッダの削除

  • ESI(Edge Side Includes)
    • ページ内のコンテンツをパーツごとにキャッシュし、CDN/Proxyで結合して1つのコンテンツとして返す技術
    • 調べた感じ結構前からある技術っぽい
    • ESIの言語仕様はW3Cに提出されているがまだ承認されていないとのこと
    • CDN企業やキャッシュプロキシサーバーによって実装されたりされてなかったりする

  • 多段プロキシ
    • プロキシを多段ではなく単純に追加していった場合、プロキシごとにキャッシュ内容が違ってしまう。また、オリジンから見るとクライアントが増えることになるため、オリジンの仕事が増える。
    • オリジンにアクセスするプロキシを一つに集約し、その他のプロキシはそのプロキシにアクセスすることでキャッシュのばらつきをなくすことができる。この方法を多段プロキシと呼ぶ。
    • URL(キャッシュキー)ごとにどのプロキシがキャッシュを行うかを決めておく方法もある。(これも多段プロキシの一種)
    • 多段プロキシはキャッシュが飛んだ時も負荷を抑えられる。

  • Proxyのヘルスチェック
    • 行う側と受ける側の設定が必要
      • 行う側
        • 正常/異常の判定方法には2種類ある
          • 定期的にヘルスチェックリクエストを行い、連続でn回成功(または失敗)したら正常(または異常)
          • 定期的にヘルスチェックリクエストを行い、直近n回のうちm回成功したら正常
        • 過敏すぎる設定は返って安定性を損なう可能性が高い
      • 受ける側
        • ヘルスチェックリクエストは基本は静的ファイルへのリクエストだが、それだけだとPHPが正常に動作しているかどうかを判断的ない
        • 外部サーバーのDB接続チェックまで行う必要はない
          • DBの不具合に引きずられて全てのアプリケーションサーバーが異常となってしまう。
          • DBの不具合はアプリケーション内で捕捉し、エラー画面を出すなどする

  • ZoneApex
    • ゾーンのドメインを指す
      • example.com であれば example.com がZoneApex
      • foo.example.com のゾーンであれば foo.example.com がZoneApex
      • サブドメインかどうかではなく、ゾーンかどうかが重要
    • ZoneApexにはCNAMEを指定することはできない
      • ZoneApexにはNSレコードとSOAレコードが必須だが、仕様上CNAMEレコードは他のレコードとの共存ができないため。
    • CDNの設定はCNAMEで行うことが多いが、対象のサイトのドメインがZoneApexの場合はCNAMEを使用できないため別の方法を取る必要がある。
      • IPアドレスで設定できるCDNを使用する。IPアドレスであればAレコードやAAAAレコードで設定可能。
      • CDNが提供するDNSサービスを使用する。
      • Aliasを指定できるDNSサービスを使用する。

@hysryt
Copy link
Owner Author

hysryt commented Oct 25, 2021

  • CDNのようなシステムを自前で用意するのはコスパが悪い
    • インフラ利用料
    • 人件費
    • 教育コスト
  • インフラを構築するよりトラフィック量による従量課金の方がコスパがいい
  • 一定量を自前のインフラで賄い、それ以上の時はCDNを使うという方法もある
  • 国内からのアクセスは自前のインフラ、海外からのアクセスはCDNという方法もある

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant