【レポート】サーバーレスアーキテクチャパターンとベストプラクティス #reinvent #ARC401

こんにちは、虎塚です。

re:Invent 2017のセッション「Serverless Architectural Patterns and Best Practices」(サーバーレスアーキテクチャパターンとベストプラクティス) の動画を視聴したのでレポートします。発表は、AWSのDrew DennisさんとMaitreya Ranganathさんでした。

本セッションでは、Webアプリケーション、データレイク、ストリーム処理、運用自動化 の4分野のインフラストラクチャについて、サーバーレスで構築する場合にどのAWSサービスをどう使うか、パターン を紹介してくれます。これらの用途でサーバーレス構成を検討している方には、参考になる部分があると思います。

Agenda

本セッションでは、サーバーレスでの構築パターンを紹介します。それぞれの話題に入る前に、すべてのパターンに共通するサーバーレスアプリケーションの基本的なコンセプトについてお話しします。

  • サーバーレスの基本
  • 1.ウェブアプリケーション
    • モバイルバックエンド、マイクロサービス、APIデプロイに
  • 2.データレイク
    • データのカタログ作成と分析処理
  • 3.ストリーム処理
  • 4.オペレーション自動化

サーバーレスの基本

AWSが提供するスペクトル

まず、AWSにはアプリケーションをデプロイする上で多くの選択肢があります。EC2、EC2上のDockerコンテナ、ECSなどにアプリケーションをデプロイできます。

次に、マネージドサービスと呼ばれるものがいろいろあります。EMR、ES、ElastiCache、RDS、Redshiftなどです。これらのサービスを使う上で、あなたはサーバに対する責任を負う必要はありませんが、サーバは依然として存在します。サーバは、サービスのスケールに必要な正しいサイズや台数を定義する際に重要です。

最後に、本日注目するサーバレスのサービス群があります。AWS Lambda、Cognito、Kinesis、S3、DynamoDB、SQS、API Gateway、CloudWatch、AWS IoTなどです。これらのサービスにはサーバが存在しないためにオペレーティングシステムの管理が不要で、運用のオーバーヘッドを減らせます。

サーバーレスとは……

サーバーレスアプリケーションについて述べる際、AWSは次の一般的な4つの定義を遵守します。

  • サーバーをプロビジョンまたは管理しない
  • アイドルタイムに課金されない
    • たとえば2ヶ月ごとに周期的に実行されるアプリケーションの場合、間の期間に動いていなければ課金されない
  • 高可用性とディザスタリカバリがビルトインされている
    • 本日取り上げるのはリージョンレベルのサーバーレスアプリケーションなので、私たちは自動的にそのリージョン内のすべてのAvailability Zoneを高可用性確保とディザスタリカバリのために使えるものとする
  • 利用に応じてスケールする
    • この4つの中で最も重要な概念
    • アプリケーションに流入するリクエストやデータ量が大きくなるとサーバーレスアプリケーションはスケールする
    • サーバー利用時よりもスケールのコアに集中できる。アプリケーションのスケールの単位がLambda関数 になる。

Lambdaの検討とベストプラクティス

このため、Lambda関数の振る舞いについて触れておくことは重要です。

  • コンテナ再利用の恩恵を得るために、AWSクライアントやデータベースクライアントのインスタンス化は、ハンドラーのスコープの外でしよう

Lambda関数を最初に呼び出した時に、コールドスタートプロセス と呼ばれるがプロセスが実行されます。このプロセスには、Lambdaへのコードのダウンロード、Lambdaホスト上でのコンテナの開始、あなたがLambda関数内で定義した初期化コードの実行が含まれます。

サンプルコードでは、外部ライブラリのインポートや、データベースコネクションの作成をハンドラーのスコープの外でしています。これらは、コールドスタートプロセスの間に実行されます。Lambda関数が実行されている間、コンテナは維持されるので、次にLambda関数が呼び出された時にこれらの処理はすでに実行済みかもしれません。サーバーレスアプリケーションでは、 できるだけコンテナを再利用してパフォーマンスの恩恵を得る ことが重要です。

  • 暖機のためにCloudWatch Eventsをスケジュールしよう

これは、コールドスタートプロセスを常に暖機しておくための一般的なプラクティスです。

  • VPC内のプライベートリソースにアクセスするためにVPC内のENI (Elastic Network Interfaces) がLambda関数に紐付けられるのも、コールドスタートの間におこなわれる

Lambda関数内でVPC内のプライベートリソースにアクセスする処理を書いている場合は、これも一般的なプラクティスです。

Lambdaベストプラクティス

その他のLambdaベストプラクティスをいくつか紹介します。

  • パッケージサイズを必要最小限にする
  • Lambdaハンドラーをコアロジックと分離する
    • Lambdaハンドラーはイベント発生ごとに実行されるが、かならずしもコアロジックはそうする必要がないはず
    • コアロジックをハンドラーの外に出すことで、ユニットテストを実装しやすくなる
  • Lambda関数の設定を変更するために、環境変数を最大限使う
    • Lambda関数内部のコードを直接変更せずに、コードの動作を変えられる
  • 依存ライブラリをLambda関数のパッケージ内にできるだけ同梱する
    • たとえばAWS SDKのバージョン違いによって問題が起きることを防ぐ
  • Lambda関数の最大メモリ使用量を正しいサイズに調節する
    • 必要な量を割り当てることで、コールドスタートにかかる時間を短くできる
    • Lambda関数呼び出しごとのメモリ使用量をCloudWatchで確認して、適正なメモリ使用量を割り当てる
  • 使用していない巨大なLambda関数を削除する (75GBの上限があるため)

AWS X-Rayとサーバーレスの統合

AWS X-Rayは、Lambdaと緊密に統合されたトレースツールです。Lambdaの設定で「Enable active tracing」チェックボックスをチェックするだけで、トレースを有効にできます。デフォルトで1秒につき1個のリクエストをサンプリングします。

