こんにちは、TAMの石川です。
Webサイトが障害やメンテナンスで停止している際に表示するSorryページをAWS上で切り替える方法として、CloudFrontのカスタムエラーページを利用するケースが多いかと思います。
今回は、Route53を活用して同じことができるかを検証してみました。
Sorryページの実現方法
AWSでSorryページを実現する方法はいくつかあります。
- CloudFrontのカスタムエラーページを利用(任意のページを表示可能)
- ALBのRule設定(カスタムページの表示可。ただし256バイトのサイズ制限あり)
- Route53のフェールオーバー設定を利用
今回は、あまり活用されていないRoute53を使った設定を試してみたいと思います。
Route53のフェールオーバー設定の仕組み
Route53はDNSのサービスであり、複雑な処理はできませんが、DNSのレコード応答を制御する以下の2つのオプションが用意されています。
Failover(フェールオーバー)
1つのDNSレコードに対し、プライマリとセカンダリを設定し、それぞれにヘルスチェックを用意します。 プライマリのヘルスチェックが失敗すると、セカンダリのレコードへ切り替わる仕組みです。
※プライマリは1つのみ登録可能です。
Weight(重み付け)
Route53の優先度付け機能を活用し、同じレコード内でヘルスチェックの状況に応じて複数のサーバーを名前解決先として設定できます。
例:
- web01(Weight: 50)
- web02(Weight: 50)
- Sorry(Weight: 0)
通常はweb01・web02へトラフィックが振り分けられますが、両方がダウンした場合はSorryページへ流れる仕組みです。
Sorryページへの遷移時の挙動とSEOの考慮点
Sorryページへのフェールオーバーは正常に動作しますが、1つ懸念点があります。
- SorryページがHTTP 200 OKで応答するため、一度Sorryに切り替わるとブラウザキャッシュが残り、ページを更新しても元のコンテンツが表示されないことがある。
- SEOを考慮すると、Sorryページのレスポンスコードは**403(Forbidden)や500(Internal Server Error)**で返すのが適切。
そのため、SEOを意識する場合は適切なエラーレスポンスを設定することが重要です。
Route53のFailoverとWeightを利用した環境構築
ここでは、Route53のFailoverおよびWeightを利用した環境を構築するためのyamlを紹介します。
注意: これらの設定にはVPC、Subnet、AMIのイメージID、Route53のHostedZoneIDなどが含まれており、適用環境を考慮する必要があります。
FilaOver設定の環境を作るyaml
Parameters:
KeyName:
Description: The EC2 Key Pair to allow SSH access to the instance
Type: "AWS::EC2::KeyPair::KeyName"
VPCID:
Type: String
Default: "vpc-6a3b310e"
SubnetID01:
Type: String
Default: "subnet-989aecee"
SubnetID02:
Type: String
Default: "subnet-989aecee"
SubnetID03:
Type: String
Default: "subnet-989aecee"
IMAGEID:
Type: String
Default: "ami-0f7691f59fd7c47af"
### Route53 ###
HostedZoneId:
Type: String
Default: "Z3P0P51LUVW4NB"
HostedZoneName:
Type: String
Default: "kapodev.work"
HostedServerName:
Type: String
Default: "hogeraccho"
SetIdentifierBase:
Type: String
Default: "hogeraccho"
Resources:
### 1 dai me ###
DEPLOYSG01:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: deploy-CFn01
GroupDescription: Allow SSH and HTTP access only MyIP
VpcId: !Ref VPCID
SecurityGroupIngress:
# http
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: "0.0.0.0/0"
# ssh
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: "0.0.0.0/0"
DEPLOYEC201:
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref IMAGEID
KeyName: !Ref KeyName
InstanceType: t4g.micro
NetworkInterfaces:
- AssociatePublicIpAddress: "true"
DeviceIndex: "0"
SubnetId: !Ref SubnetID01
GroupSet:
- !Ref DEPLOYSG01
UserData: !Base64 |
#!/bin/bash
sudo yum update -y
sudo amazon-linux-extras
sudo amazon-linux-extras install nginx1 -y
sudo systemctl enable nginx
sudo systemctl start nginx
sudo echo "deploy-CFn01" >/usr/share/nginx/html/index.html
Tags:
- Key: Name
Value: deploy-CFn01
HealthCheck01:
Type: 'AWS::Route53::HealthCheck'
Properties:
HealthCheckConfig:
IPAddress: !GetAtt DEPLOYEC201.PublicIp
Port: 80
Type: HTTP
ResourcePath: '/'
FullyQualifiedDomainName: !GetAtt DEPLOYEC201.PublicDnsName
RequestInterval: 30
FailureThreshold: 2
HealthCheckTags:
-
Key: Name
Value: deploy-CFn01
RecordSet01:
Type: AWS::Route53::RecordSet
Properties:
Failover: PRIMARY # PRIMARY / SECONDARY
HealthCheckId: !GetAtt HealthCheck01.HealthCheckId
HostedZoneId: !Ref HostedZoneId
Name: !Sub ${HostedServerName}.${HostedZoneName}
ResourceRecords:
- !GetAtt DEPLOYEC201.PublicIp
SetIdentifier: !Sub ${SetIdentifierBase}01
TTL: "60"
Type: "A" # Valid values for basic resource record sets: A | AAAA | CAA | CNAME | DS |MX | NAPTR | NS | PTR | SOA | SPF | SRV | TXT
### 2 dai me ###
DEPLOYSG02:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: deploy-CFn02
GroupDescription: Allow SSH and HTTP access only MyIP
VpcId: !Ref VPCID
SecurityGroupIngress:
# http
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: "0.0.0.0/0"
# ssh
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: "0.0.0.0/0"
DEPLOYEC202:
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref IMAGEID
KeyName: !Ref KeyName
InstanceType: t4g.micro
NetworkInterfaces:
- AssociatePublicIpAddress: "true"
DeviceIndex: "0"
SubnetId: !Ref SubnetID02
GroupSet:
- !Ref DEPLOYSG02
UserData: !Base64 |
#!/bin/bash
sudo yum update -y
sudo amazon-linux-extras
sudo amazon-linux-extras install nginx1 -y
sudo systemctl enable nginx
sudo systemctl start nginx
sudo echo "deploy-CFn02" >/usr/share/nginx/html/index.html
Tags:
- Key: Name
Value: deploy-CFn02
HealthCheck02:
Type: 'AWS::Route53::HealthCheck'
Properties:
HealthCheckConfig:
IPAddress: !GetAtt DEPLOYEC202.PublicIp
Port: 80
Type: HTTP
ResourcePath: '/'
FullyQualifiedDomainName: !GetAtt DEPLOYEC202.PublicDnsName
RequestInterval: 30
FailureThreshold: 2
HealthCheckTags:
-
Key: Name
Value: deploy-CFn02
### for Sorry ###
DEPLOYSG03:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: deploy-CFn03
GroupDescription: Allow SSH and HTTP access only MyIP
VpcId: !Ref VPCID
SecurityGroupIngress:
# http
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: "0.0.0.0/0"
# ssh
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: "0.0.0.0/0"
DEPLOYEC203:
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref IMAGEID
KeyName: !Ref KeyName
InstanceType: t4g.micro
NetworkInterfaces:
- AssociatePublicIpAddress: "true"
DeviceIndex: "0"
SubnetId: !Ref SubnetID03
GroupSet:
- !Ref DEPLOYSG03
UserData: !Base64 |
#!/bin/bash
sudo yum update -y
sudo amazon-linux-extras
sudo amazon-linux-extras install nginx1 -y
sudo systemctl enable nginx
sudo systemctl start nginx
sudo echo "deploy-CFn03" >/usr/share/nginx/html/index.html
Tags:
- Key: Name
Value: deploy-CFn03
HealthCheck03:
Type: 'AWS::Route53::HealthCheck'
Properties:
HealthCheckConfig:
IPAddress: !GetAtt DEPLOYEC203.PublicIp
Port: 80
Type: HTTP
ResourcePath: '/'
FullyQualifiedDomainName: !GetAtt DEPLOYEC203.PublicDnsName
RequestInterval: 30
FailureThreshold: 2
HealthCheckTags:
-
Key: Name
Value: deploy-CFn03
RecordSet03:
Type: AWS::Route53::RecordSet
Properties:
Failover: SECONDARY # PRIMARY / SECONDARY
HealthCheckId: !GetAtt HealthCheck03.HealthCheckId
HostedZoneId: !Ref HostedZoneId
Name: !Sub ${HostedServerName}.${HostedZoneName}
ResourceRecords:
- !GetAtt DEPLOYEC203.PublicIp
SetIdentifier: !Sub ${SetIdentifierBase}03
TTL: "60"
Type: "A"Weight設定の環境を作るyaml
Parameters:
KeyName:
Description: The EC2 Key Pair to allow SSH access to the instance
Type: "AWS::EC2::KeyPair::KeyName"
VPCID:
Type: String
Default: "vpc-6a3b310e"
SubnetID01:
Type: String
Default: "subnet-989aecee"
SubnetID02:
Type: String
Default: "subnet-989aecee"
SubnetID03:
Type: String
Default: "subnet-989aecee"
IMAGEID:
Type: String
Default: "ami-0f7691f59fd7c47af"
### Route53 ###
HostedZoneId:
Type: String
Default: "Z3P0P51LUVW4NB"
HostedZoneName:
Type: String
Default: "kapodev.work"
HostedServerName:
Type: String
Default: "hogeraccho"
SetIdentifierBase:
Type: String
Default: "hogeraccho"
Resources:
### 1 dai me ###
DEPLOYSG01:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: deploy-CFn01
GroupDescription: Allow SSH and HTTP access only MyIP
VpcId: !Ref VPCID
SecurityGroupIngress:
# http
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: "0.0.0.0/0"
# ssh
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: "0.0.0.0/0"
DEPLOYEC201:
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref IMAGEID
KeyName: !Ref KeyName
InstanceType: t4g.micro
NetworkInterfaces:
- AssociatePublicIpAddress: "true"
DeviceIndex: "0"
SubnetId: !Ref SubnetID01
GroupSet:
- !Ref DEPLOYSG01
UserData: !Base64 |
#!/bin/bash
sudo yum update -y
sudo amazon-linux-extras
sudo amazon-linux-extras install nginx1 -y
sudo systemctl enable nginx
sudo systemctl start nginx
sudo echo "deploy-CFn01" >/usr/share/nginx/html/index.html
Tags:
- Key: Name
Value: deploy-CFn01
HealthCheck01:
Type: 'AWS::Route53::HealthCheck'
Properties:
HealthCheckConfig:
IPAddress: !GetAtt DEPLOYEC201.PublicIp
Port: 80
Type: HTTP
ResourcePath: '/'
FullyQualifiedDomainName: !GetAtt DEPLOYEC201.PublicDnsName
RequestInterval: 30
FailureThreshold: 2
HealthCheckTags:
-
Key: Name
Value: deploy-CFn01
RecordSet01:
Type: AWS::Route53::RecordSet
Properties:
HealthCheckId: !GetAtt HealthCheck01.HealthCheckId
HostedZoneId: !Ref HostedZoneId
Name: !Sub ${HostedServerName}.${HostedZoneName}
ResourceRecords:
- !GetAtt DEPLOYEC201.PublicIp
SetIdentifier: !Sub ${SetIdentifierBase}01
TTL: "60"
Type: "A" # Valid values for basic resource record sets: A | AAAA | CAA | CNAME | DS |MX | NAPTR | NS | PTR | SOA | SPF | SRV | TXT
Weight: 50
### 2 dai me ###
DEPLOYSG02:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: deploy-CFn02
GroupDescription: Allow SSH and HTTP access only MyIP
VpcId: !Ref VPCID
SecurityGroupIngress:
# http
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: "0.0.0.0/0"
# ssh
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: "0.0.0.0/0"
DEPLOYEC202:
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref IMAGEID
KeyName: !Ref KeyName
InstanceType: t4g.micro
NetworkInterfaces:
- AssociatePublicIpAddress: "true"
DeviceIndex: "0"
SubnetId: !Ref SubnetID02
GroupSet:
- !Ref DEPLOYSG02
UserData: !Base64 |
#!/bin/bash
sudo yum update -y
sudo amazon-linux-extras
sudo amazon-linux-extras install nginx1 -y
sudo systemctl enable nginx
sudo systemctl start nginx
sudo echo "deploy-CFn02" >/usr/share/nginx/html/index.html
Tags:
- Key: Name
Value: deploy-CFn02
HealthCheck02:
Type: 'AWS::Route53::HealthCheck'
Properties:
HealthCheckConfig:
IPAddress: !GetAtt DEPLOYEC202.PublicIp
Port: 80
Type: HTTP
ResourcePath: '/'
FullyQualifiedDomainName: !GetAtt DEPLOYEC202.PublicDnsName
RequestInterval: 30
FailureThreshold: 2
HealthCheckTags:
-
Key: Name
Value: deploy-CFn02
RecordSet02:
Type: AWS::Route53::RecordSet
Properties:
HealthCheckId: !GetAtt HealthCheck02.HealthCheckId
HostedZoneId: !Ref HostedZoneId
Name: !Sub ${HostedServerName}.${HostedZoneName}
ResourceRecords:
- !GetAtt DEPLOYEC202.PublicIp
SetIdentifier: !Sub ${SetIdentifierBase}02
TTL: "60"
Type: "A"
Weight: 50
### for Sorry ###
DEPLOYSG03:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: deploy-CFn03
GroupDescription: Allow SSH and HTTP access only MyIP
VpcId: !Ref VPCID
SecurityGroupIngress:
# http
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: "0.0.0.0/0"
# ssh
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: "0.0.0.0/0"
DEPLOYEC203:
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref IMAGEID
KeyName: !Ref KeyName
InstanceType: t4g.micro
NetworkInterfaces:
- AssociatePublicIpAddress: "true"
DeviceIndex: "0"
SubnetId: !Ref SubnetID03
GroupSet:
- !Ref DEPLOYSG03
UserData: !Base64 |
#!/bin/bash
sudo yum update -y
sudo amazon-linux-extras
sudo amazon-linux-extras install nginx1 -y
sudo systemctl enable nginx
sudo systemctl start nginx
sudo echo "deploy-CFn03" >/usr/share/nginx/html/index.html
Tags:
- Key: Name
Value: deploy-CFn03
HealthCheck03:
Type: 'AWS::Route53::HealthCheck'
Properties:
HealthCheckConfig:
IPAddress: !GetAtt DEPLOYEC203.PublicIp
Port: 80
Type: HTTP
ResourcePath: '/'
FullyQualifiedDomainName: !GetAtt DEPLOYEC203.PublicDnsName
RequestInterval: 30
FailureThreshold: 2
HealthCheckTags:
-
Key: Name
Value: deploy-CFn03
RecordSet03:
Type: AWS::Route53::RecordSet
Properties:
HealthCheckId: !GetAtt HealthCheck03.HealthCheckId
HostedZoneId: !Ref HostedZoneId
Name: !Sub ${HostedServerName}.${HostedZoneName}
ResourceRecords:
- !GetAtt DEPLOYEC203.PublicIp
SetIdentifier: !Sub ${SetIdentifierBase}03
TTL: "60"
Type: "A"
Weight: 0まとめ
今回の検証では、AWSのRoute53を活用してSorryページのフェールオーバーを実現する方法を紹介しました。
- CloudFrontやALBのカスタムエラーページの代替手段として、Route53のFailoverやWeightを活用できる。
- Sorryページのレスポンスコードは適切に設定しないと、SEOやユーザー体験に影響を与える可能性がある。
- CloudFormationを活用することで、Failover/Weightの設定を簡単にデプロイ可能。
Sorryページの設計は、サイトの可用性とUXを考慮しながら、適切な手法を選択することが重要です。
今後、より柔軟なSorryページの実装方法についても検討していきたいと思います。
