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

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

# 最初の状態
<img width="581" alt="スクリーンショット 2019-05-21 14.09.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74551/3ae8957c-2ef0-2092-2e1d-d1169f13b6af.png">

```yaml
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になっています。

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

# 旧ALBのALIASレコードにWeightとSetIdentifierを追加
<img width="614" alt="スクリーンショット 2019-05-21 14.13.20.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74551/6936d3c0-0c35-58d8-44a7-781cc385f193.png">

```yaml
  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のレコードを追加
<img width="612" alt="スクリーンショット 2019-05-21 14.13.57.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74551/16478f0b-5dd4-ed37-4f65-a6bbc0ca3754.png">

```yaml
  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減少
<img width="616" alt="スクリーンショット 2019-05-21 14.14.04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74551/7bd07139-d08d-d59c-b8b9-0f3aa5187009.png">

```yaml
  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問い合わせの結果の比率を確認することができます。

```bash
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をゼロにする
<img width="619" alt="スクリーンショット 2019-05-21 14.14.12.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74551/47903aa3-ed54-563d-bd4c-e507797f41b7.png">

```yaml
  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レコードの削除
<img width="603" alt="スクリーンショット 2019-05-21 14.14.20.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74551/bcb520a9-d1f3-07bc-31e2-d13a1b95d325.png">

```yaml
# 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を削除
<img width="547" alt="スクリーンショット 2019-05-21 14.14.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74551/6f8b0af5-b44f-c532-f4b9-aa69cb4ae7e1.png">

```yaml
  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レコードに変化させる方法や、その逆の方法に関する説明があまりなかったので、実際に試してみました。