Open18

Datadog のホワイトペーパー "Serverless application in AWS" を読んでまとめる

hassaku63hassaku63

Sentions:

  • AWS Lambda
    • How to monitor AWS Lambda
  • Amazon API Gateway
    • How to monitor Amazon API Gateway
  • AWS Step Functions
    • How to monitor AWS Step Functions
  • AWS Fargate
    • How to monitor AWS Fargate
  • Monitoring your AWS serverless platform with Datadog
    • Visualize your serverless metrics
    • Search and analyze serverless logs in one place
    • Explore trace data with Datadog APM
    • Full visibility into your serverless ecosystem
hassaku63hassaku63

Intro

  • モニタリングの考え方は従来とは違う
    • 典型的なシステムメトリックが取れない
  • 観測可能なメトリック収集はサーバーレスにおいても重要である
    • Lambda の場合は同時実行数や割り当てられたメモリ量が重要な指標となる
      • 同時実行数を超過すると Function はタイムアウトするか、ランタイムによって kill される (※1)
  • この資料では、いくつかのシナリオを提示して、AWS でのサーバーレスのモニタリングの鍵となるメトリックを紹介するよ
    • Lambda
    • Fargate
    • API Gateway
    • Step Functions

典型的なシステムメトリックが取れない

おそらく、agent を仕込んで取得する OS レイヤのメトリックを指していると思われる。ping とかストレージのI/O, キャパシティ、あるいはCPU使用率とか

同時実行数を超過すると Function はタイムアウトするか、ランタイムによって kill される (※1)

同時実行数の超過によって引き起こされる結果はスロットリングなので、タイムアウトという表現は少々正確でないように見える(upstream 側の立場で見れば、まあ「正しい場合もある」くらいには言えそう)。

参考: https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/invocation-scaling.html

しかし、いずれにせよ同時実行数は lambda の実行リクエストの成否に直接関与する要素であるし、
メモリ量もハンドラの実行パフォーマンスに影響する重要なパラメータである(行うべき処理に対してリソースが不十分であった場合、Invocation timeout を引き起こす可能性がある)ことは疑いない。大筋として例示されたメトリックが Lambda をモニタリングする上で重要なメトリックであるとする主張は正しい

hassaku63hassaku63

How to monitor AWS Lambda

so monitoring your functions involves tracking function utilization, invocations, and concurrency (including provisioned concurrency).

Provisioned Concurrency も含めて同時実行数のメトリックを取る。これは、スロットリングによってハンドラが実行されないケースを見るためのもの。

function utilization については、意図するところが理解できず。すぐ後の文脈を見ると、おそらく Function utilization == メモリの使用量 を指しているものと思われる

Key function performance and utilization metrics

function の実行時間 (performance) と、function が実行中に使用したメモリ量 (utilization) を見る。これらを監視することで、コスト最適化に役立つ。

Function utilization のメトリックは、CloudWatch Logs の出力するログに含まれている。以下がその例;

REPORT RequestId: f1d3fc9a-4875-4c34-b280-a5fae40abcf9 Duration: 72.51
ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 58 MB
Init Duration: 2.04 ms

(注釈)これでしか見られないとしたら不便すぎる。同等の数字、他で計測できないものか?

Lambda の標準的なメトリックでは Utilization 関係は見えない。Concurrency 関係のメトリックしか出せないらしい。

Concurrency については、次のページから。

Concurrency はリージョン単位でクォータがかかる指標なので、関数単位のメトリックだけではなくリージョン全体の総和も見るべき。

また、 Provisioned Concurrency, Reserved Concurrency はカウントの仕方としては同一区分である (Reserved concurrent として扱う) らしいことが書いてあるので、Provisioned/ Reserved Concurrency を利用している環境で 意味のあるメトリックの取り方 をするためには Managing concurrency for a Lambda function の内容をきっちり読み込んでおく必要がある(ここでは割愛)

hassaku63hassaku63

↑メトリックの解釈を誤ると意味のないデータを取ってきてしまうかもしれないので、注意。

Concurreny に Provisioned / Reserved の区分があることは覚えておくとよさそう

hassaku63hassaku63