さらに、X-Rayは空港遅延マップのようなものを提供します。AWS X-RayやLambdaのアップストリーム、ダウンストリームをグラフで表示し、平均実行時間や実行時レイテンシを一目でわかるように表示します。

サンプリングの間隔をカスタマイズするには、外部の設定ファイルに定義して読み込んでください。

var AWSXRay = require('aws-xray-sdk-core');
AWSXRAy.middleware.setSamplingRules('sampling-rules.json');
var AWS = AWSXRay.captureAWS(require('aws-sdk'));
S3Client = AWS.S3();

2行目でJSONの設定ファイルを読み込んでいます。3行目は、AWS SDKを読み込むためのrequire文をラップしています。そのため、4行目のAWSクライアント (S3クライアント) は、X-Rayの設定つきでインスタンス化されます。

  • すべてのサポート言語で、Lambdaは受信したリクエストを計測する
  • SDKを使ってすべての言語上で、LambdaはX-Rayデーモンを実行する

X-Rayのトレース例

次のスライドは、X-RayがLambda関数のために提供するグラフです。

グラフの上半分は、Lambda関数自体の情報です。Dwell time (Lambda関数が実行されるまでどれくらいかかったか) や、Lambda関数の開始から終了までの全体の実行時間が表示されます。

グラフの下半分は、特定のLambda関数の情報です。初期化処理にかかった時間や、その後のハンドラの処理にかかった時間が表示されます。

Lambdaサービスの開始から、Lambda関数の呼び出しの間に、タイムラグがあるのが分かるでしょう。これが、前述したコールドスタートの課題です。

AWS Serverless Application Model (SAM)

サービスのデプロイとモデリングについて、基本のツールにもう一つ触れておきましょう。

サーバーレスアプリケーションのために、もちろんCloudFormationでAWS LambdaやAPI Gatewayをデプロイできますが、数百から数千のLambda関数を使うようになると複雑になります。AWSは、より効率的で、アプリケーションのモデリングにあたって冗長性を減らした手法が求められていることに気づきました。それで、AWS Serverless Application Model (SAM)をリリースしました。

SAMの特徴は、次のとおりです。

  • サーバーレスに最適化されたCloudFormation拡張
  • サードパーティによるCloudFormationサポートツールと一緒に使える
    • CloudFormationパラメータやCloudFormation固有の関数に対応
  • 新しいサーバーレスリソースタイプ (関数、API、テーブル) を既存のリソースと一緒に定義できる
  • アプリケーションのデプロイをサポート
    • アーティファクトをS3にアップロードする
    • CloudFormationをチェンジセットとして使える
  • Apache 2.0ライセンス

SAM Local

2017年夏にパブリックベータが発表されたSAM Localのことを聞いたことがある方も多いでしょう。

Lambda関数をサービスにデプロイする前にテストするさまざまな方法について、多くのフィードバックを受け取りました。それがまさにSAM Localが目的としているところなので、うれしく思います。

  • ローカルで関数を開発、テストできる
  • サーバーレスイベントのモックを使って関数を呼び出せる
  • ローカルテンプレートバリデーション
  • ホットリローディングありのローカルAPI Gateway
    • コードをon the flyで変更してテストできる

ユニットテストが複雑になったら、SAM Localが提供するCLIを使いましょう。CLIには、すべてのSAMコマンドと、アプリケーションをパッケージングしてデプロイのためのコマンドが含まれています。

CodePipelineによるデリバリー

もちろん、AWS環境でCloudFormationテンプレートをカスタムパイプラインにデプロイする最も良い方法は、おそらくAWS CodePipelineです。これは、AWSの継続的インテグレーションツールで、SAMテンプレートやCloudFormationテンプレートをビルトインでサポートしています。

さまざまなシナリオをCodePipelineはサポートします。パイプラインにステージやカスタムステージを追加することができます。たとえば、コードをビルドしてテストするCodeBuildingステージを追加できます。また、複数の環境にデプロイすることができます。たとえば、開発環境でテストにパスしたコードを、ステージング環境に自動でデプロイすることが可能です。

パイプラインのフローは、次のとおりです。

  1. リポジトリにコードをコミットする
  2. CodeBuildでパッケージングとテストをする
  3. CodePipeline内でCloudFormationアクションを使い、SAMテンプレートを介してスタックを作成・更新する
    • (オプション) チェンジセットを利用する
  4. ステージングなど複数環境用の変数をファイルで指定し、Lambdaに渡す
  5. アプリケーションをステージングなど複数環境でテストする
    • (オプション) 本番環境へのデプロイ前に、手動での承認をおこなう

AWS CodeDeployとLambda Canaryデプロイ

Lambdaエイリアスを複数のLambda関数バージョンにマップできる Lambdaの新機能にも触れておきます。これはLambdaの Canary Deployments でとても重要です。

  • 複数の関数バージョンの定義をSAMテンプレートに組み込むことができる
  • トラフィックの一部を新しいバージョンに誘導する
    • どれくらいの量をどのバージョンに誘導するかを指定できる
    • あるバージョンからあるバージョンへトラフィックを移す間隔を指定できる
  • CloudWatchでアプリケーションの安定性を監視する
  • 新しいバージョンのLambda関数で不具合が起きた時など、必要があればロールバックを開始する

パターン1: ウェブアプリケーション / マイクロサービス/ API

(13:20-)

ウェブアプリケーション

S3に置いた静的リソースを、CDNであるCloudFormationを介して配信する典型的なパターンからはじめましょう。このシステムでは、より直接的な呼び出しを受けるための経路としてAPI Gatewayを配置し、API GatewayのバックにあるLambda関数はDynamoDBなど他のサービスにアクセスします。

さらに、アプリケーションにサインアップ/サインインを提供するために、Amazon Cognitoを使います。Web Identity Providerと同様に、システム内でIdentity Federationをおこないます。

このパターンはさまざまなWebアプリケーションでとてもよく使われています。

事例: BustleはAWS Lambdaによって84%のコスト削減を達成

