🏃‍♂️

複数のApp Runnerサービスを同じカスタムドメインで動かす

2022/12/20に公開

AWS App Runner Advent Calendar 2022 の20日目の記事です。
App Runner は難しいことを考えずにコンソールをぽちぽちするだけで簡単にコンテナでオートスケールができるWEBサービスのインフラを構築できる素晴らしいサービスだと思います。

今回は App Runner でマイクロサービスっぽいものを構築してみた時に地味にハマった点を共有したいと思います。

やりたい構成

App Runner の手前に CloudFront を置き、URLのパスによって複数の App Runner サービスにリクエストを振り分けたい。

実際やってみるとどうなるか

CloudFront のオリジンに App Runner のデフォルトドメインを設定し、App Runner のカスタムドメインに CloudFront の代替ドメイン名と同じドメインを指定してドメインをリンクすることで、CloudFront のオリジンで App Runner を使うことができます。
(ちなみに、手前に CloudFront を置く場合はドメインのリンク時に証明書検証レコードの設定だけで良く、DNS ターゲットの設定は必要ありません)

さてここで、複数の App Runner サービスに同じカスタムドメインをリンクするとどうなるでしょうか。

同じカスタムドメインを複数の App Runner サービスにリンクすることはできます。しかし、CloudFront のオリジンには別の App Runner サービスのデフォルトドメインを指定していても、常にどちらか一方の App Runner サービスにルーティングされてしまう挙動をします。

App Runner はユーザーが作成したサービスより手前に App Runner 全体で共通のロードバランサーがあるらしく、この共通ロードバランサーが Host ヘッダーを見てユーザーが作成したサービスにリクエストを振り分けているものと思われます。

おそらく、App Runner は別のサービスで同じカスタムドメインがリンクされると、カスタムドメインのルーティング情報を単に上書きしており、別サービスであっても入り口のロードバランサーは共通なため、異なるサービスのデフォルトドメインに対してのリクエストであっても同じサービスにリクエストがルーティングされるものと思われました。


https://aws.amazon.com/jp/blogs/news/deep-dive-on-aws-app-runner-vpc-networking/

解決策

App Runner にカスタムドメインをリンクせず、Lambda@Edge を使い、CloudFront のオリジンリクエストの Host にビューワーの Host が設定されるところを、オリジンのドメイン名に書き換えます。

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    const host = request.headers.host[0].value;
    const domainName = request.origin.custom.domainName;
    
    request.headers["x-forwarded-host"] = [{ key: "X-Forwarded-Host", value: host }]
    request.headers.host[0].value = domainName;
    
    callback(null, request);
};

このようにすると、リクエストがオリジンで狙った App Runner サービスにルーティングされるようになります。

少しトリッキーではありますが、複数の App Runner サービスを1つのドメインで利用する構成を実現することができました。

注意点

App Runner 上のアプリケーション側では App Runner のデフォルトドメインでリクエストされることに注意が必要です。必要に応じて X-Forwarded-Host などのヘッダーを使ってビューワーの Host を得る必要があります。Cookie や CORS など、ドメインが絡む処理にはより細心の注意が必要と思います。

ペライチ

Discussion