AWSソリューションアーキテクトが導く『WEBサービスを進化させるアーキテクチャ』の極意とは──AWS Tech talk Night#3
アーカイブ動画
AWS SAが『TECH PLAY』の現場エンジニアにアドバイス
今回TECH PLAYの相談にアドバイスしてくれたSAは、シニアソリューションアーキテクト デベロッパースペシャリストの福井厚氏、シニアサーバーレススペシャリストの下川賢介氏、シニアマネージャ ソリューションアーキテクトの清水崇之氏の3名。
そして今回の相談者は、TECH PLAYのサービス開発を担当するプロダクトマネージャの鈴木康広氏、ソフトウェアエンジニアの中村航也氏、ソフトウェアエンジニアの青山稜河氏だ。
改めて紹介すると、TECH PLAYはイベント情報の掲載・作成から、イベント配信、企業のテックブログの集約サービスなどを提供する技術イベントプラットフォーム。
今後、様々なフォーマットに合わせた情報収集機能を迅速に実装するために、AWS Lamdaの活用について以下3つの質問を展開した。
相談1:AWS Lambdaを使って「TECH PLAY Blogs」をもっとスマートに
TECH PLAYが提供するテックブログ集約サービスは、独自実装のクローラーでRSSを取得しているが、各社によってフォーマットが異なることもあり、個別対応になっている。
これをAWS Lamdaに載せ替える実装を始めているが、「AWS Lambdaを活用するアーキテクチャとデプロイのベストクラプラクティスを知りたい」というのが最初の相談である。
AWS Lambdaとは、イベントの発生に応じてプログラムを実行する環境を提供してくれるサーバレスサービス。イベント駆動アーキテクチャがベースになっており、アップロードしたコードをユーザーが指定したタイミングで実行できる。
同期・非同期を意識せずにデプロイして実行できるため、ユーザー側はビジネスロジックやコーディングに集中できるのが大きなメリットだ。自動スケーリングやアップデートパッチの自動適用などにも活用できる。
今回AWS Lambdaを選択した理由は、RSSのフォーマットごとの対応や取得のスケジューリングなど、ある意味インフラ的な部分を自動化できれば、ユーザー体験のコアな部分によりリソースを集中することができるからだ。
現在、AWS Lambdaで実装しているフローは次の図となる。
各企業のフィードはフォーマットや必要な後処理が異なるため、設定や処理内容の異なる複数のLambda関数を使って処理する。ローカルでコーディングしたソースコードをAWSのコンソールからZIP形式でアップロードしているが、サイズ制限をオーバーする場合はS3を経由する。
各Lambda関数は共通処理も数多くあるが、それぞれ別のLambda関数としてアップロードされている。このLambda関数をAWS EventBridgeのスケジュール機能によって、1日に1回呼び出し、RSSフィードからブログを取得している。
TECH PLAYは、EventBridgeのスケジュールの設定やLambda関数の種類が増加することで、デプロイや運用に多大な負荷がかかることが懸念点だとして、次の解決法のアドバイスを求めた。AWSソリューションアーキテクトからの回答と合わせて紹介していこう。
より管理しやすいデプロイの方法について
まず、手作業によるミスを防ぐ意味でも、Lambdaのデプロイは自動化しておくこと。おすすめは、AWS Serverless Application Model(SAM)のCLIコマンドを使った自動化だ。
SAMはAWS上のサーバアプリケーションを構築するためのフレームワーク。関数、API、データベース、イベントソースマッピングなど、簡易な文法で記述でき、すべてのAWS Cloud formationのリソースタイプをサポートしている。
SAMによるサーバレスアプリケーション構築は、以下の図のように大きく3つのステップを取る。まず、SAMのテンプレートを生成する(sam initによる自動生成)。それに対してビルドコマンド(sam build)を実行する。そして、デプロイのコマンド(sam deploy)の実行によってCloud formationにトランスパイルされ、実際にAWS側でデプロイされるところまで自動で行われるという流れだ。
Cloud formationに変換されることで、SAMがサポートしていないリソースに関しても、通常のCloud formationの記述をそのままSAMのテンプレート中に書くことができる。
これらのコマンドラインをパイプラインに乗せることによって、ソースコードのアップロードからデプロイまで自動で行う。また、Lambda関数を実装するためのIAM(Identity & Access Management)ロールの管理もSAMを利用することで、より簡易に記述することが可能となる。
SAMと同じような使い方ができるAWS Cloud Development Kit (CDK) はSAMもCDKもIaC (Infrastructure as Code) でシステムを管理するものだが、その管理の対象が異なる。使い分けのポイントは、サーバレスの環境だけを管理の対象とするならSAM、サーバレスだけでなく、AWSリソースも扱いたいならCDKと考える。もちろん、SAMとCDKの連携、ローカルでSAMでテストした上でCDKでデプロイすることも可能だ。
ここでは、SAM、CDKともに「Infrastructure as Code」 という考え方が重要になる。今回は将来的な大規模化を想定した対策だが、たとえ小規模システムであっても、AWSのマネジメントコンソールによるデプロイではトラブル時の検証が難しい。
原因を探ろうとしても、画面操作が記録もされないためである。そこで正確に再現できる環境を得るためのプラクティスとして、IaCの考え方が出てくる。その1つがCloud formationであり、CDK、SAMということになる。
アドバイス:複数のLambda関数間で共通する処理の最適な実装、デプロイの方法
AWSにはLambda Layerという機能が提供されており、共通の機能をライブラリとして登録することができる。SAMで管理することも可能だ。ただし、ZIP形式でアップロードしようとする際、このLayerも含めることになるため注意が必要となる。もしZIPのファイルサイズ制限を超えるようであれば、コンテナ形式も検討すべきである。
アドバイス:EventBridgeによるスケジューリングの設定管理
ファンアウトパターンやEventBridge+DBの活用によって、スケジューリング管理をスマートにすることが可能だ。例えばRSSフィードが増えたとして、それを1日1回取得するだけであれば、Simple Notification Service(SNS)とSimple Queue Service(SQS)をSNSとSQSを組み合わせるファンアウトと呼ばれるパターンが考えられる。
具体的には、各RSSフィードごとに立てたSQSを1つのSNSトピックにサブスクライブする形だ。このSNSトピックに対し1日1回メッセージを送信するようにEventBridgeで時間指定しておけば、すべてを並列的に処理することが可能だ。イベントとしては、1つのイベントで全部のRSSを取りにいくことができる。
ただし、同じドメインのRSSフィードが存在する場合など、当該1つのドメインに短時間で大量のアクセスをしたくない要件もある。その場合、ファンアウトパターンでメッセージの処理数を指定、あるいは条件分岐で並列に実行するなど、ワークフロー側で調整することも回避策の1つだ。
また、AWS Step Functionsというマイクロサービスの管理機能を使えば、分散処理も容易に設定できる。EVENT BridgeからStep Functionsを叩き、Step Functionsから並列でRSSフィードを取りにいく方法だ。
Step Functionsには配列を渡したら配列分だけ並列に処理する機能があるので、RSSフィードが増えてもワークフローを変えずに対応できる。配列要素にはDynamoDBやRDBMSなどのデータベースも取れるため、それを使って並列実行させる方法がこの場合のビジネスロジックに合うのではないかとアドバイスされた。
いずれもメッセージやイベントを挟むことによって並列処理を行うということになるが、ファンアウトパターンの場合はSQSを使うことで、Lambda関数が実行に失敗した場合もメッセージが返るため、信頼性とか可用性の向上につながる。
相談2:AWS Fargateの運用とコストの最適化について
AWSの中でコンテナを扱える代表的なサービスの一つであるAmazon ECSには、2つのタイプがある。以下の図のように、EC2上で動く「ECS on EC2」と「ECS on Fargate」は、それぞれAWSが管理してくれる領域が異なる。
青色で示すレイヤーがユーザー側が管理しなければならない領域で、黄色がAWSが管理してくれる領域となる。
ECS on EC2では、ホストOSの管理やコンテナエージェントの設定といった低レイヤーの管理まで、ユーザー側が行う必要がある。一方、FargateではAWSが管理する領域が広く、ユーザー側はアプリケーションコンテナの管理のみとなる。
つまり、FargateはAWSマネージドなコンテナ実行基盤として抽象化されたコンテナ管理を提供するもので、ユーザーは仮想マシンを意識しない管理運用ができるというわけだ。VPCネットワーキングやElastic Load Balancer、IAMなどとの連携も容易だ。
TECH PLAYでは、インフラの管理や運用を手離れさせて開発にリソースを集中させるため、AWS青山氏の主導のもと、Fargateを導入したアーキテクチャへの移行を始めている。 Fargateを使ったTECH PLAYの管理システムは以下の図である。
フロントエンドとAPIのバックエンド、それぞれ2体のFargateが稼働しており(2体は完全分離)、フロントエンドはreact.jsとnext.js、バックエンドのAPIはPHPで動いている。
このアーキテクチャを踏まえて、相談は次の2点である。
1.Fargateのコスト効率が良い運用方法
提供サービスの増加やバックエンドとフロントエンドの切り離しによって、Fargateの台数が増えていくため、そのコストを効率化したいというのが1つ目の相談だ。Fargate自体はスケーラブル機能があるが、検証環境は立ち上げたままで行うことが多いため、コストがかかっているのではないかという懸念があるという。
これに対し、検証環境のコスト配分をマネジメントコンソールから確認することがアドバイスされた。まずはFargateがコストの中心なのか、それとも裏側にあるRDSなどが立ち上がったままであることが原因なのか、見極める必要がある。
もし後者であれば、時間とともに一旦停止させる、あるいは1つの開発が終わったら開発環境のRDSは停止しておくという運用も考えられる。Fargateが中心となってコストを圧迫しているのであれば、Lambdaを使った構成にすることも検討の余地がある。
2.Fargate Spotの効果的な使い方
「インスタンスの回収→停止」の影響を受けないようにしたいが、Fargate Spotを使い続ける限り、回収される可能性をゼロにすることができない。そこで、1つの方法としてはバランスよくオンデマンドとSpotを使う。例えば、全体の容量を6と考えたときSpotに4を割り当て、オンデマンドに2する。たとえSpotが4つ回収されても、オンデマンド2で立ち上がった状態を維持することができる。
仮に一時停止した場合、sigtermが来たらディスポーズ処理をするよう対処しておくことも1つの方法だ。ただアプリケーションロジックの中にディスポーズ処理が入ってしまうため、コンテナを使う意味がない。そうした点も踏まえると、EVENT Bridgeを経由して対象のターゲットを切り離せる方式もあるだろう。
「落ちないようにする」のではなく、「いつ落ちてもいいような設計をする」という考え方は重要だ。たとえsigtermでコンテナが落ちたとしても、次のリクエストは次の別のインスタンスが立ち上げる対策をしておくことがポイントとなる。ステートレスなアプリケーション実装をまず第一に考えることで、スケーラビリティや耐障害性の向上につながるのだ。
そこで紹介されたのが「The Twelve-Factor App」だ。Herokuのエンジニアがまとめたアプリケーションを可搬性を高くするための12の要素で、例えば「バージョン管理された1つのコードベースですべての環境をデプロイする」「依存関係は明示的に宣言し、それぞれに依存関係を持つようにする」「共通の依存関係を持たない」といった、有用な考え方を定義している。
相談3:サーバーレスアーキテクチャにおけるセキュリティ
TECH PLAYでは、これまでモノリシックなアーキテクチャで運用を続けてきたが、現在はフロントエンドとバックエンドを切り離し、機能ごとに運用するアーキテクチャの見直しプロジェクトが進行している。
例えば、バックエンドの管理機能では、サーバーレスアーキテクチャの導入を検討。絶対に避けたいリスクとして、「ユーザーの個人情報などが不正なアクセスによって流出すること」が挙げられ、より機密性の高いアーキテクチャについて相談した。
想定しているアーキテクチャとしては、フロントエンドはReact.jsで実装し、バックエンドはLambda関数を使って、DBに格納されたユーザー情報などの機密性の高い情報にアクセスする構成。Amazon API Gatewayを経由して公開する構成である。
社員専用のバックオフィスシステムで、深夜や休日は使用しない。ユーザー数は数十人程度という小規模なシステムだ。大きく3種類のユーザーに分けてアクセスを制限する。
- 管理者:アドミン権限を持つユーザー。ユーザー情報など機密性の高い情報を取り扱うLambda関数を含むすべてのLambda関数にアクセスできる。
- 運用者:それ以外の社内ユーザー。日常的な業務で機密性の高い情報にアクセスする必要はないため、機密性の高い情報にアクセスするようなLambda関数へのアクセスは不可。それ以外のlambda関数にはアクセス可能できる。
- 不正なユーザー:外部の悪意を持ったユーザー。全てのLambda関数へのアクセスを不可とする。
このようなアーキテクチャについて、2つの質問が投げかけられた。
- サーバレスと親和性の高い認証基盤
- 管理者および運用者のロールの割り当ての方法
AWS Lambdaの責任モデルは以下の図で示した通り。黄色部分がAWSが管理する範囲だが、EC2やVMベースの責任共有モデルと比較すると、AWSが管理する範囲は大きくなっている。例えば、プラットホームの管理、オペレーティングシステムとネットワークの設定の管理などはAWSマネージドが提供する部分になる。
ユーザーが管理するのは青色で示された部分。例えばアプリケーションのアイデンティティとアクセス、認証・認可の設計、あとはアプリケーションの管理となる。
その上で「認証」(認証されるユーザーと認証されないユーザーを分ける)、「認可」(認証されるユーザーがアクションできるリソースの範囲を決める)と切り分け、API設定における認証・認可について解説された。
以下の図で示すように、API GatewayのAPI設定で認証・認可の設定が可能になっている。
API GatewayがサポートするプロトコルにはREST、WebSocket、HTTP APIの3つがある。RESTの場合、アタッチできるオーソライザー(認証・認可を司る制御機能)としてIAMアクセス権限、Lambdaオーソライザー、Cognitoオーソライザーなどがあるが、今回の場合はCognitoオーソライザーが適切だ。
Cognitoオーソライザーとは、ユーザーのアイデンティティ情報を管理する機能。ユーザーがログインする際に照合するIDとパスワードの管理を行う。認証行為をユーザーに提供し、認証をパスするとトークンが発行され、そのトークンでCognitoオーソライザーと統合されたAPI Gatewayのエンドポイントにアクセスすると、認証をパスできる。
Cognitoオーソライザーにはもう1つ機能があり、ユーザーごとにグルーピングを組むことができる。「管理者」と「運用者」のパターンで、例えば田中さんと伊藤さんが「管理者」で、鈴木さんと佐藤さんが「運用者」という形でグループにし、グループごとにロール権限を与えることが可能になる。
その権限ごとにIAMアクセス権限を割り振り、パーミッションが割り当てられたロールを司るリクエストであれば、Lambda関数に通すリクエストパターンを作ることで認可が設定される。
Cognitoオーソライザーには、ユーザーの追加や削除といったAPIも容易されており、アプリケーション上でユーザー権限の変更やアカウントの作成、削除ができる。CognitoはCloudFormationで管理できるリソースなので、SAMで管理することも可能だ。
ただし、その場合、仮にSAMのリソース一式を削除したときユーザー情報も巻き込まれることになるため、Cognito側で新しいアイデンティティが登録されたり、何か変更が起きたタイミングでlambda関数を呼び出し、データストア側にアイデンティティをバックアップするような仕組みは必要となる。
基本的にスタックを分けることは、良いプラクティスとなる。認証・認可だけに限らない話だが、ライフサイクルや粒度が異なるリソースに関してはスタックを分けて管理しておくことをお勧めとのことである。
システム開発におけるソリューションアーキテクトの考え方、着眼点を垣間見ることができた今回の内容を参考に、ぜひ自社のシステム改善につなげてほしい。