異常なターゲットをトラフィックから除外する

AWSBeginner
オンラインで実践に進む

はじめに

アプリケーションには Application Load Balancer の背後に 2 台のサーバーがあります。EC2 インスタンスが稼働していても、アプリケーションは故障することがあります。このラボではヘルスチェックを設定し、実際の障害を起こして、正常なサーバーがリクエストを処理し続けることを確認します。その後、アプリケーションを復旧し、負荷分散リソースを削除します。

ALB リスナー、ターゲットグループ、EC2 の SSH 接続を理解していることが前提です。この新しい環境にはネットワーク、稼働中の 2 台、登録情報、ALB リスナーが用意されています。前のラボに依存せず、AWS CLI と接続ファイルも設定済みです。

認定試験との関連

このラボでは、次の試験項目に関する入門的な実践を行います。

ターゲットのヘルスチェックを設定する

提供されたサービスを確認し、ALB がアプリケーションの準備状態を調べる方法を設定します。

用意された作業ディレクトリに移動し、ネットワーク変数を読み込みます。

cd /home/labex/project
source launch.env

提供されたロードバランサーとグループを名前で検索します。後続コマンド用に ARN を、HTTP リクエスト用に DNS 名を保存します。

LB_ARN=$(aws elbv2 \
  describe-load-balancers \
  --names application-alb \
  --query 'LoadBalancers[0].LoadBalancerArn' \
  --output text)

TG_ARN=$(aws elbv2 \
  describe-target-groups \
  --names application-targets \
  --query 'TargetGroups[0].TargetGroupArn' \
  --output text)

LB_DNS=$(aws elbv2 \
  describe-load-balancers \
  --load-balancer-arns "$LB_ARN" \
  --query 'LoadBalancers[0].DNSName' \
  --output text)

ヘルスチェックはクライアントのリクエストとは独立して各ターゲットを調べます。パスはアプリケーションがトラフィックを処理できるかを示す必要があります。現在の設定を確認します。

aws elbv2 \
  describe-target-groups \
  --target-group-arns "$TG_ARN" \
  --query 'TargetGroups[].{Path:HealthCheckPath,Interval:HealthCheckIntervalSeconds,Timeout:HealthCheckTimeoutSeconds,Healthy:HealthyThresholdCount,Unhealthy:UnhealthyThresholdCount,Matcher:Matcher}'

提供グループは 30 秒ごとに /health をチェックします。この小さな演習では間隔を 5 秒、タイムアウトを 2 秒に設定します。連続 2 回の失敗で除外し、連続 2 回の成功で再参加させます。マッチャーは HTTP 200 を成功と判定します。

aws elbv2 \
  modify-target-group \
  --target-group-arn "$TG_ARN" \
  --health-check-protocol HTTP \
  --health-check-path /health \
  --health-check-interval-seconds 5 \
  --health-check-timeout-seconds 2 \
  --healthy-threshold-count 2 \
  --unhealthy-threshold-count 2 \
  --matcher HttpCode=200 \
  --query 'TargetGroups[].{Path:HealthCheckPath,Interval:HealthCheckIntervalSeconds,Healthy:HealthyThresholdCount,Unhealthy:UnhealthyThresholdCount}'

間隔は頻度、タイムアウトは各チェックの待ち時間を決めます。しきい値は一時的な失敗への過剰な反応を防ぎます。本番では起動と障害の特性に合わせてください。ここでは観察しやすい短い値を使います。

両方のターゲットが正常になるまで待ち、実際の状態を確認します。

aws elbv2 \
  wait target-in-service \
  --target-group-arn "$TG_ARN"

aws elbv2 \
  describe-target-health \
  --target-group-arn "$TG_ARN" \
  --query 'TargetHealthDescriptions[].{Instance:Target.Id,Port:Target.Port,Health:TargetHealth.State}' \
  --output table

AWS View を開き、2 つの healthy を確認して Send request をクリックします。実際の HTTP 200 とヘルス状態で初期条件を確認できます。

実際のアプリケーション障害を観察する

EC2 インスタンスを稼働させたまま、app-a のヘルスエンドポイントを失敗させます。

提供された名前タグで ID を取得し、SSH 用のアドレスも取得します。

APP_A=$(aws ec2 \
  describe-instances \
  --filters Name=tag:Name,Values=app-a \
  --query 'Reservations[0].Instances[0].InstanceId' \
  --output text)

APP_B=$(aws ec2 \
  describe-instances \
  --filters Name=tag:Name,Values=app-b \
  --query 'Reservations[0].Instances[0].InstanceId' \
  --output text)

APP_A_IP=$(aws ec2 \
  describe-instances \
  --instance-ids "$APP_A" \
  --query 'Reservations[0].Instances[0].PublicIpAddress' \
  --output text)

アプリケーションは毎回 /etc/report-app/config.json を読みます。healthy により /health が 200 または 503 を返します。提供された鍵と設定で SSH 接続します。jq はこのフィールドだけを変更し、一時ファイルは読み取り中の上書きを防ぎ、install は読み取り可能な権限で置き換えます。

