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

Envoyの内部構造

配布前にふるい、二つ目のプロキシを立てる

TT Labで続きを見る

目標

ブートストラップを手で書き、デプロイ前に設定だけを検査し、1台のマシンに2つ目のEnvoyを起動します。

なぜ重要なのか

設定が間違ったプロキシは、再起動した瞬間に問題が表に出ます。そのときはすでに古いプロセスが落ちているので、元に戻す時間がありません。--mode validateはポートを確保せずに設定だけを読み込み、この事故をデプロイ前に移してくれます。ブートストラップの層構造をたどれるようになれば、他人が書いた設定も数秒で読めますし、--base-idと管理ポートの危険は、一度経験しておかないと必ず本番で初めて遭遇します。

ステップ

  1. /root/envd-boot/boot.yamlにブートストラップを書いてください。管理ポートは9911、リスナーedgeは127.0.0.1:10011、stat_prefixはedge、クラスターoriginは127.0.0.1:8081です。envoy --mode validate -c /root/envd-boot/boot.yamlを実行し、その出力と終了コードを/root/envd-boot/01-validate.txtに保存してください。最後の行はrc=0である必要があります。
  2. /root/envd-boot/boot.yamlを/root/envd-boot/boot-typo.yamlにコピーしてから、HttpConnectionManagerをHttpConnectionMangerに1文字削ってください。envoy --mode validateで検査し、出力と終了コードを/root/envd-boot/02-typo.txtに保存してください(最後の行はrc=1)。
  3. /root/envd-boot/boot.yamlにnodeを追加してください。idはedge-1、clusterはenvd-edgeです。アップストリームを8081で起動してからEnvoyを起動し、管理ポートの/config_dumpからブートストラップの節のnodeだけを取り出して、/root/envd-boot/03-node.jsonに保存してください。
  4. /root/envd-boot/boot.yamlをyqで読み、5つの値を/root/envd-boot/04-layers.txtに열쇠=값形式で5行書いてください(プレースホルダーはキーと値です)。キーはlistener=(リスナー名)、filter=(ネットワークフィルター名)、stat_prefix=、route_cluster=(ルートが指すクラスター)、cluster_endpoint=(そのクラスターのエンドポイント)です。エンドポイントは주소:포트形式で書いてください(プレースホルダーはアドレスとポートです)。
  5. /root/envd-boot/boot.yamlのポートだけを変えたコピー/root/envd-boot/boot-second.yamlを作ってください(管理9912、リスナー10012)。まずオプションなしで起動して失敗を確認し、次に--base-id 7を付けて成功させてください。/root/envd-boot/05-baseid.txtに、without_base_id_rc=(0以外の値)、失敗の理由が入った行、with_base_id_ready=(2つ目のEnvoyの/readyの応答)を書いてください。
  6. Envoyを--concurrency 1 --log-level infoで起動し直し、管理ポートの/server_infoからcommand_line_optionsだけを取り出して/root/envd-boot/06-cli.jsonに保存してください。concurrencyが1、log_levelがinfoである必要があります。
  7. 管理ポートに/quitquitquitをPOSTしてEnvoyを終了させ、/root/envd-boot/07-quit.txtにafter_quit=(その後の/readyのHTTPコード)を書いてください。次にもう一度起動し、after_restart=(/readyの応答文字列)を追記してください。最後に、Envoyが9911で動いている必要があります。
  8. /root/envd-boot/08-report.mdにvalidate_rc=、typo_rc=、second_envoy=、admin_bind=の4行を書き(それぞれステップ1・ステップ2の終了コード、2つ目のEnvoyに必要だったオプション名、管理ポートをバインドすべきアドレス)、その下に学んだことを4行以上で書いてください。

参考

デプロイ前に設定だけを先に検査する

/root/envd-boot/boot.yamlにブートストラップを書いてください。管理ポートは9911、リスナーedgeは127.0.0.1:10011、stat_prefixはedge、クラスターoriginは127.0.0.1:8081です。envoy --mode validate -c /root/envd-boot/boot.yamlを実行し、その出力と終了コードを/root/envd-boot/01-validate.txtに保存してください。最後の行はrc=0である必要があります。

--mode validateは設定を読んでスキーマまで確認し、ポートを1つも確保しないまま終了します。そのため、すでにEnvoyが動いているマシンでも、CIでも実行できます。終了コードを別に書き残す必要があるのは、出力だけを見ると人間が勘違いするからです。パイプを通すと$?は最後のコマンドのものになるので、パイプなしで受けて書き留めてください。

名前を1文字間違えるとプロセスがまったく起動しない

/root/envd-boot/boot.yamlを/root/envd-boot/boot-typo.yamlにコピーしてから、HttpConnectionManagerをHttpConnectionMangerに1文字削ってください。envoy --mode validateで検査し、出力と終了コードを/root/envd-boot/02-typo.txtに保存してください(最後の行はrc=1)。