KEY FUNCTION INVOCATION METRICS

起動方法によって監視すべき内容が異なる、という話。Sync/ Async/ Event source mapping について書いてある。共通する要素もあるが、これらの呼び出しタイプによって差が出る部分もあるとのこと

Invocation の監視によって、アプリケーションのアクティビティや、関数全体のパフォーマンスを把握できる 。(具体的に Invocation とは何を指すのか?アクティビティとは?)

hassaku63hassaku63

呼び出し回数が異常傾向にあるときは、コード、あるいは Lambda から接続されているサービスに異常が出ている可能性がある。ダウンストリームのサービスに障害が発生した場合、Lambda はリトライを余儀なくされているかもしれない。

Iterator age

Iterator age:
Lambda emits the iterator age metric for stream-based invocations. The iterator age is the time between when the last record in a batch was written to a stream (e.g., Kinesis, DynamoDB) and when Lambda received the batch, letting you know if the amount of data that is being written to a stream is too much for a function to accept for processing.

There are a few scenarios that could increase the iterator age:
– a high execution duration for a function
– not enough shards in a stream
– invocation errors
– insufficient batch size

この辺は Iterator age なる用語がわかってないので据え置き。要するに、SQS や Kinesis から受け取る Batch が大きすぎないか?ということを監視したい、という話らしい。

このような場合は関数がより早く終了できるように対策を取る必要がある

Iterator Age の解釈がよくわからない。クラメソさんブログだと↓

https://dev.classmethod.jp/articles/aws-lambda-adds-enhanced-visibility-into-stream-based-processing-operations/

GALACTIC1969GALACTIC1969

もう自己解決してるかもですが、Iterator Ageは元のデータがStream(KinesisやDynamoDB Streams)に突っ込まれた時間と、Consumer(Lambda)がそれを取り出した時間の差ですね。これが短ければ短いほどデータを効率的に処理できていて、長くなっているとStreamに未処理のデータが溜まってることになります。Stream系はデータの寿命が最短24時間なので、未処理のデータがずーっと残っていると最悪Consumerが処理する前にロストということもあります。原因は原文の通り、データ処理に時間がかかってる、データ量に対してシャードが足りてない(シャードを増やすとI/Oも増やせる)、データ処理中に例外が発生して途中で詰まってる、バッチサイズが適切でない、などがあります。

hassaku63hassaku63

Iterator Age はストリームへの滞留状況がわかる。

ストリームの滞留となる原因は Lambda が重たいとか色々あるが、データの流量に対してさばけてるかどうかを一律で見られる便利指標になる。

Event Source Mapping ベースな Lambda である場合は Iterator age が有力な指標になる。IoT のようなデータ流量の多いワークロードにおいて、特に重要そう。

hassaku63hassaku63

Invocation

Anomalous changes in invocation counts could indicate either an issue with a function’s code or a connected AWS service.

For example, an outage for a function’s downstream service could force multiple retries, increasing the function’s invocation count.

呼び出し回数の異常傾向は、コード、あるいは接続しているサービスの異常を示唆している可能性がある。

例えば、lambda から接続している DDB が (スロットリングなどによって) 正しく動いていない、とか。または、もっと単純にバグによるエラー発生の場合もこれに該当しそう。


(考察)

Invocation の回数が増えてしまうのは lambda の再実行があるためなので、同期呼び出しの lambda(API Gateway の裏にある lambda)には当てはまらない話、というふうにも読める。デフォルト3回の再実行がある非同期呼び出しの場合に、この監視アプローチは有効なのではないか。

一方で、アプリケーションの立場としても、ハンドリングしきれてないエラーが発生すればそれはそれでアラートすべきとも思える。つまり、個々の Error も(アラートを伴う)監視の対象になるべきケースがあるのではないか?ということになる。対して Invocation count のアプローチは個々の障害というより総体として「アプリの可用性に影響を来す何かしら」の事象を効率良く把握できるポイントを見ている感じ。これはワークロードが太い場合は有効な方法だと思うし、本来的な「監視」の目的を捉えているようにも見える....

個々の実行レベルでアラート出すべき状況は存在しうると思うので、それと喧嘩しないためにはどうしたら良いのだろうか・・・?


