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

Envoyの内部構造

Envoy設定を読む順序

TT Labで続きを見る

一言でいうと

リスナーで受け取り、フィルターチェーンを通し、ルートが選んだクラスターのエンドポイントへ送ります。Envoyの設定は、すべてこの5つの言葉の組み合わせです。

なぜ必要なのか

Envoyの設定JSONは、初めて見ると圧倒されます。しかし構造は単純です。受け取る側と送り出す側が対になっています。

概念 対応するもの 役割
Listener nginxのserver { listen } どのアドレス・ポートで受け取るか
Filter chain nginxのlocationと各モジュール 受け取ったものをどう処理するか
Route nginxのlocationのマッチング どのバックエンドへ送るかを選ぶ
Cluster nginxのupstream バックエンドの集まり(ポリシーを含む)
Endpoint upstreamのserver1行 実際のアドレス:ポート

どう動くのか

リクエスト1つが通る道筋は次のとおりです。

클라이언트
  → Listener (0.0.0.0:15001)
    → Filter chain (TLS 종료 → HTTP 커넥션 매니저)
      → Route (Host: shop.example.com, path /api/* )
        → Cluster (outbound|8080||shop.default.svc.cluster.local)
          → Endpoint (10.244.1.7:8080)   ← 로드밸런싱으로 하나 고름

核心は、クラスターとエンドポイントが分かれていることです。クラスターはポリシー(負荷分散のアルゴリズム、コネクションプールのサイズ、外れ値検出、サーキットブレーカー)を持ち、エンドポイントはその時点の実際のアドレス一覧です。Podが起動したり落ちたりしてもクラスターの定義は変わらず、エンドポイントの一覧だけが変わります。

xDS: 設定が流れ込む通路

Envoyは設定ファイルを読み直して再起動することはありません。コントロールプレーンがgRPCストリームで設定を送り込みます。種類ごとに名前があります。

略称 何を送るか
LDS Listener
RDS Route
CDS Cluster
EDS Endpoint
SDS 証明書(Secret)

Envoyを通る1つのリクエストの順序。リスナーで受け取ってフィルターチェーンで処理し、ルートがバックエンドを選ぶと、クラスターのポリシーを経て1つのエンドポイントに届きます。コントロールプレーンはこの5つの箇所に、それぞれLDS、RDS、CDS、EDSで設定を送り込みます

Istioのistiodがしているのは、まさにこれです。書いたVirtualServiceはRDSに、DestinationRuleはCDSに、サービスのPod一覧はEDSに変換され、各サイドカーに送り込まれます。

そのため、「Istioの設定を直したのに反映されない」ときの調べ方も決まります。サイドカーが実際に受け取った設定を見ます。

istioctl proxy-config route <pod>       # RDS 로 받은 것
istioctl proxy-config cluster <pod>     # CDS
istioctl proxy-config endpoint <pod>    # EDS

VirtualServiceを読み直しても意味がありません。自分が書いたものとプロキシが受け取ったものは違うことがあり、その差こそが原因だからです。

設定が読み解けないときに実際に開く窓

Envoyの設定ダンプは数万行になります。丸ごと読もうとしても何も得られないので、質問を決めて、その部分だけを取り出します。

curl -s localhost:15000/config_dump | jq '.configs[].dynamic_listeners[]?.name'
curl -s localhost:15000/clusters | grep -E "myapi|health"
curl -s localhost:15000/stats | grep -E "upstream_rq_(5xx|pending|timeout)"
curl -s localhost:15000/server_info | jq '.state'

リクエストがどこへ行ったかには、まず統計が答えます。upstream_rq_5xxが増えたか、upstream_cx_connect_failがあるか、pendingが溜まっているかによって、見る場所が分かれます。設定を読むのはその後です。

503には原因がいくつかあり、レスポンスフラグがそれを見分けてくれます。アクセスログに%RESPONSE_FLAGS%を入れておくと、1文字のコードで出力されます。

フラグ 意味
UH そのクラスターに健全な対象が1つもありません
UF アップストリームに接続できませんでした
UO サーキットブレーカーが開いて遮断しました
NR 一致するルートがありません
URX リトライの上限に達しました

この1文字が、「設定が間違っている」のか「対象が死んでいる」のかを分けます。アクセスログにレスポンスフラグがないと、そのクラスターでは503を前に延々と推測を続けることになります。

サーキットブレーカーのデフォルト値はたいてい低すぎます。max_pending_requestsが1024なのに同時リクエストがそれより多いと、アップストリームは問題ないのにEnvoyが遮断します。UOフラグが見えたら、ここを疑います。

設定が変わっていないように見えたら、xDSを見ます。/statsの*.update_rejectedが増えているなら、コントロールプレーンが送った設定をEnvoyが拒否したということです。このときEnvoyは最後に成功した設定をそのまま維持するので、見かけ上は何も起きていません。このメトリクスにアラートを設定しておくと、実効性が高いです。

よくある勘違い

「サイドカーがリクエストを遅くする」と言われますが、Envoy自体のレイテンシは通常ミリ秒以下です。体感レイテンシの大半は、コネクションプールの設定、mTLSハンドシェイク、そしてリトライの設定に由来します。特にリトライは、障害時に負荷を何倍にも膨らませます。

クラスター名がランダムに見えるのは誤解です。outbound|8080||shop.default.svc.cluster.localは規則に従った名前で、形式は방향|포트|서브셋|호스트です(プレースホルダーは方向、ポート、サブセット、ホストです)。サブセットが空なら、DestinationRuleのsubsetを使わないという意味です。この規則を知っていれば、設定ダンプから目的の行をすぐ見つけられます。

実務で本当に大切なこと

Envoyの統計名にも規則があり、それが障害調査の地図になります。

cluster.<클러스터이름>.upstream_rq_5xx          백엔드가 낸 5xx
cluster.<클러스터이름>.upstream_rq_pending_overflow   커넥션 풀이 넘쳤다
cluster.<클러스터이름>.outlier_detection.ejections_active  쫓겨난 엔드포인트 수
listener.<주소>.downstream_cx_total             들어온 연결 수

upstream_rq_pending_overflowが増えているなら、アプリケーションが遅いのではなく、コネクションプールが小さいのです。この2つは対処が正反対です。