AWS CloudWatch Synthetics Canary + IPv6を検証してみた

みなさん、こんにちは！
AWS CloudWatch Synthetics Canary + IPv6を検証してみた結果をお伝えできればと思います。

## AWS CloudWatch Syntheticsについて
AWS CloudWatch 合成モニタリング（Canary）は、アプリケーションの可用性やパフォーマンスを監視するための機能です。
Canary（カナリア）と呼ばれるスクリプトを作成し、定期的にWebサイトやAPIのエンドポイントを模擬ユーザーとしてテストします
。異常が検出されるとアラートを発し、問題を早期に発見できます。Lambda上で動作し、Node.jsやPython（selenium）でカスタムスクリプトを記述可能。
S3やCloudWatch Logsと統合し、詳細なログやスクリーンショットを取得できます。

## Amazon CloudWatch SyntheticsにIPv6サポートが追加
2025年1月より、VPC 内で実行されている Canary が IPv6 エンドポイントにアウトバウンドリクエストを送信できるようになり、IPv6 専用のデュアルスタック対応のエンドポイントを IPv6 経由で監視できるようになりました。
また、新しいデュアルスタック互換のリージョナルエンドポイントを通じて、IPv4 と IPv6 の両方で CloudWatch Synthetics API にアクセスすることもできます。
さらに、VPC 内の Synthetics への PrivateLink アクセスが IPv6 接続を介して利用できるようになりました。

## 構成図
本記事はAWS CloudWatch Synthetics Canaryを利用して、IPv6のみのAPIアプリへのリクエストをテストします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/6de0af4d-4e4f-4c6e-a5c7-cea1c68b8613.png)

## 環境構築
### VPC
#### デュアルスタックVPCを作成
```ruby:qiita.rb
aws ec2 create-vpc --cidr-block 10.23.23.0/24 --region ap-northeast-1 --amazon-provided-ipv6-cidr-block --tag-specifications "ResourceType=vpc,Tags=[{Key=Name,Value=foov6-canaryvpc}]"
```
### サブネット
#### NAT用のIPv4のみサブネットを作成
```ruby:qiita.rb
aws ec2 create-subnet --vpc-id $vpc-id --cidr-block 10.23.23.0/25 --region ap-northeast-1 --tag-specifications "ResourceType=subnet,Tags=[{Key=Name,Value=foov6-sbnt-canarynat44}]"
```

#### AWS CloudWatch Synthetics Canary用のデュアルスタックサブネットを作成
```ruby:qiita.rb
aws ec2 create-subnet --vpc-id $vpc-id --cidr-block 10.23.23.128/25 --ipv6-cidr-block 2406:da14:1b25:1800::/64 --region ap-northeast-1 --tag-specifications "ResourceType=subnet,Tags=[{Key=Name,Value=foov6-sbnt-canary}]"
```

### NAT Gateway + Internet Gateway
#### NAT Gateway用のElastic IP作成
```ruby:qiita.rb
aws ec2 allocate-address --region ap-northeast-1
```

#### NAT Gateway用のInternet Gatewayを作成
```ruby:qiita.rb
aws ec2 create-internet-gateway --tag-specifications "ResourceType=internet-gateway,Tags=[{Key=Name,Value=foov6-canarynat-igw}]" --region ap-northeast-1
```

#### Internet GatewayにデュアルスタックVPCをアタッチ
```ruby:qiita.rb
aws ec2 attach-internet-gateway --internet-gateway-id $igw-id --vpc-id $vpc-id --region ap-northeast-1
```

#### NAT Gateway作成
```ruby:qiita.rb
aws ec2 create-nat-gateway --subnet-id $subnet-id --allocation-id $eipalloc-id --region ap-northeast-1
```

### Egress-only Internet Gateway
#### IPv6用のEgress-onlyのInternet Gatewayを作成
```ruby:qiita.rb
aws ec2 create-egress-only-internet-gateway --vpc-id $vpc-id --tag-specifications "ResourceType=egress-only-internet-gateway,Tags=[{Key=Name,Value=foov6-canary-egress-gw}]" --region ap-northeast-1
```

### IPv4 + IPv6 ルーティング
#### NATゲートウェイ用のルートテーブルを作成
```ruby:qiita.rb
aws ec2 create-route-table --vpc-id $vpc-id --tag-specifications "ResourceType=route-table,Tags=[{Key=Name,Value=foov6-canarynat44-rtb}]" --region ap-northeast-1
```

