Serverless Anti-PatternsKeisuke Nishitani (@Keisuke69)
Amazon Web Services Japan K.K.Mar 09, 2017
Photo via VisualHunt.com
ProfileKeisuke NishitaniSpecialist Solutions Architect, ServerlessAmazon Web Service Japan K.K
@Keisuke69 Keisuke69
✤ RESTおじさん✤ 餃⼦の王将エヴァンジェリスト(⾃称)✤ ⾳楽が好きです、フジロッカーです、今年も⾏きます✤ ブログ: http://keisuke69.hatenablog.jp/
Keisuke69 Keisuke69Keisuke69x
サーバレスのメリット✤ サーバレスはバックエンドのアウトソース
⎻ サーバサイドやインフラがわからないフロントエンジニアだけでシステムを実現することも可能
⎻ バックエンド側のコードとサーバが減るため開発運⽤コストが最⼩化⎻ ⾃分の書いたコードをすぐ試せる、トライ&エラーが容易⎻ サービスプロバイダによってマネージされ、スケーラビリティやキャパシティ、
セキュリティの⼼配が不要⎻ ⾮常にコスト効率化が⾼く、多くの場合コスト減が⾒込める
✤ 開発者がビジネスにフォーカスできる
AWSにおけるアプリケーションの実⾏パターン✤ Amazon Elastic Compute Cloud (EC2)
✤ Docker + Amazon EC2 Container Service(ECS)
✤ AWS Lambda
AWSのComputeサービス
Amazon EC2 Amazon ECS AWS Lambda
スケールの単位 インスタンス アプリケーション ファンクション
抽象化 ハードウェア OS ランタイム
AWSのComputeサービス
Amazon EC2 Amazon ECS AWS Lambda
スケールの単位 インスタンス アプリケーション ファンクション
抽象化 ハードウェア OS ランタイム
AWSのComputeサービス
Amazon EC2 Amazon ECS AWS Lambda
スケールの単位 インスタンス アプリケーション ファンクション
抽象化 ハードウェア OS ランタイム
AWSのComputeサービス
Amazon EC2 Amazon ECS AWS Lambda
スケールの単位 インスタンス アプリケーション ファンクション
抽象化 ハードウェア OS ランタイム
サーバレスなアプリケーションモデルイベントソース ファンクション サービスなど
JavaC#Node.jsPython
λイベント
S3にオブジェクトが作られるKinesisにストリームデータが保存されるHTTPSによるリクエストetc...
Amazon S3 Amazon DynamoDB
Amazon Kinesis
AWS CloudFormation
AWS CloudTrail
Amazon CloudWatch
Amazon SNSAmazonSES
AmazonAPI Gateway
Amazon Cognito
AWSIoT
AmazonAlexa
Cron events
DATASTORES ENDPOINTS
REPOSITORIES EVENT/MESSAGESERVICES
AWS Lambdaと連携するイベントソース
Amazon Config
Amazon Aurora
AWS Lambdaを利⽤する場合のユースケース
データの変更、システム状態の遷移もしくはユーザによるアクションといったものに対応したコードの実⾏
レスポンスのカスタマイズとAWS内の状態やデータ変更に対するワークフローのレスポンス
Web、モバイル、IoTや外部APIへのリクエストを扱うバックエンドロジックの実⾏
Data Processing
Backends
Control Systems
Real-time File Processing✤ イメージのサムネイル⽣成やビデオの変換✤ ドキュメントのメタデータをインデックス化✤ ログの処理✤ メディアコンテンツのバリデーション
元画像 サムネイル画像
1 31.ファイルストレージを
提供するAmazon S32.処理ロジックを提供す
るAWS Lambda
Real-time Stream Processing✤ クライアントのアクティビティトラッキング✤ クリックストリーム分析✤ データクレンジング✤ ログフィルタリング✤ インデクシング✤ デバイスデータのテレメトリと測定
AWSLambdaAmazonKinesis
集計処理
リアルタイム
Cloudwatch logsやSNSなどとの連携も可能
Traditional Web Backend
AP(EC2)
DB(RDS)
LB(ELB)
Web(EC2)
Static Contents(S3)
ブラウザ/モバイル CDN(CloudFront)
Traditional Web Backend✤ メリット
⎻ 実績が多く枯れた構成⎻ カスタマイズ性が⾼い⎻ 知⾒を持っている⼈が多い
✤ デメリット⎻ サーバのスペック、台数などスケールを意識して設計する必要がある⎻ サーバの運⽤は利⽤者が負う必要がある⎻ アイドル時にもコストが発⽣する
Serverless Web Backend
ブラウザ/モバイル API Gateway AWS Lambda DynamoDB etc
S3CloudFront
AWSによるマネージ(フルマネージド)
Serverless Web Backend✤ メリット
⎻ クライアント側の実装は従来とあまり変わらずノウハウを活かせる⎻ サーバの運⽤、スケールはAWSに⼀任できる⎻ サービスの組み合わせだけで、セキュアなAPIアクセス制御などが実装できる⎻ コスト効率が⾼い
✤ デメリット⎻ カスタマイズ性が低い⎻ 設計および運⽤ノウハウが枯れていない
Amazon API Gateway
統⼀化されたAPIの作成と管理
APIの定義とホスティング
クラウド上のリソースへのアクセス認証
AWSのAuthを活⽤
バックエンド保護のためのDDoS対策やスロットリング
ネットワークトラフィックの管理
AWSのサーバレスオファリング
AWS LambdaAmazon API Gateway
Amazon DynamoDB
Amazon Kinesis Amazon Mobile Analytics
Amazon SNS
Amazon Cognito
AWS IoT
Amazon S3 Amazon Elastic Transcoder
AWS CloudWatch
AWS CloudTrail
Amazon SESAmazon Machine Learning
Amazon Route53Amazon SQS
LambdaでRDBMS使いがち問題✤ Lambda + RDBMSがアンチパターンな理由
⎻ コネクション数の問題⎻ Lambdaはステートレスなプラットフォームであるため、コネクションプールの実装は難しい⎻ Lambdaがスケールする、つまりファンクションのコンテナが⼤量に⽣成された場合に、各コン
テナからDBへコネクションが張られることになり、耐えられないケースがある⎻ VPCコールドスタートの問題
⎻ VPCのコールドスタートが発⽣する場合、通常のコールドスタートに⽐べて10秒程度の時間を必要とする
✤ ベストプラクティス⎻ DynamoDBを使う⎻ 何らかの理由でRDBMSとの連携が必要な場合はDynamoDB StreamsとLambdaを利⽤
して⾮同期にする
メモリやたら少ない問題✤ メモリ設定
⎻ 設定値としてはメモリとなっているが実際はコンピューティングリソース全体の設定
⎻ メモリサイズと⽐例してCPU能⼒も割り当てられる⎻ メモリ設定はパフォーマンス設定と同義⎻ コストを気にしがちだが、メモリを増やすことで処理時間がガクンと減り、結
果的にコストはそれほど変わらずとも性能があがることもある
✤ ベストプラクティス⎻ 最⼩から少しずつ調整し、変更しても性能が変わらない値が最適値
同期実⾏にこだわりすぎ問題✤ 同期でInvokeすると同時実⾏数の制限に引っかかってつまりがち
⎻ 同時実⾏数の計算は「1 秒あたりのイベント数 * 関数の実⾏時間」⎻ ⾮同期呼び出しの場合、許可された同時実⾏数内で順次処理をし、バーストも許容さ
れている⎻ 同期呼び出しの場合、許可された同時実⾏数を超えた時点でエラーが返されてしまう⎻ 同時実⾏数は制限緩和可能だが、実際にスロットルされていない状態でいきなり申請
しても基本的に通らない
✤ ベストプラクティス⎻ できるだけ⾮同期でInvokeするのがオススメ
⎻ その処理、本当にレスポンス必要ですか?⎻ 特にAPI Gatewayとの組み合わせの場合、PUT系の処理をGatewayのバックエンドと
してLambdaで直接処理するのではなく、サービスプロキシとして構成してSQS、Kinesisに流すなどする
サーバレスに夢⾒がち問題✤ サーバレスであれば全く運⽤が必要ない、インフラ費⽤が10分の1に
なる⎻ サーバの管理は不要だが運⽤は必要⎻ コスト効率が⾼いため、サーバを並べて同様のことを実装するよりは安くなる
可能性が⾼いが、リクエスト数が多い場合などはそれなりの費⽤になる⎻ インフラ費⽤だけでなく、トータルコストで考える必要がある⎻ 複雑なことをやろうとすると、設計・開発コストがあがる可能性も⼤きい
✤ シンプルに使うべきものはシンプルに使いましょう
サーバレスで難しいことしがち問題✤ サーバレスはシンプルに使ったほうが効果は⾼い
⎻ 基本は「Aが発⽣したら処理をしてBにアウトプット」⎻ シンプルに使うべきものを複雑な箇所に適⽤しようとすると難易度は上がる
⎻ 分散環境の考慮、失敗時のリトライなど
サーバレスで難しいことしがち問題✤ サーバレスはシンプルに使ったほうが効果は⾼い
⎻ 基本は「Aが発⽣したら処理をしてBにアウトプット」⎻ シンプルに使うべきものを複雑な箇所に適⽤しようとすると難易度は上がる
⎻ 分散環境の考慮、失敗時のリトライなど
✤ サーバレス以外と同じこと⎻ 標準的なプログラミング⾔語とプロトコル⎻ 標準的なデプロイとテストのプラクティス
サーバレスで難しいことしがち問題✤ サーバレスはシンプルに使ったほうが効果は⾼い
⎻ 基本は「Aが発⽣したら処理をしてBにアウトプット」⎻ シンプルに使うべきものを複雑な箇所に適⽤しようとすると難易度は上がる
⎻ 分散環境の考慮、失敗時のリトライなど
✤ サーバレス以外と同じこと⎻ 標準的なプログラミング⾔語とプロトコル⎻ 標準的なデプロイとテストのプラクティス
✤ サーバレス特有のこと⎻ イベント/リクエストドリブン⎻ モジュラー⎻ ステートレス⎻ 12 factor/Microservices/Reactive..
サーバレスで難しいことしがち問題✤ サーバレスはシンプルに使ったほうが効果は⾼い
⎻ 基本は「Aが発⽣したら処理をしてBにアウトプット」⎻ シンプルに使うべきものを複雑な箇所に適⽤しようとすると難易度は上がる
⎻ 分散環境の考慮、失敗時のリトライなど
✤ サーバレス以外と同じこと⎻ 標準的なプログラミング⾔語とプロトコル⎻ 標準的なデプロイとテストのプラクティス
✤ サーバレス特有のこと⎻ イベント/リクエストドリブン⎻ モジュラー⎻ ステートレス⎻ 12 factor/Microservices/Reactive..
✤ つまり⎻ モノリスは分解する必要がある⎻ ファンクションのコミュニケーションをどうするか考え、サービス境界をクリアにする必要がある⎻ 障害発⽣を前提とした設計の必要性、結果整合性の理解が必要
サーバレスで難しいことしがち問題✤ サーバレスはシンプルに使ったほうが効果は⾼い
⎻ 基本は「Aが発⽣したら処理をしてBにアウトプット」⎻ シンプルに使うべきものを複雑な箇所に適⽤しようとすると難易度は上がる
⎻ 分散環境の考慮、失敗時のリトライなど
✤ サーバレス以外と同じこと⎻ 標準的なプログラミング⾔語とプロトコル⎻ 標準的なデプロイとテストのプラクティス
✤ サーバレス特有のこと⎻ イベント/リクエストドリブン⎻ モジュラー⎻ ステートレス⎻ 12 factor/Microservices/Reactive..
✤ つまり⎻ モノリスは分解する必要がある⎻ ファンクションのコミュニケーションをどうするか考え、サービス境界をクリアにする必要がある⎻ 障害発⽣を前提とした設計の必要性、結果整合性の理解が必要
✤ したがって⎻ シンプルに使うべきものはシンプルに使いましょう⎻ フルサーバレスにこだわらず、コンテナなども使って適材適所で
何となくVPC使いがち問題✤ VPCは必須でない。必要でない限り使⽤しない
⎻ 使うのはVPC内のリソースにどうしてもアクセスする必要があるときだけ⎻ VPCアクセスを有効にしているとコールドスタート時に10秒から30秒程度余計
に必要になる
✤ ベストプラクティス⎻ 同期実⾏が必要な箇所やコールドスタートを許容できない箇所ではなるだけ使
わない⎻ VPC内のリソースとの通信が必要なのであれば⾮同期にする
⎻ RDBMSのデータ同期が必要なのであればDynamoDB StreamsとLambdaを使って⾮同期に
Limit Increase通らない問題✤ イベントソースがAmazon Kinesis / DynamoDBの場合、シャード数と等しくなる
⎻ ストリームのシャードごとに同時に実⾏されるため⎻ 例:100シャードのAmazon Kinesis ストリームの場合、同時実⾏数はどの時点でも最⼤ 100 件。
そのうちアクティブなシャードが10個だった場合、実際の同時実⾏数は10となる。✤ それ以外は、(1 秒あたりのイベント数 * 関数の実⾏時間)となる。たとえば、
関数が 1 秒あたりのイベント数が10 件で平均実⾏数が3秒の場合、同時実⾏数は30となる
1s 2s 3s 4s 5s
10 req/sec
Avg 3s / exec
“同時”に実⾏されているタイミング
Limit Increase通らない問題✤ 標準では100、実績がない状態でいきなり数千とか数万を申請しても
通らない⎻ 実際にThrottleされているかどうかの確認を⎻ Throttleされているかなどはメトリクスで確認可能
✤ Limit Increaseしても性能上のボトルネックは解消しないこともある⎻ ストリーム系の場合、シャード数を増やしたりバッチサイズを調整したり⎻ そもそもファンクションの実⾏に時間がかかっている場合、レイテンシは改善
しない
コールドスタート気にしすぎ問題✤ 安定的にトラフィックが発⽣している場合、コールドスタートの発⽣
頻度は多くない⎻ コールドスタートによる遅延が⼀切許容できないのであればそもそもLambda
をやめることも検討を⎻ コールドスタートの時間は⾔語の特性と⼤きく関連する
✤ コールドスタートは⼀年前とくらべて半分くらいの時間になっている⎻ そもそも、基本的にこの領域はお客様でできることは少ない
✤ それでも気になるなら、コールドスタートを頑張って速くするしかない
コールドスタートを速くする✤ Lambdaファンクションに対してPingする
⎻ 定期的にInvokeを⾏うことでコンテナが破棄されることを回避する⎻ 5分間隔程度がオススメ⎻ API Gatewayを利⽤している場合は該当のAPIへリクエスト⎻ それ以外は必要な数だけ⾮同期でInvokeする
✤ コンピューティングリソースを増やす⎻ メモリ設定⎻ コンピューティングリソースの割当を増やすことで初期化処理⾃体も速くなる
✤ ランタイムを変える⎻ JVMの起動は遅い⎻ ただし、⼀度温まるとコンパイル⾔語のほうが速い傾向
コールドスタートを速くする✤ VPCを使わない
⎻ そもそもLambdaと関係なくENIの⽣成とアタッチ処理は遅い⎻ コールドスタートを速くしたいならデータストアはDynamoDBが鉄則⎻ DBの問題でどうしてもVPCを利⽤したい場合はDynamoDB Streamsを利⽤した⾮同期反映を
検討する
✤ パッケージサイズを⼩さくする⎻ サイズが⼤きくなるとコールドスタート時のコードのロードおよびZipの展開に時間がかかる⎻ 不要なコードは減らす⎻ 依存関係を減らす
⎻ 不要なモジュールは含めない⎻ 特にJavaは肥⼤しがち
⎻ JavaだとProGuardなどのコード最適化ツールを使って減らすという⼿もある⎻ 他の⾔語でも同様のものはある
コールドスタートを速くする✤ Javaの場合だけ
⎻ POJOではなくバイトストリームを使う⎻ 内部で利⽤するJSONシリアライゼーションライブラリは多少時間がかかるので、バイトス
トリームにしてより軽量なJSONライブラリを使ったり最適化することも可能⎻ https://github.com/FasterXML/jackson-jr⎻ http://docs.aws.amazon.com/lambda/latest/dg/java-handler-io-type-stream.html
⎻ 匿名クラスをリプレースするようなJava8の機能を利⽤しない(lambda、メソッド参照、コンストラクタ参照など)
コールドスタートを速くする✤ 初期化処理をハンドラ外に書くとコールドスタートが遅くなるので遅
延ロードを⾏う
import boto3
client = None
def my_handler(event, context): global clientif not client:
client = boto3.client("s3")
# process
IP固定したがり問題✤ API GatewayやLambdaから別システムや外部APIにアクセスする際のソー
スIPアドレスを固定したい
✤ 実は⽇本固有の事情だったりする⎻ IPを固定すること⾃体はセキュリティでもなんでもない⎻ 署名や証明書などで担保すべき⎻ IPアドレスを固定するということはスケーラビリティを捨てることにもつながる
✤ LambdaではVPCを利⽤してNATインスタンスを使うという⽅法もなくはないが…⎻ VPCのコールドスタート問題⎻ ⾃前のNATインスタンスの場合、その可⽤性、信頼性、スケーラビリティを考慮する
必要がある
監視しなくていいと思ってる問題✤ Serverless != Monitorless
✤ 処理の異常を検知して対応するのはユーザの仕事⎻ 適切にログ出⼒を⾏い、適切にモニタする
✤ ベストプラクティス⎻ CloudWatchのメトリクスを利⽤(Errors, Throttles)⎻ CloudWatch Logsへのログ出⼒とアラーム設定
信⽤しすぎ問題✤ Lambdaと⾔えど通常の他のサービスと同様に障害発⽣を前提として
実装をする⎻ リトライ⎻ Dead Letter Queueの活⽤(⾮同期の場合)
✤ 冪等性はお客様のコードで確保する必要がある⎻ AWS Lambdaで保証しているのは最低1回実⾏することであり1回しか実⾏しな
いことではない⎻ 同⼀イベントで同⼀Lambdaファンクションが2回起動されることがまれに発⽣
する⎻ DynamoDBを利⽤するなどして冪等性を担保する実装を⾏うこと
Lambdaのランタイムをエミュレートする✤ Lambdaでは各ランタイムごとで使われているAMIを公開している
⎻ Amazon Linuxを利⽤⎻ 使っているランタイムのバージョンも公開している⎻ 内部的には標準的なコンテナ技術を使っている
✤ ファンクションといってもただのプログラムなのでローカル環境でも容易に実⾏できる⎻ ハンドラを呼び出すテストドライバを書くだけ⎻ Contextとイベントをエミュレーションする必要があるが、単に必要な値を⼊
れたオブジェクトを⽤意するだけいい⎻ イベントのサンプルは公開されている
テストドライバ例
import jsonimport lambda_function
f = open(“event.json”)event = json.load(f)f.close()
context = "”
lambda_function.lambda_handler(event,context)
テストドライバ例
import jsonimport lambda_function
f = open(“event.json”)event = json.load(f)f.close()
context = "”
lambda_function.lambda_handler(event,context)
イベントを静的ファイルとして⽤意しておき、ロード
<= 必要に応じて設定する(今回は空)
<= ファンクションの実⾏
テストドライバ例
def lambda_handler(event, context):#Do something
if __name__ == "__main__":f = open("event.json")event = json.load(f)f.close()
context = ""
lambda_handler(event,context)
• Pythonの場合以下のようにすることでも可能。Javaも同様にPublicなmainメソッドを定義して内部で呼び出すことも可能
もしくは✤ Serverlessのようなローカルテスト機能をもったサードパーティ製フ
レームワークを利⽤する
✤ 各ランタイム⽤のOSSなテストツールを利⽤する
lambda-local on npm
python-lambda-local on pip
aws-lambda-local-runner on maven
もしくは✤ Serverlessのようなローカルテスト機能をもったサードパーティ製フ
レームワークを利⽤する
✤ 各ランタイム⽤のOSSなテストツールを利⽤する
lambda-local on npm
python-lambda-local on pip
aws-lambda-local-runner on maven
個⼈的なオススメは⾃分でテスト⽤のドライバを書くか、ローカルテストツールを利⽤
まとめ✤ アンチパターンはなるべく避ける
✤ ただし、絶対悪ではない。いろんな事情で仕⽅ない場合もある⎻ 外部APIがIP固定を要求しているとか
✤ できるだけ避けることを考えつつ、適当なところで⼿を打つのがいい
Top Related