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

Envoyの内部構造

書いたルートが効かない理由

TT Labで続きを見る

一言でいうと

リクエスト1つが宛先を見つけるまでに、2回選びます。まずHostヘッダーでバーチャルホストを選び、次にそのバーチャルホストのルート表を上から順に調べて最初のマッチを使います。最初の選択は具体性で、後の選択は順序で決まります。この2つが違うことが、この文章のすべてです。

なぜ必要なのか

「ルートを確かに書いたのに掛からない」という報告の半分は、ルートの問題ではありません。バーチャルホストを間違えて選んでいるのです。

ルート表(route_config)1つには、バーチャルホストが複数入ります。各バーチャルホストはdomainsのリストを持ち、リクエストのHost(HTTP/2では:authority)ヘッダーがそのうちの1つに一致して初めて、そのバーチャルホストのルート表を見ます。ここでの照合規則は、ルートとは異なります。ルートは上から順に調べて最初のマッチを使いますが、バーチャルホストは最も具体的なものを使います。

1. 정확한 이름        www.foo.com
2. 접미 와일드카드    *.foo.com   (더 긴 와일드카드가 먼저)
3. 접두 와일드카드    foo.*
4. 특별한 *          아무 이름에나

そのため、*.example.comのバーチャルホストをファイルの一番上に置いても、www.example.comを正確に書いたバーチャルホストが勝ちます。順序を入れ替えながら1日を過ごさないためには、この違いを先に知っておく必要があります。

*は、ルート表全体に1つだけ置けます。2つあるとどちらがより具体的かを決める方法がないため、Envoyは実行中にあいまいなまま放置せず、設定を読み込む時点で拒否します。

どう動くのか

バーチャルホストが決まると、その中のroutesを上から見ます。最初のマッチで止まります。マッチに使えるのはパスだけではありません。

条件 何か
prefix パスの先頭が同じなら
path パス全体が一致する場合だけ
safe_regex 正規表現に一致するなら
headers ヘッダー値の条件(完全一致・存在・前方一致・正規表現)
query_parameters クエリ文字列の条件

1つのマッチの中の条件はすべて満たす必要があります。そのため、「内部のテスターだけ新バージョンへ」のような要件は、prefixとheadersを一緒に書いて作ります。条件に合わなければ、そのルートを飛ばして下へ進み続けます。404が出るのではなく、次のルートが受けます。そのため、条件の付いた狭いルートを上に、広いprefix: "/"を一番下に置きます。

宛先も、クラスター1つだけではありません。

ヘッダーを追加する場所は3か所あります。ルート、バーチャルホスト、ルート表です。上書きではなく、それぞれが追加されます。そのため、同じ名前のレスポンスヘッダーが複数残ることがあります。

現場での姿

重みが比率どおりに出ないという報告。weighted_clustersは確率であって、配分表ではありません。さらにEnvoyのワーカースレッドはそれぞれ状態を持っているので、デフォルトのconcurrency(コア数)でリクエスト40回を数えると、毎回違う結果になります。カナリアの比率を検証するときは、標本を十分に大きくするか、ワーカーを1つに減らします。本番運用では、「比率が合わない」と言う代わりに、統計で確認します。

direct_responseでアプリに触れずに済ませること。ヘルスチェックのパス、メンテナンス案内、ボット遮断の応答のように、アプリケーションが知る必要のないものは、プロキシで完結させるほうがよいです。デプロイなしで直せますし、アプリが落ちていても応答します。

正規表現を乱用して遅くなる場合。prefixとpathは文字列比較ですが、safe_regexは正規表現エンジンを通ります。ルートが数百ある表で、前のほうをすべて正規表現で埋めると、リクエストのたびにそれをすべて実行します。正規表現は、ほかの方法ではできない場所にだけ使います。

公式ドキュメント: HTTP routing・HTTP route components

次のラボですること

バーチャルホストを4つ置いて具体性の順序を自分で確認し、*を2つ置くと設定が拒否されるのを見ます。その後、path・safe_regex・headers・query_parametersで振り分け、重み75対25を40回のリクエストで数え、direct_responseとredirectを付け、ヘッダーが3つの層でそれぞれ追加されることを確認します。