1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AWSの障害から学ぶトレードオフアーキテクチャ 〜我々は何を意識し何を守るべきか〜

1
Posted at

AWSの障害から学ぶトレードオフアーキテクチャ

はじめに

2025年10月19日〜20日(PDT)、AWS の us-east-1(バージニア北部)リージョンで大規模な障害が発生しました。起点は Amazon DynamoDB の DNS 管理システムにおける競合状態(レースコンディション)でしたが、その影響は EC2、NLB、Lambda、ECS、IAM など多くのサービスに波及し、最大で約14時間にわたりワークロードに影響を及ぼしました。

この記事では、障害そのものの詳細な分析に加えて、「自分たちのワークロードをどう守るべきだったのか」 をアーキテクチャ設計の判断軸で考えます。特に、コントロールプレーンとデータプレーンの分離、AWS Well-Architected Framework が提唱する静的安定性、そして可用性とコスト効率のトレードオフに焦点を当てます。


1. 障害の全体像

1.1 何が起きたのか

障害の根本原因は、DynamoDB の DNS 管理システム内の 競合状態 でした。DynamoDB は各リージョンで大規模なロードバランサーフリートを運用しており、数十万の DNS レコードを自動管理しています。このシステムは「DNS プランナー」と「DNS エナクター」という2つの独立したコンポーネントで構成されています。

DNS エナクターは3つの AZ で冗長に動作し、それぞれが独立して DNS プランを Route 53 に適用します。通常、この設計は高い回復力を提供しますが、今回は以下の連鎖的な事象が発生しました。

  1. 1つ目の DNS エナクターが複数エンドポイントの更新中に異常な遅延が発生
  2. その間に DNS プランナーが新世代のプランを複数生成
  3. 2つ目の DNS エナクターが最新プランを迅速に適用完了
  4. 2つ目のエナクターのクリーンアッププロセスが古いプランの削除を開始
  5. ちょうどこのタイミングで、遅延していた1つ目のエナクターが古いプランをリージョナルエンドポイントに適用(新しいプランを上書き)
  6. クリーンアッププロセスがこの古いプランを削除 → リージョナルエンドポイントの全 IP アドレスが消失
  7. アクティブなプランが存在しない一貫性のない状態に陥り、自動復旧が不可能に

1.2 影響の連鎖

この DNS 障害を起点に、以下のようにカスケード的に影響が波及しました。

DynamoDB DNS 消失(23:48 PDT)
    │
    ├─→ DynamoDB API エラー率上昇
    │       └─→ EC2: DWFM の状態チェック失敗
    │               └─→ ドロップレットリースのタイムアウト
    │                       └─→ DynamoDB 復旧後、DWFM が輻輳崩壊
    │                               └─→ 新規 EC2 インスタンス起動不可
    │                                       └─→ Network Manager のバックログ蓄積
    │                                               └─→ NLB ヘルスチェック失敗
    │                                                       └─→ Lambda / ECS / Connect 等への波及
    │
    ├─→ IAM / STS 認証失敗
    ├─→ Lambda 関数の作成・更新不可
    ├─→ Redshift クエリ失敗
    └─→ AWS サポートコンソール利用不可

1.3 タイムライン概要

時刻(PDT) イベント
10/19 23:48 DynamoDB DNS 消失、API エラー率上昇開始
10/20 00:38 エンジニアが DNS 状態の異常を特定
10/20 01:15 一時的な緩和策により内部サービスの一部が復旧
10/20 02:25 DynamoDB DNS 情報の完全復元
10/20 02:25〜 DWFM がドロップレットリース再確立を試みるも輻輳崩壊
10/20 05:28 DWFM 復旧、新規 EC2 起動再開(スロットル付き)
10/20 10:36 Network Manager のネットワーク伝播遅延が解消
10/20 13:50 EC2 完全復旧(スロットル全解除)
10/20 14:09 NLB ヘルスチェック自動フェイルオーバーを再有効化

1.4 最も重要な事実:既存リソースは無事だった

このタイムラインを見て気づく方もいるかもしれませんが、障害発生前に起動済みの既存 EC2 インスタンスは正常に稼働し続けていました。DynamoDB も、DNS による名前解決が不能になっただけで、既に確立された接続やデータ自体が破損したわけではありません。

