AWS re:Invent2019 セッション紹介 大規模サーバーレス：設計パターンと最適化(Serverless at scale: Design patterns and optimizations)

いらっしゃいませ。わたしは@shnskfjwrです。
わたしは毎日すごい量のServerless.ymlを書いていますが、だれにも読ませるつもりはありません。
ですが、わたしは最近[ベガス](https://reinvent.awsevents.com/)に行き、さまざまなAWSタフガイ達からインストラクションを受け、ついでに所持金を1024倍にしました。
せっかくなので、一番興味深かったセッションの内容を読者のみなさんに共有します。

# セッション概要

**大規模サーバーレス：設計パターンと最適化**

原題: Serverless at scale: Design patterns and optimizations
日時: 2019/12/2, 2:30 PM - 3:30 PM
場所: ARIA Resort & Casino
話者: Roberto Iturralde

※英語スキルが無いため、スライドベースの雑訳となります

# セッション内容(雑訳)

## 大規模サーバーレスのネック

大規模なサーバーレス環境を構築するにあたり、ネックになるのは以下の6つです。

![IMG_0385.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501670/b80fce77-5f4a-795a-8865-b435c82cf6d3.jpeg)

- タイムアウト
    - API Gateway のタイムアウト(29秒)
    - Lambda 実行時間のタイムアウト(15分)
    - Lambda-RDS 間のコネクション数※
- スループット
    - Lambda の依存関係
    - Lambda の同時実行数制限
    - SecretManager の同時実行数制限
    - CloudWatch Metrics の同時実行数制限

※[RDS Proxy](https://aws.amazon.com/jp/rds/proxy/)の発表はこのセッションより後(だったと思う)


例えば、同期型サーバーレスAPIのイラッとくるポイントはこんなところでしょう。

![IMG_0392.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501670/515e05ea-b67d-1d46-b4bb-51b315dafe45.jpeg)


- DBの負荷がクエリのパフォーマンスに影響し、Lambdaの実行時間が伸びる
- Lambdaの実行時間が長くなるとコストが増加するうえ、API Gatewayのタイムアウト(29秒)が発生する
- Lambdaのタイムアウト(15分)も発生する
- Lambdaの同時実行数制限によりAPI Gatewayがエラーになる
- SecretManager や CloudWatch Metrics の同時実行数制限に引っかかるかもしれない


まとめるとこんな感じです。1つずつ解消していきましょう。

![IMG_0397.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501670/efe00cec-75ed-6632-0811-c3d032642f3d.jpeg)


## SecretManager の同時実行数制限の最適化

Lambdaハンドラーの外部でSecretを取得します。Secretは初期化時に取得されます。
より頻繁に更新するユースケースでは、ポリシーで有効期限を設定した上でキャッシュします。

![IMG_0398.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501670/1582260f-b1ac-e8fc-1963-adb7d29491a5.jpeg)


## CloudWatch Metrics の同時実行数制限の最適化

CloudWatch Launches Embedded Metric Format(EMF)を使用します。
指定された形式で標準出力に記録することにより、CloudWatch が非同期でメトリクスに変換してくれます。
参考URL： https://aws.amazon.com/jp/about-aws/whats-new/2019/11/amazon-cloudwatch-launches-embedded-metric-format/

![IMG_0400.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501670/82eba0a5-9d53-a90c-18cc-f9479a07be48.jpeg)
![IMG_0401.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501670/493ff188-a9ae-f74e-103c-31e30f848f5f.jpeg)

しかし…
残りはどうしましょう？
![IMG_0402.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501670/1a1a6597-3191-1402-c5b0-8926a08e9809.jpeg)

## 重要な問題

重要な問題、それは「そのAPIはレスポンスが必要か？」ということです。
![IMG_0403.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501670/092b2ee3-27ad-4e84-49aa-77cd3edc9a3d.jpeg)


## APIレスポンスが不要の場合

いいえ、知る必要があるのは

- リクエストが正常に処理されたこと
- ペイロードが永続的に保存されたこと

だけです
![IMG_0404.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501670/47cf88fa-c3c3-761c-5720-7455f6c93d6b.jpeg)


APIレスポンスが不要の場合は、次の構成が可能です。

![IMG_0407.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501670/e9815b3f-3f0b-c820-38d0-31722d51cda8.jpeg)

1. API GatewayをSQSに直接統合します。リクエストがSQSに保存されると、API Gatewayはレスポンスを返します。
2. SQSをLambdaのイベントソースとして設定します
3. Lambdaは予め指定された同時実行数に従って動作し、RDSのコネクションを制御します

SQSは障害対策(DLQ)をお忘れなく。
![IMG_0408.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501670/796cf290-c1b5-af2c-d586-05315d9fb75d.jpeg)

まだLambda本体のタイムアウトが発生している場合は…
これ(左)をこれ(右)に替えてください。
![IMG_0409.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501670/d6d352e9-0cf4-9090-f390-05a2f93ede69.jpeg)

つまりStep Function です。
並列で動かせるロジックはStep Function化しましょう。
![IMG_0410.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501670/3f38aa34-bc25-1fe3-70b0-5326aa0f93ec.jpeg)

## APIレスポンスが必要の場合

はい
処理を続行するにはレスポンスが必要です。
![IMG_0411.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501670/f2ff38ef-9cf2-39d0-7528-011b12a6ed90.jpeg)

APIレスポンスが必要な場合に考慮すべきポイント

![IMG_0413.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501670/25491f29-5f0c-1726-9825-d5894096f33c.jpeg)


- API Gatewayの制限
    - スループットを低く
    - クライアント側からリトライできる仕組みが必須
    - クライアントへの影響
- データベースの選定
    - Aurora Serverless
    - DynamoDB
    - 将来機能強化された他サービス
- 同期から非同期への基本パターン
    - Polling
    - Webhooks
    - WebSockets


API非同期化の3パターンを説明。

### Polling

![IMG_0417.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501670/286666ab-3c3b-c095-ef11-3a8f91c38d2b.jpeg)

1. クライアントはリクエストを送信し、requestIDを受け取る
2. バックエンドのサービスは非同期で動作し、ジョブステータスを更新する
3. クライアントはジョブステータスに対してポーリングを実施する
4. ジョブステータスが完了であるならば、クライアントは結果を取得する

![IMG_0418.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501670/adc0a325-1060-debf-7017-c45b7a39ddc8.jpeg)

- メリット
    - クライアントの変更が最小限
    - 既存のバックエンドをラップできる
- デメリット
    - ポーリング頻度によっては応答が遅延する
    - クライアントとサーバーのリソースが無駄遣いされる

### Webhooks

![IMG_0419.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501670/2b82b835-23c2-5bf1-81fc-03ec26fb8eba.jpeg)

※(オプション)クライアントとサービスの間で信頼関係を結ぶ
1. クライアントはリクエストを送信し、API Gatewayはキューを保存したらレスポンスを返す
2. バックエンドのサービスは非同期で動作する
3. ジョブの完了後、バックエンドがクライアントにコールバックする

![IMG_0420.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501670/ef4e36a6-ff71-4bd3-d078-ccdbdb5b009c.jpeg)

- メリット
    - ポーリングを行わないため、サーバーとクライアントのリソース集約度が低い
    - SNSは配信の再試行を行う
- デメリット
    - クライアントがWebエンドポイントをホストする必要がある
    - 再試行ポリシーが必要
    - for untrusted clients, you are responsible for clients demonstrating trust and retries

### WebSockets
![IMG_0422.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501670/7c86602e-556e-c75e-85dc-1b5540453305.jpeg)

1. クライアントはリクエストを送信し、SFnの実行ARN、OpenConnワーカーのタスクトークン、およびWebSocketエンドポイントを受け取ります
2. クライアントはSFnの実行ARNとタスクトークンを使用してWebSocketエンドポイントへの接続を開きます。LambdaはOpenConnタスクを完了します
3. DoWork(SFnのタスク)が完了すると、SFn並列状態が完了し、コールバックを送信します
4. クライアントはWebSocket経由で更新を受け取ります

![IMG_0423.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501670/c4f6bbb7-0866-644e-9340-401fd8d20867.jpeg)

- メリット
    - OpenConnection を介した直接の通知であること
    - クライアントのためより豊富なイベントであること
    - Webおよびモバイルクライアントフレンドリー
- デメリット
    - WebSocketプロトコルと関連ライブラリの知識が必要
    - WebSocketはすべてのブラウザでサポートされているわけではありません
    - 1秒あたり500接続の制限があります

参考URL： http://bit.ly/aws-poll-to-push

## まとめ
![IMG_0424.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501670/095c2ece-3e47-22af-ab9f-4b428606ee83.jpeg)

- 負荷テスト。各種サービスのスケーリング動作を理解すること。
- コンポーネントを分離すること
- リトライはバックオフとともにあること
- バッチ処理を活用すること
- don't orchestrate code in Lambda(おそらくSFnを検討すべきという意)
- 可能な限り非同期パターンを利用すること

# 感想
たとえ英語スキルが十分であったとしても、きちんと理解できたか非常に怪しいものではありますが、今後の開発に取り込める部分は多々あると感じました。

なお、本セッションはすでに動画が公開されています。
本記事で紹介できなかった内容もたくさんありますので、興味をもった方は動画を御覧ください。わたしが聞いた時は(多分)なかった[RDS Proxy](https://aws.amazon.com/jp/rds/proxy/)への言及もあります。
https://www.youtube.com/watch?v=dzU_WjobaRA