ALBの新機能 Target Optimizer による同時接続数の制御を試してみた

ALBの新機能 Target Optimizer による同時接続数の制御を試してみた

2025年11月にリリースされたALBの新機能、Target Optimizerを検証。エージェント連携によりターゲットの同時接続数を厳密に制御し、過負荷時に即座に503を返す挙動を確認しました。生成AI・LLMアプリのGPU枯渇対策(バルクヘッド)に活用できます。
2025.11.22

2025年11月20日、Application Load Balancer (ALB) の新機能、ターゲットオプティマイザー (Target Optimizer) がリリースされました。

https://aws.amazon.com/jp/blogs/networking-and-content-delivery/drive-application-performance-with-application-load-balancer-target-optimizer/

これまではWAFやWebサーバ側で行っていた同時接続数の制御が、ALBとターゲット間の連携によってより効果的に行えるようになります。 今回、ALB配下のターゲットの同時接続数を一定以下に制御し、過剰な接続による性能劣化を回避できるか、試す機会がありましたので、紹介します。

環境構成

今回は ECS のサイドカー構成を模倣するため、EC2 上で Docker を利用し、--network host モードでエージェントとアプリケーションを同居させました。

  • OS: Amazon Linux 2023 (al2023-ami-2023.9.20251117.1-kernel-6.1-x86_64)
  • App: Node.js (クエリパラメータで遅延を制御)
  • Agent: AWS Target Optimizer Agent

EC2 (Docker & アプリ)

1秒の遅延を意図的に発生させる Node.js アプリケーションと、ALB からのリクエストを中継・制御する Target Optimizer agent を起動しました。

#!/bin/bash
# 1. システムパッケージを最新化
yum update -y

# 2. Dockerをインストール
yum install -y docker

# 3. Dockerサービスを起動・有効化
systemctl start docker
systemctl enable docker

# 4. Target Optimizer agentイメージをダウンロード
docker pull public.ecr.aws/aws-elb/target-optimizer/target-control-agent:latest

# 5. Node.jsアプリケーションのディレクトリを作成
mkdir -p /app

# 6. 遅延可能なNode.jsアプリケーションを作成(クエリパラメータ ?delay=<ms> で遅延時間を指定)
cat > /app/app.js << 'EOF'
require('http').createServer(async (req, res) => {
  const delay = parseInt(new URL(req.url, 'http://localhost').searchParams.get('delay')) || 0;
  if (delay > 0) await new Promise(r => setTimeout(r, delay));
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end(`OK (${delay}ms delay)\n`);
}).listen(8080, () => console.log('Server running on port 8080'));
EOF

# 7. Dockerfileを作成
cat > /app/Dockerfile << 'EOF'
FROM node:alpine
COPY app.js .
CMD ["node", "app.js"]
EOF

# 8. Dockerイメージをビルド
cd /app && docker build -t delay-app .

# 9. Node.jsアプリケーションコンテナを起動
docker run -d \
  --name delay-app \
  --restart unless-stopped \
  --network host \
  delay-app

# 10. Target Optimizer agentコンテナを起動
docker run -d \
  --name target-optimizer-agent \
  --restart unless-stopped \
  --network host \
  -e TARGET_CONTROL_DATA_ADDRESS=0.0.0.0:80 \
  -e TARGET_CONTROL_CONTROL_ADDRESS=0.0.0.0:3000 \
  -e TARGET_CONTROL_DESTINATION_ADDRESS=127.0.0.1:8080 \
  -e TARGET_CONTROL_MAX_CONCURRENCY=1 \
  public.ecr.aws/aws-elb/target-optimizer/target-control-agent:latest

ALB ターゲットグループ作成

検証用のALBを作成しました。

ターゲットグループ作成時、 --target-control-port 設定を実施しました。
これにより、ALB は指定したポート(今回は3000)を通じてエージェントと通信し、混雑状況を把握します。

Target Optimizer有効のTarget Groupを作成(CLI)

aws elbv2 create-target-group \
  --name optimized-tg \
  --protocol HTTP \
  --port 80 \
  --vpc-id <VPC-ID> \
  --target-control-port 3000 \ 
  --region ap-northeast-1

# --target-control-port でエージェントとの通信ポートを指定
  • マネージドルール(GUI)設定

ターゲットコントロールポート設定

今回 EC2のエージェントに合わせ、ターゲットコントロールポートは「3000」。 Target Optimizer agentコンテナのポート「80」を通信先としました。

動作検証

リクエスト毎に意図的に「1秒」の遅延を発生させ、同時接続数制限(今回は1)を超えた場合にどう挙動するかを確認しました。

テストスクリプト
#!/bin/bash
DELAY=${1:-0}
ALB_DNS=$(aws elbv2 describe-load-balancers --region ap-northeast-1 --query 'LoadBalancers[0].DNSName' --output text)
URL="http://$ALB_DNS/?delay=$DELAY"

run_requests() {
  local PARALLEL=$1
  local RESULT=$(seq 1 $PARALLEL | xargs -P $PARALLEL -I {} curl -s --http1.1 -o /dev/null -w "%{http_code}\n" "$URL" 2>/dev/null)
  local S=$(echo "$RESULT" | grep -c "200" || true)
  local F=$(echo "$RESULT" | grep -c "503" || true)
  echo "$S $F"
  sleep 1
}

echo "=== テスト設定 ==="
echo "遅延時間: ${DELAY}ms"
echo "URL: $URL"
echo ""

echo "=== ウォームアップ中(2並列、10秒間)==="
START_TIME=$(date +%s)
WARMUP_SUCCESS=0
WARMUP_FAILED=0

