バックエンドサーバーが削除された後、または異常と宣言された後、バックエンドサーバーへの既存の接続は一定期間開いたままになります。 リクエストは引き続きバックエンドサーバーに転送できます。 この場合、リクエストエラーがトリガーされるか、バックエンドサーバー上のサービスをアンデプロイできません。 このようなエラーを防ぐには、Application Load Balancer (ALB) インスタンスの接続ドレインを有効にします。 バックエンドサーバーが削除された場合、または異常と宣言された場合、データ送信は、接続の排出タイムアウト期間が終了するまで維持されます。 接続ドレインタイムアウト期間が終了すると、接続はプロアクティブに閉じます。 これにより、サービスの正常なアンデプロイが保証されます。
シナリオ
接続のドレインは、次のシナリオに最適です。
バックエンドサーバーの削除: バックエンドサーバーを削除する前に、サーバーが既存のリクエストを処理する時間を確保するために、長い接続ドレインタイムアウト期間を指定することを推奨します。
異常なバックエンドサーバー: エラーがクライアントに返される前に障害のある接続を閉じることができるように、短いヘルスチェックタイムアウト期間を指定することを推奨します。
上記のシナリオで接続ドレインを使用するには、ビジネス要件に基づいて適切な接続ドレインタイムアウト期間を指定する必要があります。
シナリオ1: バックエンドサーバーの削除
次の図は、この例のシナリオを示しています。 ECS01を削除するとき。 ALBは、既存のリクエストのみを処理し、新しいリクエストを受け付けないECS01にリクエストを配信しなくなりました。
接続のドレインを無効にすると、ECS01は既存のすべてのリクエストが処理されたときにセッションを閉じます。
接続ドレインを有効にし、接続ドレインタイムアウト期間を指定する場合:
ECS01に進行中の要求がある場合、ALBは、接続排出タイムアウト期間が終了すると、ECS01上のセッションを閉じます。
ECS01に進行中の要求またはアクティブな接続がない場合、ALBは、接続の排出タイムアウト期間が終了するかどうかに関係なく、ECS01を直ちに削除します。
ECS01に送信中のリクエストがある場合、ECS01が削除されたときに接続が閉じられ、500エラーコードがクライアントに返されます。 たとえば、接続のドレインタイムアウト期間を15秒に指定しても、ECS01がリクエストを処理するのに30秒かかる場合、ECS01がすべての応答を送信する前に接続が閉じられます。 その結果、クライアントは500エラーコードを受信する。
説明ECS01が削除されても再追加された場合、接続ドレインタイムアウト期間が終了する前に既存のセッションは影響を受けません。 接続ドレインタイムアウト期間は、ECS01が削除されたときに始まります。 ECS01のステータスは、既存のセッションが終了する前に変更されません。 このプロセス中、ECS01は既存のリクエストのみを処理し、新しいリクエストを受け付けません。 既存のセッションは、接続排出タイムアウト期間が終了すると閉じられます。
ECS01を削除し、接続ドレインを有効にし、接続ドレインタイムアウト期間を指定します。 ECS01のステータス変更を次の図に示します。
シナリオ2: 異常なバックエンドサーバー
ECS01がヘルスチェックによって異常であると宣言された場合、ALBはECS01にリクエストを配信しなくなります。 この場合、ECS01は既存の要求を処理できますが、新しい要求を受け入れません。
接続のドレインを無効にすると、ECS01が再び正常と宣言されるまで、ECS01は新しい要求を受け入れません。
接続ドレインを有効にし、接続ドレインタイムアウト期間を指定する場合:
接続排出タイムアウト期間が終了すると、ALBはECS01の既存のセッションを閉じます。
ECS01の構成更新など、サーバグループが更新された場合、ECS01の接続状態は変更されません。 ECS01は既存のリクエストを処理できますが、新しいリクエストは受け付けません。 ECS01が正常であると宣言された場合でも、接続ドレインタイムアウト期間が終了すると、ALBはECS01の既存のセッションを閉じます。
説明接続排出タイムアウト期間が終了すると、ALBはECS01の既存のセッションを閉じます。 ECS01が正常であれば、ECS01は新しい要求を受け入れることができる。 ECS01が異常である場合、ECS01は新しい要求を受け入れません。
設定の更新中にバックエンドサーバーが異常であると宣言された場合、接続のドレインはトリガーされません。 接続のドレインは、サービスエラーのためにバックエンドサーバーが異常であると宣言された場合にのみトリガーされます。
次の図は、異常と宣言されたときのECS01のステータスの変化を示しています。
ビジネス要件に基づいて、適切な接続ドレインタイムアウト期間を指定します。 この例では、シナリオ1: バックエンドサーバーの削除を使用して、WebSocketセッションとHTTPセッションの接続ドレインを有効にする方法を示します。
注意事項
標準およびWAF対応のALBインスタンスのみが接続ドレインをサポートしています。 基本ALBインスタンスは接続のドレインをサポートしていません。
Function Computeタイプのサーバーグループは、接続のドレインをサポートしていません。
接続のドレインを有効にするには、WebSocketプロトコルを使用することを推奨します。 HTTPシナリオでは、HTTPリクエストがタイムアウトまたは制限される場合があります。 このような問題を防ぐために、接続の排出タイムアウト期間をALB接続要求のタイムアウト期間よりも大きい値に設定することを推奨します。 デフォルトの接続排出タイムアウト時間は、HTTPリクエストが誤ってクローズされないように、接続リクエストのタイムアウト時間よりも長くなっています。 接続リクエストのタイムアウト期間を設定する方法の詳細については、「HTTPリスナーの追加」をご参照ください。
前提条件
標準またはWAF対応のALBインスタンスが作成され、そのALBインスタンス用にサーバータイプのサーバーグループが作成されます。 この例では、標準ALBインスタンスが使用されています。 詳細については、「ALBインスタンスの作成」および「サーバーグループの作成と管理」をご参照ください。
ALBインスタンス用にポート
80
を使用するHTTPリスナーが作成され、リスナーはサーバーグループに関連付けられています。 詳細については、「HTTPリスナーの追加」をご参照ください。ECS01とECS02が作成されます。 インスタンスの作成方法については、「ウィザードを使用したインスタンスの作成」をご参照ください。
ECS02がサーバーグループに追加され、クライアントからECS02上のサービスにアクセスできます。 詳細については、「ALBインスタンスを使用してIPv4サービスを提供する」および「ALBを使用してIPv6サービスの負荷を分散する」をご参照ください。
説明この例では、64ビットAlibaba Cloud Linux 3.2104オペレーティングシステムを実行するクライアントが使用されています。 PythonがクライアントのオペレーティングシステムとECS01にインストールされていることを確認してください。 Pythonのインストール方法の詳細については、「Pythonのダウンロード」をご参照ください。 この例では、Python3.xが使用されます。
この例では、サービスはECS02で実行されます。 サービスを実行できるバックエンドサーバーがある場合は、ECS02を作成する必要はありません。
データ同期タスクの設定
この手順では、接続ドレインを使用してWebSocketセッションとHTTPセッションをドレインする方法と、異なる接続ドレイン状態でALBによって要求が処理される方法を示します。
WebSocketセッションの接続ドレイン
ステップ1: 接続のドレインを有効にする
この例では、サーバグループが既に用意されている。 接続のドレインは、サーバーグループを変更することで構成されます。 サーバーグループがない場合は、サーバーグループを作成するときに接続のドレインを有効にできます。
ALBコンソールにログインします。
上部のナビゲーションバーで、サーバーグループがデプロイされているリージョンを選択します。
左側のナビゲーションウィンドウで、
を選択します。[サーバーグループ] ページで、管理するサーバーグループのIDをクリックします。
[詳細] タブで、[基本情報] セクションの [基本情報の変更] をクリックします。
[基本情報の変更] ダイアログボックスで、[詳細設定] をクリックし、[接続ドレイン] をオンにします。
接続ドレインタイムアウトを300秒に設定し、[保存] をクリックします。
ステップ2: Verify the result
バックエンドサーバーでの接続ドレインの設定
リモートでECS01にログオンします。 詳細については、「接続方法の概要」をご参照ください。
次のコマンドを実行してWebSocketファイルを作成し、WebSocketディレクトリを開きます。
mkdir WebSocket cd WebSocket
次のコマンドを実行して、依存関係パッケージをインストールします。
pip install tornado pip install websocket-client
次のコマンドを実行して、server.py設定ファイルを変更します。
vim server.py
I
キーを押してエディターを開き、次のパラメーターを設定してWebSocketサービスを有効にします。#!/usr/bin/env python3 # encoding=utf-8 import tornado.websocket import tornado.ioloop import tornado.web from datetime import datetime # The WebSocket handler class WebSocketHandler(tornado.websocket.WebSocketHandler): def open(self): current_time = datetime.now() formatted_time = current_time.strftime("%Y-%m-%d %H:%M:%S") print("Time:", formatted_time, "The WebSocket connection is established") def on_message(self, message): current_time = datetime.now() formatted_time = current_time.strftime("%Y-%m-%d %H:%M:%S") print("Time:", formatted_time, "Received a message:", message) self.write_message("The server received your message:" + message) def on_close(self): current_time = datetime.now() formatted_time = current_time.strftime("%Y-%m-%d %H:%M:%S") print("Time:", formatted_time, "The WebSocket connection is closed") # Routes application = tornado.web.Application([ (r"/websocket", WebSocketHandler), ]) if __name__ == "__main__": print("WebSocket Server Start on 8080 ...") application.listen(8080) tornado.ioloop.IOLoop.current().start()
変更が完了したら、
Esc
キーを押して:wq
と入力し、enterキーを押して設定ファイルを保存して閉じます。
server.pyファイルのディレクトリで、次のコマンドを実行してWebSocketサービスを有効にします。
python3 server.py
次の応答メッセージは、WebSocketサービスが有効になっていることを示します。
Websocket Server Start on 8080 ...
サーバーグループへのECS01の追加
ALBコンソールにログインします。
上部のナビゲーションバーで、サーバーグループがデプロイされているリージョンを選択します。
左側のナビゲーションウィンドウで、
を選択します。[サーバーグループ] ページで、管理するサーバーグループを見つけ、[操作] 列の [バックエンドサーバーの変更] をクリックします。
[バックエンドサーバー] タブで、[バックエンドサーバーの追加] をクリックし、[サーバーの選択] で [ECS01] を選択し、[次へ] をクリックします。
[ポート /重み] ステップで、[ECS01] を選択し、ポートを [
8080
] に設定して、[OK] をクリックします。
クライアントでの接続ドレインの設定
クライアントにログインし、コマンドラインインターフェイス (CLI) を開きます。 次のコマンドを実行してWebSocketファイルを作成し、WebSocketディレクトリを開きます。
mkdir WebSocket cd WebSocket
次のコマンドを実行して、依存関係パッケージをインストールします。
mkdir WebSocket cd WebSocket
次のコマンドを実行して、client.pyファイルを変更します。
vim client.py
I
キーを押してエディターを開き、次のパラメーターを設定してWebSocketクライアントのアクセスサービスを有効にします。#!/usr/bin/env python3 # encoding=utf-8 import websocket import time from datetime import datetime def on_message(ws, message): print("Received a server message:", message) if __name__ == "__main__": ws = websocket.WebSocket() ws.connect("ws://<ALB domain name>:80/websocket") # Enter the domain name of your ALB instance print("The WebSocket connection is established") try: while True: current_time = datetime.now() formatted_time = current_time.strftime("%Y-%m-%d %H:%M:%S") print("Packet time:", formatted_time) ws.send("Hello, Server!") result = ws.recv() on_message(ws, result) time.sleep(1) except Exception: print("The WebSocket connection is closed")
変更が完了したら、
Esc
キーを押して:wq
と入力し、enterキーを押して設定ファイルを保存して閉じます。
client.pyファイルのディレクトリで、次のコマンドを実行してECS01にアクセスします。
python3 client.py
次の応答メッセージは、クライアントがECS01にアクセスできることを示します。
The WebSocket connection is established Packet time: 2024-04-28 17:00:53 Received server message: The server received your message: Hello, Server! Packet time: 2024-04-28 17:00:54 Received server message: The server received your message: Hello, Server!
ECS01は次の応答メッセージを返します。
WebSocket Server Start on 8080 ... Time: 2024-04-28 17:00:53 The WebSocket connection is established Time: 2024-04-28 17:00:53 Received message: Hello, Server! Time: 2024-04-28 17:00:54 Received message: Hello, Server!
バックエンドサーバーの削除
バックエンドサーバーを削除する前に、接続のドレインタイムアウト期間を指定します。
ALBコンソールにログインします。
上部のナビゲーションバーで、サーバーグループがデプロイされているリージョンを選択します。
左側のナビゲーションウィンドウで、
を選択します。バックエンドサーバーを削除するサーバーグループのIDをクリックします。
[バックエンドサーバー] タブでECS01を見つけ、[操作] 列の [削除] をクリックします。
[バックエンドサーバーの削除] メッセージで、[OK] をクリックします。
接続のドレインタイムアウト期間が終了するまで待ちます
この例では、接続ドレインタイムアウト期間を300秒に設定しています。 その結果、ECS01の接続は、ECS01を削除してから300秒後に閉じられます。
テスト結果では、ECS01でWebSocket接続が確立されてから、ECS01でWebSocket接続がクローズされるまでの時間は330秒です。 接続排出タイムアウト時間とは、ECS01が削除されてからWebSocket接続が閉じられるまでの時間で、約300秒です。
クライアントは次の応答メッセージを返します。
Packet time: 2024-04-28 17:06:23 Received server message: The server received your message: Hello, Server! Packet time: 2024-04-28 17:06:24 The WebSocket connection is closed
ECS01は次の応答メッセージを返します。
Time: 2024-04-28 17:06:22 Received message: Hello, Server! Time: 2024-04-28 17:06:23 Received message: Hello, Server! Time: 2024-04-28 17:06:23 The WebSocket connection is closed
HTTPセッションの接続ドレイン
HTTPSシナリオでは、クライアントが受け取る応答は、接続の排出タイムアウト期間、接続要求のタイムアウト期間、およびバックエンドサーバーの処理時間によって異なります。
接続の排出タイムアウト期間がバックエンドサーバーの処理時間より短い場合、ECS01からの応答は中断されます。 その結果、クライアントはHTTP 500ステータスコードを受信します。
バックエンドサーバーの処理時間が接続要求のタイムアウト期間より長い場合、ECS01からの応答はタイムアウトします。 その結果、クライアントはHTTP 504ステータスコードを受信します。
この例では、接続の排出タイムアウト期間は15秒に設定され、バックエンドサーバーの処理時間は30秒に設定されています。 この例では、ECS01からの応答が中断され、クライアントがHTTP 500ステータスコードを受信します。
ステップ2では、接続要求のタイムアウト時間をデフォルト値の60秒に設定します。これは、バックエンドサーバーの処理時間 (30秒) よりも長くなります。 その結果、接続排出タイムアウト時間 (15秒) がバックエンドサーバーの処理時間 (30秒) より短いため、HTTP 504のステータスコードは返されませんが、HTTP 500のステータスコードが返されます。
この例では、Pythonコードで
time.sleep
関数を使用して、バックエンドサーバーの処理時間をシミュレートします。
ステップ1: 接続ドレインの有効化
この例では、サーバグループが既に用意されている。 接続のドレインは、サーバーグループを変更することで構成されます。 サーバーグループがない場合は、サーバーグループを作成するときに接続のドレインを有効にできます。
ALBコンソールにログインします。
上部のナビゲーションバーで、サーバーグループがデプロイされているリージョンを選択します。
左側のナビゲーションウィンドウで、
を選択します。[サーバーグループ] ページで、管理するサーバーグループのIDをクリックします。
[詳細] タブで、[基本情報] セクションの [基本情報の変更] をクリックします。
[基本情報の変更] ダイアログボックスで、[詳細設定] をクリックし、[接続ドレイン] をオンにします。
タイムアウト期間を15秒に設定し、[保存] をクリックします。
手順2: 接続要求のタイムアウト時間の指定
ALBコンソールにログインします。
上部のナビゲーションバーで、ALBインスタンスがデプロイされているリージョンを選択します。
[インスタンス] ページで、管理するALBインスタンスのIDをクリックします。
[リスナー] タブで、管理するHTTPリスナーのIDをクリックします。
[基本情報] セクションで、[リスナーの変更] をクリックします。
[リスナーの変更] ダイアログボックスで、[詳細設定] の右側にある [変更] をクリックします。
[Connection Request Timeout] をデフォルトのタイムアウト時間である60秒に設定し、[Save] をクリックします。
ステップ3: Verify the result
バックエンドサーバーでの接続ドレインの設定
リモートでECS01にログオンします。 詳細については、「接続方法の概要」をご参照ください。
次のコマンドを実行してHTTPフォルダを作成し、HTTPディレクトリを開きます。
mkdir http cd http
次のコマンドを実行して、http_server.py設定ファイルを変更します。
vim http_server.py
I
キーを押してエディターを開き、次のパラメーターを設定してHTTP Serverサービスを有効にします。#!/usr/bin/env python3 # encoding=utf-8 from http.server import SimpleHTTPRequestHandler, HTTPServer from datetime import datetime import time class DelayedHTTPRequestHandler(SimpleHTTPRequestHandler): def do_GET(self): current_time = datetime.now() formatted_time = current_time.strftime("%Y-%m-%d %H:%M:%S") print("Time:", formatted_time, "Received the GET request and will respond in 30 seconds....") time.sleep(30) # Configure the time.sleep function to simulate the backend server processing time SimpleHTTPRequestHandler.do_GET(self) PORT = 8080 server = HTTPServer(("", PORT), DelayedHTTPRequestHandler) print(f"Serving HTTP on 0.0.0.0 port {PORT} (http://0.0.0.0:{PORT}/) ...") server.serve_forever()
変更が完了したら、
Esc
キーを押して:wq
と入力し、enterキーを押して設定ファイルを保存して閉じます。
http_server.pyのディレクトリに入り、次のコマンドを実行してHTTP Serverサービスを開始します。
python3 http_server.py
次の応答メッセージは、HTTP Serverサービスが実行中であることを示します。
Serving HTTP on 0.0.0.0 port 8080 (http://0.0.0.0:8080/) ...
ECS01をサーバーグループに追加する
ALBコンソールにログインします。
上部のナビゲーションバーで、サーバーグループがデプロイされているリージョンを選択します。
[サーバーグループ] ページで、管理するサーバーグループを見つけ、[操作] 列の [バックエンドサーバーの変更] をクリックします。
[バックエンドサーバー] タブで、[バックエンドサーバーの追加] をクリックし、[サーバーの選択] で [ECS01] を選択し、[次へ] をクリックします。
[ポート /重み] ステップで、[ECS01] を選択し、ポートを [
8080
] に設定して、[OK] をクリックします。
クライアントでの接続ドレインの設定
クライアントにログインし、コマンドラインインターフェイス (CLI) を開きます。 次のコマンドを実行してECS01にアクセスします。
curl http://<ALB domain name>:80/ -v
次の応答メッセージは、ALBインスタンスがバックエンドサービスにアクセスできることを示しています。
* About to connect() to alb-nssnq5a********.cn-guangzhou.alb.aliyuncs.com port 80 (#0) * Trying 10.X.X.225... * Connected to alb-nssnq5a********.cn-guangzhou.alb.aliyuncs.com (10.X.X.225) port 80 (#0) > GET / HTTP/1.1 > User-Agent: curl/7.29.0 > Host: alb-nssnq5a********.cn-guangzhou.alb.aliyuncs.com > Accept: */*
サーバーは次の応答メッセージを受信します。
Serving HTTP on 0.0.0.0 port 8080 (http://0.0.0.0:8080/) ... Time: 2024-02-07 13:57:33 Received the Get request and will respond in 30 seconds....
バックエンドサーバーの削除
バックエンドサーバーを削除する前に、接続のドレインタイムアウト期間を指定します。
ALBコンソールにログインします。
上部のナビゲーションバーで、サーバーグループがデプロイされているリージョンを選択します。
左側のナビゲーションウィンドウで、
を選択します。バックエンドサーバーを削除するサーバーグループのIDをクリックします。
[バックエンドサーバー] タブでECS01を見つけ、[操作] 列の [削除] をクリックします。
[バックエンドサーバーの削除] メッセージで、[OK] をクリックします。
接続のドレインタイムアウト期間が終了するまで待ちます
結果は、接続の排出タイムアウト期間がバックエンドサーバーの処理時間より短い場合、クライアントはHTTP 500ステータスコードを受信することを示しています。
* About to connect() to alb-nssnq5a********.cn-guangzhou.alb.aliyuncs.com port 80 (#0)
* Trying 10.X.X.224...
* Connected to alb-nssnq5a********.cn-guangzhou.alb.aliyuncs.com (10.1.0.224) port 80 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: alb-nssnq5a********.cn-guangzhou.alb.aliyuncs.com
> Accept: */*
>
< HTTP/1.1 500 Internal Server Error
< Date: Wed, 07 Feb 2024 06:02:24 GMT
< Content-Type: text/html
< Content-Length: 186
< Connection: close
< Via: HTTP/1.1 SLB.87
<
<html>
<head><title>500 Internal Server Error</title></head>
<body bgcolor="white">
<center><h1>500 Internal Server Error</h1></center>
<hr><center>nginx</center>
</body>
</html>
* Closing connection 0
関連ドキュメント
サーバーグループの作成時に接続のドレインを有効にする方法の詳細については、「サーバーグループの作成と管理」をご参照ください。
サービスのグレースフルデプロイメントを実装するには、スロースタートモードを有効にします。 詳細については、「スロースタートを使用してサービスのグレースフルデプロイメントを実装する」をご参照ください。
WebSocketとHTTPの詳細については、「HTTPリスナーの追加」および「WebSocketを使用してリアルタイムメッセージングを有効にする」をご参照ください。