Additionally, if your functions are located in multiple regions, you can use the invocation count to determine if functions are running efficiently, with minimal latency.

複数リージョンに分散する場合は、 呼び出し回数を利用して、最短のレイテンシで効率よく呼び出せているかどうか を判断できる
→ちょっと意味がわからないしユースケースの想像もつかない。リージョンごとでレイテンシを比較する、ということ?

hassaku63hassaku63

MONITORING FUNCTION CONCURRENCY

同時実行数の制約はリージョン単位である、という話が書いてある。

Invocation Count と一緒に監視することで、水平スケールの調整であったり、過剰なプロビジョニングを調整できたりする

→ 積極的にアラートするほどの話かどうか。致命的な障害とは言えないので、インシデントレベルとしては緊急度低めに扱うのが妥当そうに見える

You can also reserve concurrency to ensure that a function doesn’t process too many requests and overwhelm a downstream service.

ダウンストリームのキャパが決まってる場合に Provisioned concurrency の設定は有効(DDB のユニットが決めうちの場合に、lambda が並列実行しすぎると都合が悪い)

ただし、そのことで lambda の upstream が詰まる可能性はあるので、そこで Iterator age の監視が行きてくる

hassaku63hassaku63

MONITORING PROVISIONED CONCURRENCY

Allocating a sufficient level of provisioned concurrency (e.g., the number of warm instances) for a function helps reduce the likelihood that it will encounter cold starts, which can be critical for applications that experience bursts in traffic during specific times of the day (e.g., a food delivery application).

スパイクアクセスが想定される場合に Provisioned concurrency が有効。

時間帯によってウォームアップの数を変えたい場合は Application AutoScaling が利用できる。

https://docs.aws.amazon.com/autoscaling/application/userguide/what-is-application-auto-scaling.html

Provisioned concurrency は利用率のメトリックが取れるので、過剰(あるいは過小)な Provisioned concurrecy がないかをこのメトリックから確認できる。

hassaku63hassaku63

API Gateway

何某かの API サービス(ここでは、3rd party サービス提供の外部 API ではなく、内製で開発したバックエンド API のようなものを想定)に依存する機能は、API がコケると失敗してしまう

APIのパフォーマンスを監視すること、特にエラーや遅延の指標を見ることは、サーバーレスアプリケーションを顧客に提供するための重要な要素

Monitoring the performance of your APIs — particularly by looking at error and latency metrics—is an important part of ensuring your serverless application is available for customers.

hassaku63hassaku63

KEY API GATEWAY METRICS

次の2つ

  • 5xx Errors
  • Integration latency and latecy

5xx Errors

503 (Service Unavailable) is a common server error for API Gateway. These errors are typically caused by misconfigured gateways (e.g., referencing a function that no longer exists) or if there are too many requests to process.

API Gateway における 503 (Service Unavailable) は典型的なサーバーサイドのエラー。だいたいは設定ミス。あるいは、バックエンドの lambda がアクセス過多になり処理しきれなくなっている(スロットリングのことを言っているように読める)。


if there are too many requests to process と書いてあるのはよくわからない。429 ではないのだろうか??

https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/http-503-lambda-limit-execeeded-error.html

CloudFront の lambda edge の話になってしまうが、バックエンドの lambda のスロットリングは CloudFront としては 503 (Limit Exceeded) になる。API のフロントエンドにあたる CloudFront からすればサーバーサイドの問題なので、429 ではなく 503 を 返すこと自体は違和感ない


余談:

API Gateway で Cognito Authorizer を使っている場合に、API Gateway に対する HTTP Request に対するレスポンスが CloudFront から返ってくることがある。該当ケースは忘れてしまったが、たしか CORS や response header のマッピングなどなど設定のミスだったはず。。403 とかだったはず

おそらくエッジからのレスポンスなので CloudFront 経由のレスポンスになってるんだと思われるが、どういう仕様に基づくのか、一次情報は不明。誰かこのへんの内訳知っている人がいたら補足して欲しい...。

https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/http-503-service-unavailable.html

https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/api-gateway-api-endpoint-types.html

