ApiGatewayの切り替えがCNAMEの書き換えだけではうまくいかなかった時の話

記事タイトルとURLをコピーする

CS1の石井です。

直近でApiGatewayを使ったサービスの切り替え作業を行いました。 切り替えの作業の中で調査したり、気づいたことを本記事にてまとめます。

切り替え作業の概要

以下の図ような構成で、api.hoge.co.jpというドメインに対して通信を行うサービスがあるとします。

旧ApiGatewayと記載しているApiGatewayから、別アカウントの新ApiGatewayと記載しているエンドポイントに通信の経路を変える、という作業段取りを考えてみます。

当初私は一番簡単に移行できそうな方式である以下の段取りを考えました。

  1. 旧アカウントでカスタムドメイン(api.hoge.co.jp)を削除
  2. 新アカウントでカスタムドメイン(api.hoge.co.jp)を作成
  3. DNSにapi.hoge.co.jpのCNAMEを新ApiGatewayのエンドポイントのDNS名に張り替え

公式のドキュメントにも同種のやり方が記載がありました。 ただし、これは手順1と2の間でダウンタイムが発生します。そのため、ダウンタイムが発生しない方法がないか調べてみました。

DNS切り替えによる移行

ダウンタイムを発生させない方式でぱっと思いついたのがDNSの切り替え方式です。

当初ラフに以下のような段取りをイメージしました。

なお、先に結論ですが、このイメージの段取りは間違っています。

端末が「api.hoge.co.jp」に対して名前解決したとき、CNAMEで新ApiGateway側のエンドポイント名が返されます。 しかし、このエンドポイント名は「new.hoge.co.jp」というカスタムドメインで払い出されています。

DNSサーバから端末にエンドポイント名をさえ返せば「new.hoge.co.jp」というカスタムドメインが付与されたエンドポイントに通信がうまくいくのでしょうか?少し疑問なので検証してみます。

検証

検証のテーマ

移行先ドメイン(pilot)が付与されたAPI Gatewayに現行ドメイン(current)をダウンタイム0で移行できるか?

検証用リソース

検証用アカウントに以下のリソースを作成しました。

ドメイン API Gatewayドメイン名 Lambdaのレスポンス
pilot.ishii.ci.serverworks.tech d-fw7skgqc19.execute-api.ap-northeast-1.amazonaws.com pilot
current.ishii.ci.serverworks.tech d-5952zc85ga.execute-api.ap-northeast-1.amazonaws.com current

検証用リソースは以下のリポジトリに保存しています。使い方はREADMEに記載しました。

https://github.com/nov03/extest

初期状態の確認

作成したリソースに対してCURLを実行してみます。

$ curl https://current.ishii.ci.serverworks.tech/message
{"message":"current"}$ 
$ curl https://pilot.ishii.ci.serverworks.tech/message
{"message":"pilot"}$ 
$ 

想定通りドメインに対応したメッセージが表示されることを確認できました。

CNAMEの書き換え

ApiGatewayとLambdaの初期状態の動作確認が完了しました。

次はエイリアスレコードで登録されたcurrent側のドメインをpilot側のApiGatewayのドメイン名(d-fw7skgqc19)で書き換えてみます。

digで名前解決の状況を確認してみます。

$ dig current.ishii.ci.serverworks.tech CNAME +short
d-fw7skgqc19.execute-api.ap-northeast-1.amazonaws.com.
$ 

反映されていることを確認できました。

切り替えの確認

currentにアクセスするとpilotのCNAMEが返却されるので、切り替えが完了したように思えますが、実際に試すとcurrentのApiGatewayが反応してしまいます。

$ dig current.ishii.ci.serverworks.tech CNAME +short
d-fw7skgqc19.execute-api.ap-northeast-1.amazonaws.com.
$ curl https://current.ishii.ci.serverworks.tech/message
{"message":"current"}$ 
$ dig current.ishii.ci.serverworks.tech CNAME +short
d-fw7skgqc19.execute-api.ap-northeast-1.amazonaws.com.
$ 

直接IPやAPI Gatewayドメイン名を指定して実行したらどうなるのか

ドメイン指定だとcurrent側に行ってしまいますが、直接ApiGatewayのIPやカスタムドメイン名を指定したらどうなるのでしょうか?


