AWSでSorryページの実装:Route53によるフェールオーバー設定

こんにちは、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ページの実装方法についても検討していきたいと思います。

よかったらシェアしてね!
目次