maybe daily dev notes

私の開発日誌

AWS CDK Tips: クロスリージョンのデプロイ

AWS CDK TIpsシリーズの記事です。

AWSでサービスを構築する際、単一リージョンで提供するサービスであっても、クロスリージョンのデプロイが必要になる場合がまれにあります。AWS CDKでは、そのような構成も簡単に実装可能です。今回はCDKを使ったクロスリージョンアプリのデプロイ方法をまとめます。

クロスリージョンの必要な状況

まず、クロスリージョンのデプロイが必要になるのはどのような場合でしょうか?

DRやレイテンシー低減を考慮したマルチリージョンのアーキテクチャでは、もちろん必要でしょう。しかし実はそうでない場合、つまり単一リージョンで提供するサービスであっても、次の場合などにクロスリージョンのデプロイが必要になります:

1. CloudFrontでカスタムドメインを使いたい

CloudFront Distributionはデフォルトで cloudfront.net ドメインのURLを発行します。これを独自ドメインに設定するためには、AWS Certificate Manager (ACM) の証明書をDistributionに紐付ける必要があります。このとき、ACMの証明書は必ず us-east-1 リージョンで作成されたものでなければなりません (ドキュメント)。

このため、すべてのリソースをus-east-1にデプロイする場合を除いて、複数のリージョンを跨いでリソースを作成する必要が生じます。

2. AWS WAFを使いたい

AWS WAFを使う場合、Web ACLを作成する必要があります。Web ACLはWAFの挙動を定義するためのリソースで、us-east-1 リージョンでのみ作成可能です (ドキュメント)。

CloudFront DistributionやAmazon API Gateway APIなどを作成する際に、Web ACLのARNを追加指定することで、アクセスがWAFを経由するようになります。Web ACLを参照するリソースがus-east-1以外にあれば、クロスリージョンの参照が必要になります。

3. 他にも、色々

他にも上記のようなケースは考えられます。 例えばLambda@Edgeも、関数はus-east-1のみにデプロイ可能です (ドキュメント)。 こういった制約を網羅的に知るのは難しいので、サービスを使う時に適宜調べると良いでしょう。

また、特定のリージョンでは提供されていないサービスや、逆に特定のリージョンでのみ提供されるサービスもあります (リージョンごとの対応サービスリスト)。そうした場合も、その制約を回避するためにクロスリージョンのデプロイが必要になることがあるでしょう。

CDKでクロスリージョンデプロイする方法

前置きが長くなりましたが、ここから本題です。上記のような場合に使えるいくつかの方法を紹介します。

1. CDKネイティブのクロスリージョン参照

CDKでは2.50.0から簡単にクロスリージョンのデプロイができるようになりました (PR)。以下のコードは、東京リージョンからヴァージニアリージョンのリソースを参照する例です。クロスリージョンのリソースの受け渡し (スタック間参照) が実現できています。

const app = new cdk.App();

const virginia = new VirginiaStack(app, 'VirginiaStack', {
  env: {
    region: 'us-east-1',
  },
  crossRegionReferences: true,
});

new TokyoStack(app, 'TokyoStack', {
  env: {
    region: 'ap-northeast-1',
  },
  crossRegionReferences: true,
  certificate: virginia.certificate,
});

class VirginiaStack extends cdk.Stack {
  readonly public certificate: Certificate;

  constructor(scope: Construct, id: string, props: cdk.StackProps) {
    super(scope, id, props);

    this.certificate = new Certificate(this, 'Certificate', { /* 略 */ });
  }
}

関連する2つのスタック両方に crossRegionReferences: true のpropを渡すことに注意してください。 今はpreview機能としての提供であり、crossRegionReferences がFeature Flagとして働きます。おそらくGAされればこのフラグは不要になり、完全に透過的にクロスリージョン参照を実装できるようになるでしょう。

2. カスタムリソースを使う

1の方法ではスタックをリージョンごとに作る必要があるため、やや面倒に感じることもあるでしょう。代わりに、CloudFormationのカスタムリソース機能を利用することができます。例えば ap-northeast-1のスタックにデプロイしたカスタムリソースから、us-east-1のAPIを叩いて必要なリソースを作成するのです。

このアプローチが実現されたものはいくつかあり、例えば DnsValidatedCertificateACM Certificateを作成・検証するカスタムリソースですが、作成するリージョンを任意に指定可能です。ただしこちらは既にDeprecatedであり、非推奨です。 また別の例として、AWS Prototyping SDK (PDK)*1では、AWS WAFのWeb ACLをリージョンまたぎで作成できます(参照: cloudfront-web-acl.ts)。

この方法はシングルスタックに保てるという利点はあるものの、大抵カスタムリソースのハンドラ実装がCFnの再発明になり諸々の困難が予測されます。それらを覚悟の上でなら、こちらの方法もアリでしょう。

3. cdk-remote-stack (昔の方法)

従来CDKでクロスリージョン参照といえば、cdk-remote-stack を使う方法が主流でした (更にその前は同じような機能を各自で実装していました)。ググって出てくる記事もこの方法が多いと思います。私もよくお世話になったライブラリです。

2023年では1の方法が利用できるため、この方法をあえて使う理由は無くなったと考えて良いでしょう。ただしこちらは弱い参照 (後述)なので、人によってはもうしばらく出番があるかもしれません。

1の仕組み

せっかくなので1 (CDKネイティブのクロスリージョン参照) の仕組みもまとめます。意外と手の込んだ仕様で面白いです。

基本的な思想は、CloudFormationのクロススタック参照の挙動を模擬する方針のようです。このため、例えば他のスタックに参照されているパラメータは更新や削除ができません。使用者はダウンタイムを回避するため、複数段階に分けて安全にデプロイする必要があります。(もし使用中のパラメータを書き換える事ができたら、参照している側はデプロイされるまで古いパラメータを利用し続けることになります。これでは動作の保証ができず、危険です。)

図にするとこのようなものです。Lambda関数は、CDKが自動的に作成するカスタムリソースのハンドラです。参照の更新時や削除時の挙動は、従来のクロススタック参照と似たものになります。

クロススタック参照に似た強い参照の挙動は、検証時など頻繁にリソースを更新・削除する用途では不便だという声もあります。これを受けて、弱い参照の実装も検討されているようです (RFC)。元のPR にも strong-ref というキーワードが頻出しますし、また「この仕組みはそのまま弱参照の実装に利用可能だ」という旨も記載されています。期待しておきましょう。

まとめ

AWS CDKを使えば、クロスリージョンのシステムも簡単にデプロイできます。便利に使っていきましょう!

*1:これはAWS Prototypingのオーストラリアチームが主に開発したコンストラクトライブラリです。日本ではあまり見ない方法もあるので面白いです。AWS Prototyping SDK