$ 
$ dig current.ishii.ci.serverworks.tech +short
d-fw7skgqc19.execute-api.ap-northeast-1.amazonaws.com.
18.179.234.192
52.197.75.112
18.180.177.88
$ 
$ curl  https://d-fw7skgqc19.execute-api.ap-northeast-1.amazonaws.com/message
{"message":"Forbidden"}$ 
$ curl  https://18.179.234.192/message
{"message":"Forbidden"}$ 
$ 

どちらもForbiddenで返されてしまいます。

Api Gatewayのログを確認しましたが、アクセスされたログがありません。どうやらエンドポイントまで到達できていないようです。

ホストヘッダーを指定して再度検証

AIに一連のやり取りを質問してみたらホストヘッダー不足を指摘していたので、ホストヘッダーを指定して実行してみます。

$ curl -H "Host: current.ishii.ci.serverworks.tech"  https://d-fw7skgqc19.execute-api.ap-northeast-1.amazonaws.com/message
{"message":"current"}$ 
$ curl -H "Host: pilot.ishii.ci.serverworks.tech"  https://d-fw7skgqc19.execute-api.ap-northeast-1.amazonaws.com/message
{"message":"pilot"}$ 
$ 

成功しました。

ホストヘッダーを指定したら接続先はpilot側のApiGatewayですが、Hostヘッダーの指定だけでcurrentとpilotの両方にアクセスできるようになりました。

次はpilot側のIPを直指定してアクセスしてみます。

$ dig d-fw7skgqc19.execute-api.ap-northeast-1.amazonaws.com +short
18.176.12.246
13.113.202.245
52.199.194.91
$ curl -k -H "Host: pilot.ishii.ci.serverworks.tech"  https://18.176.12.246/message
{"message":"pilot"}$ 
$ curl -k -H "Host: current.ishii.ci.serverworks.tech"  https://18.176.12.246/message
{"message":"current"}$ 

こちらも問題なくできました。

ApiGatewayさえ到達した後はHostヘッダーで指定されたドメインが付与されているエンドポイントが反応するという仕様のようです。

※なお、存在しないIPや名前解決できないDNS名を指定してもApiGatewayに到達できないのでHostヘッダーさえ指定すれば何でもいい、というわけではありません。

注意点:ドキュメントを探してもこの仕様について記載がなかったので、2024/11の時点でこのような動作をしているようです

今後この仕様が変わるかもしれないので、その点注意していただければと思います。

検証の結論

Hostヘッダーで指定されたドメインと、そのドメインに合致するエンドポイントが反応する、という動きを行います。

別アカウントののApiGatewayで同名のカスタムドメインの発行はApiGatewayの仕様上できないため、CNAMEを使った切り替えは不可能だと考えました。

別のやり方の検討

CNAMEを使った切り替えが不可能であればカスタムドメインにワイルドカードを使用するというやり方ならできるのではないかと考えてみました。

ワイルドカードを使う移行

概要

ワイルドカードのカスタムドメインを発行し、現行のエンドポイントに関連付けておきます。

ワイルドカードのカスタムドメインとは「*.hoge.co.jp」といった形式のカスタムドメインで「fuga.hoge.co.jp」や「moge.hoge.co.jp」といったアクセスも受ける事が可能です。

このやり方であれば現行ドメインを削除している間、ワイルドカード付ドメインが現行ドメインへのアクセスを代わりに受けることができます。

注意点:本検証では環境を正確に作成できず、後述の類似環境での検証となります。

具体的な段取り

  1. 旧アカウントで「*.ishii.ci.serverworks.tech」というカスタムドメインを発行する。
  2. 現行ApiGatewayに「*.ishii.ci.serverworks.tech」を関連付ける。
  3. Route53の現行ドメインをCNAMEで「*.ishii.ci.serverworks.tech」のApiGatewayドメイン名で書き換える。
  4. 旧アカウントで現行カスタムドメインを削除。
  5. 新アカウントで現行カスタムドメインを発行し、ApiGatewayに関連付ける。
  6. Route 53で新アカウントのApiGatewayをエイリアスレコードで登録する。

検証

新アカウント側でワイルドカード付のカスタムドメインを発行してみます。

You are not allowed to create the domain name because it would conflict with a domain name owned by another account.

早速エラーが出ました。ドキュメントには別アカウントでワイルドカードドメインを発行する場合はAWSサポートの協力が必要なようです。

