string(4) "blog"

CONTENTS

2020.7.20

Amplify+AppSync+AuroraServerlessを一ヶ月半使って分かった苦労話

written by 山本 竜二
amplify+aurora

先日、弊社の新しいプロダクト開発で、Amplifyを使っているという記事を書きました。

AWSの膨大なサービスから、プロジェクトに最適なサービスの組み合わせを考えるのは大変ですよね。

Amplifyを使えば、バックエンドの構成を意識せずにWebアプリやモバイルアプリが作れる。そんなふうに考えていた時期が私にもありました。

Amplify + AppSync + AuroraServerlessを主軸として開発を行っていますが、まぁ色々とハマるポイントが多いです。しかも、まだ歴史が浅いので情報が少ない!

今回はAmplifyを一ヶ月半使ってみて分かった、苦労話を紹介しますので、採用を検討している方は是非参考にしてみてください。

構成図

細かいお話をする前に、今回のプロダクトで使用するAWS構成図を載せておきます。

AWS構成図

当初の想定ではDynamoDBを使うつもりはなかったのですが急遽追加しました。理由は後ほど説明します。

メインのデータストアとしてAuroraServerlessを採用した理由は、扱うデータの特性的にRDBの方が向いているという事と、Amplify+AppSync+AuroraServerlessの組み合わせについての技術検証を兼ねていたからです。

うぅ・・素直にDynamoDBを使っておけば、こんな苦労する事が無かったかもしれません。まぁ何事も経験ですねー。苦労を楽しめる人間になりたいです。

さて、それではここからはAmplifyとAppSyncに焦点を当てて説明したいと思います。というのもAuroraServerless単体で見ると、特にハマりポイントって無かったんですよね。

Amplify編

まずはAmplify編から行ってみましょう。

SSR(サーバサイドレンダリング)に対応していない

Amplifyは、SSR(サーバサイドレンダリング)に対応していません。

Amplifyのコミュニティに対してSSR対応要望が上がっているのでいずれ対応すると思いますが、現状のところ使えません。

今回のプロダクトは、TwitterやFacebookへの共有機能があるので、OGP対応はしておきたいところです。しかしSPAで作っているので、そのままではOGP対応ができません。※固定レスポンスを返すOGP対応なら可能です。

なんてこった!!どうしよう?

いやそもそも、Amplifyが悪いっていうかOGP対応が必要なのに、先に調べてない私が悪いんですが・・・。

そこで白羽の矢が立ったのが、Lambda@Edgeです。
AWSにLambda@Edgeがあって良かった〜。OGP対応無理なんて言ったら、住宅ローンを抱えたまま露頭に迷うところでした。

AmplifyコンソールではLambda@Edgeが使えない

Lambda@Edgeとは、AWSのCDNサービスであるCloudFrontと連動するLambdaです。CloudFrontへのリクエストをオリジンに飛ばす前に、Lambdaを挟んで処理する事ができます。

TwitterやFacebookに共有すると、それぞれのbotがOGPのメタ情報を取りにきます。

そこで、Lambda@Edgeを使ってbotからのアクセスであれば正規のページを返すのでは無く、OGPのメタ情報だけを返してしまう事で、OGP対応ができるという仕組みです。

もう少し詳しく説明します。

1) CloudFront経由でアクセスがあるとLambda@Edgeにて、Twitter、Facebookのbotからのアクセスか判断する(user-agentを見る)

2) botからの場合、URLからキー情報を取得し、DynamoDBからOGPに必要なタイトルとか画像パスを取得する

3) 画像パスをもとにS3から画像を取得する

4) OGP情報(meta情報)だけを持つ最低限のHTMLを作成して返却する

という流れになります。

因みに、メインのデータストアであるAuroraServerlessからデータを取得せずに、DynamoDBから取得するようにしたのは、低レイテンシを実現させたかったからです。Lambda@Edgeは普通のLambdaより更に制限が多いので、処理が重くなる事は避けたいです。

ところが、ここでも一点問題が。

Amplifyには、Amplifyコンソールという、コードをビルドしデプロイまでしてくれるサービスがあります。すごく便利なサービスで、Githubリポジトリの指定ブランチが更新されたら勝手にビルドしてデプロイまでやってくれます。

従来であればCodePipelineやCodeBuild、CodeDeployを使って自前でやっていたDevOpsを、Amplifyコンソールなら、画面からポチポチっとやるだけで実現できてしまいます。

しかも、フロントエンドとバックエンドそれぞれをデプロイしてくれるスグレモノです。これが1〜2分の設定と数分の待ち時間で出来上がるのは圧巻です。

私も当然のごとくAmplifyコンソールを使っていた訳ですが、なんとAmplifyコンソールでデプロイした環境にはLambda@Edgeが使えないのです。

技術的には裏でCloudFrontが使われているっぽいのですが、CloudFrontの画面上には表示されずAWS管理となってそうです。

