AWSで大規模障害 ― マルチAZ構成でも防げなかった理由と今後の対策

2025年10月20日(日本時間)、Amazon Web Services(AWS)において大規模なサービス障害が発生しました。障害は主に米国東部リージョン(US-EAST-1)で発生し、世界中の多くのオンラインサービスやアプリケーションが一時的に停止しました。

本稿では、今回の障害の概要と影響範囲、マルチAZ構成を採用していても防げなかった理由、そして今後の設計指針について整理します。

障害の概要

AWS公式ステータスによれば、障害はUS-EAST-1リージョンにおける複数サービス(DynamoDB、Lambda、API Gateway、STSなど)で発生し、エラー率の急上昇およびレイテンシの増大が確認されました。

この影響により、Snapchat、Fortnite、Signal、Perplexity、Alexaなど、多数の世界的サービスが同時にダウンしました。AWSはその後「主要な根本原因を特定し、主要機能は緩和された(fully mitigated)」と発表していますが、一部では依然として遅延やキュー残りが発生していると報じられています。

マルチAZ構成でも防げなかった理由

AWSが推奨するマルチAZ構成は、高可用性を実現するための基本的な冗長化手法です。しかし今回の障害では、マルチAZ構成を採用していても影響を受けたケースが確認されました。その理由は、障害のレイヤーが「AZ内」ではなく「リージョン全体」に及んでいたためです。

制御プレーン障害が発生

今回の障害は、個別のデータセンターやハードウェア障害ではなく、AWSの制御プレーン(サービス間のAPI、認証、ルーティングなど)に影響したとみられています。

その結果、各AZの物理的な可用性は保たれていても、API呼び出しやリソース管理を行う中核部分が機能しなくなり、アプリケーションレベルでの可用性が確保できなくなりました。

マルチAZ構成の限界

マルチAZ構成は、AZ単位の停電・ネットワーク断などに対しては効果的ですが、制御プレーンや内部ネットワーク層に障害が発生した場合には対応できません。

このため、同一リージョン内の冗長化では「論理的単一障害点(Single Point of Failure)」が残るという設計上の限界が明確に浮き彫りになりました。

今後の対策と設計指針

今回の障害を踏まえると、ランニングコストと求める耐障害性のバランスをどう考えるかが構成設計の分岐点となります。冗長化を強化するほど可用性は向上しますが、運用コストや構成の複雑さも増大します。以下では、代表的な構成パターンと考慮点を整理します。

マルチAZ構成+別リージョンでのコールドスタンバイ構成

平常時は単一リージョンで稼働させ、障害時にのみ別リージョンの環境を立ち上げる方式です。コストを抑えつつ大規模障害に備えられますが、復旧までに数時間単位のダウンタイムが発生します。

  • メリット:低コストで広域障害への備えが可能
  • デメリット:RTO(復旧時間目標)が長く、人的操作が必要

マルチAZ構成+別リージョンでのウォーム/ホットスタンバイ構成

別リージョンにも一定のリソースを常時稼働させておき、障害時に迅速に切り替える構成です。ウォームは部分稼働、ホットは完全稼働を指します。コストと可用性のバランスを柔軟に調整できます。

  • メリット:自動フェイルオーバーが可能でRTOを短縮できる
  • デメリット:維持コスト・設計複雑度が上昇

マルチリージョン構成

複数リージョンで同時稼働させるアクティブ/アクティブ構成です。トラフィック分散や地理的冗長化に優れ、リージョン全体の障害にも耐えられますが、最も高コストかつ複雑です。

  • メリット:最高水準の可用性と応答性能を確保
  • デメリット:整合性維持とコストが大きな課題

マルチクラウド構成

AWS単独ではなく、GCPやAzureなど複数のクラウドを併用する構成です。ベンダー依存リスクを分散できますが、各クラウドのAPI差異・運用統合コストが課題になります。

  • メリット:クラウド全体の障害に対する強靭性を確保
  • デメリット:開発・運用の複雑化、スキル要求の増大

災害対策設計(DR: Disaster Recovery)の最低要件

