「サーバレスがもてはやされてるけど RDS 使いたい時もあるじゃん?」について調べた

Docker・Kubernetes を前提としたコンテナアプリケーションの他に、AWS Lambda のような FaaS・サーバレスアプリケーションが主流になって久しい。それまでの「MVC アプリ + DB (RDS)」といったモノリシックな構成は、スケーラビリティに欠けるなどの理由で避けられるようになってきた。

しかし、サーバレスやコンテナといったステートレスなアプリ基盤でも、何らかのデータを永続化したり、永続化データを取得して返したり、といったことは発生しうる。そんな時、データ永続化層に RDS を組み合わせて良いものか、何をどう扱うのが良いものなのか。今さらな話題かもしれないが、素人が調べたことを簡単にまとめておく。

目次

サーバレスと RDS の組合せはアンチパターン

まず、「サーバレスと RDS」の組み合わせはアンチパターン。組み合わせた設計にしないのが妥当といわれている。なぜかというと、リクエストの数だけアプリコンテナが作成され、コンテナごとに DB コネクションが張られてしまうからである。

じゃあサーバレス基盤において、データ永続化はどうしたらいいの?というと…

サーバレスアプリには「分散型 DB」を組み合わせる

サーバレスアプリケーションには、Amazon DynamoDB のような分散型 DB (NoSQL 系 DB) を組み合わせることが一般的には推奨されている。

Amazon DynamoDB などは、データ一貫性を犠牲にすることでスケールできるようにしているので、サーバレスアプリ側の接続数増加にもスケールアウトで対応できるようになっているワケだ。

ということで、無尽蔵にスケールしうるサーバレスアプリに対しては、RDS よりもスケールさせやすい、分散型 DB を選択するのが妥当であろう。

分散型 DB のマネージドサービスは、VPC の準備なども必要ないことから管理コストは低いが、一方でその仕組みから、検索処理やトランザクション処理が苦手だったりもする。こうした用途で使いたい場合はどうしたらいいだろうか。

サーバレス・分散型 DB の組み合わせで検索処理を高速に行うには

分散型 DB はデータが分散配置されているため、それらを通して検索するような処理はパフォーマンス面で不利だったりする。そんな時に高速に検索処理を行うにはどうしたらいいかというと、RDS とのハイブリッド構成という案がある。

検索処理を行うサーバレス関数が結局 RDS に接続するので、そこのリクエスト量と DB 性能は考慮しておかないといけない。

サーバレス + RDS でコネクションプーリングしたい

少し考え方を変えて、「サーバレスアプリと RDS の間に、コネクションプーリングしてくれる場所を作れないかしら?」と思ったところ、AWS RDS Proxy というサービスがあった。

AWS に限ったサービスではあるものの、サーバレスといえば Lambda、といってもいいぐらいデファクト・スタンダードなサービスなので、採用は比較的容易であろう。

他には、Azure も Functions の機能次第で、似たような調整ができるようだ。

マイクロサービスをまたがるトランザクションをいかに扱うか

Kubernetes 上にデプロイした複数のマイクロサービスだったり、複数のサーバレス関数なんかで、トランザクションを扱いたい時にどうしたらいいかを調べた。

→ そこで、分散トランザクションという考え方・仕組みで対処する必要がある。分散トランザクションを実現する代表的なパターンが2つある。

いずれのパターンであっても、トランザクション内のコミットとロールバックに対応する処理を自分で実装する必要がある。

どちらのサービス連携方式であっても、TCC パターンと Saga パターンの両方が実装できる。オーケストレーションの方は分かりやすいと思うが、コレオグラフィの方は

  1. サービス A が処理を完了したら、サービス B に通知を送る
  2. 通知を受け取ったサービス B が処理を完了したら、サービス C に通知を送る

…といった形で、サービスがネストされたような呼び出し方になる。

AWS のサービスでいくと、AWS Step Functions で Saga パターンの管理がしやすそうだとか、AWS AppSync でトランザクション結果をリアルタイム通信で通知できそうだとか、クラウドサービスを利用することで扱いやすくなるみたい。

