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

Envoyの内部構造

Envoyを自分で立ち上げて壊す

TT Labで続きを見る

目標

Envoyを自分で起動して、ルーティング・タイムアウト・リトライ・外れ値検出を1つずつ作っては壊してみます。IstioのデータプレーンがまさにこのEnvoyなので、ここで学ぶことはそのままメッシュの障害対応に役立ちます。

環境

envoy --version
curl -s localhost:9901/stats     # admin (띄운 뒤)

開始用の設定が/opt/lab/envoy/minimal.yamlにあります。コピーして書き換えてください。

アップストリームを作る

python3 -m http.server 8081 &          # 정상

失敗するアップストリームや遅いアップストリームが必要なら、/opt/lab/envoy/upstream.pyを使ってください。

python3 /opt/lab/envoy/upstream.py 8082 fail &   # 항상 503
python3 /opt/lab/envoy/upstream.py 8083 slow &   # 3초 걸림

起動と再起動

setsid --fork nohup envoy -c e.yaml --log-level warn > envoy.log 2>&1 </dev/null
# 고친 뒤에는
pkill -f 'envoy -c'
setsid --fork nohup envoy -c e.yaml --log-level warn > envoy.log 2>&1 </dev/null

setsid --forkを必ず付けてください。ただの&で起動すると、シェルが切り替わるときに一緒に終了します。--forkなしでsetsid nohup … &と書いて起動すると、対話シェルでは生き残りますが、採点やステップの準備のようにシェルが短時間だけ接続して切れる経路では、そのシェルが終わるときに一緒に終了します。--forkはもう一度分岐してPID 1にぶら下がるため、どちらの経路でも生き続けます。採点は動いているEnvoyのadminを見るので、止まっていると、ステップ1から失敗します。

設定が間違っていると、Envoyは起動せずに終了します。理由はenvoy.logの1行目にあります。

Envoyが一度に起動できるのは1つだけ

ポートを変えても、2つ目は次のように終了します。

unable to bind domain socket with base_id=0, errno=98 (see --base-id option)

ポートの問題ではなく、共有メモリのドメインソケットが重なっているのです。設定を2つ比較するときは、pkill -f 'envoy -c'で停止してから起動し直してください(または--base-id 1)。

アクセスログが見えないとき

よくある原因は2つあります。

読む順序

Envoyの設定は、この順序で読むと迷いません。

listener → filter chain → http_connection_manager → route_config → cluster

ステップ

  1. 最小構成で起動する → 01-boot.txt
  2. adminで内部をのぞく → 02-admin.txt
  3. ルートの順序 → 03-route.txt
  4. タイムアウト → 04-timeout.txt
  5. リトライ → 05-retry.txt
  6. 外れ値検出 → 06-outlier.txt
  7. レスポンスフラグ → 07-flags.txt
  8. 整理 → 08-notes.md

最小構成で起動する

リスナー1つ・クラスター1つのEnvoyを起動し、リクエストがアップストリームまで届くことを確認して、01-boot.txtに残してください。

/opt/lab/envoy/minimal.yamlをコピーして書き換えてください。起動するときは必ず付けるオプションがあります(setsid --fork)。ただの&で起動するとシェルが切り替わるときに一緒に終了し、採点で失敗します。--forkがあれば、もう一度分岐してPID 1にぶら下がるので、シェルが終わっても生き残ります。

setsid --fork nohup envoy -c e.yaml --log-level warn > envoy.log 2>&1 </dev/null

確認: curl -s localhost:10000/。Envoyの設定は、listener → filter chain → route → clusterの順に読むと迷いません。

adminで内部をのぞく

adminインターフェース(9901)からクラスター一覧とconfig_dumpのセクション名を取り出して、02-admin.txtに残してください。

curl -s localhost:9901/clusters、curl -s localhost:9901/config_dump | jq -r '.configs[]."@type"'。本番運用で「Envoyが自分の設定を本当に受け取ったか」を確認する唯一の方法がconfig_dumpです。ファイルに書いたものとEnvoyが保持しているものは、異なることがあります。

先に書いたルートが勝つ

/apiと/の2つのルートを別々のクラスターへ送り、順序を入れ替えると同じリクエストの結果が変わることを示してください。03-route.txtに、before= / after=の2行で、同じリクエストの応答を残します。

Envoyはルートを上から順に調べ、最初に一致したところで止まります。prefix: "/"を上に置くと、その下はすべて無効になります。同じ/api/xリクエストを2通りの順序でそれぞれ送り、次のように残してください(使った設定も一緒に)。

before=api:8084 /api/x
after=ok:8081 /api/x

実務での「ルートを追加したのに効かない」の大半は、これが原因です。

遅いアップストリームを切断する

3秒かかるアップストリームに1秒のルートタイムアウトを設定して504を発生させ、アクセスログのレスポンスフラグと一緒に04-timeout.txtに残してください。

ルートにtimeout: 1sを設定します。アクセスログのフォーマットに%RESPONSE_FLAGS%を入れると、UT(Upstream Timeout)が出力されます。かかった時間がちょうど1000msかを確認してください。それがEnvoyが切断した証拠です。

ログがすぐに見えなければ、10秒ほど待ってください。Envoyはファイルのアクセスログをまとめて書き込みます。リクエストの直後にgrepしても、まだ出力されていません。

リトライが実際に何回送られたか

常に503を返すアップストリームにnum_retries: 3のリトライを設定し、統計でリトライ回数を確認して05-retry.txtに残してください。

retry_policy: {retry_on: "5xx", num_retries: 3}を設定します。確認はcurl -s localhost:9901/stats | grep upstream_rq_retryで行います。アクセスログにはURX(リトライの使い切り)が出力されます。リトライは無料ではありません。アップストリームがすでに落ちかけているなら、リトライが負荷を4倍にします。

故障したエンドポイントを外す

エンドポイントが2つのクラスターで、1つだけが503を返すようにし、外れ値検出によってその1つが外れることを統計で示して、06-outlier.txtに残してください。

outlier_detection: {consecutive_5xx: 2, interval: 1s, base_ejection_time: 30s}を設定します。20回ほどリクエストした後で、curl -s localhost:9901/stats | grep -E 'ejections_active|membership_healthy'を実行します。正しく動けば、20回中18回ほどが成功します。最初の2回で故障した側を見つけた後は、二度とそちらへ送らないからです。

レスポンスフラグを読む

これまでに作った失敗のアクセスログを集めて、07-flags.txtに残してください。異なるフラグが最低2種類必要です。

UTはアップストリームのタイムアウト、URXはリトライの使い切り、UFは接続失敗、NRは一致するルートなし、UHは正常なアップストリームなしです。

集めるときは2点に注意してください。ログはまとめて書き込まれるため10秒ほど待つ必要があり、Envoyを再起動するとログファイルが空になります。失敗を作るたびに、その場その場で追記するほうが安全です。

このフラグを読めることが、EnvoyとIstioの障害対応で最も長く役立つ技術です。5xxを見てアプリを調べる前に、まずフラグを見ます。

3つのことを整理する

08-notes.mdに、次の3つを3行以上で書いてください。ルートが効かないときに最初に見るもの、リトライが危険になる場合、UTとURXの違いです。

本文に순서、재시도、플래그が含まれている必要があります(韓国語で、順に「順序」「リトライ」「フラグ」を意味する語です)。