はじめに
前回、TerraformでEC2を2AZ・2台構成に変更し、ALBを利用して複数のEC2へアクセスを振り分ける構成を作成しました。
構成としては以下のような形です。
Internet
|
ALB
/ \
EC2 (1a) EC2 (1c)
\ /
RDS
EC2を1台だけ配置していた構成と比べると、片方のEC2に障害が発生しても、もう片方でサービスを継続できるため可用性は高くなります。
しかし構成全体を見直してみると、
「EC2を2AZに配置しただけで、システム全体をMulti-AZ化したと言えるのか?」
という疑問が出てきました。
EC2以外にも、RDSやNAT Gatewayが特定のAZに依存していれば、そこが障害点として残ります。
そこで今回は、前回のEC2 Multi-AZ化からさらに範囲を広げ、
- RDSのMulti-AZ化
- NAT GatewayのAZ依存の見直し
- Regional NAT Gatewayへの変更
を行いました。
EC2だけMulti-AZにしても十分ではなかった
前回までの構成では、EC2を以下のように配置しました。
AZ 1a AZ 1c
EC2-A EC2-B
\ /
ALB
これにより、EC2単体の障害だけでなく、一方のAZに障害が発生した場合にも、もう一方のAZでアプリケーションを継続できる構成にしました。
しかし、システムはEC2だけで構成されているわけではありません。
例えばEC2が正常でも、
EC2 ○
↓
RDS ×
となれば、Webアプリケーションは正常に動作できません。
同様に、Private SubnetのEC2が外部通信に利用しているNAT Gatewayが特定AZに依存している場合、そのAZの障害によって通信経路に影響する可能性があります。
そのため今回は、
「EC2が何台あるか」ではなく、「AZ障害が発生した場合にシステム全体がどうなるか」
という視点で構成を見直しました。
RDSをMulti-AZ化する
まず見直したのがRDSです。
Single-AZ構成の場合、RDSに障害が発生するとデータベースが利用できなくなり、EC2が2台とも正常でもアプリケーションに影響します。
そこでRDSをMulti-AZ構成に変更しました。
イメージとしては以下です。
AZ 1a AZ 1c
Primary RDS ----------> Standby RDS
通常はプライマリDBを利用し、障害が発生した場合には別AZのスタンバイへフェイルオーバーできる構成です。
Terraformでは、RDSでMulti-AZを有効化しました。
resource "aws_db_instance" "this" {
# 省略
multi_az = true
}
これによって、
EC2
├─ 1a
└─ 1c
RDS
├─ Primary
└─ Standby
という形になり、アプリケーション層だけでなくデータベース層についてもAZ障害を意識した構成になりました。
ここで、
「Multi-AZはEC2を複数AZに置くだけではなく、依存しているリソースも含めて考える必要がある」
と気付きました。
NAT GatewayにもAZ依存が残っていた
次に見直したのがNAT Gatewayです。
Private Subnetに配置したEC2からインターネットへ通信するため、NAT Gatewayを利用しています。
以前は1つのAZにZonal NAT Gatewayを配置していました。
イメージとしては以下のような構成です。
AZ 1a AZ 1c
Private EC2 Private EC2
\ /
\ /
NAT Gateway (1a)
|
IGW
EC2自体は1aと1cに分散していますが、外向き通信については1aのNAT Gatewayに依存していました。
そのため、
「EC2をMulti-AZ化した一方で、通信経路は特定AZに依存している」
という状態でした。
Zonal NAT Gatewayを複数配置する方法
Zonal NAT Gatewayを使ったまま冗長化する場合は、各AZにNAT Gatewayを配置する方法があります。
AZ 1a AZ 1c
EC2 EC2
| |
NAT GW NAT GW
| |
IGW IGW
この場合、それぞれのPrivate Subnetから同じAZのNAT Gatewayへ通信させます。
これによって、一方のAZに障害が発生しても、もう一方のAZではNAT Gatewayを利用できます。
ただし構成としては、
- AZごとのNAT Gateway
- NAT Gateway用のSubnet
- AZごとのRoute Table設定
など、管理対象が増えます。
今回の構成では、冗長性を確保しながら構成を簡素化する方法としてRegional NAT Gatewayを採用しました。
Regional NAT Gatewayへ変更
Regional NAT Gatewayでは、特定の1AZにNAT Gatewayを固定するのではなく、VPC単位で利用します。
Terraformでは、Regional NAT Gatewayとして作成するよう変更しました。
resource "aws_nat_gateway" "this" {
vpc_id = var.vpc_id
availability_mode = "regional"
tags = {
Name = "${var.name_prefix}-RegionalNAT"
}
}
これまでのZonal NAT Gatewayでは、NAT Gatewayを配置するSubnetなどを意識する必要がありました。
Regional NAT Gatewayでは、AWS側でAZへの展開が管理されるため、
AZごとにNAT Gatewayを用意して管理する構成を簡素化できる
点をメリットとして感じました。
今回Regional NAT Gatewayを採用した理由は、
「Multi-AZ構成に合わせて外向き通信も特定AZへの依存を減らしたかったこと」
と、
「Zonal NAT GatewayをAZごとに管理するより構成を簡素化できること」
の2点です。
Regional NAT Gateway作成時にIAMエラー
Regional NAT GatewayをTerraformで作成しようとした際、GitHub Actionsの terraform apply でエラーが発生しました。
原因は、
iam:CreateServiceLinkedRole
の権限不足でした。
Regional NAT GatewayではService Linked Roleが利用されます。
Service Linked Roleは、特定のAWSサービスがユーザーに代わって必要なAWSリソースを操作するために利用するIAMロールです。
通常のIAMロールでは、利用者側で用途や権限を設計します。
一方、Service Linked Roleは特定のAWSサービスに紐づいており、そのサービスが必要とする権限や信頼関係をAWS側が定義します。
今回、Regional NAT Gatewayを初めて作成する際に必要となるService Linked Roleを作成する権限が、GitHub Actionsで利用しているIAMロールにありませんでした。
そのため必要なIAM権限を追加し、再度 terraform apply を実行することで作成できました。
このエラーから、
「Terraform上ではNAT Gatewayを作成しているだけに見えても、AWS内部では別のIAMリソースが必要になる場合がある」
ということを学びました。
構成を見直してどう変わったか
最初は、EC2を1台から2台にすることをMulti-AZ化として考えていました。
変更前
ALB
|
EC2
|
RDS
そこからEC2を2AZへ配置しました。
ALB
|
├─ EC2 (1a)
└─ EC2 (1c)
|
RDS
さらに今回、RDSとNAT GatewayについてもAZ障害を意識して見直しました。
ALB
/ \
EC2 (1a) EC2 (1c)
\ /
RDS Multi-AZ
Regional NAT Gateway
今回の変更によって、
- アプリケーション層:EC2を2AZへ分散
- データベース層:RDSをMulti-AZ化
- 外向き通信:Regional NAT Gatewayへ変更
という形で、EC2だけではなくシステム全体を見ながらMulti-AZ構成を考えられるようになりました。
今回学んだこと
今回一番大きかった学びは、
「Multi-AZ = EC2を2つのAZに配置することではない」
ということです。
最初はEC2を1aと1cに配置した時点で、冗長化構成としてかなり完成したと考えていました。
しかし、システム全体を見ると、
ALB
↓
EC2
↓
RDS
だけでなく、EC2が利用するネットワーク経路なども含めて複数の要素があります。
その中のどこかが特定のAZに依存していれば、AZ障害時の影響が残ります。
そのため、単純に
「リソースを2台にする」
のではなく、
「このAZが利用できなくなった場合、どのリソースに影響が出るのか」
という視点で構成を見ることが重要だと学びました。
今後
現在、EC2は1aと1cに1台ずつ配置しているため、一方のEC2に障害が発生しても、もう一方でサービスを継続できます。
ただし、障害が発生したEC2を自動的に新しいEC2へ置き換える仕組みはありません。
通常
1a : EC2-A
1c : EC2-B
↓
EC2-A障害
1a : ×
1c : EC2-B
そのため次は、Launch TemplateとAuto Scaling Groupを利用して、
EC2障害
↓
ASGが検知
↓
新しいEC2を起動
↓
必要台数へ自動復旧
という構成を検討しています。
今回のMulti-AZ化では、
「障害時にも別AZでサービスを継続できる構成」
を考えました。
次はそこからさらに一歩進めて、
「障害後に自動的に正常な台数へ戻る構成」
について学習していきます。