それらの1つとして、女性をターゲットにしたエンターテイメント・ライフスタイルWebサイトのBustleは、伝統的なインフラからサーバーレス環境に以降したことで、オペレーションコストを84%削減しました。

  • これまでBustleは、Webサイトのスケーリングと高可用性のメンテナンスのために重い管理をしなければならなかった
  • AWS LambdaとAPI Gatewayを利用するサーバーレスアーキテクチャに移行した
  • 約84%のコスト削減を体験した
  • 今ではエンジニアはイノベーションに集中できるようになった

BustleのCTOは、オペレーションのオーバーヘッドを心配しすぎる必要がなくなったと述べています。これは、高可用性のキャバビリティを考えなければいけなかったサーバがなくなったことを、端的に言い表しています。

サーバーレスアプリケーション

サーバーレスアプリケーションでのセキュリティ には、伝統的なアプリケーションと少し異なるところがあるため、話題にする価値があるでしょう。

先に挙げたそれぞれのAWSサービスが持つ多くのセキュリティ機能のすべてを使うことはできませんが、そのうちのいくつかを使うことができます。

  • S3バケットに対して CloudFrontのオリジンアクセスアイデンティティ を使える
  • CloudFrontとAPI Gatewayは、AWS Certificate ManagerまたはACM TLS証明書 を現在サポートしている
    • API GatewayやCloudFrontエンドポイントのドメインのためにカスタムドメイン名を作ったり、TLS証明書を管理したりするのが簡単になった
  • IAMで、API GatewayにLambda関数を呼び出す権限を与えたり、Lambda関数にダウンストリームサービス (この例ではDynamoDB) を呼び出す実行ロールを与えたりできる
  • API Gatewayメソッドの認証
    • さまざまな方法があるが、一般的なのはIAM認証
    • APIの特定のメソッドを呼び出す際に、IAMクレデンシャルを要求できる
  • CognitoでIAMクレデンシャルを扱ういくつかのシナリオについては後述

カスタム認証

IAM認証に加えて、API Gatewayにはカスタム認証の機能があります。

上の図のCustom Authorizer Lambda関数の目的は、IAMポリシーを返すことです。このIAMポリシーは、API Gatewayの背後にあるLambda関数を実行する特定のメソッドへのアクセス可能性を検証します。

API Gatewayには、次の2種類のカスタム認証があります。

  • トークンタイプ: リクエストのヘッダで渡される認証用トークンを利用
    • リクエスト内のトークンを検証するためにOpenIDやCognitoプロバイダとやりとりする
  • リクエストタイプ: すべての種類のヘッダ、クエリ文字列、パス、ステージ変数やコンテキスト変数などを利用
    • トークンタイプよりも、カスタマイズ性と柔軟性がある
    • 例: アプリケーションをデプロイしたステージ (環境) ごとに異なる認証スキームや認証戦略を使いたい場合は、リクエストタイプの認証が適切

Amazon Cognitoでの認証

Cognitoを使った認証のシナリオをいくつか見てみましょう。

パターンA

図の左側にいる3人のユーザは、Google、Facebook、Amazon.comなどのWeb Identity Providerを利用します。Web Identity Providerは、ユーザにCognitoにトークンを送り返します。このトークンは、IAMロールを介してAWSクレデンシャルを使うために、Cognitoに送信されて交換されます。

これは、API内に定義した/webリソースへのアクセスを得るために、Web Identity Providerに基づいてIAM認証を使う一般的なパターンです。

パターンB

もう一つのシナリオでは、Cognito内にユーザープールを定義します。これは、AWS上でメンテナンス可能なユーザーとグループのディレクトリです。

このシナリオは、パターンAのトークン交換と同様にIAM認証の実装を提供します。しかし、パターンBの素晴らしい点は、ロールを得るためにグループのメンバーシップと属性を使えるところです。たとえば、もし私が特定のグループ、たとえば開発部署に所属していれば、そのことをもってIAMロールへのアクセスを得ることができます。

パターンC

最後のシナリオは、API Gatewayがビルトインでサポートする認証方法です。これは、前述のIAM認証やカスタム認証と違って、Cognitoのユーザープールに特化して設計されています。認証局から受け取ったJSONのWebトークンを使って、リソースへのアクセスを直接バリデートされます。

API Gatewayでマルチリージョン

数ヶ月前にAWSは、API Gatewayのリージョナルエンドポイントという大きな発表をしました。これは、API GatewayをCloudFrontから切り離すもので、本当に大きなリリースでした。なぜなら、この機能でマルチリージョンのサーバーレスアプリケーションが構築できるからです。

このパターンでは、API Gatewayのリージョンエンドポイントを使い、異なるリージョンのAPI Gatewayに同一のカスタムドメイン名を関連づけます。

たとえば、Route 53のようなDNSサービスでCNAMEの重み付けルーティングを使って、APIのカスタムドメイン名に対するリクエストのトラフィックを誘導します。普段はすべてのリクエストを1つのリージョンにルーティングしておいて、フェールオーバーが発生した時には、それらのリクエストを同じ名前を使って別のリージョンに自動的に誘導できます。または、両方のリージョンを利用するアクティブ-アクティブ戦略も実装できます。

サーバーレスウェブアプリケーションのための便利なフレームワーク

サーバーレスウェブアプリケーションに適応する上で便利なフレームワークやライブラリをいくつか紹介します。

パターン2: データレイク

(21:00-)

サーバーレスデータレイクの特性

データ分析について考える上で重要なのは、あなたのニーズは固定されておらず、将来のある時点で変化しうるということです。そのため、変化に迅速に対応できるように基礎的なコンポーネントを構築して、データレイクを適応させることが重要です。

  • すべての組織的なデータの収集/保存/処理/利用/分析
  • Schema on Read

組織的なデータをすべてデータレイクに投入しましょう。将来どのようにデータを使いたくなるか分かりませんが、スキーマの実装をScheme on Readの戦略でおこなうことで、後から特定のスキーマに合わせた問い合わせができます。