いずれの構成を採る場合でも、最低限、別リージョンでサービスを再開できるDR設計を持つことが不可欠です。データバックアップ、DNSフェイルオーバー、クロスリージョンレプリケーションなど、迅速に代替リージョンへ切り替える仕組みを整備しておくべきです。

おわりに

今回のAWS障害は、クラウド基盤における高可用性設計の限界を改めて浮き彫りにしました。マルチAZ構成は依然として有効な手法であるものの、それだけで「クラウドは止まらない」と断言することはできません。過去にはAzureやGoogle Cloud Platform(GCP)でも大規模な障害が発生しており、大手クラウドベンダーであっても絶対的な安心・安全が保証されるわけではありません。

また、ランサムウェア被害の事例では、バックアップ自体が無事であったにもかかわらず、復旧がうまくいかなかったケースも報告されています。したがって、単にバックアップを取得するだけでなく、少なくとも年に一度はバックアップからの復旧訓練を実施し、実際に復元できることを検証することが重要です。

今回の事象は、制御プレーンやサービス間依存を含むリージョン単位の障害リスクを前提とした設計、およびバックアップ運用の実効性確保の重要性を改めて認識させるものとなりました。

参考文献

Amazon API Gateway REST APIにIP制限をかける

API Gateway REST APIに対してIP制限をかける必要があったので手順を確認しました。

動作確認用のAPI Gateway REST APIを作成する

まずはAPI GatewayでAPIを作成します。以下の手順に従って作成していきます。

手順

API Gatewayコンソールhttps://console.aws.amazon.com/apigatewayにアクセスします。

選択肢の中から、REST APIの構築ボタンをクリックします。

API Gatewayを作成することは今回の主眼とは関係ないため、サンプルAPIを選択して作成します。

REST APIPetStoreが作成されました。

これは必須ではありませんが、デプロイする前にテストをして動作確認をしておきます。/ > /pets > POSTをクリックして、「テスト」を選択します。

手順に従って、{"type": "dog","price": 249.99}というテキストをリクエスト本部に貼り付け、テストボタンをクリックします。

ステータスが200になっていればテスト成功です。

動作確認ができたらAPIをデプロイボタンをクリックしてデプロイします。

今回はテスト用なので手順と同じくtestというステージを作成します。

デプロイはすぐに完了します。

動作確認のためにURLを呼び出すボタンをクリックしてのURLをコピーし、実際に使用可能なことを確認します。

自端末のターミナルからcurlコマンドで実行してみます。

$ curl https://dy1o7l2qa9.execute-api.us-east-1.amazonaws.com/test
<html>
    <head>
        <style>
        body {
            color: #333;
            font-family: Sans-serif;
            max-width: 800px;
            margin: auto;
        }
        </style>
    </head>
    <body>
        <h1>Welcome to your Pet Store API</h1>
        <p>
            You have successfully deployed your first API. You are seeing this HTML page because the <code>GET</code> method to the root resource of your API returns this content as a Mock integration.
        </p>
        <p>
            The Pet Store API contains the <code>/pets</code> and <code>/pets/{petId}</code> resources. By making a <a href="/test/pets/" target="_blank"><code>GET</code> request</a> to <code>/pets</code> you can retrieve a list of Pets in your API. If you are looking for a specific pet, for example the pet with ID 1, you can make a <a href="/test/pets/1" target="_blank"><code>GET</code> request</a> to <code>/pets/1</code>.
        </p>
        <p>
            You can use a REST client such as <a href="https://www.getpostman.com/" target="_blank">Postman</a> to test the <code>POST</code> methods in your API to create a new pet. Use the sample body below to send the <code>POST</code> request:
        </p>
        <pre>
{
    "type" : "cat",
    "price" : 123.11
}
        </pre>
    </body>
</html>%

大丈夫そうですね。

リソースポリシーで特定のIPのみがアクセスできるように制限する

次にリソースポリシーで特定のIP(今回は自端末のIP)のみアクセスできるように制限をかけてみます。

手順は以下の「特定の IP アドレスのみが API Gateway REST API にアクセスすることを許可するリソースポリシーを作成およびアタッチする」に従って行っていきます。

手順