#### NAT GatewayのルートテーブルにNATサブネット関連付けする
```ruby:qiita.rb
aws ec2 associate-route-table --route-table-id $rtb-natgw-id --subnet-id $subnet-natgw-id --region ap-northeast-1
```

#### AWS CloudWatch Synthetics Canary用のルートテーブルを作成
```ruby:qiita.rb
aws ec2 create-route-table --vpc-id #vpc-id --tag-specifications "ResourceType=route-table,Tags=[{Key=Name,Value=foov6-canary-rtb}]" --region ap-northeast-1
```

#### AWS CloudWatch Synthetics Canary用のルートテーブルにCanary用のデュアルスタックサブネットを関連付けする
```ruby:qiita.rb
aws ec2 associate-route-table --route-table-id $rtb-canary-id --subnet-id $subnet-canary-id --region ap-northeast-1
```

#### IPv4のインターネット向けのルートを追加
##### NAT Gatewayのルートテーブル
```ruby:qiita.rb
aws ec2 create-route --route-table-id $rtb-natgw-id --destination-cidr-block 0.0.0.0/0 --gateway-id $igw-id --region ap-northeast-1
```

##### AWS CloudWatch Synthetics Canaryのルートテーブル
```ruby:qiita.rb
aws ec2 create-route --route-table-id $rtb-canary-id --destination-cidr-block 0.0.0.0/0 --gateway-id $nat-id --region ap-northeast-1
```

#### IPv6のインターネット向けのルートを追加
```ruby:qiita.rb
aws ec2 create-route --route-table-id $rtb-canary-id --destination-ipv6-cidr-block ::/0 --gateway-id $eigw-id --region ap-northeast-1
```

### AWS CloudWatch Synthetics Selenium サンプルスクリプト
```ruby:qiita.rb
from selenium.webdriver.common.by import By
from aws_synthetics.selenium import synthetics_webdriver as syn_webdriver
from aws_synthetics.common import synthetics_logger as logger

def main():

    url = "$URL-or-ENDPOINT-TO-TEST"

    # Set screenshot option
    takeScreenshot = True

    browser = syn_webdriver.Chrome()
    browser.get(url)

    if takeScreenshot:
        browser.save_screenshot("loaded.png")

    response_code = syn_webdriver.get_http_response(url)
    if not response_code or response_code < 200 or response_code > 299:
        raise Exception("Failed to load page!")
    logger.info("Canary successfully executed.")

def handler(event, context):
    # user defined log statements using synthetics_logger
    logger.info("Selenium Python heartbeat canary.")
    return main()
```

### AWS CloudWatch Synthetics Canaryを作成
#### CloudWatch > Application Signals > Synthetics Canaries > Canaryを作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/1372e04f-15eb-4bab-b30d-8fe4d8c1c596.png)

まずはハートビートを選択し、 CloudWatch Synthetics Canaryを使用してモニタリングを行うための所有権を指定する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/dfab044f-f770-498f-a6d7-c4bef98777b2.png)

上記のSeleniumのサンプルスクリプトを利用する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/0ae054db-7669-40b0-8c7e-a7771de3247c.png)

スクリプトの実行スケジュールを指定する。テストのため、毎回手動で実行するように設定する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/29bbc092-0f99-4bba-bc1b-6b7e55ea22da.png)

データ保持、データストレージ、アクセス許可については、デフォルトの値を選択する。
VPCは「Allow IPv6 traffic for dual-stack subnets」を有効にするとCanaryがIPv6のインターネット向けの通信ができるようになります。
※サブネットがデュアルスタックに対応していない場合、選択できないのでご注意ください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/08e25ff2-72b1-4591-b913-98dcbbc59f39.png)

#### スクリプトの結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/7f7c4439-7420-418d-a690-0b85f38f5d22.png)

### Dual Stack or IPv6-Only?
試しでIPv4のインターネット向けのルーティングがなくなった場合、Canaryのテストが成功するか失敗するかを確認してみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/a6111039-f5fd-4723-b55b-9438d4505473.png)

残念ながら、Synthetics Canaryに付与されているサブネットにNAT Gateway及びIPv4のインターネット通信がないとテストが失敗します。

## 参考
* [VPC で Canary を実行する](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Canaries_VPC.html)