また、次のことに対応する必要があります。

  • 構造化/半構造化/非構造化データ
  • 伝統的なBI/分析ツールから、AI/Machine Learningを使ったユースケースまで
  • 迅速な自動データ読み込み
  • EDW (エンタープライズデータウェアハウス) との相互補完
    • データウェアハウスにデータを投入する前のステージング環境としてデータレイクを使うのは一般的
  • コンピューティングとストレージを分離して実装
    • 1つのデータレイクを複数のコンピューティング環境に渡って実装し、それぞれを独立して一時的にスケーリングさせることで、実行中にだけ課金されるようにできる

AWSサーバーレス データレイク

次の図は、データレイクのアーキテクチャです。

まず、データレイクの中心はS3です。S3には、データレイクに関係する多くの機能があります。

次に、さまざまな形式でデータを受け入れる必要があるため、データ読み込みは重要です。後ほど、ストリーム処理と大量データの読み込みについて説明します。

  • Amazon Kinesis Streams
  • Amazon Kinesis Firehose
  • AWS Direct Connect

それから、カタログ作成と検索も重要です。この後、いくつかのパターンについて説明します。

  • Amazon DynamoDB
  • AWS Glue
  • Amazon ES

コアである分析とデータ処理も、とても重要です。また、サーバーレスではありませんが、EMRもこの箇所で重要な役割を果たします。

  • AWS Lambda
  • Amazon Athena
  • Amazon QuickSight
  • AWS Glue
  • Amazon Redshift Spectrum

データレイクでもセキュリティと監査は重要です。データアクセスの資格を管理し、異なるユーザ/グループやデータの構成要素に対してアクセス権を与えたり、データを暗号化したりしたいと思います。

  • AWS IAM
  • Key Management Service
  • AWS CloudTrail
  • Amazon Macie

Amazon Macieはデータを分類するサービスです。S3に格納したファイルのコンテンツに基づいて、機械学習アルゴリズムを使ってデータタイプを自動的に推論します。

最後に、データレイクのためのユーザインタフェースです。ユーザにポータルへログインさせて、データレイクから必要な情報を発見、検索させたり、同じタスクをAPIでおこなったりできます。もちろん、それらのAPIは、IAMで実装したセキュリティポリシーに準拠した動作をします。

  • Amazon Cognito
  • API Gateway
  • AWS IAM

Amazon S3の基本

データレイクの基本としてS3をおさらいしましょう。

  • ストレージのためのコンピューティングクラスタ実行が不要
  • 仮想的な無制限のオブジェクトとボリューム
  • とても高いバンド幅 - 集計のスループット制限なし
    • データのアップロードやダウンロードのためにジョブやワーカーを並列実行すると、バンド幅はそれに応じて増える
  • 用途に応じて選べる複数のストレージクラス
    • 頻繁にアクセスしない古いデータは、異なるストレージに移せる
  • バージョニング
  • 暗号化
  • CloudTrail Dataイベント
    • S3オブジェクトがいつ削除/追加されたかだけでなく、ファイルがいつ読み取りされたかも監査できる
  • S3組み込みのAnalyticsとInventory
    • 利用プロファイルやS3内のインベントリ情報 (データサイズ、ファイル数など) を見ることができる
  • AWS Configによる自動化されたチェック
    • ある日誰かがS3バケットにパブリックな読み取りアクセスをつけることが心配な場合、AWS Configに監視させて、事象が発生した時に通知を受けられる
  • S3オブジェクトへのタグ付け

検索とデータのカタログ作成

今回は、検索とカタログ作成のパターンを2つ紹介します。

イベントベース

最初は、イベントベースのパターンです。

  • S3にデータが到着したら、そのファイルのメタデータをDynamoDBに保存するために、Lambdaを実行する
    • データセットの例: IoTのユースケースならデバイスに関する情報、特定のオブジェクトのバージョンヒストリーなど
  • DynamoDB StreamsとLambdaを使って更新されたストリームを得る
  • サーチインデックスを作成して検索可能にするために、追加の情報を加えてAmazon ESにデータを送信する

これはデータレイクにデータカタログを提供するとても良い方法です。全体的な説明は、次のページをご覧ください。

Amazon S3上のデータレイクへのインスタントなクエリ

AWS Glueは、スケジュールベースまたはオンデマンドでクローラをディスパッチします。自動的にファイルの中身を見て、ファイルのスキーマを推論し、カタログを作成します。AWS Glueがクローラで自動作成したカタログは、Hiveのメタデータストアに準拠しているので、AthenaやRedshiftなど他の分析サービスで使えます。そのため、定義されたテーブルとこれらの分析サービスをすぐに作成して、お手元のBIツール (Amazon QuickSightなど) でクエリを投げ始めることができます。

データ分析と処理

データレイクに関係するデータ分析と処理の観点から見ると、S3の情報とデータにアクセスでき、分析やデータ処理のワークロードを実行できるツールはたくさんあります。

今回は、AthenaとLambdaを使ったサブパターンに注目します。

Athena - サーバーレスなインタラクティブクエリサービス

Athenaは、サーバーレスなインタラクティブクエリサービスです。S3に保存したデータにSQLでクエリする手段を提供します。

Athenaの素晴らしいところは、非常に高速であることです。例として、次のクエリで170 GBのデータを45秒以下でスキャンできました。GROUP BY句を指定したにもかかわらずです。

SELECT gram, year, sum(count) FROM ngram
WHERE gram = 'just say no'
GROUP BY gram,year ORDER BY year ASC;

4GBのデータをほぼ1秒で処理できるといえます。Athenaは、データ操作のエンジンにPrestoを使っています。

Athenaのベストプラクティス