REST API type のドキュメントで、CloudFront にルーティングされるパターンがあった。たぶんこれ

Integration latency and latecy

CloudWatch は 2つのレイテンシに関するメトリックを提供する。

1つは Integration latency metric

こちらは、API Gateway の先にある(インテグレーションした)機能の 実体(?) に関するレイテンシ

The integration latency metric measures the time it takes for a function to return a response to API Gateway after it submits a request and can help you monitor the responsiveness of your functions.

もう1つは latency

これは API Gateway を利用する側の視点で見た (end-to-end) レイテンシ

The latency metric measures the end-to-end responsiveness of your API calls—the time it takes for the API Gateway to return a response after it receives a request from the client.

Integration latencylatency は、両者を比較して眺めることが大事。

Integration latency の増加は "Cold request" (いわゆる lambda の cold start など)や、インテグレーション先のサービス呼び出し(例えば異なるリージョンの ECS など)の呼び出しによって影響を受ける可能性があり、End-to-end の latency は一般的な AWS サービスの呼び出しにかかるレイテンシの影響を受ける。インテグレーション先の問題かどうかを切り分けるためには双方のメトリックを見る必要がある。

hassaku63hassaku63

MONITORING API GATEWAY LOGS

エラーやレイテンシの監視は問題の断片的な情報しか教えてくれない。問題を fix するにはより細かい情報が必要であり、そのために API Gateway のアクセスログを活用できる。

(例えば、Request/Response のパラメータ、ペイロードなどの情報が確認できる)

https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-logging.html#apigateway-cloudwatch-log-formats

API Gateway のログはどのようなときに役立つのか?例えば、↓

  • 特定の API にアクセスしてきたクライアントが権限不足であること (※1)
  • (API Gateway の) ダウンストリームである lambda が不正なレスポンスを返した

For example, monitoring API Gateway logs can help you determine if an increase in API errors is caused by insufficient permissions for a client accessing a specific API, or a downstream Lambda function returning a malformed response.

API Gateway のログはアクセスのコンテキスト情報を持っているため、トラブルシュートに役立つ。そして、IAM のパーミッション設定不備を End-to-end で可視化できる

API Gateway logs provide the context needed for troubleshooting issues with your APIs, so you have end-to-end visibility into each API endpoint misconfigured IAM role.

※1 ... IAM のパーミッションが原因でリクエストが失敗した場合、バックエンド (lambda など) にログが残らない


感想:

最後の IAM に関する記述が面白かった。監視というよりは E2E テストの文脈という感じがある。

※実際「監視は継続的なテストである」という言葉もあるので、ここでそういう言及の仕方があることは自然な発想なのかもしれない

また、サーバーレスのテストだと unit-test と e2e の間はあんまり作り込み過ぎない(超意訳)ように、と Serverless community hero の Yan cui が述べている。サーバーレスの世界だと機能開発レベルの話で構成図が変わることもよくあるので、中途半端な粒度のテストがたくさんあると、そのたびにテストの書き直しが発生してしまうので開発サイクルが鈍化する。よって、E2E レベルのような大きな視点で行うテストを手厚くするほうが費用対効果に優れる、としている

(パーミッション周りの)テストツールとしての API Gateway logs という考え方はあるのかもしれない。

hassaku63hassaku63

Step Functions

p.16 〜

CloudWatch が提供するメトリクスを使って、ステートマシンの実行 (execution) をトラッキングできる。

execution を監視することで、クォータのへ抵触や、無限にステートマシンが実行されてしまうリスクを検査できる


ステートマシンが無限ループの構造でもないと上記のリスクはあたらないのでは?という気はした。極端に高い並列度で実行リクエストがあった場合を想定するならばあたっているが...。

hassaku63hassaku63

Key AWS Step Functions Metrics

(1) Execution time

ステートマシンは最長で1年間実行中状態を保つことできる。しかし、実行中のステートマシンが終了する前に新たな実行をトリガーすることができるので、潜在的にステートマシンが無限ループに陥る可能性をはらんでいる。(※1)

Execution time を監視することで、ステートマシンが極端に長期間実行されているケースを特定することができる。

(2) Failed executions