注記 別の AWS アカウントで作成済みのカスタムドメイン名と競合するようなワイルドカードカスタムドメイン名を作成することはできません。たとえば、アカウント A で a.example.com が作成済みである場合、アカウント B はワイルドカードカスタムドメイン名として *.example.com を作成できません。 アカウント A とアカウント B の所有者が同じである場合は、AWS サポートセンターに連絡して例外をリクエストできます。

類似環境での検証の段取り

AWSサポートに協力仰ぐほどのものでもないかと考え、シングルアカウントでワイルドカードドメインを発行して検証してみます。

段取りは以下のように変更しました。

検証

ワイルドカード付カスタムドメインを作成し、APIマッピングを実行しました。

Hostヘッダーを指定し、エンドポイントが機能しているかアクセスしてみます。

$ curl -H 'Host: current.ishii.ci.serverworks.tech' https://d-pej1dud012.execute-api.ap-northeast-1.amazonaws.com/message
{"message":"current"}$ 
$ 

エンドポイント自体は問題ないことがわかりました。次はCNAMEを書き換えてみます。

digで名前解決を行い、正常にCNAMEが帰ることを確認しました。

$ dig current.ishii.ci.serverworks.tech +short CNAME
d-pej1dud012.execute-api.ap-northeast-1.amazonaws.com.
$ 

最後にcurrent.ishii.ci.serverworks.techのカスタムドメインを削除します。このコマンドによりワイルドカード付ドメインに対して通信が流れるようになります。

$ aws apigateway delete-domain-name --domain-name current.ishii.ci.serverworks.tech
$ echo $?
0
$ 

# current.ishii.ci.serverworks.techのカスタムドメインのApiGatewayドメイン名に対するdig
$ dig d-5952zc85ga.execute-api.ap-northeast-1.amazonaws.com +short
$ 

CURLの実行

$ curl https://current.ishii.ci.serverworks.tech/message
{"message":"current"}$ 
$ 

期待通り、ワイルドカードドメインのエンドポイントが反応してcurrentというメッセージを返してくれました。

次は削除したcurrent.ishii.ci.serverworks.techを再度作成して、pilot側に紐づけてみます。

カスタムドメインの発行とマッピング設定

これでcurrentのドメインにアクセスすればpilotが返ってくるはずです。

$ curl https://current.ishii.ci.serverworks.tech/message
{"message":"pilot"}$ 
$ 

期待通り、pilotが返ってきました。長くなりましたが、ワイルドカード付きのカスタムドメインであればダウンタイム0で移行ができそうと思います。

ただ、上述の通り別アカウントでワイルドカードドメインの発行にはAWSサポートの協力が必要です。

別アカウントへの移行というテーマから検証内容が少しずれているので、実際にやる場合はよく検証を行ってください。

おまけ

CloudFrontも同じ仕様なのでは?と思いCloudFront2つを作成し、それぞれのオリジンにpilotとcurrentを紐づけました。

#正常の動作確認
$ curl https://cloudfront-current.ishii.ci.serverworks.tech/message
{"message":"current"}$ 
$ curl https://cloudfront-pilot.ishii.ci.serverworks.tech/message
{"message":"pilot"}$ 

# currentへのアクセス時にHostヘッダーをpilotに指定 
$ curl -H "Host:cloudfront-pilot.ishii.ci.serverworks.tech" https://cloudfront-current.ishii.ci.serverworks.tech/message
{"message":"pilot"}$ 
$ 

# pilotへのアクセス時にHostヘッダーをcurrentに指定 
$ curl -H "Host: cloudfront-current.ishii.ci.serverworks.tech" https://cloudfront-pilot.ishii.ci.serverworks.tech/message
{"message":"current"}$ 
$ 

CloudFrontのオリジンに対応した代替ドメイン名をHostヘッダーに付与するとApiGatewayと同様にHostヘッダーに付与されたエンドポイントが反応するようです。

CloudFrontもワイルドカードの代替ドメインの付与ができるため、同じ段取りで以降ができるかと思います。別の機会にCloudFrontでも検証してみたいと思いました。

まとめ

  • ApiGatewayとCloudFrontはCNAMEの書き換えだけではエンドポイントは切り替わらない
  • ApiGatewayもCloudFrontもHostヘッダーで指定されたドメインが付与されているエンドポイントが通信を受ける