ssh -F ssh_config "ubuntu@$APP_A_IP" \
  'sudo jq ".healthy = false" /etc/report-app/config.json > /tmp/report-config.json && sudo install -m 644 /tmp/report-config.json /etc/report-app/config.json'

サーバー内部から直接確認します。-o /dev/null は本文を破棄し、-w は HTTP ステータスを表示します。

ssh -F ssh_config "ubuntu@$APP_A_IP" \
  'curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8081/health'

503 が返るはずです。これはアプリケーション障害であり EC2 停止ではありません。2 回のチェックを待って理由を確認します。

sleep 12

aws elbv2 \
  describe-target-health \
  --target-group-arn "$TG_ARN" \
  --query 'TargetHealthDescriptions[].{Instance:Target.Id,Health:TargetHealth.State,Reason:TargetHealth.Reason}' \
  --output table

503 は 200 と一致しないため、APP_A は unhealthy、理由は Target.ResponseCodeMismatch となります。APP_B は healthy のままです。チェックは非同期なので、切り替え中なら少し待って再確認します。

EC2 インスタンスが稼働したままであることを確認します。

aws ec2 \
  describe-instances \
  --instance-ids "$APP_A" "$APP_B" \
  --query 'Reservations[].Instances[].{Instance:InstanceId,State:State.Name}' \
  --output table

ALB 経由で 6 回の独立したリクエストを送ります。

for request in 1 2 3 4 5 6; do
  curl --config client.conf -sS "http://$LB_DNS/health"
  echo
done

すべての応答が APP_B を示すはずです。AWS View で異常なターゲットと正常なターゲットを確認します。Send request を何度かクリックし、正常なサーバーが応答することを確認します。正常なターゲットがあれば、ALB は故障ターゲットを除外します。

異常なターゲットを除外し、正常なサーバーが応答する

例では一方が Target.ResponseCodeMismatch を示し、正常なターゲットから実際の HTTP 200 が返っています。あなたの ID は異なるため、自分の正常なターゲットと応答を照合してください。

すべてが異常な場合、ALB は fail open で異常なターゲットにも転送することがあります。app-b を正常に保つことが重要です。全ターゲット異常を、ALB がすべての転送を停止する証拠と考えないでください。

ターゲットを復旧して再参加させる

app-a を復旧し、チェック成功後にリクエスト処理へ戻ることを確認します。

同じ安全なファイル置換で healthy だけを復元します。

ssh -F ssh_config "ubuntu@$APP_A_IP" \
  'sudo jq ".healthy = true" /etc/report-app/config.json > /tmp/report-config.json && sudo install -m 644 /tmp/report-config.json /etc/report-app/config.json'

直接アクセスで 200 が返ることを確認します。

ssh -F ssh_config "ubuntu@$APP_A_IP" \
  'curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8081/health'

アプリケーションは復旧しましたが、ALB は設定された連続成功を確認する必要があります。正常状態を待って両方を確認します。

aws elbv2 \
  wait target-in-service \
  --target-group-arn "$TG_ARN"

aws elbv2 \
  describe-target-health \
  --target-group-arn "$TG_ARN" \
  --query 'TargetHealthDescriptions[].{Instance:Target.Id,Health:TargetHealth.State}' \
  --output table

両方が healthy になったら、再び 6 回リクエストします。

for request in 1 2 3 4 5 6; do
  curl --config client.conf -sS "http://$LB_DNS/health"
  echo
done

両方の ID を探します。AWS View の正常な 2 つのカードと Send request の応答を確認します。アプリケーションを修復してチェックを成功させたのであり、インスタンスの置換や登録解除はしていません。

負荷分散リソースを削除する

復旧したサーバーとネットワークを残し、提供された負荷分散設定を削除します。

リスナー ARN を取得し、リスナー、ALB、ターゲットグループの順で削除します。

LISTENER_ARN=$(aws elbv2 \
  describe-listeners \
  --load-balancer-arn "$LB_ARN" \
  --query 'Listeners[0].ListenerArn' \
  --output text)

aws elbv2 \
  delete-listener \
  --listener-arn "$LISTENER_ARN"

aws elbv2 \
  delete-load-balancer \
  --load-balancer-arn "$LB_ARN"

aws elbv2 \
  delete-target-group \
  --target-group-arn "$TG_ARN"

両方のリソース一覧が空であることを確認します。

aws elbv2 \
  describe-load-balancers \
  --query 'LoadBalancers[].LoadBalancerName'

aws elbv2 \
  describe-target-groups \
  --query 'TargetGroups[].TargetGroupName'

両クエリの [] と AWS View の空の領域を確認します。提供された EC2 とネットワークは残してください。

まとめ

ALB ヘルスチェックを設定し、実際の障害を起こして EC2 停止と区別しました。正常なサーバーへのトラフィックを確認し、復旧後は両方の応答を確認しました。最後に負荷分散リソースを削除し、サーバーとネットワークを残しました。

次のラボでは、既存サーバーを手動修復する代わりに Auto Scaling グループで故障インスタンスを置き換えます。