Athenaを効率良く使うためのベストプラクティスから、特に重要なものをいくつかご紹介します。

  • 参考: Top 10 Performance Tuning Tips for Amazon Athena | AWS Big Data Blog

  • データをパーティションする

    • Athenaはスキャンしたデータ量に対して課金するので、スキャンするデータ量を減らすことで節約できる
    • S3に次のようなパスでデータを格納すると、Athenaがデータを自動でパーティションする
    • s3://bucket/flight/parquet/year=1991/month=1/day=2/
    • データを手動でパーティションすることもできる
  • S3にデータを格納する時はカラムナフォーマットを推奨
    • Apache Parquet, AVRO, ORC
  • ファイルサイズの最適化
    • 大きな1つのファイルや小さいたくさんのファイルは、S3とやりとりする際のオーバーヘッドを増やす
    • 大きな1つのファイルに対しては並列処理ができない
  • 分割圧縮でファイルを圧縮
    • Parquetは分割圧縮をサポートするのでAthenaに最適。gzipやbzip2も良い

サーバーレスバッチプロセスのパターン

これは、バッチプロセスのためのよりDIY寄りなアプローチです。

データソースから取り出したデータを、まずLambda関数 (Splitter) で、1行ごと、もしくは特定のサイズで分割します。その後、データ処理のために並列で実行されるLambda関数群 (Mappers) にデータを渡して、結果を永続化ストレージに書き込みます。最後に、データを収集するLambda関数 (Reducer) が、S3に結果を格納します。

事例: Fannie Maeのサーバーレス金融モデリング

Fannie Maeは、上記にとてもよく似た構成を採用しました。Fannie Maeでは、住宅ローンのシミュレーションシステムを、サーバーベースのアプローチからサーバーレスへ移行しました。

フィナンシャルモデリングは、プロジェクトのために将来のキャッシュフローをモンテカルロ法でシミュレーションするプロセスです。これは住宅ローンのリスクを管理するために日次で使われています。

毎月、数億のキャッシュフロープロジェクトに対して、数百の経済シナリオで評価します。2千万の住宅ローンのシミュレーションを1回1.4時間でおこないます。既存のサーバーベースのアプローチによるプロセスよりも、4倍も高速になりました。

詳細は、セッション 'SRV317 Unlocking High Performance Computing for Financial Services with Serverless Compute' で聴くことができます。サーバーレスに興味のある、特に金融業界の方にお勧めです。

Pywren

あなたがすでにPythonのコードを持っていて、自前でMapperやReducerを書きたくない場合には、pywrenのようなライブラリをおすすめします。pywrenは、Lambdaの水平スケールを支援します。

  • ピーク時に10TFLOPSのコンピューティングパワーを提供
    • 1,000の同時実行関数
  • S3に対して 60 GB/sec の読み取りと 50 GB/sec の書き込みパフォーマンスを実現

パターン3: ストリーム処理

(33:35-)

あなたは大きな成功をおさめているeコマースWebサイトの設計と運用の責任者であると想像してください。サイバーマンデー1にWebサイトで発生したすべてのクリックストリームデータを収集しました。これまでそのデータを24時間かけて日次で処理してきましたが、マネジメントがダウンタイムを1日から数分に短縮したいと言いました。これは、ストリーミングデータの入力、そしてストリーミングデータを処理するアプリケーションの古典的な例です。

ストリーム処理の特性

クリックストリーム分析などの多くのアプリケーションに見られるストリーム処理の一般的な特性は、次のとおりです。

  • 高い読み込み率
  • 準リアルタイム処理 (イベント発生時点からデータが処理されるまでのレイテンシが低いこと)
  • スパイクのあるトラフィック (多くのデバイスが断続的なネットワーク接続をする)
    • たとえば、サイバーマンデーには、他の時期の平均よりもトラフィックが何倍も大きくなる
  • データの耐久性
  • メッセージの順序

ストリーミングデータの入力

マネジメントのニーズをどのようにして満たすか、最初の例を見てみましょう。

このアーキテクチャのコアは、図の中心にあるAmazon Kinesis Firehoseです。Kinesis Firehoseは、大量のデータをバッファ処理しながら小さなバッチで取り込み、宛先のサービスへ届けます。Kinesis Firehoseは、すべてのデータ処理をストリーム内でするのではなく、システムに入ってくるハイレートのメッセージと、データのバッチ処理を扱う宛先のサービスとを本質的に分離します。

rawレコードのデータを直接Kinesis Firehose APIに送信できます。または、ログファイルを監視するKinesis agentをインストールして、ログに新しいメッセージが書き込まれたらKinesis Firehoseストリームに送信できます。

レコードがストリームに送信されたら、そのレコードを変換できます。Lambda関数を呼び出したり、Lambda関数にrawデータを送信したり、Lambda関数の中にメッセージを変換するカスタムロジックを持たせたりできます。たとえば、Lambda関数内で数値のタイムスタンプを人間が読みやすい形式のタイムスタンプに変換できます。また、DynamoDB等のデータベースからデータを探してユーザ情報を取り出し、レコードに意味付けをすることもできます。

データ変換が終わったら、これらのレコードはKinesis Firehoseに戻されます。Firehoseはマイクロバッチを作成し、それらのデータを宛先サービスに送ります。たとえば、変換後のデータを格納するためにS3に送ったり、Redshiftに変換後のデータを直接送って、データ分析用のテーブルに自動で読み込ませたりできます。データをAmazon Elasticsearch Serviceに送り、インデックスして検索可能にして、Kibanaのような可視化ツールで見ることもできます。

図の右下にあるのは、一般的に ソースレコードバックアップ と呼ばれる機能です。データを変換する際に、変換前のrawレコードのコピーを保持しておきたくなるかもしれません。この機能は、Firehoseにアクセスしてデータのコピーを手に入れ、定義したS3バケットに送信します。

上のようなパイプラインをセットアップした後は、CloudWatchでパフォーマンスを監視できます。Firehoseのパブリッシュに関するメトリックスを使って、S3に送信中のデータがキューの先端からどれくらい離れているかを見ることで、入力されてくるデータに対して処理がどのくらい遅れているかを確認できます。

ベストプラクティス

