バックエンドサーバーを削除したり、ヘルスチェックに失敗したりしても、確立された接続はすぐには終了しません。これらの持続的接続により、サービスのグレースフルシャットダウンが妨げられたり、リクエストエラーが発生したりする可能性があります。これを回避するために、Application Load Balancer (ALB) の接続ドレイン機能を利用できます。バックエンドサーバーが削除されたり、異常になったりすると、接続ドレイン機能により、既存の接続は設定された期間、処理を継続できます。この期間が経過すると、ALB はアクティブに接続を閉じ、サービスのグレースフルシャットダウンを保証します。
利用シーン
接続ドレインは、以下の 2 つのシナリオで使用します。
バックエンドサーバーの削除:バックエンドサーバーを削除する前に、接続ドレインのタイムアウトを長めに設定します。これにより、処理中のリクエストを完了させることができます。
ヘルスチェックの失敗:バックエンドサーバーがヘルスチェックに失敗した場合、タイムアウトを短めに設定します。これにより、障害のある接続を迅速に終了させ、クライアントでエラーが発生するのを防ぐことができます。
どちらのシナリオも同じ接続ドレイン機能を使用するため、特定のビジネスニーズに基づいて適切な接続ドレインタイムアウトを設定する必要があります。
シナリオ 1:バックエンドサーバーの削除
このトピックでは、次の図に示すシナリオを使用します。バックエンドサーバー ECS01 を削除すると、ALB は新しいリクエストの送信を停止します。その後、ECS01 は処理中のリクエストのみを処理し、新しいリクエストは受け入れません。
接続ドレインが無効の場合、ECS01 への接続は突然終了し、処理中のリクエストが中断される可能性があります。
接続ドレインが有効で、タイムアウトが設定されている場合:
バックエンドサーバー ECS01 に処理中のリクエストがある場合、ALB は接続ドレインのタイムアウトに達した時点で ECS01 上の既存の接続を閉じます。
バックエンドサーバー ECS01 に処理中のリクエストやアクティブな接続がない場合、ALB は接続ドレインのタイムアウトを待たずに、すぐに削除プロセスを完了します。
バックエンドサーバー ECS01 が接続ドレインのタイムアウトに達した時点でまだリクエストを処理している場合、接続は終了します。クライアントは 500 番台のエラーレスポンスを受け取ります。例えば、接続ドレインのタイムアウトが 15 秒に設定されているが、リクエストの処理に 30 秒かかる場合、ECS01 がレスポンスを送信する前に接続が終了します。この場合、クライアントは 500 番台のエラーレスポンスを受け取ります。
説明ECS01 を削除した後にサーバーグループに再追加しても、削除されたインスタンス上の接続のドレインプロセスには影響しません。その状態は変わらず、処理中のリクエストのみを処理し、新しいリクエストは受け入れません。ALB は、接続ドレインのタイムアウトに達した時点で ECS01 上のこれらの既存の接続を閉じます。
次の図は、接続ドレインが有効になっているサーバーグループから削除された後の ECS01 の状態遷移を示しています。
シナリオ 2:ヘルスチェックの失敗
バックエンドサーバー ECS01 がヘルスチェックに失敗すると、ALB は新しいリクエストの送信を停止します。この時点で、ECS01 は処理中のリクエストのみを処理し、新しいリクエストは受け入れません。
接続ドレインが無効の場合、ECS01 は再びヘルスチェックに合格するまでこの状態を維持し、その時点で新しいリクエストの受け入れを開始します。
接続ドレインが有効で、タイムアウトが設定されている場合:
ALB は、接続ドレインのタイムアウトに達した時点で ECS01 上の既存の接続を閉じます。
サーバーグループが更新された場合 (例えば、ECS01 の構成が変更された場合)、ECS01 の接続状態は変わりません。処理中のリクエストのみを処理し続け、新しいリクエストは受け入れません。更新後に ECS01 がヘルスチェックに合格したとしても、ALB は接続ドレインのタイムアウトに達した時点で ECS01 上の既存の接続を閉じます。
説明接続ドレインのタイムアウトに達して ALB が ECS01 上の既存の接続を閉じた後、ECS01 がヘルスチェックに合格すれば、新しいリクエストを受け入れることができます。ヘルスチェックに失敗した場合、ECS01 は新しいリクエストを受け入れません。
構成の更新によってヘルスチェックが失敗した場合、接続ドレインはトリガーされません。システムは、バックエンドサービスの問題によって引き起こされたヘルスチェックの失敗に対してのみ接続ドレインを開始します。
次の図は、ヘルスチェックに失敗した後の ECS01 の状態遷移を示しています。
ビジネスシナリオに基づいて接続ドレインを構成できます。このトピックでは、シナリオ 1:バックエンドサーバーの削除を例として、ALB が WebSocket および HTTP セッションをどのように中断するかを説明します。
注意事項
標準型および WAF 有効化型の ALB インスタンスのみが接続ドレインをサポートします。ベーシック ALB インスタンスはこの機能をサポートしていません。
Function Compute タイプのサーバーグループは接続ドレインをサポートしていません。
WebSocket プロトコルを使用する場合、接続ドレインを有効にできます。HTTP シナリオでは、リクエストにはタイムアウト制限があります。HTTP リクエストが途中で終了するのを防ぐために、接続ドレインのタイムアウトを ALB インスタンスの接続リクエストタイムアウトより大きい値に設定することを推奨します。ALB によって設定されるデフォルトの接続ドレインタイムアウトは、すでに接続リクエストタイムアウトより大きくなっています。接続リクエストタイムアウトの設定方法の詳細については、「HTTP リスナーの追加」をご参照ください。
前提条件
標準型または WAF 有効化型の ALB インスタンスと、そのインスタンス用のサーバータイプのサーバーグループを作成済みであること。このトピックでは、標準型の ALB インスタンスを例として使用します。詳細については、「ALB インスタンスの作成と管理」および「サーバーグループの作成と管理」をご参照ください。
ALB インスタンスのポート
80に HTTP リスナーを構成し、そのリスナーをサーバーグループに関連付けていること。詳細については、「HTTP リスナーの追加」をご参照ください。2 つのバックエンドサーバー ECS01 と ECS02 を作成済みであること。詳細については、「ウィザードを使用したインスタンスの作成」をご参照ください。
ECS02 インスタンスをサーバーグループに追加済みで、クライアントが ECS02 上で実行中のサービスにアクセスできること。詳細については、「ALB を使用した IPv4 サービスの負荷分散の実現」および「ALB を使用した IPv6 サービスの負荷分散の実現」をご参照ください。
説明このトピックでは、クライアントに Alibaba Cloud Linux 3.2104 64 ビットオペレーティングシステムを使用します。ご利用のクライアントとバックエンドサーバー ECS01 に Python がインストールされていることを確認してください。オペレーティングシステムに Python がインストールされていない場合は、Python 公式ウェブサイトでインストール手順をご確認ください。このトピックでは、Python 3.x を例として使用します。
このトピックでは、バックエンドサーバー ECS02 は代表的なサービスを実行します。同様のサーバーがすでにある場合は、新しく作成する必要はありません。
操作手順
このトピックでは、接続ドレインを有効にした ALB が WebSocket および HTTP リクエストをどのように処理するかを説明します。
WebSocket セッションの中断動作
ステップ 1:接続ドレインの有効化
このステップでは、前提条件で用意したサーバーグループに対して接続ドレインを有効にする方法を説明します。この機能は、サーバーグループの作成時に有効にすることもできます。
ALB コンソールにログインします。
上部のナビゲーションバーで、サーバーグループがデプロイされているリージョンを選択します。
左側のナビゲーションウィンドウで、を選択します。
サーバーグループページで、対象のサーバーグループを見つけ、その ID をクリックします。
詳細 タブで、基本情報 セクションを見つけ、基本情報の変更 をクリックします。
基本情報の変更 ダイアログボックスで、詳細設定 をクリックし、接続ドレイン を有効にします。
グレースフル・シャットダウンのタイムアウト を 300 秒に設定し、保存 をクリックします。
ステップ 2:結果の検証
サーバーの構成
ECS01 インスタンスにログインします。詳細については、「接続方法」をご参照ください。
次のコマンドを実行して、WebSocket という名前のディレクトリを作成し、そこに移動します。
mkdir WebSocket cd WebSocket次のコマンドを実行して、必要な依存関係をインストールします。
pip install tornado pip install websocket-client次のコマンドを実行して、server.py 構成ファイルを編集します。
vim server.pyiキーを押して編集モードに入り、次のコードを追加して WebSocket サービスを開始します。#!/usr/bin/env python3 # encoding=utf-8 import tornado.websocket import tornado.ioloop import tornado.web from datetime import datetime # WebSocket ハンドラ 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, "WebSocket connection opened") 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 message:", message) self.write_message("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, "WebSocket connection closed") # ルーティング 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 をクリックします。
クライアントの構成
クライアントにログインし、コマンドラインインターフェイスを開きます。次のコマンドを実行して、WebSocket という名前のディレクトリを作成し、そこに移動します。
mkdir WebSocket cd WebSocket次のコマンドを実行して、必要な依存関係をインストールします。
pip install websocket-client次のコマンドを実行して、client.py ファイルを編集します。
vim client.pyiキーを押して編集モードに入り、WebSocket クライアントがサービスにアクセスするための次のコードを追加します。#!/usr/bin/env python3 # encoding=utf-8 import websocket import time from datetime import datetime def on_message(ws, message): print("Received message from server:", message) if __name__ == "__main__": ws = websocket.WebSocket() ws.connect("ws://<domain_name>:80/websocket") # <domain_name> を実際のドメイン名に置き換えます。 print("WebSocket connection opened") try: while True: current_time = datetime.now() formatted_time = current_time.strftime("%Y-%m-%d %H:%M:%S") print("Sending time:", formatted_time) ws.send("Hello, Server!") result = ws.recv() on_message(ws, result) time.sleep(1) except Exception: print("WebSocket connection closed")構成を変更した後、
Escキーを押し、:wqと入力して Enter キーを押し、ファイルを保存して閉じます。
client.py があるディレクトリに移動し、次のコマンドを実行してサーバー ECS01 にアクセスします。
python3 client.py次の応答は、アクセスが成功したことを示します。
WebSocket connection opened Sending time: 2024-04-28 17:00:53 Received message from server: Server received your message: Hello, Server! Sending time: 2024-04-28 17:00:54 Received message from server: Server received your message: Hello, Server!サーバー ECS01 は次の応答を返します。
WebSocket Server Start on 8080 ... Time: 2024-04-28 17:00:53 WebSocket connection opened 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 が削除されてから約 300 秒後に ALB が接続を終了することを示しています。
テスト結果では、ECS01 サーバー上の WebSocket 接続が開いてから閉じるまでの時間差は 330 秒です。接続ドレインのタイムアウトは、バックエンドサーバー ECS01 が削除されてから WebSocket 接続が閉じるまでの期間であり、約 300 秒です。
クライアントは次の応答を返します。
Sending time: 2024-04-28 17:06:23 Received message from server: Server received your message: Hello, Server! Sending time: 2024-04-28 17:06:24 WebSocket connection closedECS01 サーバーインスタンスは次の応答を返します。
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 WebSocket connection closed
HTTP セッションの中断動作
HTTP シナリオでは、クライアントが受け取る応答は、接続ドレインのタイムアウト、接続リクエストタイムアウト、およびバックエンドサーバーの処理時間に依存します。
接続ドレインのタイムアウト < バックエンドサーバーの処理時間の場合、ECS01 が応答を送信し終える前に接続が中断されます。この場合、クライアントは 500 番台のエラーレスポンスを受け取ります。
バックエンドサーバーの処理時間 > 接続リクエストタイムアウトの場合、ECS01 へのリクエストがタイムアウトします。この場合、クライアントは 504 エラーレスポンスを受け取ります。
このトピックでは、接続ドレインのタイムアウトを 15 秒、バックエンドサーバーの処理時間を 30 秒とします。このシナリオでは、応答が送信される前に ECS01 への接続が中断され、クライアントは 500 番台のエラーレスポンスを受け取ります。
ステップ 2:ALB 接続リクエストタイムアウトの設定では、接続リクエストタイムアウトは 60 秒 (デフォルト値) に設定されており、バックエンドサーバーの処理時間 (30 秒) よりも長いです。したがって、504 エラーは返されません。代わりに、接続ドレインのタイムアウト (15 秒) がバックエンドサーバーの処理時間 (30 秒) よりも短いため、500 番台のエラーが返されます。
このトピックでは、Python コードの
time.sleep関数を使用して、バックエンドサーバーの処理時間をシミュレートします。
ステップ 1:接続ドレインの有効化
このステップでは、前提条件で用意したサーバーグループに対して接続ドレインを有効にする方法を説明します。この機能は、サーバーグループの作成時に有効にすることもできます。
ALB コンソールにログインします。
上部のナビゲーションバーで、サーバーグループがデプロイされているリージョンを選択します。
左側のナビゲーションウィンドウで、を選択します。
サーバーグループページで、対象のサーバーグループを見つけ、その ID をクリックします。
詳細 タブで、基本情報 セクションを見つけ、基本情報の変更 をクリックします。
基本情報の変更 ダイアログボックスで、詳細設定 をクリックし、接続ドレイン を有効にします。
グレースフル・シャットダウンのタイムアウト を 15 秒に設定し、保存 をクリックします。
ステップ 2:ALB 接続リクエストタイムアウトの設定
ALB コンソールにログインします。
上部のナビゲーションバーで、ALB インスタンスがデプロイされているリージョンを選択します。
インスタンス ページで、対象の ALB インスタンスを見つけ、その ID をクリックします。
リスナー タブをクリックし、対象の HTTP リスナーを見つけ、リスナー ID をクリックします。
基本情報 エリアで、リスナーの変更 をクリックします。
リスナーの変更 ダイアログボックスで、詳細設定 の隣にある 変更 をクリックします。
接続リクエストタイムアウト を 60 秒 (デフォルト値) に設定し、保存 をクリックします。
ステップ 3:ドメイン名の名前解決の構成
左側のナビゲーションウィンドウで、を選択します。
インスタンス ページで、ALB インスタンスのドメイン名をコピーします。
次の手順を実行して CNAME レコードを作成します:
説明ドメイン名が Alibaba Cloud Domains を使用して登録されていない場合は、DNS レコードを構成する前にドメイン名を Alibaba Cloud DNS に追加する必要があります。詳細については、「ドメイン名の管理」をご参照ください。
Alibaba Cloud DNS コンソールにログインします。
権威 DNS 名前解決 ページで、ドメイン名を見つけ、操作 列の 解決設定 をクリックします。
ドメイン名詳細ページの 解決設定 タブで、Add Record をクリックします。
Add Record パネルで、パラメーターを構成し、OK をクリックします。次の表にパラメーターを説明します。
パラメーター
説明
レコードタイプ
ドロップダウンリストから [CNAME] を選択します。
ホストレコード
ドメイン名のプレフィックスを入力します。この例では、@ を入力します。
説明ルートドメインを使用する場合は、
@を入力します。Query Source
デフォルトを選択します。
レコード値
CNAME、つまり ALB インスタンスのドメイン名を入力します。
[TTL]
DNS サーバーにキャッシュされる CNAME レコードの生存時間 (TTL) を選択します。この例では、デフォルト値を使用します。
ステップ 4:結果の検証
サーバーへのログインと構成
ECS01 インスタンスにログインします。詳細については、「ECS インスタンスへの接続方法の選択」をご参照ください。
次のコマンドを実行して http フォルダを作成し、http ディレクトリに移動します。
mkdir http cd http次のコマンドを実行して、http_server.py 構成ファイルを編集します。
vim http_server.pyiキーを押して編集モードに入ります。次に、次の内容を追加して HTTP サーバーを開始します。#!/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, "GET request received. Responding after a 30 second delay....") time.sleep(30) # time.sleep 関数を使用して、バックエンドサーバーの処理時間をシミュレートします。 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 サーバーを開始します。
python3 http_server.py次の応答が返された場合、HTTP バックエンドサーバーが開始されています。
Serving HTTP on 0.0.0.0 port 8080 (http://0.0.0.0:8080/) ...
バックエンドサーバー ECS01 をサーバーグループに追加
ALB コンソールにログインします。
上部のナビゲーションバーで、サーバーグループがデプロイされているリージョンを選択します。
サーバーグループページで、対象のサーバーグループを見つけ、操作 列の バックエンドサーバーの変更 をクリックします。
バックエンドサーバー タブで、バックエンドサーバーの追加 をクリックします。バックエンドサーバーの追加 パネルで、ECS01 を選択し、次のステップ をクリックします。
ポート/重み ウィザードで、ECS01 インスタンスを選択し、ポートを
8080に設定し、OK をクリックします。
クライアントへのログインと構成
クライアントにログインします。コマンドラインウィンドウを開きます。次のコマンドを実行して、バックエンドサーバー ECS01 にアクセスします。
curl http://<domain_name>:80/ -v次の応答は、ALB がバックエンドサービスにアクセスできることを示します。
* About to connect() to www.example.com port 80 (#0) * Trying 10.X.X.225... * Connected to www.example.com (10.X.X.225) port 80 (#0) > GET / HTTP/1.1 > User-Agent: curl/7.29.0 > Host: www.example.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 a GET request. Responding after a 30-second delay....
バックエンドサーバーの削除
バックエンドサーバーを削除する前に、グレースフル接続タイムアウトを設定します。
ALB コンソールにログインします。
上部のナビゲーションバーで、サーバーグループがデプロイされているリージョンを選択します。
左側のナビゲーションウィンドウで、を選択します。
対象のサーバーグループを見つけ、サーバーグループ ID をクリックします。
バックエンドサーバー タブをクリックし、対象のバックエンドサーバー ECS01 を見つけ、操作 列で 削除 をクリックします。
バックエンドサーバーの削除 ダイアログボックスで、[OK] をクリックします。
接続ドレインの待機
テスト結果は、ALB に設定されたグレースフル接続タイムアウト < バックエンドサーバーの処理時間の場合、クライアントが 500 エラーレスポンスを受け取ることを示しています。
* About to connect() to www.example.com port 80 (#0)
* Trying 10.X.X.224...
* Connected to www.example.com (10.XX.XX.224) port 80 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: www.example.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関連ドキュメント
サーバーグループの作成時に接続ドレインを有効にする方法については、「サーバーグループの作成と管理」をご参照ください。
サービスのグレースフルなロールアウトを行うには、スロースタート機能を有効にします。詳細については、「ALB のスロースタートによるグレースフルなサービスロールアウトの実現」をご参照ください。
WebSocket および HTTP プロトコルの詳細については、「HTTP リスナーの追加」および「ALB で WebSocket プロトコルを使用したリアルタイム情報のプッシュ」をご参照ください。