冒頭まとめ
AWS で 503 Service Unavailable を受け取ったとき、最初に確定すべきなのは「どのコンポーネントが503を返したのか」です。代表的な発生源は3つあります。第一に、Application Load Balancer(ALB)です。公式ドキュメントのとおり、ALB が503を返すのはターゲットグループに登録済みターゲットが存在しない場合です。第二に、Classic Load Balancer で、こちらはロードバランサー自体の一時的な容量不足か、登録インスタンスが存在しない場合です。第三に、S3 などの AWS サービス自体が過負荷の保護として返す503で、S3 では 503 Slow Down という形をとります。
注意すべき誤解が1つあります。「ヘルスチェックに失敗してターゲットが全部 unhealthy になると503になる」という説明を見かけますが、ELB の公式仕様では逆です。登録済みターゲットがすべて unhealthy の場合、ロードバランサーは状態にかかわらず全ターゲットへリクエストを振り分けます(fail open と呼ばれる動作)。つまり全滅時の症状は503ではなく、ターゲット自身が返すエラー(502や504など)として現れます。ALB の503は「unhealthy だから」ではなく「そもそも登録がないから」です。
エラーの概要
503 は「一時的にサービスを提供できない」ことを示すコードですが、AWS の構成ではロードバランサー・マネージドサービス・自分のアプリケーションのどれもが503を返しうるため、コードの数字だけでは原因の場所が分かりません。ALB が自身で生成する503は、ブラウザでは「503 Service Temporarily Unavailable」と表示され、CloudWatch では HTTPCode_ELB_5XX_Count(内訳として HTTPCode_ELB_503_Count)に計上され、ALB のアクセスログでは elb_status_code が 503 になります。逆に、ターゲット(アプリケーション)が返した503は HTTPCode_Target_5XX_Count とアクセスログの target_status_code 側に記録されます。この記録の場所の違いが、発生源を確定する決め手です。
まず最初に:どこが503を返したかを確定する
3つの確認で発生源を特定します。
第一に、CloudWatch メトリクスです。HTTPCode_ELB_503_Count が増えていればロードバランサー自身が生成した503(原因1・2)、HTTPCode_Target_5XX_Count 側ならターゲットのアプリケーションが返した503です(この場合の調査対象はアプリケーション側です)。
第二に、ALB のアクセスログです。elb_status_code = 503 で target_status_code が空(-)なら、リクエストはターゲットに届く前に ALB で503になっています。
第三に、S3 など API 呼び出しでの503なら、エラーメッセージ自体に発生源が書かれています。S3 の場合は Status Code: 503 とともに Slow Down という文言が含まれます(原因3)。
よくある原因と解決手順
原因1:ALB のターゲットグループに登録済みターゲットがない
公式ドキュメントが ALB の503の原因として挙げているのは、ターゲットグループに登録済みターゲットが存在しないことです。まず実際の登録状態を確認します。
aws elbv2 describe-target-health \
--target-group-arn <ターゲットグループのARN>
出力が空(TargetHealthDescriptions が [])なら、これが原因です。典型的な経緯は3つあります。ECS のサービスで実行中のタスクが0件になっている(デプロイの失敗や停止)、Auto Scaling グループがターゲットグループに関連付けられておらず、起動したインスタンスが自動登録されていない、そして Terraform などのコード化した構成でターゲットグループは作ったものの、ターゲットを結び付ける定義(aws_lb_target_group_attachment など)を書き忘れている、というものです。Auto Scaling グループとの関連付けは次で確認できます。
aws autoscaling describe-auto-scaling-groups \
--auto-scaling-group-names <ASG名> \
--query 'AutoScalingGroups[0].TargetGroupARNs'
対処は、ターゲットの登録(または登録される仕組みの修復)です。なお公式ナレッジセンターには部分的な503の変種も記載されています。クロスゾーン負荷分散を無効にしている場合、ターゲットのいないアベイラビリティゾーンのノードに届いたリクエストだけが503になります。「一部のアクセスだけ503」の場合はゾーンごとのターゲット配置を確認してください。
ターゲットが登録されているのに unhealthy な場合、前述のとおり全滅時は fail open で振り分けられるため、503の原因はここではありません。unhealthy の解消はヘルスチェック失敗の調査(セキュリティグループ、ヘルスチェックのパスと応答コード、アプリケーションの起動時間)として別途行います。
原因2:Classic Load Balancer の容量不足またはインスタンス未登録
Classic Load Balancer の503について、公式ドキュメントは2つの原因を挙げています。1つ目はロードバランサー自体の処理容量の不足で、これは一時的な事象であり、通常は数分以上続きません。続く場合は AWS サポートへの連絡が案内されています。2つ目は登録インスタンスが存在しないことで、対処はロードバランサーが応答する各アベイラビリティゾーンに最低1台のインスタンスを登録することです。状態は CloudWatch の HealthyHostCount メトリクスで確認できます。
原因3:S3 などサービス側の過負荷保護(503 Slow Down)
S3 は、特定のプレフィックス(オブジェクトキーの先頭部分)へのリクエストレートが急増すると、内部で処理能力を拡張する間、503 Slow Down を返すことがあります。
AmazonS3Exception: Slow Down (Service: Amazon S3; Status Code: 503; Error Code: 503 Slow Down; Request ID: ...)
公式の対処は3つです。第一に、指数バックオフ(失敗のたびに待ち時間を倍増させる)での再試行。AWS SDK には503への自動再試行が組み込まれているため、SDK 経由なら再試行回数の設定を見直します。第二に、リクエストレートを一気に上げず、徐々に増やすこと。S3 は背後で自動的にスケールするため、急激なスパイクを避ければ503は減ります。第三に、オブジェクトを複数のプレフィックスに分散することです。レートの上限はプレフィックス単位で適用されるため、キー設計で負荷を分散できます。発生状況は CloudWatch の S3 リクエストメトリクス(5xx エラー)や S3 Storage Lens で監視できます。
S3 以外のサービスからの503が広範囲・突発的に出ている場合は、AWS Health Dashboard で障害情報を確認し、指数バックオフでの再試行で復旧を待つのが基本です。
補足:503ではない類似エラー
503の原因として語られがちですが、別のコードが返る問題があります。Lambda の同時実行数や DynamoDB のスループットなど、呼び出し量の上限超過は、多くの AWS サービスではスロットリング専用のエラー(429 や 400 系のエラーコード)として返ります(AWS の 429 エラー)。ロードバランサー配下のターゲットが接続できない・応答しない場合は 502 または 504 です(AWS の 502 エラー、AWS の 504 エラー)。受け取ったコードがこれらなら、503ではなくそれぞれの調査に切り替えてください。
切り分けの順序
- CloudWatch とアクセスログで、503を生成したのがロードバランサーかターゲットかを確定する(HTTPCode_ELB_503_Count か HTTPCode_Target_5XX_Count か、elb_status_code か target_status_code か)。
- ALB 由来なら、describe-target-health でターゲットの登録状態を確認する。空なら登録の仕組み(ECS タスク数、ASG の関連付け、IaC の定義漏れ)を修復する(原因1)。クロスゾーン無効時はゾーンごとの配置も確認する。
- Classic Load Balancer 由来なら、HealthyHostCount と登録インスタンスを確認する。登録済みで一時的なら容量不足の可能性があり、数分待って続くならサポートへ(原因2)。
- S3 など API 呼び出しの503なら、指数バックオフで再試行しつつ、レートの漸増とプレフィックス分散を検討する(原因3)。広範な503は Health Dashboard を確認する。
確認コマンド集
# 1. ターゲットグループの登録状態と健康状態を確認
aws elbv2 describe-target-health --target-group-arn <ターゲットグループのARN>
# 2. Auto Scaling グループとターゲットグループの関連付けを確認
aws autoscaling describe-auto-scaling-groups \
--auto-scaling-group-names <ASG名> \
--query 'AutoScalingGroups[0].TargetGroupARNs'
# 3. ALB 自身が生成した503の件数を確認
aws cloudwatch get-metric-statistics \
--namespace AWS/ApplicationELB \
--metric-name HTTPCode_ELB_503_Count \
--dimensions Name=LoadBalancer,Value=<ロードバランサーの識別子> \
--start-time <開始時刻> --end-time <終了時刻> \
--period 300 --statistics Sum
# 4. 応答がロードバランサー由来かを直接確認
curl -i http://<ロードバランサーのDNS名>/
Editor’s Note
原因1の実例として、AWS 公式の質問フォーラム re:Post の投稿があります(503 Service Temporarily Unavailable Load Balancer、2024年)。Terraform で ALB・ターゲットグループ・リスナーを一式定義したのに、DNS 名にアクセスすると 503 Service Temporarily Unavailable が返るという報告です。回答では、ターゲットグループは作られているものの EC2 インスタンスをターゲットグループに結び付ける定義(aws_lb_target_group_attachment)が書かれておらず、ターゲットが未登録のままであることが指摘され、公式ドキュメントの「503はターゲット未登録」という記載がそのまま裏付けられています。構成をコードで管理していると、リソースそのものは揃っているのに「結び付け」だけが漏れる、という形で503が起きやすいことを示す実例です。
AWS の503は、同じ数字でもロードバランサーの構成不備からサービスの過負荷保護まで意味の幅が広いコードです。数字ではなく「どこが記録したか」(ELB のメトリクスか、ターゲットのメトリクスか、エラー文言のサービス名か)から調査を始めることが確実な近道です。
免責事項:本記事の内容は、執筆時点の公開情報をもとに作成したものです。ソフトウェアの仕様は予告なく変更されることがあります。最新の情報は各ツールの公式サポートページをご確認ください。本記事の情報を利用した結果生じたいかなる損害についても、著者および運営者は責任を負いかねます。
この記事でエラーは解決しましたか?