このパターンを実装する時のベストプラクティスをいくつか紹介します。

  • Firehoseの buffer sizebuffer interval を調整しよう
    • これらの2つのパラメータは相互作用して、どれくらいの頻度でメッセージを宛先に送るかと、どれくらいのサイズのバッチでレコードを処理するかを決める
    • たとえば、処理対象が大きなオブジェクトに偏っていると、データ変換用のLambda関数の呼び出しやS3 PUTアクションが減り、コストが低くなる
  • ストレージコストを減らすために圧縮を有効にしよう
    • 同様にデータ送信のコストも減らせる
  • データ変換のためにソースレコードバックアップを有効にしよう
    • 変換エラーが起きた時にデータをリカバーするため

処理データの宛先にRedshiftがある場合は、次のドキュメントも参考にしてください。

センサーデータの収集

別のシナリオを考えてみましょう。ホリデーシーズンに百万個のコネクテッドサーモスタットを販売したとします。購入した人たちが自宅でサーモスタットのスイッチを入れると、サーモスタットのデータを収集してインテリジェントな動作をさせるクラウドベースのサービスを提供したいと思います。

サーモスタットは低電力デバイスなので、メモリやCPUをあまり持っていませんし、ネットワークへの常時接続性もありません。IoTデバイスのために最適化されたプロトコルを使いたいと思います。この例では、MQTTを使用しました。サーバーサイドはAWS IoTです。AWS IoTはMQTT越しにメッセージを受け取り、デバイスから来たデータを測定します。

データがAWS IoTまで来たら、IoTルールを記述できます。IoTルールは、SQLで入ってくるメッセージをフィルタリングします。たとえば、温度や湿度などの異なるメッセージタイプがある場合、フィルタを使って温度だけを読んで、それに反応できます。また、ルールを使ってIoTアクションを設定できます。設定したルールにしたがって、データを宛先に送信できます。この例では、次のパターンが可能です。

  • S3にrawデータやセンサーデータを直接格納する
  • データをFirehoseに送り、それからS3に送信する
  • データ処理を準リアルタイムに実行したい場合、データをKinesis Streamに送り、それをフックアップしてリアルタイムアプリケーションで処理する

リアルタイム分析

リアルタイムアプリケーションの例を見てみましょう。温度センサーを持っているとします。温度データにはノイズが多くなりがちなことが知られています。この測定値を毎分スムーズに処理したいものとします。デバイスのオーナーが設定した閾値と実測値を比べて、平均の温度が閾値を超えたらアラートを送りたいと思います。

次の図は、上記を実現するパイプラインです。

図の左側から、Kinesis Streamにデータが入ってきます。1つ前のスライドで見たIoTパイプライから入ってくると考えてください。入ってきたデータをKinesis Analyticsアプリケーションに関連づけます。

Amazon Kinesis Analytics

Kinesis Analyticsは、ストリームから入ってきたデータを準リアルタイムに処理できるサービスです。ロジックはSQLを使って表現できます。

ストリーミングSQLの一部をウォークスルーで見てみましょう。

CREATE OR REPLACE PUMP "STREAM_PUMP" AS INSERT INTO "DESTINATION_SQL_STREAM"
SELECT STREAM "device_id"
STEP("SOURCE_SQL_STREAM_001".ROWTIME BY INTERVAL '1' MINUTE) as "window_ts", 
SUM("measurement") as "sample_sum", 
COUNT(*) AS "sample_count" 
FROM "SOURCE_SQL_STREAM_001" 
GROUP BY "device_id", 
STEP("SOURCE_SQL_STREAM_001".ROWTIME BY INTERVAL '1' MINUTE);
  • 1, 2, 6, 7行目: 送信先 (destination) と同様に送信元 (source) をセットアップする
  • 3行目と8行目: データ処理のための1分間隔のtumbling windowを設定
  • 4, 5行目と7行目のGROUP BYパラメータ: データの集計
    • センサーデータを合計するとともに、データの個数をカウントして、平均値を出す
    • すべての測定値をデバイスIDでグルーピングする

この処理の結果、測定値セットの平均を毎分1個、各デバイスにつき得られます。複雑な処理も、数行のSQLでシンプルに表現できることがわかるでしょう。

Error stream

測定値を集計できたら、それらのデータを宛先のKinesis Streamsにもう一度送り、閾値に達したデータに対してLambda関数でカスタムロジックを実行します。ここでは、送られてきた測定値の平均値と、DynamoDBから取り出したユーザ定義の閾値を比べ、必要に応じてAWS SNSでアラートを送ります。

上の図の下のほうにあるもう一つのストリームは、Error streamです。メッセージストリームを処理する時、Kinesis Analyticsは、フォーマットにミスのあるレコードに遭遇することがあります。Kinesis Analyticsは、フォーマットミスのレコードやエラーレコードを指定した宛先に送ります。このケースでは、Firehoseストリームに送り、それらのレコードのコピーをS3に保存します。そのため、後でこれらのデータを修復して処理を再試行できます。これは良いプラクティスです。

Amazon Kinesis StreamsとAWS Lambda

上の図の中央にあるKinesis StreamsがLambdaをトリガーする部分に注目しましょう。この部分に関するベストプラクティスをいくつか紹介します。

Kinesisはシャードでスケールします。各シャードは、1MB/secの入力と2MB/secの出力のキャパシティを持ちます。これがKinesis streamの設定でのスケーリング単位になります。次のことが重要です。

  • Kinesis Streamsの シャード の数は、Lambda関数の 同時呼び出し数 に対応する
    • 1対1関係。5個のシャードがあれば、Lambdaの同時呼び出しは5回
    • つまり、1回のLambda呼び出しでシャードのキャパシティをフルに処理できるように、Lambda関数のコードを書く必要がある
  • Batch size は、Kinesis StreamがLambda関数を呼出すごとに送るレコード数の最大値を設定する
    • デフォルトは100だが、スループットの高いアプリケーションの場合はより高い値に調整する (最大値は1000)
    • 大きな値を設定することで、一度のLambda関数呼び出しで処理できるメッセージ数を増やす

