ロードバランサーの振り分けとヘルスチェック
どの単位で処理先が決まり、変更後にどの接続が残るかを確認して、切離し時の操作を説明します。
説明
ロードバランサーの振分けでは、接続を単位にするか、HTTP要求の内容を使うかで扱いが変わります。さらに、処理先が不健康になった場合や保守で切り離す場合は、新しい要求と既存接続を分けて考える必要があります。
L4では一度選んだ接続先を保持し、L7では/apiと/assets/の要求を別のグループへ送る固定モデルを使います。a・bの処理先を使って健康状態、draining、cookieによる固定を順に変えます。利用可能な対象がない場合は転送しない方針です。
どの単位で処理先が決まり、変更後にどの接続が残るかを確認して、切離し時の操作を説明します。
1L4では同じ接続を同じ処理先へ送る
L4はTCPなどの接続単位を扱うため、このモデルではpathの内容を振分けに使いません。c1で異なるpathを送ります。
from balance import Balancer
l4 = Balancer("L4")接続単位の振分けを用意する
l4.request("c1", "/api/orders")c1の最初の転送先を選ぶ
l4.request("c1", "/assets/logo.svg")同じ接続の異なる要求を送る両方ともaです。2回目はexisting connectionで、HTTPのpathが変わっても同じ接続先を使っています。
2L7では要求のpathからグループを選ぶ
L7の固定規則では/apiはa・b、/assets/はstaticへ送ります。同じフロント側の接続識別子で要求します。
l7 = Balancer("L7")要求内容による振分けを用意する
l7.request("c1", "/api/orders")API用グループへ送る
l7.request("c1", "/assets/logo.svg")静的ファイル用グループへ送る/apiはa、/assets/はstaticへ送られます。HTTPを解釈する位置で、要求内容を使った振分けが可能になります。
3別のL4接続は別の処理先になる
新しい接続c2で要求します。選択は固定のラウンドロビンです。
l4.request("c2", "/api/orders")新しい接続の転送先を選ぶ
print(l4.connections接続と処理先の対応を読む)c2はbになり、接続表にはc1→a、c2→bが残ります。アプリが接続を長く再利用する場合、要求数がそのまま均等に分かれるとは限りません。
4異常なサーバーを新規接続の振り分け先から外す
bのhealthをFalseにして、新しいc3と既存のc2を使います。
l4.health["b"] = Falsebを新規選択の候補から外す
l4.request("c3", "/api/orders")新規接続の選択を確認する
l4.request("c2", "/api/orders")既存接続が自動移動しないことを確認する新しいc3はaへ送られますが、c2はbとの接続なのでconnection_failedです。別の接続先へ、新しい接続としてやり直す処理が必要になります。
5drainingでは既存接続を残す
aをdrainingへ入れます。新しい接続を増やさず、処理中の接続を終えるための状態です。
l4.draining.add("a")aへの新規接続を止める
l4.request("c4", "/api/orders")新しい接続先が残るか確認する
l4.request("c1", "/api/orders")既存接続の継続を確認する新しいc4には候補がありませんが、既存のc1はaへ届きます。drainingへ設定した時点で、すべての接続が終了したわけではありません。
6復旧した対象へ新しい接続を送る
bを健康状態へ戻してc4を要求します。
l4.health["b"] = Truebを利用可能な候補へ戻す
l4.request("c4", "/api/orders")新しい接続を復旧先へ送るc4はbへ送られます。aをdrainingにしたままでも、別の利用可能な対象があれば新しい接続を受け付けられます。
7古い接続を閉じてから再接続する
c1を閉じ、同じクライアントの新しい接続として再度要求します。
l4.close_connection("c1")aへ向いていた接続を閉じる
l4.request("c1", "/api/orders")新規接続として選び直す今度はbが選ばれます。既存接続の継続と新規選択が別なので、接続の終了・再接続まで含めて保守の完了を確認します。
8cookieによる固定と処理先の除外
L7でbを指すcookieを使い、その後bを不健康にして同じcookieを送ります。cookieはこの例では対象名を記録した固定値です。
l7.request("c5", "/api/orders", cookie="b")cookieで利用可能なbを選ぶ
l7.health["b"] = False固定先のbを候補から外す
l7.request("c6", "/api/orders", cookie="b")同じcookieの扱いを再確認する最初はsticky cookieでb、除外後はaが選ばれます。固定先を使えなくなったときに、別の処理先でもセッション状態を扱える設計が必要です。
まとめ
接続単位とHTTP要求単位では、振分けを選び直すタイミングが異なります。
新規の振分け除外と、既存接続の終了を別々に確認します。
cookieによる固定でも処理先変更は起こり得るため、状態保持を別に設計します。
操作履歴と解説を振り返れます。
Python 3
from balance import Balancer
l4 = Balancer("L4")
l4.request("c1", "/api/orders")
l4.request("c1", "/assets/logo.svg")L4 connection=c1 path=/api/orders target=a reason=new selection result=delivered L4 connection=c1 path=/assets/logo.svg target=a reason=existing connection result=delivered
l7 = Balancer("L7")
l7.request("c1", "/api/orders")
l7.request("c1", "/assets/logo.svg")L7 connection=c1 path=/api/orders target=a reason=new selection result=delivered L7 connection=c1 path=/assets/logo.svg target=static reason=new selection result=delivered
l4.request("c2", "/api/orders")
print(l4.connections)L4 connection=c2 path=/api/orders target=b reason=new selection result=delivered
{'c1': 'a', 'c2': 'b'}l4.health["b"] = False
l4.request("c3", "/api/orders")
l4.request("c2", "/api/orders")L4 connection=c3 path=/api/orders target=a reason=new selection result=delivered L4 connection=c2 path=/api/orders target=b reason=existing connection result=connection_failed
l4.draining.add("a")
l4.request("c4", "/api/orders")
l4.request("c1", "/api/orders")no eligible target L4 connection=c1 path=/api/orders target=a reason=existing connection result=delivered
l4.health["b"] = True
l4.request("c4", "/api/orders")L4 connection=c4 path=/api/orders target=b reason=new selection result=delivered
l4.close_connection("c1")
l4.request("c1", "/api/orders")L4 connection=c1 path=/api/orders target=b reason=new selection result=delivered
l7.request("c5", "/api/orders", cookie="b")
l7.health["b"] = False
l7.request("c6", "/api/orders", cookie="b")L7 connection=c5 path=/api/orders target=b reason=sticky cookie result=delivered L7 connection=c6 path=/api/orders target=a reason=new selection result=delivered
演習完了