うぅ・・・一難去ってまた一難。そういえば20年前に亡くなったお婆ちゃんが、「Amplifyには気をつけ〜やぁ」って遺言を残していた気がします。ありがとうお婆ちゃん。忠告を忘れて見事にハマってしまったよ。

結局、Amplifyコンソールは、バックエンドのデプロイだけに使い、フロントエンドは自前でCodePipeline+CodeBuildを使ってデプロイする事にしました。

これまでのプロジェクトで蓄積したCloudFormationのテンプレートを使いまわしたので、それほど苦労した訳でも無いんですが、すげーと思っていたAmplifyコンソールが使えないのは悲しいです。

プロジェクトメンバーに「Amplifyコンソールすげーぞ。」と言ってしまったのに、どうしてくれるんだコノヤロー。ちょっと想定からハミ出した事をやろうとすると従来の手法に戻るのは辛いです。

Mockが使えない

AmplifyにはMockという、AppSync(GraphQL)をローカル環境でテストする為の仕組みが用意されています。ローカルで編集したAppSyncのスキーマ定義を、わざわざAWSにアップしなくてもローカル環境でQueryやMutationを試す事ができるスグレモノです。

ところが、MockはAuroraServerlessに対応していないんです。

Mock環境を構築してもエラーが出るから、私の設定がミスっているのだと思って試行錯誤してましたが、対応していないという結論に行き着きました。

ここは解決策が無く、ローカルで編集したスキーマ定義を毎回AWSに反映 (amplify push)する必要があるので結構面倒です。(もしくはローカルで編集した結果をAWS管理コンソールを使って、Appsyncを直接更新するか)

バックエンドへの反映に時間が掛かる

先程、Mockが使えないので、毎回AWSに反映(amplify push)する必要がある書きました。

amplify push を実行すると、CloudFormationが実行されてAWSに反映する仕組みなのですが、AppSyncのスキーマ定義が膨らんでくると、それに伴うリゾルバのマッピングテンプレートも増えてきて、反映に凄い時間が掛かるようになってきます。

当然、記述ミスとかがあるとCloudFormationの処理に失敗するのですが、何故か一通り全部の処理が実行されるまで待つ必要があるので結構時間を無駄にします。amplify push した瞬間にエラーが見つかった時は、自分自身に舌打ちしてトイレ休憩です。

対策として、自動生成されたけど使わないマッピングテンプレートを削除したり、同じ内容のマッピングテンプレートを共通ファイルにして、更新対象を減らすようにしました。

AuroraSeverlessは自力で構築する必要がある

バックエンドの構築がコマンド1つで簡単でできるのがAmplifyの良さの1つだと思います。

しかーし!AuroraServerlessの構築はAmplifyのコマンド(CLI)に対応していません。

AWSの管理コンソールやCloudFormationを使い、AuroraServerlessを構築するところまでは自力で行う必要があります。クラスタやパラメータグループ、セキュリティ設定などを行いサービスが利用できる状態に持って行くところまでは頑張りましょう。Data APIを有効化するのも忘れずに。

ただしAuroraServerlessの構築まで終われば、Amplifyのコマンドを使ってAppSyncのデータソースとして登録する事ができます。
以下、AmplifyでAuroraServerlessを使う時の参考になる公式ページになります

Amplify Framework Documentation

DynamoDBだったら、Amplifyのコマンドでサクっと構築できるのでちょっと残念ですね。RDBとなると設定項目が多くてコマンドだけで進める事ができないんでしょうかね。

AppSync編

次はAppSync編です。

自動生成されるマッピングテンプレートが使えない

AuroraServelessを自力で構築した後、AmplifyのコマンドでAppSyncのデータソース登録と、既存のテーブルからAppSync(GraphQL)のCRUD操作を含むスキーマ定義を自動生成する事ができます。

コマンド例)

$amplify add api
$amplify api add-graphql-datasource

上記コマンドを使い、対話形式でAuroraServerlessのリージョンやクラスタを選択する事ができます。

CRUD操作に必要なスキーマ定義が自動生成できるのはメッチャ便利です。
しかも、スキーマ定義だけでなく、リゾルバと呼ばれるデータソースとの紐付けまで自動的にやってくれます。

リゾルバには、リクエスト用とレスポンス用のマッピングテンプレートがあり、それぞれデータソースに対してのInput、Outputの処理を記述する事ができます。

これも自動生成されます。例えばHogeというテーブルに対するQueryだと以下ようなリクエストのマッピングテンプレートが生成されます。

{
  "version": "2018-05-29",
  "statements":   ["SELECT * FROM Hoge WHERE id=$ctx.args.id"]
}

AppSyncからパラメータとして渡ってくる id で抽出するSQLですね。

最初は純粋に感動しました。だって、CRUD操作の為のSQLを書かなくても勝手に作ってくれるのだから。

