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

Envoyの内部構造

同じアップストリームで方式だけ変えて数える

TT Labで続きを見る

目標

負荷分散の方式を1つずつ変えながら実際の分配を数え、ハッシュベースの方式の2つの性質(同じキーは同じ所へ・1台外してもその1台の取り分だけが移動する)を自分で確認します。

なぜ重要なのか

負荷分散は設定1行ですが、その1行がキャッシュヒット率とセッションの維持、そしてサーバーを外すときの衝撃の大きさを、すべて決めます。文書で読むとどれももっともらしく見えますが、数字を自分で数えてみると、「ランダムは小さな標本でこれだけ偏るのか」「1台外してもこの程度しか移動しないのか」が体に残ります。特に、キーを取り出せなかったリクエストがランダムに流れるという事実は、セッション固定の障害の常連の原因ですが、設定を見ただけでは見えません。

ステップ

  1. アップストリーム3つをokで起動してください(8088・8089・8090)。/root/envd-lb/lb-rr.yamlに、lb_policy: ROUND_ROBINのSTATICクラスターpoolと/ルートを置き、--concurrency 1を付けて起動してください(管理9951、リスナー127.0.0.1:10051)。9回リクエストして、/root/envd-lb/01-rr.txtにp8088=・p8089=・p8090=・total=の4行を書いてください。
  2. /root/envd-lb/lb-rr.yamlを/root/envd-lb/lb-weight.yamlにコピーして、エンドポイントごとにload_balancing_weightを指定してください。8088は2、残りの2つは1です。その設定で起動し直して12回リクエストし、/root/envd-lb/02-weight.txtにp8088=・p8089=・p8090=・total=の4行を書いてください。
  3. /root/envd-lb/lb-random.yamlを作ってください。重みは置かず、lb_policyだけをRANDOMに変えた設定です。その設定で起動して40回リクエストし、/root/envd-lb/03-random.txtにp8088=・p8089=・p8090=・total=・max_gap=の5行を書いてください(max_gapは、最も多く受けた数から最も少なく受けた数を引いた値)。
  4. /root/envd-lb/lb-ring.yamlを作ってください。lb_policyはRING_HASH、ring_hash_lb_config.minimum_ring_sizeは1024、ルートのhash_policyはヘッダーx-userです。その設定で起動してから、u1からu9までの9人のユーザーで、それぞれ1回ずつリクエストし、/root/envd-lb/04-map.txtに사용자 포트の形式で9行書いてください(プレースホルダーはユーザーとポートです)。
  5. ユーザーu1で4回、u2で4回リクエストして、/root/envd-lb/05-sticky.txtにu1_ports=(受け取ったポート4つを空白区切りで)、u1_distinct=(異なるポートの数)、u2_distinct=の3行を書いてください。
  6. /root/envd-lb/lb-ring2.yamlを作ってください。ステップ4の設定からエンドポイント8090だけを外したものです。その設定で起動し直してから、同じ9人のユーザーで再度リクエストし、/root/envd-lb/06-remap.txtに사용자 포트の形式で9行書いてください(プレースホルダーはユーザーとポートです)。その後、ステップ4の表と比べて、/root/envd-lb/06-moved.txtにmoved=(移動したユーザーの数)、stayed=、total=9の3行を書いてください。
  7. /root/envd-lb/lb-query.yamlを作ってください。エンドポイントは再び3つで、hash_policyはヘッダーの代わりにクエリパラメーターuidです。その設定で起動してから、?uid=u1で3回、?uid=u2で3回リクエストし、/root/envd-lb/07-key.txtにu1_distinct=、u2_distinct=、header_ignored=(ヘッダーx-user: u1だけを付けてクエリなしで3回リクエストしたときの、異なるポートの数)の3行を書いてください。
  8. /root/envd-lb/08-report.mdに、even_spread=(ステップ1の3つの値をカンマ区切りで)、heavy_share=(ステップ2で重み2を指定したエンドポイントが受けた割合、パーセントの整数)、sticky=(ステップ5で同じユーザーが1か所に固定されたらyes)、moved_users=(ステップ6の値)の4行を書き、その下に学んだことを4行以上書いてください。

参考

均等に分けるのがデフォルト

アップストリーム3つをokで起動してください(8088・8089・8090)。/root/envd-lb/lb-rr.yamlに、lb_policy: ROUND_ROBINのSTATICクラスターpoolと/ルートを置き、--concurrency 1を付けて起動してください(管理9951、リスナー127.0.0.1:10051)。9回リクエストして、/root/envd-lb/01-rr.txtにp8088=・p8089=・p8090=・total=の4行を書いてください。

ラウンドロビンは、リストを順番に1つずつ選びます。デフォルトで、サーバーのスペックが同じでリクエストのコストが似ているときに、最も予測しやすい方式です。ワーカースレッドごとに自分の順番を別々に記憶するので、デフォルトのconcurrency(コア数)で数えると、9回では3対3対3になりません。このラボでワーカーを1つにする理由が、それです。

スペックの違うサーバーを1つのグループに入れる

/root/envd-lb/lb-rr.yamlを/root/envd-lb/lb-weight.yamlにコピーして、エンドポイントごとにload_balancing_weightを指定してください。8088は2、残りの2つは1です。その設定で起動し直して12回リクエストし、/root/envd-lb/02-weight.txtにp8088=・p8089=・p8090=・total=の4行を書いてください。

