こんにちは。 ALIS リードエンジニアの冨樫(@keillera)です。
ALISブロックチェーンブログ、今回は ALIS のサーバサイドアーキテクチャの説明として、ALIS のサーバサイド構成について記載します。
ALIS の構成は単に処理性能を重視しただけでなく、非機能要件(特に可用性、運用・保守性)も意識した作りになっています。これによりサービス稼働後からまだ3ヶ月程度ではありますが、サービスを停止させることなく、また運用や保守も詰まることなくスムーズに行えています。
また、ALISではブロックチェーン処理以外はフルサーバレスで構成していますが実際に稼働を経て、ブロックチェーンとサーバレスは相性が良いことが実感できました。今回はそういった観点も含めて記載するので、フルサーバーレスのケースだけでなく、DApps 作成の1つのケースとしても参考にして頂けると嬉しいです。
目次:
1. ALIS サーバサイド構成について
1.1. ブロックチェーンの構成
1.2. フルサーバーレスの構成
2. ブロックチェーンとサーバレスアーキテクチャとの相性について
3. サーバレスアーキテクチャの辛みと回避法
4. まとめ
ALIS のサーバサイドはブロックチェーンを除き、全てフルサーバレスで構築しています。下記 ALIS 構成図で説明すると、赤枠がブロックチェーンでEC2を利用していますが、その他はフルサーバレスでの構築となります。
ブロックチェーン処理は現在プライベートチェーンとして、上記 ALIS 構成図の赤枠にある通り、LB と EC2 を組み合わせたシンプルな構成で構築しています。当初はEthereum のメインネットでの実装を検討しましたが、Ethereum のスケーリング問題が顕在化する等の理由により、現在はプライベートチェーンを使用しています。プライベートチェーンを選定した経緯や詳細については、前回のブログ を参照ください。
(Github)
ブロッチェーン以外は、フルサーバーレスで構築しており、大きく下記の4つの処理を動かしています。それぞれについて説明していきます。
① 静的ファイル処理
② SSR 処理(Nuxt.js 処理)
③ API 処理
④ バッチ処理
① 静的ファイル処理
静的ファイル処理は 下記 ALIS 構成図の赤枠箇所になります。js ファイルや画像ファイルの格納先として利用しており、シンプルに CloudFront、S3 のみで構築しています。
CloudFront を利用することでキャッシュによる処理速度向上だけでなく、AWS Shield が有効になり DDoS 保護が有効なります。また、S3 だけでは独自ドメインによる SSL/TLS プロトコルの利用はできませんが、CloudFront を利用することで利用可能になります。
上記の通り、CloudFront を利用すると様々なメリットを享受できるため、S3 に直接アクセスさせずに CloudFront を利用した構成を採用しています。
② SSR処理(Nuxt.js)
SSR 処理は下記 ALIS 構成図の赤枠箇所になります。この SSR 処理は主にブログのOGP展開を行うために用意しています。こちらもシンプルな構成でCloudFront、API Gateway、Lambda を利用しています。
構成としてはシンプルですが、特徴的なところがあり、Nuxt.js をLambda 上で 動かしています。Nuxt.js をどこで動かすかについては、EC2 や Elastic Beanstalk の利用も検討しましたが、非機能要件(特に可用性や運用・保守性)を考慮すると Lambda を使いたく、あまりプロダクト利用での前例はありませんでしたが、上記の構成を採用しました。
結果として、構築には手間取りましたが、稼働後は特に問題が発生すること無く動いており、想定通り非機能要件に関するメリットを享受できています。オススメできる構成です。
手間取った構築や実装は、下記リポジトリにて公開してるので、詳細については下記を参照ください。
※ リポジトリ内のファイル構成や、開発・デプロイ方法等の解説については別記事にて投稿致します。
③ API 処理
API 処理は下記 ALIS 構成図の赤枠箇所になります。構成としてはCloudFront、Cognito、API Gateway、Lambda(Python3.6)、DynamoDB、ElasticSearch と利用サービスは多いですが、いわゆる良くある構成です。
API 処理ですが、利用できる AWS サービスは積極的に利用する方針で設計しています。デメリットとして、AWS によるベンダーロックインによるリスクや移行性の低下はありますが、万が一AWSに何かあった場合も移行できないわけではなく致命的ではありません。それよりも開発箇所を減らせることや、またマネージドサービスによる運用負荷低減によるメリットが大きいです。
また、サーバレスアーキテクチャでDBを利用する場合、コネクション数の都合上 RDBMS の利用は難しく現状 NoSQL である DynamoDB を使うことになりますが、DynamoDBは万能ではなく、トランザクション処理が効かない、検索の負荷が高いといったことがあります。このため、AWS サービスで解決できる処理はなるべくそのサービスに任せる設計で構築しています。
API 処理の詳細については下記リポジトリを参照ください。
※ リポジトリ内のファイル構成や、開発・デプロイ方法等の解説については別記事にて投稿致します。
④ バッチ処理
バッチ処理は下記 ALIS 構成図の赤枠箇所になります。Cloudwatch Event、StepFunctions、Lambda(Python3.6) を利用した構成です。
バッチ処理は、特に特筆するところはありませんが、複雑なバッチ処理を開発する場合は、まずはシンプルな処理に分け、それらを StepFunctions で管理する構成を採用しています。
Lambda から Lambda を呼び出すだけであれば "Lambda→SNS→Lambda" の構成でも対応可能ですが、特に理由がなければ、視覚的なワークフローが利用できる、StepFunctions の利用がオススメです。視覚的になることで運用時だけでなく開発時でもバグ発生箇所を把握しやすい点や、ワークフローにて Lambda にステート管理をさせずに済みシンプルな実装になるため、単純に生産性を上げるだけでなく品質向上にも役立ちます。
ブロックチェーンとサーバレスアーキテクチャですが、実際に利用してみたところ相性が良いことが実感できました。
何が良かったかというと、サーバレスアーキテクチャは、上記の ③API 処理 でも記載しましたが、トランザクション処理が不得意なので、お金のような整合性が重要であるミッションクリティカルなデータの取り扱いには向いていません。
逆に、ブロックチェーンはお金といったミッションクリティカルなデータの取り扱い向いています。
このため、ブロックチェーン側で、サーバレスアーキテクチャの苦手としているミッションクリティカルなデータを取り扱えるので、相性良く開発することができます。
とはいえ、ブロックチェーンを使えば簡単にサーバレスアーキテクチャを採用できるかと言うとそういうわけではありません。
ミッションクリティカルなデータをブロックチェーンに任せることができたとしても、それ以外のデータで整合性を担保したくなる(トランザクション処理を利用したくなる)ケースがあります。これをどのようにサーバレスアーキテクチャで処理するかといったところに辛みがあります。ALIS も例外ではなく同様のケースがありました。
ではどうやって対応したかというと、本当にデータ整合性が必要化どうかをサービス観点含め整理し、データの処理順を考慮することでトランザクション処理を発生させない設計を行うことで回避しました。
少しわかりにくいと思うので、ALIS で記事に「いいね」をした場合のデータ保存処理を例に説明します。
【ALIS で記事に「いいね」をした場合のデータ保存処理】
ALIS では記事に「いいね」を行った場合、対象記事の「いいね」数の増加と、記事投稿者へ「いいね」されたことの通知の、2つの処理を行っています。
この2つの処理のデータ管理としてDB(DynamoDB)を利用し、下記 2 テーブルにデータを保存しています。
① 対象記事への「いいね」を管理するテーブル
② 記事投稿者への通知を管理するテーブル
イメージは下記図の通りです。
ここで問題になってくるのが、①、②のデータ整合性です。2つのテーブルにデータを保存する場合、トランザクション処理があればいいですが DynamoDB にはありません。このため、① に保存した後、②への保存が失敗した場合どのように対処すべきかが問題になります。
イメージは下記図の通りです。
このような場合に ALIS ではどういった対応しているかというと、「通知」はミッションクリティカルなデータではありません。最悪「通知」がされなかったとしてもサービスは継続可能です。逆に「いいね」はALISの場合トークン算出に紐づくデータで重要なので、「いいね」についてはデータ保存を担保したいです。
このため、先に「いいね」の保存を行い、もし「通知」の保存が失敗したとしても、ロールバックをさせずに処理を継続させる(トランザクション処理をあきらめる)設計で対応し、この問題を回避しています。
もちろん「通知」されないことは良いことではありませんが、保存ができないと行った問題の発生頻度は低く、また回避のためにトランザクション処理をアプリで実装するくらいであれば、その分のリソースを他の開発に向けるほうが、サービス全体を見た時に価値を出せます。
何でもかんでもトランザクション処理を入れないでも良いというわけではありませんが、利用するサービスでのインパクトを考慮し設計することで、上記のような辛みはありますが、サーバレスアーキテクチャを利用した構築を行えます。
サーバレスアーキテクチャはまだ発展途上で、技術として枯れていないため前例が少なく、特にフルサーバレスとなると開発難易度が高めに感じます。
ただ、ブロックチェーンを利用した場合、前述したとおりサーバレスアーキテクチャの開発難易度を下げれるケースがあるので、特に DApps の開発を検討されている方は、ブロックチェーン以外の構成については、サーバレスアーキテクチャの採用を一度検討してみるのも有りかと思います。
もし採用した場合は、是非その知見を ALIS に投稿し共有頂けると嬉しいです!
ご意見・ご指摘等ありましたらコメントください!
マサカリは優しく投げて頂けると助かりますm(_ _)m
・ALIS リードエンジニア 冨樫(@keillera)
・この記事は、運営による記事のためいいねによるトークン配布はありません
・ALISではエンジニア・R&Dメンバー絶賛募集中です 😉