Route53の加重ルーティング機能で、あるALBへのトラフィックを段階的に別のALBに切り替える


やりたいこと

  • あるALBへのトラフィックを別のALBに切り替える
  • 一気に切り替えるのではなく、段階的に切り替える
  • aws Management Consoleでの操作はせずに、CloudFormationでの操作のみ

最初の状態

Resources:
  Route53HostedZoneNakedDomain:
    Type: "AWS::Route53::HostedZone"
    Properties:
      Name: !Ref NakedDomain

  ElasticLoadBalancingV2LoadBalancerOld:
    Type: 'AWS::ElasticLoadBalancingV2::LoadBalancer'
    Properties:
      Name: old-alb
      Scheme: 'internet-facing'
      Subnets:
        - !Ref DummySubnetId1
        - !Ref DummySubnetId2

  ElasticLoadBalancingV2LoadBalancerNew:
    Type: 'AWS::ElasticLoadBalancingV2::LoadBalancer'
    Properties:
      Name: new-alb
      Scheme: 'internet-facing'
      Subnets:
        - !Ref DummySubnetId1
        - !Ref DummySubnetId2

  Route53RecordSetTest:
    Type: AWS::Route53::RecordSet
    Properties:
      HostedZoneId: !Ref Route53HostedZoneNakedDomain
      Name: !Ref NakedDomain
      AliasTarget:
        DNSName: !GetAtt ElasticLoadBalancingV2LoadBalancerOld.DNSName
        HostedZoneId: !GetAtt ElasticLoadBalancingV2LoadBalancerOld.CanonicalHostedZoneID
      Type: A

ALBが2つ(old, new)あり、DNSの名前解決の結果はすべてold側のALBになっています。

なお、これ以降の説明では Route53HostedZoneNakedDomainElasticLoadBalancingV2LoadBalancerOldElasticLoadBalancingV2LoadBalancerNew が変化することはありませんので、CloudFormationテンプレートからは省略します。

旧ALBのALIASレコードにWeightとSetIdentifierを追加

  Route53RecordSetTest:
    Type: AWS::Route53::RecordSet
    Properties:
      HostedZoneId: !Ref Route53HostedZoneNakedDomain
      Name: !Ref NakedDomain
      AliasTarget:
        DNSName: !GetAtt ElasticLoadBalancingV2LoadBalancerOld.DNSName
        HostedZoneId: !GetAtt ElasticLoadBalancingV2LoadBalancerOld.CanonicalHostedZoneID
      Type: A
      Weight: 100 # 追加
      SetIdentifier: alb-old # 追加

旧ALBのALIASレコードにWeightとSetIdentifierを追加することによって、ALIASレコードを加重ALIASレコードに変化させます。
この時点ではまだRoute53へのAレコードの問い合わせ結果に変化はありません。

Weight=0で新ALBのレコードを追加

  Route53RecordSetTest:
    Type: AWS::Route53::RecordSet
    Properties:
      HostedZoneId: !Ref Route53HostedZoneNakedDomain
      Name: !Ref NakedDomain
      AliasTarget:
        DNSName: !GetAtt ElasticLoadBalancingV2LoadBalancerOld.DNSName
        HostedZoneId: !GetAtt ElasticLoadBalancingV2LoadBalancerOld.CanonicalHostedZoneID
      Type: A
      Weight: 100
      SetIdentifier: alb-old

  Route53RecordSetTest2: # 追加
    Type: AWS::Route53::RecordSet
    Properties:
      HostedZoneId: !Ref Route53HostedZoneNakedDomain
      Name: !Ref NakedDomain
      AliasTarget:
        DNSName: !GetAtt ElasticLoadBalancingV2LoadBalancerNew.DNSName
        HostedZoneId: !GetAtt ElasticLoadBalancingV2LoadBalancerNew.CanonicalHostedZoneID
      Type: A
      Weight: 0
      SetIdentifier: alb-new

Weightを0にした状態で新ALBのALIASレコードを追加します。
この時点でもまだRoute53の問い合わせ結果に変化はなく、すべてのAレコード問い合わせに対して旧ALBのIPアドレスを返します。

新ALBのALIASレコードのWeight増加 & 旧ALBのALIASレコードのWeight減少

  Route53RecordSetTest:
    Type: AWS::Route53::RecordSet
    Properties:
      HostedZoneId: !Ref Route53HostedZoneNakedDomain
      Name: !Ref NakedDomain
      AliasTarget:
        DNSName: !GetAtt ElasticLoadBalancingV2LoadBalancerOld.DNSName
        HostedZoneId: !GetAtt ElasticLoadBalancingV2LoadBalancerOld.CanonicalHostedZoneID
      Type: A
      Weight: 100 # 変化
      SetIdentifier: alb-old

  Route53RecordSetTest2:
    Type: AWS::Route53::RecordSet
    Properties:
      HostedZoneId: !Ref Route53HostedZoneNakedDomain
      Name: !Ref NakedDomain
      AliasTarget:
        DNSName: !GetAtt ElasticLoadBalancingV2LoadBalancerNew.DNSName
        HostedZoneId: !GetAtt ElasticLoadBalancingV2LoadBalancerNew.CanonicalHostedZoneID
      Type: A
      Weight: 100 # 変化
      SetIdentifier: alb-new