この事実が、以降の議論の出発点になります。


2. コントロールプレーンとデータプレーン

2.1 概念の整理

AWS のサービスは、内部的に コントロールプレーンデータプレーン に分離して設計されています。この概念はネットワーク機器(ルーター)の世界に由来するもので、AWS の障害分離境界(Fault Isolation Boundaries)の中核をなす考え方です。

コントロールプレーン は、リソースの作成・変更・削除などの管理操作(CRUDL: Create, Read, Update, Delete, List)を担います。データプレーン は、日常的なサービストラフィックの処理を担います。

具体的には以下のように分類できます。

サービス コントロールプレーン データプレーン
EC2 RunInstances, TerminateInstances インスタンス上のトラフィック処理
DynamoDB CreateTable, UpdateTable GetItem, PutItem, Query
Lambda CreateFunction, UpdateFunctionCode 関数の実行(Invoke)
Route 53 レコードの作成・変更(Management API) DNS クエリの応答、ヘルスチェック
ELB/NLB ロードバランサーの作成・変更 トラフィックの分散処理
Auto Scaling スケーリングポリシーの設定 スケーリングの実行(EC2 起動 = CP依存)

2.2 設計上の違い

AWS は公式ドキュメントで、コントロールプレーンとデータプレーンの設計目標が異なることを明示しています。

  • データプレーン: 高い可用性目標で設計され、比較的シンプル
  • コントロールプレーン: 強い整合性と耐久性を優先して設計され、相対的に可用性が低い

これは設計上のトレードオフです。コントロールプレーンは「正しい状態を確実に反映すること」を重視するため、分散システムにおける CAP 定理の文脈では一貫性(Consistency)寄りの設計になりがちです。一方、データプレーンは「リクエストに応答し続けること」を重視し、可用性(Availability)寄りの設計となっています。

2.3 今回の障害との関連

今回の障害を、コントロールプレーン/データプレーンの視点で整理すると、影響の構造が明確に浮かび上がります。

