同じアップストリームで方式だけ変えて数える
目標
負荷分散の方式を1つずつ変えながら実際の分配を数え、ハッシュベースの方式の2つの性質(同じキーは同じ所へ・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行を書いてください。 /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行を書いてください。/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は、最も多く受けた数から最も少なく受けた数を引いた値)。/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行書いてください(プレースホルダーはユーザーとポートです)。- ユーザー
u1で4回、u2で4回リクエストして、/root/envd-lb/05-sticky.txtにu1_ports=(受け取ったポート4つを空白区切りで)、u1_distinct=(異なるポートの数)、u2_distinct=の3行を書いてください。 /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行を書いてください。/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行を書いてください。/root/envd-lb/08-report.mdに、even_spread=(ステップ1の3つの値をカンマ区切りで)、heavy_share=(ステップ2で重み2を指定したエンドポイントが受けた割合、パーセントの整数)、sticky=(ステップ5で同じユーザーが1か所に固定されたらyes)、moved_users=(ステップ6の値)の4行を書き、その下に学んだことを4行以上書いてください。
参考
- 分配を数えるすべてのステップは、
--concurrency 1を付けて起動する必要があります。ワーカーが複数あると、順番がワーカーごとに別々に動き、数字が毎回変わります。 - アップストリームは
python3 /opt/lab/envoy/upstream.py <포트> ok(プレースホルダーはポート番号です)で起動します。応答本文がok:<포트> <경로>(プレースホルダーはポート番号とパスです)の形なので、どちらが受けたかを数えられます。 - Envoyを起動し直す前には
pkill -x envoyで片付け、起動は/readyがLIVEを返すまで回るループで待ってください。 - 異なる値の個数は
sort -u | wc -lで数えます。 - よくある間違い:
hash_policyをルートではなくクラスターに書いてしまうことです。キーはリクエストから取り出すので、ルートのrouteの下に書きます。
均等に分けるのがデフォルト
アップストリーム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行以上書いてください。
表を作る目的は、「どの方式をいつ使うか」を、次に自分で選べるようにすることです。値は前のステップのファイルから取り、説明の行には、各方式が何を諦めて何を得るのかを書いてください。