@typeはコメントではなく、どのプロトコルバッファメッセージとして解釈するかを選ぶキーです。名前が一覧にないと、Envoyはそのフィルターを解釈する方法がなく、設定をまるごと拒否します。本番運用では、この誤字が再起動した瞬間に表に出ますが、そのときはすでに古いプロセスが落ちた後です。

このプロキシが何者かを設定に書く

/root/envd-boot/boot.yamlにnodeを追加してください。idはedge-1、clusterはenvd-edgeです。アップストリームを8081で起動してからEnvoyを起動し、管理ポートの/config_dumpからブートストラップの節のnodeだけを取り出して、/root/envd-boot/03-node.jsonに保存してください。

nodeは、このプロキシがコントロールプレーンに自分を紹介するラベルです。静的設定だけなら、なくても起動しますが、xDSをつなぐと、サーバーはこの値でどのプロキシにどの設定を渡すかを選びます。/config_dumpの最初の節がBootstrapConfigDumpで、その中にbootstrap.nodeがあります。jqでその部分だけを取り出してください。

5つの層をパスでたどる

/root/envd-boot/boot.yamlをyqで読み、5つの値を/root/envd-boot/04-layers.txtに열쇠=값形式で5行書いてください(プレースホルダーはキーと値です)。キーはlistener=(リスナー名)、filter=(ネットワークフィルター名)、stat_prefix=、route_cluster=(ルートが指すクラスター)、cluster_endpoint=(そのクラスターのエンドポイント)です。エンドポイントは주소:포트形式で書いてください(プレースホルダーはアドレスとポートです)。

Envoyの設定を読む順序は、いつも同じです。listener → filter_chain → http_connection_manager → route_config → cluster。この5つの位置を指でたどれるようになれば、初めて見る設定でも道に迷いません。yq '.static_resources.listeners[0].name'のように、1層ずつ下りてみてください。値を目で書き写さず、ツールで取り出すと間違えません。

2つ目のEnvoyが起動しない

/root/envd-boot/boot.yamlのポートだけを変えたコピー/root/envd-boot/boot-second.yamlを作ってください(管理9912、リスナー10012)。まずオプションなしで起動して失敗を確認し、次に--base-id 7を付けて成功させてください。/root/envd-boot/05-baseid.txtに、without_base_id_rc=(0以外の値)、失敗の理由が入った行、with_base_id_ready=(2つ目のEnvoyの/readyの応答)を書いてください。

Envoyは起動するときに共有メモリ領域を1つ確保します(ホットリスタートのときに統計を引き継ぐための場所です)。その領域の名前はbase idで決まり、デフォルト値は0なので、同じマシン上の2つ目のプロセスは、ポートをすべて避けてもその場所でぶつかります。失敗ログの1行目がそのまま教えてくれます。失敗したほうはすぐに終了するので、setsidなしでそのまま実行して終了コードを受け取れば十分です。

コマンドラインオプションの記録先を確認する

Envoyを--concurrency 1 --log-level infoで起動し直し、管理ポートの/server_infoからcommand_line_optionsだけを取り出して/root/envd-boot/06-cli.jsonに保存してください。concurrencyが1、log_levelがinfoである必要があります。

設定ファイルにないものが動作を変える場所が、まさにコマンドラインです。--concurrencyはワーカースレッド数で、デフォルト値がコア数なので、ワーカーごとに負荷分散の状態が別々に動きます。分配を数える実験がぶれる最も一般的な原因です。実際に何が適用されたかは、推測せずに/server_infoから読んでください。jq '.command_line_options'でその部分だけを取り出します。

管理ポートは落とすボタンまで持っている

管理ポートに/quitquitquitをPOSTしてEnvoyを終了させ、/root/envd-boot/07-quit.txtにafter_quit=(その後の/readyのHTTPコード)を書いてください。次にもう一度起動し、after_restart=(/readyの応答文字列)を追記してください。最後に、Envoyが9911で動いている必要があります。

管理ポートには統計だけがあるわけではありません。/quitquitquitはプロセスを終了させ、/drain_listenersはトラフィックを切断します。認証がないので、このポートに届く人は誰でもプロキシを落とせます。だからadmin.addressを0.0.0.0にしてはいけません。終了したサーバーにcurlすると接続自体ができないので、HTTPコードは000になります。

デプロイ前の点検表にまとめる

/root/envd-boot/08-report.mdにvalidate_rc=、typo_rc=、second_envoy=、admin_bind=の4行を書き(それぞれステップ1・ステップ2の終了コード、2つ目のEnvoyに必要だったオプション名、管理ポートをバインドすべきアドレス)、その下に学んだことを4行以上で書いてください。

点検表は他人が読む文章です。値を書くときはどこから出た値かがわかるように書き、説明の行には「何をした」ではなく「だから次からは何をする」を書いてください。second_envoy=にはオプション名をそのまま書きます。