コントロールプレーンが影響を受けたもの:

  • EC2 の新規インスタンス起動(RunInstances
  • Lambda 関数の作成・更新
  • ECS / EKS のコンテナ起動
  • DynamoDB への新規接続の確立(DNS 解決不能)

データプレーンが維持されたもの:

  • 既存 EC2 インスタンスのトラフィック処理
  • DynamoDB の既存接続を通じたデータ読み書き(接続維持できていた場合)
  • 既に起動済みの Lambda 関数の実行基盤

つまり、「すでにプロビジョニングされ、稼働していたリソースは動き続けた」 のです。これは偶然ではなく、AWS がコントロールプレーンとデータプレーンを分離して設計していることの直接的な結果です。

コントロールプレーンはデータプレーンよりも統計的に障害が発生しやすい。データプレーンはコントロールプレーンから到着するデータに依存するが、コントロールプレーンの障害に直面しても既存の状態を維持し、動作を継続する。

— AWS Well-Architected Framework, Reducing the Scope of Impact with Cell-Based Architecture


3. 静的安定性:Well-Architected Framework の知恵

3.1 静的安定性とは

AWS Well-Architected Framework の信頼性の柱では、REL11-BP05「静的安定性を使用してバイモーダル動作を防止する」 というベストプラクティスが定義されています。

バイモーダル動作 とは、通常モードと障害モードでワークロードが異なる振る舞いをすることです。典型的な例が「AZ 障害時に新しいインスタンスを起動して対応する」という設計です。平常時は少ないインスタンス数で運用し、障害時にスケールアウトするこの設計は、障害時にコントロールプレーンへの依存が発生します。

静的安定性 とは、依存するサービスやコンポーネントに障害が発生しても、現在の状態を維持し続けられる設計を指します。静的に安定したシステムは、障害時にも通常時と同じモードで動作し続けます。

3.2 具体例:3AZ 構成におけるキャパシティ設計

静的安定性の代表例を見てみましょう。

バイモーダル設計(静的安定性が低い):

通常時: 各 AZ に 2 台(計 6 台)、必要キャパシティ = 6 台分
AZ 障害時: 残り 2 AZ の 4 台 + Auto Scaling で 2 台追加起動 → 6 台分確保
            ↑ ここで RunInstances(コントロールプレーン)に依存

静的安定設計:

通常時: 各 AZ に 3 台(計 9 台)、必要キャパシティ = 6 台分
AZ 障害時: 残り 2 AZ の 6 台でそのまま処理 → コントロールプレーンへの依存なし

後者は平常時に 50% のオーバープロビジョニングが必要ですが、障害時に新しいリソースを起動する必要がなく、コントロールプレーンに依存しません。

3.3 REL11-BP04「復旧中はコントロールプレーンではなくデータプレーンを利用する」

Well-Architected Framework のもう一つの重要なベストプラクティスが、REL11-BP04 です。これは「障害からの復旧や緩和を実施する際、コントロールプレーンではなくデータプレーンに依存すべき」という原則です。

たとえば Route 53 の場合、ルーティングポリシーの変更はコントロールプレーン操作ですが、DNS クエリの応答やヘルスチェックの実行はデータプレーン操作です。Route 53 のデータプレーンは 100% の可用性 SLA で設計されています。そのため、障害時のフェイルオーバーには Route 53 のレコード変更ではなく、Amazon Application Recovery Controller(ARC)のようなデータプレーンベースのメカニズムを使うことが推奨されています。

3.4 今回の障害で静的安定性が効いた領域

今回の障害に照らすと、以下のように整理できます。

静的安定性が効いたケース:

  • 障害前に起動済みの EC2 インスタンスは、コントロールプレーン障害の影響を受けずに稼働を継続した
  • 十分なキャパシティが事前にプロビジョニングされていたワークロードは、スケーリング不要で影響を回避できた

静的安定性が不足していたケース:

  • 障害時にスケールアウトが必要だったワークロードは、EC2 の起動不可によりキャパシティを確保できなかった
  • NLB のヘルスチェック失敗により新規ノードの組み込みが失敗し、キャパシティが減少した

4. Auto Scaling のジレンマ

4.1 Auto Scaling はコントロールプレーン操作

ここまでの議論を踏まえると、ある重要な事実に行き着きます。

EC2 Auto Scaling のスケールアウトは、EC2 の RunInstances API(コントロールプレーン)に依存しています。

つまり、今回のような EC2 コントロールプレーンの障害下では、Auto Scaling によるスケールアウトは機能しません。CloudWatch アラームがトリガーされ、Auto Scaling が新しいインスタンスの起動を試みても、RunInstances API が insufficient capacityrequest limit exceeded を返すため、キャパシティを追加できないのです。

4.2 しかし、Auto Scaling はコスト効率に優れている

一方で、Auto Scaling を使わないという選択は、コスト面で大きなペナルティを伴います。

たとえば、ピーク時に 20 台、通常時に 5 台必要なワークロードを考えます。

戦略 常時稼働台数 コスト(概算) CP 障害耐性
Auto Scaling 5〜20台(動的) 低い 低い
ピーク分を常時確保 20台(固定) 高い(4倍) 高い

年間で考えると、オーバープロビジョニングのコスト差は膨大になり得ます。ここに 可用性とコスト効率のトレードオフ が明確に存在します。

4.3 Auto Scaling が「不向き」なわけではない

ただし、ここで注意すべき点があります。今回のような大規模なコントロールプレーン障害は、AWS の長い歴史の中でも極めてまれな事象です。Auto Scaling は平常時の需要変動への対応、個別インスタンスの障害からの復旧、AZ 単位の障害対応など、日常的なレジリエンスにおいて極めて有効です。

問題は、「Auto Scaling だけに依存する設計」がコントロールプレーンの広域障害に対して脆弱である ということです。これは Auto Scaling の欠点ではなく、設計上の前提の問題です。


5. トレードオフの構造

5.1 可用性とコスト効率の本質的な緊張関係

ここまでの議論を抽象化すると、以下のトレードオフ構造が見えてきます。

コントロールプレーンへの依存度
    高い ──────────────────── 低い
    │                          │
    ├─ コスト効率: 高い         ├─ コスト効率: 低い
    ├─ 柔軟性: 高い            ├─ 柔軟性: 低い
    ├─ 平常時の最適化: 容易     ├─ 平常時の最適化: 困難
    │                          │
    ├─ CP 障害耐性: 低い       ├─ CP 障害耐性: 高い
    └─ 復旧のリスク: 高い      └─ 復旧のリスク: 低い

これは二者択一ではなく、ワークロードの特性と要件に応じてスペクトラム上のどこに位置するかを選択する 問題です。

5.2 判断の軸

トレードオフを判断する際の主要な軸は以下の3つです。

ビジネスインパクト: そのワークロードが停止した場合の損害はどの程度か。EC サイトのチェックアウト処理と、社内のバッチレポート生成では、許容できるダウンタイムが全く異なります。

SLA / SLO の要件: 99.9% と 99.99% の可用性の間にはコストと複雑性の大きな差があります。年間ダウンタイムに換算すると、99.9% は約8.7時間、99.99% は約52分です。今回の EC2 への影響は約14時間でした。

コスト制約: 高い予算を確保できれば全てをマルチリージョンで構成することも可能ですが、現実にはコスト制約があります。重要なのは「どこにコストをかけるか」の優先順位付けです。


6. とりうる選択肢の比較

今回の障害を踏まえ、コントロールプレーン障害への耐性を高めるための主要な選択肢を比較します。

6.1 比較表

観点 ① Auto Scaling(標準構成) ② 事前キャパシティ確保(静的安定性) ③ マルチリージョン構成
CP 障害耐性 ✕ スケールアウト不可 ◎ CP に依存しない ◎ 別リージョンで継続
コスト ◎ 需要に応じた最小構成 △ オーバープロビジョニング ✕ 二重運用コスト
平常時の効率 ◎ 需要追従 △ 余剰リソースが発生 △ 同期・運用オーバーヘッド
運用複雑性 ○ AWS ネイティブで容易 ○ 設計はシンプル ✕ データ同期・DNS管理が複雑
導入難易度 ○ 低い ○ 低い ✕ 高い
AZ 障害耐性 ○ AZ 間で再分散 ◎ 事前分散済み ◎ リージョン単位で分離
適合するワークロード コスト重視、需要変動大 可用性重視、需要予測可能 ミッションクリティカル

6.2 各選択肢の実装パターン

① Auto Scaling(標準構成)

最もポピュラーな構成です。CloudWatch メトリクスに基づくターゲット追跡スケーリングや、ステップスケーリングを利用します。

  • メリット: コスト最適化に最も効果的。需要の日次・週次変動が大きいワークロードに最適
  • コントロールプレーン障害時: スケールアウトが停止。現在の DesiredCapacity で稼働するインスタンスは維持されるが、追加のキャパシティは確保できない
  • 緩和策: MinCapacity を十分に高く設定しておくことで、ベースラインのキャパシティを確保。ただし、これは事実上「②事前キャパシティ確保」とのハイブリッドになる

② 事前キャパシティ確保(静的安定性)

必要なキャパシティを事前にプロビジョニングしておく戦略です。

  • EC2: On-Demand Capacity Reservations(ODCR)を使用して、特定の AZ でインスタンスタイプの容量を予約。Savings Plans や Reserved Instances と組み合わせてコストを最適化
  • EKS: Node を事前にプロビジョニングし、Cluster Autoscaler / Karpenter によるノード追加に依存しない設計。Pause Pod パターンで余剰キャパシティを確保
  • コスト最適化のポイント: 全てを静的にプロビジョニングするのではなく、ベースライン部分は静的確保 + バースト部分は Auto Scaling というハイブリッドアプローチが現実的
キャパシティ
    │
    │  ╔══════════════════╗
    │  ║  Auto Scaling     ║  ← バースト需要(CP 障害時は利用不可を許容)
    │  ╠══════════════════╣
    │  ║  静的確保分        ║  ← ベースライン需要(CP 障害時も維持)
    │  ╚══════════════════╝
    └──────────────────────── 時間

③ マルチリージョン構成

最も堅牢ですが、最もコストと複雑性が高い選択肢です。

  • Active-Active: 複数リージョンで常時トラフィックを処理。DynamoDB Global Tables、Aurora Global Database、Route 53 / Global Accelerator によるトラフィック分散
  • Active-Standby: プライマリリージョンが障害時にセカンダリへフェイルオーバー
  • 今回の障害との関連: DynamoDB Global Tables を使用していたお客様は、他リージョンのレプリカテーブルへの接続とリクエストは正常に行えていた

⚠ マルチリージョン構成の落とし穴:フェイルオーバー手段自体の CP/DP 依存

マルチリージョン構成を採用していても、フェイルオーバーの仕組み自体がコントロールプレーンに依存していたら、肝心な障害時に切り替えられない という問題があります。ここでも CP/DP のトレードオフが存在します。

Route 53 を例に見てみましょう。Route 53 は内部的にコントロールプレーンとデータプレーンが明確に分離されています。

操作 プレーン 可用性設計
DNS クエリの応答 データプレーン 100% SLA
ヘルスチェックの実行・評価 データプレーン 100% SLA
レコードの作成・変更・削除(Management API) コントロールプレーン 強い整合性を優先

ここで注意すべきは、Route 53 のコントロールプレーンは us-east-1(バージニア北部)に集約されている という点です。つまり、今回のように us-east-1 で広域障害が発生した場合、Route 53 のレコード変更による手動フェイルオーバーも影響を受けるリスクがありました。

これに対して、Amazon Application Recovery Controller(ARC) のルーティングコントロールはデータプレーン操作として設計されています。

フェイルオーバー手段 プレーン us-east-1 障害時
Route 53 レコードの手動変更 コントロールプレーン ✕ 影響を受ける可能性
Route 53 ヘルスチェック + フェイルオーバールーティング データプレーン ◎ 自動で切り替わる
ARC ルーティングコントロール データプレーン ◎ 5 リージョン分散クラスタで管理

Route 53 のヘルスチェックによる自動フェイルオーバーはデータプレーンで動作するため、コントロールプレーン障害時にも機能します。しかし、意図的にトラフィックを切り替えたい場合(段階的フェイルオーバーやフェイルバックなど)には、ARC のルーティングコントロールが有効です。ARC は 5 つの異なるリージョンに分散されたクラスタ上で動作するため、単一リージョンの障害に対して高い耐性を持ちます。

つまり、マルチリージョン構成の中にも、さらにコントロールプレーン依存度のトレードオフがある のです。せっかくマルチリージョンに投資しても、フェイルオーバーの仕組みが CP に依存していれば、その投資は障害時に十分に活かされません。

フェイルオーバーフロー全体の CP/DP マッピング

Route 53 やARC だけでなく、マルチリージョン Active-Standby のフェイルオーバーを「一連のフロー」として見ると、各ステップに CP/DP が混在していることがわかります。

ステップ 具体的な操作 CP / DP 実行リージョン
1. 障害検知 Route 53 ヘルスチェック DP グローバル分散
2a. トラフィック切り替え(自動) Route 53 フェイルオーバールーティング DP グローバル分散
2b. トラフィック切り替え(手動) Route 53 レコード変更 CP us-east-1
2c. トラフィック切り替え(ARC) ARC ルーティングコントロール DP 5リージョン分散
3. DB フェイルオーバー Aurora Global Database フェイルオーバー CP セカンダリリージョン
4. 新規リソース起動(必要な場合) EC2 RunInstances 等 CP セカンダリリージョン
5. DNS キャッシュ反映 クライアント側の DNS 解決 DP クライアント側

ここで重要なのは、ステップ3の DB フェイルオーバーもコントロールプレーン操作である という点です。Aurora Global Database のフェイルオーバーは管理 API 経由の操作であり、CP に分類されます。ただし、セカンダリリージョンで実行されるため、プライマリ(us-east-1)の障害からは分離されています。

整理すると、CP 操作のリスクは実行リージョンによって異なります。

  • 障害リージョン内の CP 操作 → 今回のような障害で直撃を受ける(Route 53 レコード変更、EC2 起動など)
  • 別リージョンの CP 操作 → プライマリの障害からは分離されているが、CP 操作であること自体のリスクは残る(Aurora フェイルオーバーなど)
  • DP 操作 → 障害時にも高い信頼性で動作する(ヘルスチェック、ARC ルーティングコントロールなど)

「マルチリージョンにすれば全て解決」ではなく、フェイルオーバーフローの中でどのステップが CP に依存し、それがどのリージョンで実行されるかを把握しておくこと が、マルチリージョン構成の信頼性を実質的に左右します。

6.3 ハイブリッドアプローチの推奨

現実的には、単一の戦略ではなく ワークロードの重要度に応じたハイブリッドアプローチ が最も合理的です。

重要度: 高  ─→ マルチリージョン Active-Active
              (決済処理、認証基盤など)

重要度: 中  ─→ 静的安定性 + Auto Scaling ハイブリッド
              (Web アプリケーション、API サーバーなど)

重要度: 低  ─→ Auto Scaling 標準構成
              (開発環境、バッチ処理、内部ツールなど)

【コラム】UAE リージョン障害が示すもう一つの視点

本記事ではバージニア北部リージョンのコントロールプレーン障害を中心に議論してきましたが、守るべき障害シナリオはそれだけではありません。2026年3月1日、AWS の ME-CENTRAL-1(UAE)リージョンで、全く性質の異なる障害が発生しました。

何が起きたか

UAE リージョンのデータセンターにドローンが直撃し、火災が発生。消防当局が電力を完全遮断しました。当初は mec1-az2 の1AZのみの影響でしたが、その後 mec1-az3 にも被害が拡大し、3AZ 中 2AZ が物理的にダウン。残る mec1-az1 も EC2 API エラーが増加し、リージョン全体が事実上の機能不全に陥りました。EC2、S3、DynamoDB、RDS、Lambda など38以上のサービスが影響を受け、AWS は顧客に対して 別リージョンへのワークロード移行 を推奨する事態となっています。

※本障害は執筆時点(2026年3月)で復旧途中であり、AWS の公式ポストモーテムは未発表です。

バージニア北部障害との決定的な違い

観点 バージニア北部(2025/10) UAE(2026/3)
原因 ソフトウェア(DNS 競合状態) 物理的破壊(ドローン攻撃)
影響レイヤー コントロールプレーン中心 コントロールプレーン + データプレーン両方
既存リソース 稼働継続 電力遮断で停止
AZ の影響 各 AZ のデータプレーンは健全 2/3 AZ が物理的に喪失

最も重要な違いは、UAE 障害ではデータプレーンも含めて停止した という点です。バージニア北部障害では「既存リソースは動き続けた」ことが静的安定性の有効性を証明しましたが、UAE障害ではそもそもリソースが動作する基盤(電力・物理インフラ)が失われています。

障害シナリオによって有効な対策は変わる

この2つの障害を並べると、対策の有効性が障害の種類によって根本的に変わることがわかります。

障害シナリオ Auto Scaling 事前キャパシティ確保 マルチリージョン
CP 障害(バージニア北部型)
AZ / リージョンの物理的喪失(UAE型)

静的安定性(事前キャパシティ確保)は、コントロールプレーン障害に対しては非常に有効ですが、物理的なインフラ喪失に対しては無力です。マルチリージョン構成だけが、両方の障害シナリオに対応できます。

このことは、トレードオフの議論に 「自分たちが守りたい障害シナリオは何か?」 という問いを加える必要があることを意味しています。コントロールプレーン障害からの防御で十分なのか、それとも物理的なリージョン喪失まで想定する必要があるのか。その答えによって、投資すべき対策とコストは大きく変わります。


7. まとめ

障害から読み取るべき3つの教訓

1. 「何が壊れたか」だけでなく「何が壊れなかったか」に注目する

今回の障害で、既存の EC2 インスタンスやプロビジョニング済みのリソースが動き続けたという事実は、AWS のコントロールプレーン/データプレーン分離設計が意図通りに機能したことを示しています。この設計原則を理解することで、障害の影響範囲を予測し、対策を講じることができます。

2. 静的安定性は「贅沢」ではなく「設計判断」である

オーバープロビジョニングは無駄に見えるかもしれませんが、コントロールプレーン障害という現実のリスクに対する保険です。Well-Architected Framework が示す通り、静的安定性のコストとビジネス価値を天秤にかけたうえで、意識的に判断すべきものです。

3. トレードオフを意識して、自分たちのワークロードに合った選択をする

Auto Scaling、事前キャパシティ確保、マルチリージョン構成のどれが「正解」かは、ワークロードの SLA 要件、ビジネスインパクト、コスト制約によって変わります。重要なのは、トレードオフの存在を認識したうえで、意図的に選択すること です。「知らなかった」と「理解したうえで受容した」は全く異なります。


参考資料

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?