サーバレスでロックや排他制御は?

Amazon DynamoDB なんかはレコード作成時の前提条件が定義できるので、コレを用いて排他制御ができるみたい。

そもそも RDS 相手にサーバレスを選定する必要はあるのか?

さて、ココまで色々とサーバレス + RDS の扱いについて調べたことをまとめてきたが、ココでガラッと入れ替えて、RDS と組み合わせる相手がサーバレスアプリである必要があるのか、を考えてみる。

RDS Vs. 分散型 DB

まず、なぜ RDS を使いたいかというと、「データ一貫性」や「高速な検索処理」が狙いであろう。しかし、RDS はスケーラビリティに欠け、リクエスト数が見えない用途ではスペックを持て余したり不足したりすることが交互に発生しうる。

一方、リクエスト数の急激な変化に対応できるスケーラビリティを追求したいのであれば、分散型 DB を選択すれば良い。この場合、「データ一貫性」や「高速な検索処理」はある程度犠牲になる。

データ永続化層の目的や性質として、どちらを追求するか、まずは明らかにする。

サーバレス・アーキテクチャを選ぶ狙いをおさらいする

続いてアプリのアーキテクチャ。

昔ながらのモノリシックなアプリケーション構成は、M・V・C が密結合になり、変更にかかるコストが大きくなりがちである。そこで、変更に強いアプリにしたいために、フロントエンドアプリとバックエンドサーバの分離だったり、バックエンドもマイクロサービス化するとかサーバレス関数に分けるとかして、疎結合に作ることが増えた。

なぜ変更に強くしたいかというと、変更の頻度が多いからであろう。事業・ビジネスの変化やスピードに合わせて、アプリも頻繁に、かつ高速に変更していきたいからこそ、それに対応できるアーキテクチャとして、サーバレスを選定するワケだ。

サーバレスやマイクロサービス構成のデメリットは、高遅延であること、低効率であること、動作が不安定であることだ。処理ごとに複数のサービスを呼び出すため、どうしても速度は出ないし、ネットワーク上のトラブルに巻き込まれるリスクも増える。一部のサービスだけ処理に失敗したりすることもあり得ることを、事前に了承しておかないといけない。

すなわち、高遅延・低効率・不安定さを飲み込んだ上で、それでも改修スピードを上げることを望むなら、サーバレスやマイクロサービスといった構成を選択する意味があるワケだ。

逆に、環境がしばらく変化せず、業務が固定的なシステムであれば、モノリシックなアプリ構成を今から選定することだって間違いではないのだ。

要件とメリデメから構成要素を選定する

それぞれの技術の特徴やメリデメが分かったところで、対象のシステムの要件から構成要素を考え直してみよう。

別のまとめ方をすると、こんな感じ。

…ということだ。

モノリシック・密結合な構成というのは、新規の開発はやりやすかったりする。一度作ったらしばらく放置できるようなアプリであれば、こうした構成も悪くはないのだ。

もちろん、複数の目的が被っていて、一部でハイブリッドな構成を選択する場面もあるだろうが、基本はこのような考え方で技術選定をすればよいだろう。全てをサーバレスで作る必要が本当にあるのだろうか? ということを、もう一度考えても良いだろう。

マイクロサービスについては、設計や構成を適切に行わないとメチャクチャになるので、以下の点に注意したい。

以上

「RDS を使いたいけど、サーバレスとは相性が悪い…?」そんな疑問から色々調べた結果、「別にサーバレスに拘らなくて良い」「モノリスが向いている場合すらある」という話にまで発展した。

サーバレスに対しては分散型 DB を組み合わせるのが妥当だが、検索処理など一部では RDS を併用する、ハイブリッドな構成もアリということで、だいぶ理解が深まったと思う。

Saga パターンなど、マイクロサービスにおけるトランザクションの実現方法を学べた。

参考文献