でも、単体のテーブルに対して主キーのみで抽出するようなケースってあまりないので、そのままでは使えません。そもそも主キーのみでしか抽出しないんだったら、DynamoDB使えよって話かもしれません。

単体テーブルしか使わないQueryであれば、少しSQLを修正して使えるケースもありますが、複数テーブルをJOINするようなQueryの場合は、自動生成されたものを使わないので、コツコツと自力で作りました。

複数テーブルのJOINをしたり自由なSQLを書けるのはAuroraServerlessのメリットでもありますね。

因みにDynamoDBであれば、スキーマ定義で typeに@modelというアノテーションをつけるだけで、DynamoDBのテーブルを勝手に作ってくれるし、CRUD操作のスキーマも自動生成されます。

おそらくリゾルバを修正せずにそのまま使えるのでは無いかと。
※ DynamoDBを本格的に触った事が無いので認識に齟齬があるかもしれません。

DynamoDBのシンプルな作りが、Amplifyと相性が良いんでしょうね。

VTLが辛い

AppSyncには、パイプラインリゾルバという、複数のQueryやMutationを連続して処理する仕組みがあります。Aテーブルから抽出した結果を使って、Bテーブルから抽出する。みたいな入れ子構造を処理したい時に便利です。

ここでもマッピングテンプレートを使う必要がありますが、VTL自体のショボさというか、JavaScriptなら簡単にできる事が、VTLだと自前でループ処理を作って処理する必要があったりと大変です。

あと、VTLの文法が初見だと理解に苦しみます。以下、レスポンスのマッピングテンプレートの例ですが、#で始まったりするから、AWSが裏方で使う触っちゃ駄目系だと勘違いしました。実際は簡単な処理を書いているだけですが、なんか解読する気が失せませんか?(VTL好きの方、ごめんなさい)

#set( $output = $utils.rds.toJsonObject($ctx.result) )
#if( $output.length() < 2 )
  $util.error("Invalid response from RDS DataSource. See info for the full response.", "InvalidResponse", {}, $output)
#end
#set( $output = $output[1] )
#if( $output.isEmpty() )
  #return
#end
$utils.toJson($output[0])

もし「バックエンドの処理は全てVTLで書け」と言われたら萎えますね。

解決策として、リゾルバのデータソースとしてLambdaを指定すれば、JavaScriptやPythonなど馴染みのある言語でデータの編集を行う事が可能になります。

VTLで頑張るか、Lambdaを噛ますかは悩みどころです。
最初はVTLで頑張ろうと思いやってましたが、途中らは2つ以上の処理が必要なら、 AppSync → Lambda → AppSync → AuroraServerlessという流れでバックエンドのビジネスロジックを書くようにしました。かなり開発効率は上がったと思います。

AppSync→Lambda→AuroraServerlessでも可能ですが、AppSync経由でAuroraにアクセスするように統一する事で、Subscription(リアルタイム通知)にも対応できるし、何よりAppSyncは実装が楽です。

Lambdaを使う場合、ペイロードのサイズに6Mバイトの制限があるので注意が必要です。画像や動画で6Mバイトを超えるデータを扱う場合は、フロントエンド側から直接S3にアップロードするなどの対応が必要となってきます。

おわりに

Amplify+AppSync+AuroraServerlessを一ヶ月半使ってみた感想を書いて終わりにしたいと思います。

Amplifyは、純粋にバックエンドの構築が簡単に行えるので、サーバレス構想があるなら一考の価値があると思います。

AppSyncは、AuroraServerlessとの組み合わせが発展途上という印象があるものの、以下の点に優れており、こちらも一考の価値があると思います。

  • バックエンドの実装コストをかなり軽減できる(単純なCRUDならロジック不要)
  • リアルタイム通知や、オフライン同期機能など自前で実装すると大変な機能が提供されている
  • Lambdaを用いる事で柔軟に処理ができる

AuroraServerlessは、DataAPIを通してアクセスする事でコネクションプールを必要しない為、AppSyncやLambdaとの相性が良いです。ただしトランザクション管理ができないので、RDBの設計・実装になれている場合は注意が必要です。

Amplify + AppSync + AuroraServerlessの組み合わせは、DynamonoDBとの組み合わせと比べると、若干利便性に欠ける印象ですが、それでもインフラ構築、バックエンド実装の負担を減らし、使い慣れたRDBが利用できるのでメリットは大きいと思います。

また、AmplifyはOSSで有志のエンジニアが日々、機能拡張を進めてくれています。今、不便と思っている事が近日中には解決される事もあり得るので最新情報を追っておくと良いでしょう。

以上。同じ構成を考えている方にとって採用の判断基準になれば幸いです。

Fixelは常に新しい技術を追いながら、単なる技術集団では無く、UXデザインから実装、インフラ構築、運用まで一貫してご支援させて頂きます。

「○○したいんだけどどうすればいいの?」などあれば、弊社まで是非お気軽にお声掛けください。

一覧に戻る
お問合わせ