Fan-out Pattern

ときどき、Lambda関数で捌きたいデータ量が多すぎて、1つのシャードから入ってくるメッセージのレートを維持できないと感じることがあるかもしれません。こういったケースでは、Fan-outパターン を検討するとよいでしょう。

このパターンでは、Lambda関数のロジックを2つの部分に分けます。最初のパートがDispatcherで、Kinesis Streamsからメッセージの大きなバッチを受け取り、それを小さなバッチに分割して、処理用のLambda関数を並列に呼び出す責任を持ちます。この右側にある処理用のLambda関数が、Processorです。

このパターンを使うことで、処理に並列性を持たせて、高いスループットと低いレイテンシーを実現できる でしょう。その代わり、並列に処理を実行するので、メッセージの順序性は諦める ことになります。1回の呼び出しで順序のあるメッセージセットを処理するのではなく、すべてを並列に処理するため、メッセージが順序にしたがって処理されることは保証されません。

システムの要件によってはFan-outパターンがフィットするでしょうし、しない場合もあるでしょう。

事例: Thomson Reuters - Product Insight

このストリーム処理パターンを使った顧客事例として、Thomson Reutersを紹介します。Thomson Reutersは、ニュースとデータフィードを全世界のビジネスやプロフェッショナルに提供しています。

すべてのプロダクトについて、システムがどのように使われているかを知りたがっているプロダクトチームがいます。プロダクトチームは、使用分析トラッキングを使って、プロダクトインサイトなプロダクトを作りたいと思っています。このソリューションは、クリックストリームやすべてのプロパティの分析から情報を収集して、先ほどの構成に似たパイプラインを介してフィードを発行します。そして、プロダクトチームがプロダクトを改善できるように、準リアルタイムに確認できるインサイトを生成します。

彼らが共有してくれた統計によると、現在はピーク時で4,000リクエスト/秒のリクエストという条件でシステムをテストしていて、これが10,000リクエスト/秒を超えて、ほぼ月間250億リクエストにまで増えるそうです。リクエストが入ってきてから、ダッシュボードにインサイトが表示されるまでのEnd-to-Endのレイテンシーは、10秒未満です。

また、彼らは、このサービスを稼働してから一度もレコードを失っていないと喜んでいます。

これは、ストリーム処理パイプラインの成功例のひとつです。

ベストプラクティス

さらにいくつかベストプラクティスを紹介します。

  • Amazon Kinesis StreamsによってLambdaがトリガーされる時の batch size を調整する
    • 大きなbatch size = 少ないLambda呼び出し回数
    • Lambdaは呼び出し回数に対して課金されるので、呼び出し回数を抑えることで費用を節約できる
  • Lambda関数に割り当てる メモリ を調整する
    • 大きなメモリ = 短い実行時間
  • クライアントサイドのパイプライン処理を書くために Kinesis Producer Library (KPL) を使う
    • 複数のメッセージをローカルでバッチ処理して、1つのKinesisレコードに入れる
    • Amazon Kinesis Streamのキャパシティをフルに使い、API呼び出しをより少なくする

関連サービスの比較

ストリーム処理に関連するサービスとして、Kinesisをここまで見てきましたが、SQSやSNSもストリーム処理のためのサーバーレスアーキテクチャに使われます。さまざまな角度からこれらのサービスを比較しました。

ビジネス要件に応じて、これらのサービスの中から1個またはそれ以上のフィットするユースケースを採用してください。重要な点を2つ説明します。

メッセージの順序について

  • Kinesis Streams: シャード内で強く保持
  • SQS
    • Standardキュー: ベストエフォート (保証されない)
    • FIFOキュー: メッセージグループ内で強く保持
  • SNS: なし

時間的に前後した場合

  • Kinesis Streams: retention period内であればメッセージを再処理できる (シャードイテレータでアクセス)
  • SQS: 一度通知を受けるともう送られてこない
  • SNS: SQSと同じ

パターン4: 運用の自動化

(49:28-)

自動化の特性

自動化ソリューションには次のような特性があります。今回は、上から3個のパターンを紹介します。

  • 定期的なジョブ
  • イベントトリガーのワークフロー
  • セキュリティポリシーの強制
  • 監査と通知
  • アラームへの応答
  • AWS機能の拡張
    • 例: カスタムオーダーのスケーリングの作成

サーバーレスのパターンが適合するのは、これらについて、高可用性、スケーラブル、監査可能性が要求される時です。

定期的なジョブ: AWS Ops Automator

定期的なジョブのためのパターンを見てみましょう。EBSスナップショットの取得と、どれくらいの期間それらのスナップショットを残したいかを設定するという例です。

非常に大きなAWSアカウントのフリート (サーバ群) を持っていて複数のAWSリージョンを使っている場合、それらすべてを調整してEBSスナップショットを長期間追跡するのは難しくなりがちです。そこで、Ops Automatorと呼ぶソリューションを作りました。

次のリンク先にQuick Startがあり、上の図の構成をCloudFormationでデプロイできます。

ロジックとイベントトラッキングのためのマスターアカウント用テンプレートと、アクションをトリガーするためのスレーブアカウント用のテンプレートがあります。マスターアカウントでタスクのプロパティ、期間、頻度を定義します。これらのプロパティは、すべてDynamoDBテーブルに保存されます。

プロセスは、定期的なイベントを設定したCloudWatch Eventが、Lambda関数のイベントハンドラをトリガーするところから始まります。Lambda関数はDynamoDBからタスクの定義を取り出し、何を実行する必要があるかを決め、1個またはそれ以上のイベントハンドラーやタスクexecutorを呼び出したりします。これらのLambda関数は、スレーブアカウント内でRoleをassumeして、必要なアクションを実行します。

EBSスナップショットを取るためにしなければいけないことは、EC2インスタンスにOpsAutomatorTaskListというと呼ばれる特別なタグをつけることだけです。タグの値をCreateSnapshotに設定します。こうすることで、Lambda関数が、このEC2インスタンスのスナップショットを作成します。この仕組みは、複数アカウント、複数リージョンで動作します。

