※追記アリ: サービス仕様の変更により、すでにこの話は古くなっています。
長年独自ドメインの下でプロフィールサイトをVPSに置いていたのですけど、いまどき静的サイトを返すのに自力でnginx動かす必要もなさそうだし、VPSの料金もかかってるし、AWSでなんとかしようと考えました。
さらに、せっかくだからサーバーレスアーキテクチャーっぽくしようと思い、「サイトにアクセスが来たとき、Lambdaで自ブログのRSSを取得して、ページに最新記事のリンクを埋め込む動的な構成にしてみよう」と考え、AWSでちょっと動的なページを作ろうとしたわけなのですが、当初想定していた以上にいろいろ大変だった、という記録です。
ぼくの考えた設計(第1形態)
はじめはわりと簡単にできるやろ〜と気楽に考えてました。こんな感じで。
- API GatewayからLambdaにつなげ、LambdaでRSSを読み込み、動的にページを生成して返す。
- 独自ドメインを使いたいので、Route53でAliasにAPI GatewayのURLを指定することで遷移させる。
問題点
まず最初の想定外として、API GatewayにHTTPでアクセスができなかったこと。
- API GatewayはHTTPSアクセスしかできないため、独自ドメインで運用する場合は証明書を取得しなければならなかった。
- 証明書にお金を払いたくはなかったのでAmazon Certificate Managerでリクエストしようとした。
- しかし
ACMの証明書はCloudFrontとELBにしか対応しておらず、API Gatewayに直接適用できなかった。(2017-03-10追記:ACMがAPI Gatewayに対応しました→ Amazon API Gateway Integrates with AWS Certificate Manager (ACM)) もしやりたければCloudFrontに証明書を適用した上で、CloudFrontのoriginとしてAPI Gatewayを指定する必要があった。- おまけにAPI Gatewayは内部でCloudFrontを利用しているのでなんかダサイ。
まぁとはいえ引き下がるのも何なので、やむを得ずCloudFront→API Gatewayの二重構成にしようと考えました。そのためにACMで証明書をリクエストしようとしたのですが、ここでさらに問題発覚。
- ACMで証明書をリクエストする場合、ドメインの認証にはドメインに対して送信されたメールを受信する必要があるため、Route53→SESを連携してメールを受信しなければならなくなった。
もちろん、元々VPSに対してアクセスさせていたドメインなので、VPS内でメール受信をすればいい話でもあったのですが、この時点で気が早いことにDNS設定を変更済みだったため、またVPSへフォールバックするのも面倒になり、SESを使うパターンを採用することに。
ここで改めて全体を設計し直します。
ぼくの考えた設計(第二形態)
個人の、しかもほとんどアクセスのないプロフィールサイトなのでサクッと作るつもりが、だいぶ構成要素が増えてしまいました。Route53→CloudFront→API Gatewayを経由してLambdaをキックしてHTMLを取得。CloudFrontにはACMの証明書を適用し、HTTPSを有効化。さらにRoute53のMXレコードでSESへメール受信させる設定。書いてないですけど、受信したメールはS3バケットに蓄積させています。
たぶん、ちゃんと設定すればこれで動きます。が、なんか完成させられず。。
問題点
- CloudFrontからHTTPSを返すAPI Gatewayの連携がなんか上手く行かなかったようで、403が返ってきた。。
- というかもはやAPI GatewayからHTTPSを返すことにこだわる必要ないんでは、アクセスがあったときに動的なページ生成するんじゃなくて、あらかじめLambdaで静的ページ作っておいてS3に置いとけばいいんではと思うに至った。
んで最終形態へ。
ぼくの考えた設計(最終形態)
サービスの数は相変わらず多いですが、構成はシンプルになった気がしています。
- Lambdaファンクションは生成したHTMLをreturnするのではなく、S3バケットに保存する形に変更。
- このLambdaをAPI Gatewayに設定し、自ブログの更新があったことをトリガーに、IFTTTのMakerチャンネルで叩かせる。これにより、ブログ更新タイミングで最新記事リンクが掲載された静的ページが作られる感じになる。
- Route53→CloudFrontは、先のLambdaで静的ページを保存したS3バケットをoriginにする。
そもそもにして、例えば天気の情報のようなリアルタイム性の高いコンテンツでなければ、動的にページ生成する必要もないわけで。最初から気付けよという感じですが、まぁ動的なページ生成やってみたかったんだもの。
得られた教訓
- 現状のサーバーレスな構成はどう足掻いてもマネージドサービスの仕様に縛られる。なんとなく理解して「こんなことできそうだな」と思っても、細かく仕様を確認すると実はできない、みたいなことは往々にしてある。
- ピタゴラスイッチ的に「頑張っていろんなサービスを繋げて目的を成し遂げる」ことへの違和感がある。例えば今回、CloudFrontは本質的機能であるCDNは必要なく、API GatewayとACMを結びつけるためだけに利用しているという本末転倒感がなんとなく気持ち悪い(ここでいうピタゴラスイッチは、事物を本来の目的外の用途で使用し、組み合わせて一つのことを成そうとする、的な意味です)。
- 取りあえず作り始めて、仕様が想定と異なったから設定を変えて、別のサービスを新しく組み入れて、という作業をマネジメントコンソールで進めると、あっという間に構成がブラックボックスになる。おそらく、最初からTerraformなりServerless Frameworkなりといったデプロイツールを経由して組み立てるのがベター。
今回は個人でのニーズだったのでよいのですけど、企業でこれと同じことをやる場合、RSSの取得だけならフロントエンドで処理させる手もありますし、繋ぎ合わせた複数のサービスを管理するより、1台のウェブサーバーを管理する方がコストが低いという判断もあると思います。
手段と目的を逆転させずにAWSを活用する視点は忘れないようにしたいものだな、というのが今回の感想です。