新ALBと旧ALBのWeightを変化させることによって、Route53が新ALBのIPアドレスを返るようになります。
新旧どちらの結果がRoute53から返されるのかは確率的に決まり、それらの確率の比はweightパラメータで制御されます。
それぞれのWeightの比を徐々に変化させることによってトラフィックを徐々に新ALBに流すことができます。
以下のコマンドを実行することによって、実際のDNS問い合わせの結果の比率を確認することができます。

sort <(for i in {1..1000} ; do dig @権威DNS +norec +short A 調べたいドメイン ; done) | uniq -c | sort -rn -k1

なお、この時に問い合わせ結果に加重がつくのはRoute53の権威DNSサーバーとキャッシュDNSサーバーの間の問い合わせのみであり、キャッシュDNSサーバーとスタブリゾルバーの間の問い合わせ結果には加重が付きません。

旧ALBのALIASレコードのWeightをゼロにする

  Route53RecordSetTest:
    Type: AWS::Route53::RecordSet
    Properties:
      HostedZoneId: !Ref Route53HostedZoneNakedDomain
      Name: !Ref NakedDomain
      AliasTarget:
        DNSName: !GetAtt ElasticLoadBalancingV2LoadBalancerOld.DNSName
        HostedZoneId: !GetAtt ElasticLoadBalancingV2LoadBalancerOld.CanonicalHostedZoneID
      Type: A
      Weight: 0 # 変化
      SetIdentifier: alb-old

  Route53RecordSetTest2:
    Type: AWS::Route53::RecordSet
    Properties:
      HostedZoneId: !Ref Route53HostedZoneNakedDomain
      Name: !Ref NakedDomain
      AliasTarget:
        DNSName: !GetAtt ElasticLoadBalancingV2LoadBalancerNew.DNSName
        HostedZoneId: !GetAtt ElasticLoadBalancingV2LoadBalancerNew.CanonicalHostedZoneID
      Type: A
      Weight: 100 # 変化
      SetIdentifier: alb-new

新ALBへのトラフィックを徐々に増やしながら旧ALBのトラフィックを徐々に減らすということを進めると、最終的には旧ALBのweightがゼロになります。
この時点で旧ALBへのトラフィックがゼロになる気もしますが、実際にはキャッシュDNSサーバー内のキャッシュが無効化されるまで待つ必要があります。
ALIASレコードのTTLは60secなので、最大で60秒程度の待ち時間があります。

ここまでの手順でトラフィックの切り替えはなされたので、後は中途半端になってしまったリソース達のお掃除です。

旧ALBのALIASレコードの削除

# Route53RecordSetTest: ← 削除

  Route53RecordSetTest2:
    Type: AWS::Route53::RecordSet
    Properties:
      HostedZoneId: !Ref Route53HostedZoneNakedDomain
      Name: !Ref NakedDomain
      AliasTarget:
        DNSName: !GetAtt ElasticLoadBalancingV2LoadBalancerNew.DNSName
        HostedZoneId: !GetAtt ElasticLoadBalancingV2LoadBalancerNew.CanonicalHostedZoneID
      Type: A
      Weight: 100
      SetIdentifier: alb-new

weight=0になっている旧ALBのALIASレコードは不要なので、CloudFormationのリソースを削除します。

新ALBのALIASレコードのWeightとSetIdentifierを削除

  Route53RecordSetTest2:
    Type: AWS::Route53::RecordSet
    Properties:
      HostedZoneId: !Ref Route53HostedZoneNakedDomain
      Name: !Ref NakedDomain
      AliasTarget:
        DNSName: !GetAtt ElasticLoadBalancingV2LoadBalancerNew.DNSName
        HostedZoneId: !GetAtt ElasticLoadBalancingV2LoadBalancerNew.CanonicalHostedZoneID
      Type: A
#     Weight: 100 削除
#     SetIdentifier: alb-new 削除

新ALBのALIASレコードのWeightとSetIdentifierは不要になったので削除します。

まとめ

Route53の加重ルーティング機能を使うことでALBの切り替えを行うことができました。
ドキュメントには通常のALIASレコードを加重ALIASレコードに変化させる方法や、その逆の方法に関する説明があまりなかったので、実際に試してみました。