サーバーを増やすとき、いつも同じスペックで増えるとは限りません。新しく買った機材が2倍速ければ、2倍を受け持たせるほうがよく、そのときに使うのがエンドポイントの重みです。ラウンドロビンは重みを反映して回りますが、重み2は「2回に1回多く」ではなく「全体に占める取り分が2倍」という意味です。合計が4なので、12回なら6対3対3になります。

均等に見えるが、均等ではない

/root/envd-lb/lb-random.yamlを作ってください。重みは置かず、lb_policyだけをRANDOMに変えた設定です。その設定で起動して40回リクエストし、/root/envd-lb/03-random.txtにp8088=・p8089=・p8090=・total=・max_gap=の5行を書いてください(max_gapは、最も多く受けた数から最も少なく受けた数を引いた値)。

ランダムは、状態をまったく記憶しない方式です。そのため、ワーカーがいくつあっても結果が同じで、エンドポイントが出入りするときに再計算するものもありません。その代わり、標本が小さいと目に見えて偏ります。ラウンドロビンが9回で正確に3対3対3だったことと比べてみてください。リクエストが毎秒数千件ある場所では、この差は消えるので、規模の大きな場所でデフォルトとして使うこともあります。

同じユーザーは常に同じサーバーへ

/root/envd-lb/lb-ring.yamlを作ってください。lb_policyはRING_HASH、ring_hash_lb_config.minimum_ring_sizeは1024、ルートのhash_policyはヘッダーx-userです。その設定で起動してから、u1からu9までの9人のユーザーで、それぞれ1回ずつリクエストし、/root/envd-lb/04-map.txtに사용자 포트の形式で9行書いてください(プレースホルダーはユーザーとポートです)。

ハッシュベースの方式は、リクエストから取り出したキーをハッシュしてリング上の位置を探し、その位置から時計回りに最も近いエンドポイントを選びます。同じキーは常に同じ位置へ行くので、同じユーザーが同じサーバーに固定されます。ローカルキャッシュのヒット率が上がり、サーバーがセッションを持っていても構いません。キーはhash_policyが選びます(ヘッダー・クッキー・クエリパラメーター・送信元IP)。minimum_ring_sizeが小さいと、リングが粗くなって分布が偏ります。

同じキーを4回送っても位置が変わらない

ユーザーu1で4回、u2で4回リクエストして、/root/envd-lb/05-sticky.txtにu1_ports=(受け取ったポート4つを空白区切りで)、u1_distinct=(異なるポートの数)、u2_distinct=の3行を書いてください。

この性質がないと、ローカルキャッシュのあるサービスでは、同じユーザーのリクエストが毎回別のサーバーへ行き、キャッシュがほとんど当たりません。逆に、この性質に頼ると、特定のユーザー1人がサーバー1台を1人で重くしてしまう危険も一緒に生まれます。キーを何にするかが、そのために重要です。異なる値の個数はsort -u | wc -lで数えます。

サーバーを1台外すと、何人が移動するか

/root/envd-lb/lb-ring2.yamlを作ってください。ステップ4の設定からエンドポイント8090だけを外したものです。その設定で起動し直してから、同じ9人のユーザーで再度リクエストし、/root/envd-lb/06-remap.txtに사용자 포트の形式で9行書いてください(プレースホルダーはユーザーとポートです)。その後、ステップ4の表と比べて、/root/envd-lb/06-moved.txtにmoved=(移動したユーザーの数)、stayed=、total=9の3行を書いてください。

これが、ハッシュリングを使う本当の理由です。単純に「ハッシュ値をサーバー数で割った余り」を使うと、サーバー数が変わった瞬間にほぼ全員が移動します。リング方式では、消えたサーバーに割り当てられていたキーだけが移動し、残りはそのままです。移動した人が何人か、自分で数えてみてください。joinやpasteで2つの表を並べて比較すると楽です。

キーをヘッダーからクエリ文字列に変える

/root/envd-lb/lb-query.yamlを作ってください。エンドポイントは再び3つで、hash_policyはヘッダーの代わりにクエリパラメーターuidです。その設定で起動してから、?uid=u1で3回、?uid=u2で3回リクエストし、/root/envd-lb/07-key.txtにu1_distinct=、u2_distinct=、header_ignored=(ヘッダーx-user: u1だけを付けてクエリなしで3回リクエストしたときの、異なるポートの数)の3行を書いてください。

キーを何から取り出すかが、そのまま「何を同じものと見なすか」です。ユーザーセッションならクッキー、テナントならヘッダー、キャッシュキーならクエリパラメーターが自然です。キーを取り出せなければハッシュがないので、Envoyはそのリクエストをランダムに送ります。そのため、キーが抜けたリクエストは固定されません。最後の行は、それを確認するものです。

方式ごとの性質の表にまとめる

/root/envd-lb/08-report.mdに、even_spread=(ステップ1の3つの値をカンマ区切りで)、heavy_share=(ステップ2で重み2を指定したエンドポイントが受けた割合、パーセントの整数)、sticky=(ステップ5で同じユーザーが1か所に固定されたらyes)、moved_users=(ステップ6の値)の4行を書き、その下に学んだことを4行以上書いてください。

表を作る目的は、「どの方式をいつ使うか」を、次に自分で選べるようにすることです。値は前のステップのファイルから取り、説明の行には、各方式が何を諦めて何を得るのかを書いてください。