アーキテクチャ大喜利 「もし、⚪︎⚪︎な仕様のサービスを立ち上げるなら、あなたはどんなアーキテクチャを組みますか?」著名企業のエンジニアに聞いた
システムのアーキテクチャを設計する際には、単に技術的な知識だけがあればよいわけではありません。企業の事業戦略や開発組織の規模、サービスのビジネス特性など、さまざまな制約や要件を考慮する必要があります。最適なアーキテクチャを構築することは、一筋縄ではいかないのです。
では、優れたスキルを持つエンジニアは、具体的に何を念頭に置いてアーキテクチャを構築するのでしょうか。今回は、架空のWebアプリの仕様をテーマとして掲げ「もし、そのアプリを立ち上げるならば、どのようなアーキテクチャを提案するのか」を、著名IT企業のアーキテクトの方々に伺いました。いうなれば、お題に沿ってアーキテクチャを回答する“アーキテクチャ大喜利”です。
今回ご協力いただいたのは、アソビュー株式会社 CTOの兼平大資さんと株式会社タイミー プラットフォームエンジニアリング1G GMの橋本和宏さん。お二人が何を考慮してどのようなアプローチを取るのかを、ぜひご覧ください。
架空のWebアプリのお題
あなたは、ある開発組織に参画してシステムのアーキテクチャ構築を担当することになりました。新規でファッションECサイトを立ち上げる場合、どのようなインフラ・アーキテクチャにするでしょうか。以下の条件・制約があるものとします。
- そのシステムの開発・運用に50名くらいのエンジニアが携わる想定です。
- 開発組織の人数は、今後も急激には増えないものとします。そのため、運用にかなりの人員・工数が必要なアーキテクチャは避けてください。
- ShopifyのようなフルマネージドなECプラットフォームは使わない前提です。
- (現実的には、これほどうまくいくケースはほぼあり得ないですが)そのECサイトはローンチ直後からテレビCMを放映し、大量のユーザーが訪れるサイトになります。その後もECサイトは人気を博し、日本国内の他の著名ECサイトと比較しても遜色がないほどのアクセスが継続するものとします。 今後も継続してテレビCMなどのマーケティング施策を実施するため、突発的なアクセスのスパイクが起こることがあります。パフォーマンス要件として商品の検索や参照などの読み込み、カート投入や購入などの書き込み処理を1台のデータベースだけではさばき切れない規模です。
- このECサイトは自社の商品を販売します(モール型ECサイトではありません。そのためテナント設計などは基本的に考慮しなくてよいです)。
- エンドユーザー向けのWebアプリとモバイルアプリ、社内向けのAdmin Webアプリを用意します。Admin Webアプリでどこまでの機能を提供するかは、アーキテクトであるあなたの裁量に任せます(たとえば、Admin Webアプリのアーキテクチャはかなりシンプルにしておいて、発生頻度の低いオペレーションについてはアプリで賄わず手作業でデータベースにクエリを発行して対応する、などの割り切りをしても構いません)。
- 大きく分けると、今回構築するアーキテクチャでは「認証・認可」「商品」「検索」「カート」「レジ」「決済」「購入履歴」の要素を扱うものとします。
- 「物流」「広告」「ポイント」など他の要素を考慮するとアーキテクチャの規模が大きくなり過ぎるため、今回の記事ではスコープ外とします。
- 「決済」については今回の記事では「クレジットカードによる決済」のみをスコープとします。電子マネーなどを考慮し始めると連携対象のシステムが多くなり過ぎるためです。
- 「商品」「検索」「カート」「レジ」「決済」それぞれの要素で、どのような機能を持たせるかはアーキテクトであるあなたの裁量に任せます。
- 今後の展開として不足している機能拡張は行いますが、マルチプロダクト化などは考慮しなくてよいです。
アソビュー株式会社 CTO 兼平大資さん
はじめに
アーキテクチャ設計において、現状のプロダクト特性と将来の展開は重要な要素です。今回の検討では、以下の点が参考になります。
- エンジニア数は現在50人で、急激な増加は見込んでいない
- 機能拡張は予定しているが、マルチプロダクト化は行わない
- テレビCMなどにより、著名なECサイトに匹敵するアクセスが発生する
もちろん、予期しない変化に備えて柔軟性や拡張性を持たせることは可能です。しかし、過度に将来の可能性を考慮し過ぎると、成長に対してオーバーヘッドになるリスクもあります。特に、50人規模であれば、開発チームは数チーム程度で、現時点ではマルチテナントやマルチプロダクトのような複雑なシステム要件はありません。そのため、複雑さを避け、少数のチームでも迅速に成長させられる点を重視しています。
今回、マイクロサービス化は行わないものの、アプリケーションを分割する選択肢はあります。ただし、詳細な性能要件があるわけではないため、最もシンプルなモノリシック構成を採用しています。そのため、「認証・認可」「商品」「検索」「カート」「レジ」「決済」などの機能は全て単一のアプリケーションに集約することになります。
主要なシステム説明
インフラ
クラウドサービスプロバイダーの選択に制約はありませんが、本システムの実現にはAWSを採用しています。
アプリケーション
主にリアルタイムで処理をするECサイトアプリケーション(Adminも含む)と、非同期で処理を行うワーカーアプリケーションの2種類のアプリケーションを利用しています。アプリケーションの実行環境に制約はありませんが、Amazon ECS(AWS Fargate)のような、ある程度マネージドで管理され、トラフィックに対して水平スケールが容易な実行環境を利用するのが、開発組織の規模としても適していると思います。
データベース
今回はクラウドサービスプロバイダーに依存した機能をあまり利用していませんが、データベースはプロバイダー固有の機能を活用しています。
Amazon Auroraのクラスターでは、高速なレプリケーションがマネージドで提供されています。さらに、リーダーインスタンスにカスタムエンドポイントを設定できるため、処理の特性に応じて可用性を分離できます。この機能を利用して、エンドユーザーからの同期的なアクセスを専用のリードインスタンスで処理できるようにしています。また、比較的高負荷や長時間になりやすい非同期処理は、独立したリードインスタンスを参照するようにしています。
一方、この構成ではライターインスタンスが単一障害点となります。そのため、後述する「高トラフィック対策」で対処し切れない場合には、複雑なアーキテクチャになるものの、ライターインスタンスも含めたシャーディングやNewSQLなどを検討する必要があります。
KVS
セッション管理、キャッシュ、カート情報、在庫管理など、複雑な処理は不要だが高いI/Oパフォーマンスが求められるデータにはRedisを採用しています。AWSを利用する場合、負荷特性に応じて柔軟にスケールアウトやスケールアップが可能です。
この構成の場合、Redisは単一障害点になりやすい面がありますが、必要に応じて用途別に分割することで対応できます。状況によってはAmazon DynamoDBとの使い分けも考えられますが、開発・運用時に2種類のデータストアを管理する複雑さを避けるため、Redisに統一しています。
オブジェクトストレージ
商品画像、バナー、アイコンなどの静的ファイルの配信には、Amazon S3を利用しています。
検索エンジン
柔軟な商品検索やレコメンデーションを実現するために、Amazon OpenSearch Serviceを採用しています。最近のデータベースでは、別途検索エンジンを用意しなくても、それなりの検索機能を提供するようになっています。しかし、商品検索の最適化はECサイトのコア機能となるため、今回は専用の検索エンジンを用意する構成を選択しました。
アプリケーションの構成
ECアプリケーション
ECとAdminを分割する選択肢もありますが、ユーザーや商品情報など、双方で共通して利用したいビジネスロジックが存在します。アプリケーションを分割すると、これらのロジックを共有しにくくなり、生産性に影響が出る可能性があります。
一方で、同一アプリケーションにまとめると、ソースコードやデプロイ時のコンフリクトが発生しやすくなり、ノイジーネイバーによる可用性懸念も発生します。さまざまなトレードオフがありますが、今回はできるだけモノリシックにまとめることを前提に構成を検討しています。モノリシックアーキテクチャの選択は、システム運用面だけでなく、ローカル開発や検証環境の扱いやすさなど、開発効率にも直接的に影響します。
もちろん、同一アプリケーションに統合することで、複数のチームによるデプロイが衝突するリスクは増えます。しかし、システム分割に伴うDevOpsのオーバーヘッドを考慮すると、現在のチーム規模では、コミュニケーションで解決するほうが費用対効果が高いと考えています。
また、DDD(ドメイン駆動設計)の境界づけられたコンテキストやモジュラモノリスによる垂直分割など、同一アプリケーション内でも自律性を保てる設計手法のナレッジも昨今増えてきています。ただし、ノイジーネイバー問題は依然として課題です。ECサイトとAdminのようにサービスレベルが明らかに異なる部分や、ECサイト内でも負荷特性が異なるページがあります。
こうした負荷の違いを考慮してアプリケーションを分割することも一つの手段ですが、Amazon ECSのようなサービス実行環境を利用することで、同一のソースコードからサービスだけを分割し、負荷特性に応じてトラフィックを適切に振り分けることが可能です。不要なソースコードが含まれることでリソース効率は低下しますが、開発・運用コストを考慮すると、サービス単位での分割が実際には適している場合もあります。
また、WebアプリとモバイルアプリはHTMLやAPIの返却データに違いがあるものの、WebでもAPIを利用し、モバイルでもWebビューを活用することがあるため、同一のアプリケーションへアクセスするようにしています。
ワーカーアプリケーション
リアルタイム処理に適さない長時間処理と高負荷の処理、非同期で問題ない処理を担当します。今回は対象外ですが、物流や外部システムとの連携においても、非同期処理は重要な要素となります。
開発には使い慣れたフレームワークやライブラリを使用するため、独立したアプリケーションとしていますが、用途に応じてAWS Lambdaなどの選択肢も検討可能です。また、可能な限りワーカーで対応しますが、定期的なメール送信など状況によっては別途バッチ処理の仕組みが必要になることもあると思います。
高トラフィック対策
今回のアーキテクチャで特に重要なのは、高トラフィックへの対策です。パフォーマンスに応じた詳細な調整が最終的には必要ですが、ステートレスで水平スケールしやすいアプリケーションとは異なり、ボトルネックになりやすいデータベース負荷の軽減が課題となります。そのため、読み込みと書き込みを分けて対策を講じています。
読み込み
静的ファイルや変化の少ないデータには、Amazon CloudFrontを利用してキャッシュを返します。これにより、アプリケーションへのトラフィックを抑制し、急激な負荷にも対応できます。ただし、Amazon CloudFrontなどのCDN(Contents Delivery Network)では、リアルタイム性の高いデータの更新や削除時のキャッシュ管理が課題となります。また、ユーザー固有のデータなど、キャッシュヒット率が低いデータも存在します。このようなデータに対しては、Redisを活用して高速化を図りつつ、データベースへの負荷を軽減します。
書き込み
在庫やカートなど、書き込み負荷の高いデータについてもRedisを活用しています。特にテレビCMやセール時の商品購入によるトラフィックの増加は、単一のデータベースでは処理し切れない可能性があります。そこで、在庫のように一貫性と高速な処理が求められるデータアクセスはRedisで処理します。一方、購入履歴やメール送信など非同期処理が可能なものは、Amazon SQSを経由してワーカー側で負荷を分散し、データベースの更新を順次実行する構成としています。
その他
今回は与えられた題材を基に、一つの案として構成を検討しました。実際の運用では、さらに詳細な要望や制約が生じます。そのため、要件に応じて構成やパフォーマンスを調整し、最適化を進める必要があります。負荷テストなどを実施しながら、最適なアーキテクチャを模索していくことが重要だと考えます。
株式会社タイミー プラットフォームエンジニアリング1G GM 橋本和宏さん
注目する前提と方針
お題に対して以下のような前提や方針としました。
インフラ寄りのアーキテクチャに注目する
言語やフレームワークなどには極力触れず、クラウドインフラに着目したアーキテクチャ設計としました。我々、プラットフォームエンジニアリンググループではクラウドインフラと開発基盤(CI/CDなど)を主に扱っており、開発そのものへのフォーカスよりもインフラ・アーキテクチャに興味・関心を寄せているからです。
サービス機能と設計
設計に関する前提から以下を方針としました。また、インフラ的なアーキテクチャに集中するため、個々の機能実装にフォーカスし過ぎない(例:認証についてはXXという実装にするので○○を使う等)検討をしました。
- 認証や商品検索、カート、購入関連など最低限の機能の実装のみと仮定
- “物流、広告などの周辺機能は考慮外とする”、とあるのでデータ分析基盤(BI)などについても考慮しないこととする
開発・運用に関わる人数規模は50名
開発・運用に関わる人数規模からくるシステムへの投資を推定しました。
- 少なくとも小さな規模感ではなく一定の複雑性を持った構成でも“運用を回せる”期待値で設計する
- このため、サーバーレス、マネージドサービスに過度に寄らず、セルフマネージドも含めた製品選定を行う
負荷対策への考慮
前提にも“これほどうまくいくケースはほぼあり得ないものの”とありますが、アクセス急増の想定があることを設計方針に加えました。
- 先の開発規模感や一定のメディア露出などがあることから資金的にも一定の潤沢さがあるものとする
- コスト削減よりも事前にアクセス急増による負荷増加に対処しやすい構成にする
構成はシンプルに
お題にマルチプロダクト化は検討しないこととあるため、一つのプロダクトを運用効率性を持って動かすことができるアーキテクチャを前提としました。“構成はシンプルに”といえども、さまざまな解釈がありますが、多様なプロダクト・要件に対応できるような柔軟性は過度に考慮しなくてもよいものと解釈しました。
アーキテクチャ概略図
本アーキテクチャの概略図となります。各コンポーネントについては次に取り上げていきます。
クライアントへのプレゼンテーション
CDN(Contents Delivery Network)
CDNとしてAmazon CloudFrontを用います。
Amazon S3(静的ウェブサイトホスティング)
ECサイトであることから多くの画像等のアセットがあるものと想定しました。これらのアセットはAmazon S3に配置されるようにし、静的ウェブサイトホスティングにより公開します。またアクセスの都度Amazon S3へのアクセスが発生するとコストやレイテンシーの点で無駄が発生するため、Amazon CloudFrontによりキャッシュするようにします。
ALB(Application Load Balancer)
ALBを用います。
アプリケーション実行環境
アプリケーション実行環境としてAmazon ECS(AWS Fargate)を用います。コンテナ実行環境であるAmazon ECSと、そのデータプレーン(実行サーバー)のインスタンス管理が不要になるAWS Fargateを組み合わせて用います。
各種サーバー
アプリケーションサーバー
Webアプリケーションサーバーが実行されます。CPU使用率によりサーバー数が増加するオートスケーリングの設定をしておき、負荷対策とします。
注)Amazon ECSではタスクという表現が正しいのですが、Amazon ECSをご存じない方にとってはわかりにくいため、サーバーという表現にしています。
非同期処理の部分はAmazon SQSを用いてメッセージを送信します。
ワーカーサーバー
Webアプリケーションサーバーと異なるグループ(タスク定義)でワークロードを実行します。Amazon SQSからメッセージを受信し、非同期処理を実行します。非同期処理はスケールダウンによる停止で処理中断が発生すると問題になることがあるため、オートスケーリングは設定せず余裕を持った台数で固定して運用するものとします。
バッチサーバー
ECS Scheduled Tasksにより定期的なバッチ処理を行います。Cron書式で定義すると指定したコンテナが実行され、終了後はサーバーも自動的に削除されます。
RDB(Relational Database)
これは一般的にRDBを使うこととします。AWSなのでAmazon Auroraを使うことにしました。
- Amazon Aurora Serverlessではなくプロビジョニングを選択
- Amazon Aurora Serverlessは細やかなオートスケーリングによりコスト最適化をしやすいことが魅力だが、プロビジョニングのほうがコストの余裕が見込める
- また、ある程度“温まった(=メモリに乗った)”状態にできる点もプロビジョニングにしたポイント
- Amazon Auroraを使うのもスケーリング(リーダーインスタンスを並列で増やす、カスタムエンドポイント、インスタンスタイプをワークロードに合わせて変更できること)も考慮しているため
RDBの負荷対策
負荷の増減への対処はAuroraクラスターの機能を利用します。Amazon Auroraではライターエンドポイントとリーダーエンドポイントが標準で自動的に作成されます。リーダーインスタンスは複数台を任意で追加できるため、リーダーエンドポイントは水平スケーリングがしやすい機構になっています。このため、できる限り参照系クエリはリーダーエンドポイントへ接続するようにします。
また、カスタムリーダーエンドポイントを作成でき、任意の用途別グループに分けることができます。非同期ワーカー(後述)や、バッチ処理用途で異なるワークロード(重いクエリを投げるなど)がある場合に手軽に構成を変えて負荷をコントロールできます。
RDBのスケーリングポリシー
Auroraクラスターのボトルネックは1台しか構成することができないライターインスタンスになります。適切なパフォーマンスチューニングやリーダーインスタンスの活用がなされている限りは、今回のお題のシステムであれば64コア/512GBから128コア/1024GBのインスタンス規模で賄えると想定しています。ここでは少し過剰な検討にはなりますが、万が一これらのスペックでも足りない場合どうするのかという仮想ケースを考えてみたいと思います。 対応案は以下の2つです。
- Auroraクラスターのままで垂直分割
- より多くのワークロードを処理できるRDB製品等の選定
前者の垂直分割はアクセスの過多によってAuroraクラスターを複数に分割する手法になります。案として挙げていますが、よほどのことがない限り選択すべきではないと考えています。歴史的には高負荷システムで採用されてきた手法ではありますが、アプリケーションの複雑性が増すことや、複数クラスターゆえにインフラ運用やコスト最適化が複雑になるため
後者が選択肢となるなかで、RDB製品“等”としているとおり含みを持たせていますが、ここでは現行のRDBからのマイグレーション先となるため互換性を重視します。超高負荷に耐え得る、Amazon Auroraと互換性が期待できるものとして、TiDBがあります。MySQL互換性をうたっているので、前提としてAmazon Aurora MySQLを使っている場合は候補となるでしょう。
TiDBは金融業界(日本であれば電子マネー等の決済システム)やソーシャルゲーム業界での利用実績が多いようです。これらのシステムはスパイク性のアクセスがとても高い性質があるため、相当量のアクセスに耐えることができそうです。しかし、このお題の規模感で最初に選択するには過剰過ぎるかもしれず、万が一の移行先候補までとし採用はしませんでした。
KVS
KVSとしてRedis(Amazon ElastiCache)を使います。用途はAmazon Auroraのキャッシュ用途を想定します。Redisはin-memoryで高速な動作をするKVSではありますが、RDBに対するキャッシュ等で効率的に利用すればするほど負荷が集中してボトルネックになる可能性があります。このため性能上限に近づいた場合のスケーリング戦略をどうするかを考える必要があります。
Redisのスケーリングポリシー
- 機能や用途によりElastiCacheインスタンスを分けて増設していく“垂直分割”方式とする
- RDBでは垂直分割は禁じ手としたが、KVSは比較的用途ごとに分散する使い方であっても困ることはないかと考えている
- たとえば認証用Redisと検索結果用Redisを、インスタンスを分けて増やす形となる
- Redis Cluster等の水平分割のキャッシュが対案として存在するが、Redisクライアントライブラリの挙動などで一定のハマりポイントがあったり、運用上の大変さ(詳細を述べると長くなるので割愛)があったりするので、開発や運用のわかりやすさを優先し、かつ負荷対策も必要十分である先述の垂直分割方式とする
おわりに
以上となります。もう少し踏み込んで検討したいことが多くありましたが、Findy Toolsに掲載されるアーキテクチャ大喜利企画ということで、あまり長文になってもいけませんので、アーキテクチャはシンプルなものとして、どのようなプロダクトを使うのかに焦点を当てたものとしました。皆様のアーキテクチャ検討の参考になれば幸いです。