TT Lab
はじめる
学ぶ 学習パス コース

Envoyの内部構造

切る実験より遅くする実験

TT Labで続きを見る

一言でいうと

リクエストは、ルートに届く前にフィルターチェーンを通ります。チェーンには順序があり、最後には必ず、リクエストを実際に送り出す終端フィルター(router)が来ます。その前に何を挟むかによって、障害注入・レート制限・認証・ヘッダー操作が、アプリケーションのコードを直さずに付きます。

なぜ必要なのか

「決済サービスが遅くなったら、注文サービスはどうなりますか」という質問に答える方法は2つあります。1つはコードを読んで推論することで、もう1つは実際に遅くしてみることです。前者は、間違った答えを自信を持って出します。タイムアウトがどこに設定されているか、コネクションプールがいくつか、リトライが何回かが、コードの複数の場所に散らばっているからです。

プロキシにフィルターを挟めば、後者が可能になります。アプリケーションは、自分が実験対象であることも知らず、デプロイも必要ありません。設定だけを変えて、元に戻します。

レート制限も同じ理由でここにあります。すべてのサービスがそれぞれレート制限を実装すると、言語ごとに別のライブラリを使い、動作も微妙に違ってきます。プロキシで1回やれば、全員が同じ規則を得られます。

どう動くのか

順序が意味を持ちます。フィルターは、書いた順にリクエストを見ます。そのため、認証フィルターはレート制限より前に置くのが普通で(認証もされていないリクエストにトークンを使う理由がありません)、routerは必ず最後です。routerは、リクエストをアップストリームへ送り出し、応答を受け取ってくる終端フィルターなので、その後ろに何かを置くと、永遠に実行されません。Envoyはこの状態を実行中に放置せず、設定を読み込む時点で拒否します。

障害注入(fault)は、2つのことができます。

どちらも、percentageで割合を、headersで条件を付けられます。条件を付けることが核心です。条件なしで有効にすると、すべてのユーザーが実験対象になります。そして割合は、リクエストごとに独立して引く確率であり、「20回中ちょうど10回」ではありません。

ここでよく誤解される点が1つあります。遅くする実験のほうが、切断する実験より重要です。切断されればすぐにわかりますが、遅くなると、接続がたまり、スレッドが塞がれ、そのあとでやっと障害になります。タイムアウトとサーキットブレーカーが正しく設定されているかは、delayでしか表に出ません。

ローカルレート制限(local_ratelimit)は、トークンバケットです。max_tokensの分だけ入り、fill_intervalごとにtokens_per_fillの分だけ補充されます。トークンがなければ429が返ります。重要な性質は、名前にある「ローカル」です。そのプロキシの中だけで数えます。プロキシが10台なら、全体の上限は事実上10倍になります。その代わり、外部に問い合わせないので遅延がなく、そのサービスが死んでも影響がありません。

同じフィルターを場所ごとに変える。フィルターはリスナーに付きますが、設定はルートやバーチャルホスト単位で上書きできます(typed_per_filter_config)。ログインのパスだけ厳しく、参照のパスはゆるく、といった要求が、こうして解決します。グローバルな設定は無効にしておき、ルートでだけ有効にする形もよく見られます。

現場での姿

障害注入を有効にしたまま忘れた場合。条件なしで5%に設定したままのabortが、数週間後に「たまに500が出ます」という報告になって戻ってきます。障害注入は必ず条件を付け、有効にした事実をどこかに記録し、終わったら削除します。

レート制限の上限が実際と違う場合。プロキシの台数を計算に入れないと、上限が意図の何倍にもなります。逆に台数で割っておくと、トラフィックが片方に偏ったときに、不当にブロックされます。統計のenabledとrate_limitedの割合を見て調整します。

レート制限を有効にしたのに、誰もブロックされない場合。2つのつまみを混同していることがほとんどです。filter_enabledは「このリクエストをこのフィルターの対象にするか」で、filter_enforcedは「対象になったリクエストを実際にブロックするか」です。後者を0にすると、トークンは数えますが、ブロックはしません。上限を決める前に、実際のトラフィックで測る観察モードになります。統計には、その数が別に残ります。

フィルターの順序を変えて、性能が悪くなった場合。重いフィルター(外部に問い合わせる認証のようなもの)をチェーンの前のほうに置くと、あとでどうせブロックされるリクエストにも、そのコストを払うことになります。安くて、たくさん弾けるフィルターを前に置くのが基本です。

公式ドキュメント: Fault Injection・Local rate limit・HTTP filters

次のラボですること

終端フィルターを最後でない位置に置くと設定が拒否されるのを最初に見て、条件を付けた中断と固定の遅延を、自分で入れてみます。その後、割合だけで切断して20回中何回当たるかを数え、トークンバケットで429を作った後、同じフィルターをルートごとに変えて設定し、片方だけを厳しくします。最後に、フィルターが残した統計を読みます。