while [ $(($(date +%s) - START_TIME)) -lt 10 ]; do
  read S F <<< $(run_requests 2)
  WARMUP_SUCCESS=$((WARMUP_SUCCESS + S))
  WARMUP_FAILED=$((WARMUP_FAILED + F))
done

echo "ウォームアップ完了: 成功=$WARMUP_SUCCESS, 失敗=$WARMUP_FAILED"
echo ""
sleep 2

echo "=== Target Optimizer 並列負荷テスト (各30秒間) ==="
echo "並列数 | 成功(200) | 失敗(503) | 成功率"
echo "-------|-----------|-----------|-------"

for PARALLEL in 1 2 4 8 16; do
  START_TIME=$(date +%s)
  SUCCESS=0
  FAILED=0

  while [ $(($(date +%s) - START_TIME)) -lt 30 ]; do
    read S F <<< $(run_requests $PARALLEL)
    SUCCESS=$((SUCCESS + S))
    FAILED=$((FAILED + F))
  done

  TOTAL=$((SUCCESS + FAILED))
  if [ $TOTAL -gt 0 ]; then
    RATE=$(awk "BEGIN {printf \"%.1f%%\", ($SUCCESS*100.0/$TOTAL)}")
  else
    RATE="0.0%"
  fi
  printf "%6d | %9d | %9d | %s\n" $PARALLEL $SUCCESS $FAILED "$RATE"
done

echo ""
echo "=== テスト完了 ==="

検証結果

並列数を 1 から 16 まで増やして負荷をかけました。 EC2は2台構成、各エージェントの設定は MAX_CONCURRENCY=1 です。

並列数 成功(200) 失敗(503) 成功率
1 15 1 93.8%
2 21 9 70.0%
4 30 30 50.0%
8 30 90 25.0%
16 30 210 12.5%

並列数を増やしても、成功数は「30」で頭打ちになり、超過分は期待通り 503 Service Unavailable となりました。

※ 成功数が30件である理由: アプリの処理時間(1秒) + スクリプトの待機時間(1秒) = 1サイクル約2秒。 30秒間のテストでおよそ15サイクル実行され、2台のEC2がそれぞれ1リクエストずつ処理したため(15回 × 2台 = 30回)と考えられます。

アクセスログの確認

ALBのアクセスログからも、Target Optimizerの動作が確認できました。

  • 成功ログ(200)

処理時間(target_processing_time)が 1.004s となっており、アプリでの1秒遅延を経て正常に返されていました。

項目
プロトコル http
時刻 2025-11-21T16:10:02.724784Z
ALB app/alb-XXXXX/a7c2180283d10881
クライアント 203.xx.xx.xx:53426
ターゲット 172.31.24.14:80
処理時間 request=0.000s / target=1.004s / response=0.000s
ELBステータス 200
ターゲットステータス 200
受信バイト 144
送信バイト 163
リクエスト GET /?delay=1000 HTTP/1.1
User-Agent curl/8.11.1
http 2025-11-21T16:10:02.724784Z app/alb-XXXXX/a7c2180283d10881 203.xx.xx.xx:53426 172.31.24.14:80 0.000
1.004 0.000 200 200 144 163 "GET http://my-alb-XXXXX.ap-northeast-1.elb.amazonaws.com:80/?delay=1000 HTTP
/1.1" "curl/8.11.1" - - arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/optimized-tg/
41e7338699cf44e9 "Root=1-69208ed9-4b1b24d81a741acd3b4a42dd" "-" "-" 0 2025-11-21T16:10:01.720000Z 
"forward" "-" "-" "172.31.24.14:80" "200" "-" "-" TID_7290691baf7245469508aa6a42bd733c "-" "-" "-"
  • 失敗ログ(503)

ターゲットに到達する前(target ipが -)に、ALBまたはエージェントの判断により 503 が返却されています。
Target Optimizerにより、バックエンドのアプリに負荷をかけない保護が実現できていることが確認できました。

項目
プロトコル http
時刻 2025-11-21T16:10:01.738713Z
ALB app/alb-XXXXX/a7c2180283d10881
クライアント 203.xx.xx.xx:53464
ターゲット - (到達せず)
処理時間 request=-1s / target=-1s / response=-1s
ELBステータス 503
ターゲットステータス -
受信バイト 144
送信バイト 162
リクエスト GET /?delay=1000 HTTP/1.1
User-Agent curl/8.11.1
http 2025-11-21T16:10:01.738713Z app/alb-XXXXX/a7c2180283d10881 203.xx.xx.xx:53464 - -1 -1 -1 503 - 144 
162 "GET http://my-alb-XXXXX.ap-northeast-1.elb.amazonaws.com:80/?delay=1000 HTTP/1.1" "curl/8.11.1" - - 
arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/optimized-tg/41e7338699cf44e9 "Root=1
-69208ed9-4a2f731a0e5fabfe6f9d184f" "-" "-" 0 2025-11-21T16:10:01.736000Z "forward" "-" "-" "-" "503" "-" 
"-" TID_3e941d6f5423694e9f3f8e512cc30b7c "-" "-" "-"

まとめ

今回の検証により、Target Optimizer を利用することで、アプリケーションの限界を超えたリクエストを ターゲットに到達させる前に 遮断できることが確認できました。

ALBレベル(エージェント連携)で制御されるため、アプリケーションプロセスを守る「流量制限(バルクヘッド)」の手段として非常に有効です。

今回はEC2上のDockerで検証しましたが、特に Amazon ECS のサイドカーパターンで導入する場合、タスクごとの厳密な同時実行数の制御に利用できます。

特に、1リクエストの処理時間が長く、GPUリソースなどに物理的な並列限界がある「生成AI・LLMアプリケーション」などで、システム全体の安定稼働を守る手段としてご活用ください。

この記事をシェアする

FacebookHatena blogX

関連記事