最も良い点は、たくさんのタスクがどのように実行されたかを、CloudWatch Logsと同じようにDynamoDBに情報を書き込むことで、追跡できることです。プロセスにエラーが発生したら、EmailまたはSNSで自動的に通知します。

これは、一般的な定期タスクに対する素晴らしいソリューションです。このフレームワーク全体を拡張して、他のタスクにも適用できます。Redshiftクラスタをビルトインでサポートしていますし、サーバにアクションをおこなうSystem Managerタスクを呼び出したり、Inspectorのようなセキュリティアクションをトリガーしたりもできます。

イベントトリガーのワークフロー: 画像認識と画像処理

異なるユースケースを見て見ましょう。もっと複雑なワークフローを持っているとします。ユーザが画像をアップロードし、アップロードされた画像に対して即座にワークフローアクションを実行するWebサイトを運営しているとします。

たとえば、上の構成では、ユーザがアップロードした画像がS3に格納されると、Event NotificationがLambda関数をトリガーして、Lambda関数がStep Functionsのステートマシンを呼び出します。Step Functions内には、複数のLambda関数によって実行される複雑なワークフローがあります。最初のLambda関数は、画像からイメージタイプやEXIFヘッダーなどのメタデータを取り出します。さらに、他のLambda関数を並列に呼び出します。1つはAmazon Rekignitionを呼び出して、シーンからオブジェクトを探し出そうとします。もう1つは、画像のサムネールを作成します。最後に、4個目のLambda関数が、これらすべての情報をDynamoDBに書き込みます。結果として、ユーザが画像をアップロード後、ページをリフレッシュすると、画像のメタデータ、サムネール、オブジェクトタグなどが表示されます。

これは、画像処理をするためのリファレンスアーキテクチャの一例です。このパターンを拡張して、実行したい他のイベントに適用することもできます。

Step Functionsのステートマシン

この背後にあるステートマシンをさらっと見ておきましょう。フローチャートのようですね。

複数のステップが強調動作します。並列実行するステップやエラーハンドリングも定義できますし、どこかのステップで問題が起きた時には処理の中断もできます。この方法の利点は、ステップを簡単に後から追加できることです。

セキュリティポリシーの強制

最後の例は、セキュリティポリシーを強制するパターンです。あなたの組織の決まりでは、「外部からのRDBへのアクセスをセキュリティポリシーのルールで決して許可しない」ものとします。ポリシーを強制する方法はいろいろありますが、この例では、 環境がポリシーを満たしているかをワンステップで検知する 方法を使います。

この方法は、誰かがそういったルールを作成したら、あらゆるセキュリティグループの変更を検知するCloudWatch Eventがトリガーされるところから始まります。そのイベントをサブスクライブするLambda関数は、許可されたタイプのルールと禁止されたタイプのルールをチェックするカスタムロジックを持っています。作成されたルールが禁止されているものだった場合は、即座にEmailアラートを送信し、そのルールを削除するためのAPIを呼び出します。RDSが外部に露出されていた時間は数秒で、問題はほぼ何も発生しないでしょう。

このパターンで、イベントを他のAWSアカウントにルーティングすることもできます。

事例: Autodesk - Tailor

運用自動化のパターンを採用した顧客は、Autodeskです。Autodeskは、AWSアカウントのプロビジョニングと管理にサーバーレスを採用しました。

Autodeskは、建設業者にソフトウェアを提供しています。実験的なプロジェクトのためのエンジニアリングチームを多数抱えていますが、AWSアカウントを新しく作るのに多大な時間をかけたり、セキュリティ要件を満たすのに大変な労力を払っていたため、イノベーションを素早く起こすことができずにいました。

そこで、Autodeskは tailor と呼ばれるツールを作成しました。tailor (仕立て屋) という名前が示唆するように、このツールはAWSアカウントを企業標準に合わせて「仕立て」ます。

  • AWSアカウント作成の自動化
  • IAM, CloudTrail, AWS Config, Direct Connect, VPCを企業標準にしたがって設定
  • 企業標準の強制
  • コンプライアンスのための監査

新しいアカウントのプロビジョニングに10時間かけていた以前の手動プロセスを、 10秒 に短縮しました。Autodeskは、今では多くのプロジェクトを同時に実施できるようになりました。

さらに、Tailorのフレームワークはすべてオープンソースです。あなたが同じ種類の課題を抱えているなら適用できますし、拡張もできます。

ベストプラクティス

サーバーレスで運用を自動化する際のベストプラクティスをいくつか紹介します。

  • AWS APIを呼ぶ時の APIスロットリング は、エクスポネンシャルリトライバックオフのアルゴリズムで処理する (AWS SDKは対応済み)
  • オペレーションにとって意味のある カスタムメトリックス をLambda関数から発行する
    • 例: EBSボリュームのスナップショット数をトラッキングする
  • Lambda関数に対して X-Ray トレーシングを有効にする
  • トラブルシューティングにそなえて、自動化のためのイベントトリガーを 無効化 する方法をドキュメントに残す

まとめ

このセッションでは、DevOpsツールを使ってサーバーレスデプロイを自動化する方法を紹介しました。また、次の4つのパターンについて、深く見てきました。

  • Webアプリケーション
  • データレイクの基礎部分
  • ストリーム処理
  • 運用自動化

これらのパターンをあなたの組織に持ち帰って、あなたが抱えるビジネス課題の解決にフィットするのはどのパターンかをぜひ考えてみてください。サーバーレスで構築したものを皆に共有して、サーバーレス革命に参加しましょう。

おわりに

AWSサービスを使ったサーバーレス構成で、さまざまな種類のインフラストラクチャをどのように構築するかが、よく整理されたセッションでした。自社の組織やお客様の課題に適合するパターンが見つかれば、試してみようと思います。

それでは、また。


  1. ブラックフライデー翌週の月曜日。北米では大規模なセールがおこなわれます。