アプリケーションの健全性を見る上での重要指標。

Lambda のメトリックと突合することで、失敗した原因を特定するのに役立つ。

例えば、失敗したステートマシンの実行数とLambdaのエラー数の両方が同じように増加している場合、問題は特定のLambda関数に関連している可能性がある。(※2)

If the number of Lambda errors is low, then the cause of the execution errors could be a misconfigured IAM role. (※3)


※1 ... ある単一の「実行」に関して、実行ステートを外部からの制御で任意の場所に飛ばす、みたいな話ではない(実行ステートを強制遷移する手段はあるが、Suceeded/Failed のみ)。ここでの想定はステートマシンが無限ループしうる構造をしている場合の話をしている。クォータのドキュメントはこちら

※2 ... 人間が個別で探す手間を掛けたくないくらいの規模感想定、単位期間あたりの実行数が多いワークロードであることを前提にしているように読めた。全体の実行件数がたかだか500-600件程度で5,6% ほどが失敗したと仮定すると、マネジメントコンソールから個々に Fail の調査をするような原始的な方法でも十分間に合った実績がある(張り付きで見ている必要はあったが)。なのでここでの話はもっと大きなワークロードでより重要になる話だと思われる。ちなみに、総体として失敗箇所の傾向を掴むのであれば X-Ray でも "一応は" 俯瞰ができる(ノードの縮尺感と文字の表示サイズが若干噛み合ってなく、見づらいという問題はある)

※3 ... これはちょっと意味がわからなかった。ロールの設定ミスであれば該当の処理に分岐した場合全部コケるものでは?と考えているので


Execution Failed の監視について、見解を補足

1件単位で失敗を検知して、それぞれに対して復旧措置を行いたいワークロードの場合は、AWS や Datadog の標準的な手段では役に立たないと見ている。

※アラートすること自体に意義があり、きちんとした情報収集やトラブルシュートは別途マネコンなり CLI なりで見に行く、というオペをやるのであれば CloudWatch Alarm や Datadog で十分まかなえる。このセクションで書く話は通知文面の時点である程度の情報を知りたい、というニーズありき

これは「どの execution で失敗したのか」をアラートして欲しいのに、その情報が通知する時点で得られないことが理由。

※バッチ処理のワークロードにおいて、チャンク(1件ずつ)にバラしてステートマシンに並列処理させるシステムを組んでいたので試してみたのだが、2021年6月時点ではエラー発生の監視は以下で紹介する方法だと実用に耐えないと判断している

CloudWatch Metrics による監視は公式ドキュメントExecutionFailed の時間あたりの発生件数を見る方法が紹介されている。が、これは「単位時間あたりにX件失敗した」を取っているだけなので、「ある "実行" が失敗した」をトリガーするものではなくアラート情報に特定情報を含められない。

Datadog の方は内部仕様が非公開であるが、 Describe 系の権限を要求しているためおそらくステートマシンの個々の実行をポーリングベースで見ているのだと思われる。しかし、アラート本文として埋め込み可能なテンプレート変数に ExecutionArn はサポートされていないので、こちらもアラートの通知に知りたい情報を含めることができない。

通知の時点で情報量を持たせたい場合に取れる方法は、CloudWatch Logs (for Step Functions) の subscription filter で失敗のログを検知すること。この方法であれば少なくともログの中に ExecutionArn やエラーメッセージは含まれるので、通知する価値のある情報を出すことができる。ログを受け取って通知する Lambda の存在が必要になるので、そこの実装コストが発生する。また、それなりのステートマシン規模、それなりの実行頻度を見込むのであれば、直接 Lambda を呼ぶのでなく Kinesis を噛ませるなどの対応も必要になる。

ステートマシンの全ノードに Fail の分岐を想定し、その分岐先で通知サービス(SNS など)に飛ばす、というような手も一応は考えられる。ただしこのアプローチは無駄にステートマシンの遷移が増えて見づらくなるし、ステートマシンや Lambda のタイムアウトなどアプリケーションコードに直接的に由来しない失敗原因を全部を拾いきるのが難しいこともありあまり現実的なプランではなさそう...という印象を持っている。