API Gatewayコンソールhttps://console.aws.amazon.com/apigatewayにアクセスします。

リソースから先ほど作成したPetStoreAPIを選択します。

左のナビゲーションペインでリソースポリシーを選択すると、以下のような画面が表示されます。

デプロイ直後はリソースポリシーは設定されていないため、ポリシーを作成ボタンをクリックし、ポシリーの詳細下のドロップダウンリストからIP範囲拒否リストを選択すると、以下のようなポリシーが表示されます。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Deny",
            "Principal": "*",
            "Action": "execute-api:Invoke",
            "Resource": "execute-api:/{{stageNameOrWildcard}}/{{httpVerbOrWildcard}}/{{resourcePathOrWildcard}}",
            "Condition" : {
                "IpAddress": {
                    "aws:SourceIp": [ "{{sourceIpOrCIDRBlock}}", "{{sourceIpOrCIDRBlock}}" ]
                }
            }
        },
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "execute-api:Invoke",
            "Resource": "execute-api:/{{stageNameOrWildcard}}/{{httpVerbOrWildcard}}/{{resourcePathOrWildcard}}"
        }
    ]
}

色々試してみたのですが、許可のみ記載したポリシーだと指定したIPまたはCIDRブロックのみを許可できるようです。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "execute-api:Invoke",
            "Resource": "execute-api:/*",
            "Condition" : {
                "IpAddress": {
                    "aws:SourceIp": [ "{{sourceIpOrCIDRBlock}}" ]
                }
            }

        }
    ]
}

このポリシー中の{{sourceIpOrCIDRBlock}}を自身のグローバルIPに置き換えてください。

ポリシーが作成できたら、右下の変更を保存ボタンをクリックします。リソースポリシーを変更したら、それを反映させるために左のナビゲーションペインでリソースを選択し、APIをデプロイボタンをクリックしします。

先ほど作成したステージを選択してデプロイします。

デプロイが完了したことを確認します。

動作確認

設定が完了したので、動作確認をします。まずは自端末から変わらずアクセスできることを確認します。

$ curl https://dy1o7l2qa9.execute-api.us-east-1.amazonaws.com/test
<html>
    <head>
        <style>
        body {
            color: #333;
            font-family: Sans-serif;
            max-width: 800px;
            margin: auto;
        }
        </style>
    </head>
    <body>
        <h1>Welcome to your Pet Store API</h1>
        <p>
            You have successfully deployed your first API. You are seeing this HTML page because the <code>GET</code> method to the root resource of your API returns this content as a Mock integration.
        </p>
        <p>
            The Pet Store API contains the <code>/pets</code> and <code>/pets/{petId}</code> resources. By making a <a href="/test/pets/" target="_blank"><code>GET</code> request</a> to <code>/pets</code> you can retrieve a list of Pets in your API. If you are looking for a specific pet, for example the pet with ID 1, you can make a <a href="/test/pets/1" target="_blank"><code>GET</code> request</a> to <code>/pets/1</code>.
        </p>
        <p>
            You can use a REST client such as <a href="https://www.getpostman.com/" target="_blank">Postman</a> to test the <code>POST</code> methods in your API to create a new pet. Use the sample body below to send the <code>POST</code> request:
        </p>
        <pre>
{
    "type" : "cat",
    "price" : 123.11
}
        </pre>
    </body>
</html>%

問題なくアクセスできています。

次に異なるグローバルIPを持つ端末からアクセスしてみてください。例えば、PCからアクセスしている場合はスマートフォンのWiFiをOFFにして、ブラウザで同じアドレスにアクセスしてみてください。

{"Message":"User: anonymous is not authorized to perform: execute-api:Invoke on resource: arn:aws:execute-api:us-east-1:********0149:dy1o7l2qa9/test/GET/"}

IP制限が効いていると、上記のようなメッセージが返ってきます。

まとめ

IP制限や経路制限を変える方法はいくつかあります。

  • HTTP APIで作成し、VPCリンクで制限する
  • REST APIプライベートで作成し、リソースポリシーで許可するVPCを制限

今回はAPI Gatewayを作成し直す訳にはいかない事情があったので、このような対応にしました。

モバイルバージョンを終了