Lambdaで使用中のファイルディスクリプタを確認する
Lambda関数のクォータの1つにファイルディスクリプタ:1024
があります。今回、Lambdaで使用中のファイルディスクリプタ数を調査する機会がありましたので、その手順を紹介します。
背景
Lambda(Node.js)のInvoke ErrorとしてError: connect EMFILE
となる事象が発生しました。EMFILEエラーの原因として「/tmpディレクトリの容量」「ファイルディスクリプタ」の制限にかかっていることが考えられるとのことでしたので、使用中のファイルディスクリプタ数を確認することとなりました。
使用中のファイルディスクリプタ数を確認する
Lambdaで使用中のファイルディスクリプタ数はLambda Insightsを利用することでCloudWatch Logsに出力することが可能です。
Lambda Insightsを有効化
Lambdaの管理コンソールから対象となる関数を選択し、[設定]タブより[モニタリングおよび運用ツール]を開き、右上の[編集]を開きます。
モニタリングツールの編集ウィンドウが開きますので、[CloudWatch Lambda インサイト]の[拡張モニタリング]を有効化し、[保存]します。
CloudWatch Lambda インサイトを有効化するとLambdaレイヤーにLambdaInsightsExtension
というレイヤーが自動的に追加されます。
これで準備は完了です。
Lambda Insightsの出力を確認
Lambda Insightsの出力を確認するため、対象の関数を実行します。その後、CloudWatch管理コンソールの左ペインより、[インサイト]-[Lambda Insights]を開きます。マルチファンクションモードの表示になっている場合、[関数の概要]欄に先程Lambda Insightを有効化した関数のサマリーが表示されています。(出力に少々時間が掛かることがあります)
対象の関数を選択し、[パフォーマンスログを表示]を開きます。
CloudWatch Logs Insightsの画面が開きますので、デフォルトで表示されている以下のクエリを実行します。Lambda Insightsのログはロググループ/aws/lambda-insights
に出力されます。
fields @timestamp, @message, @logStream | filter function_name = "function-name" | sort @timestamp desc | limit 20
使用中のファイルディスクリプタ数のメトリクスはfd_use
になりますので、以下の例ではファイルディスクリプタを20
使用していることが判ります。
[ { "@timestamp": "2023-06-08 13:13:12.905", "@message": { "tx_bytes": 20007, "cold_start": false, "agent_version": "1.0.229.0", "cpu_user_time": 70, "agent_memory_max": 9, "billed_duration": 1703, "fd_use": 20, "threads_max": 10, "total_network": 68168, "cpu_total_time": 90, "billed_mb_ms": 217984, "tmp_free": 538324992, "total_memory": 128, "rx_bytes": 48161, "tmp_max": 550461440, "tmp_used": 12136448, "function_name": "function-name", "event_type": "performance", "cpu_system_time": 20, "_aws": { "CloudWatchMetrics": [ { "Namespace": "LambdaInsights", "Dimensions": [ [ "function_name" ] ], "Metrics": [ { "Name": "memory_utilization", "Unit": "Percent" }, { "Name": "total_memory", "Unit": "Megabytes" }, { "Name": "used_memory_max", "Unit": "Megabytes" }, { "Name": "cpu_total_time", "Unit": "Milliseconds" }, { "Name": "tx_bytes", "Unit": "Bytes" }, { "Name": "rx_bytes", "Unit": "Bytes" }, { "Name": "total_network", "Unit": "Bytes" }, { "Name": "tmp_used", "Unit": "Bytes" } ] } ], "Timestamp": 1686229655885, "LambdaInsights": { "ShareTelemetry": true } }, "used_memory_max": 65, "request_id": "ca90dbb7-d533-4920-9890-fe5ca9b48423", "fd_max": 1024, "duration": 1702, "agent_memory_avg": 9, "memory_utilization": 50 }, "@logStream": "function-name/fZpF5gCwBoxnyMJCpCUtC0gAl" } ]
使用中のファイルディスクリプタ数を可視化
Lambda Insightsで取得されるメトリクスの内、CloudWatch メトリクスとして時系列集計データとして可視化されるのはLambdaInsights
ネームスペースに登録される以下の8つのみです。
メトリクス名 | ディメンション | 説明 |
---|---|---|
cpu_total_time | function_name function_name、バージョン |
cpu_system_time と cpu_user_time の合計 単位: ミリ秒 |
init_duration | function_name function_name、バージョン |
Lambda 実行環境のライフサイクルの init フェーズで費やされた時間 単位: ミリ秒 |
memory_utilization | function_name function_name、バージョン |
最大メモリは、関数に割り当てられたメモリのパーセンテージとして測定されます 単位: パーセント |
rx_bytes | function_name function_name、バージョン |
関数の受信バイト数 単位: バイト |
tx_bytes | function_name function_name、バージョン |
関数の送信バイト数 単位: バイト |
total_memory | function_name function_name、バージョン |
Lambda 関数に割り当てられたメモリの量。これは関数のメモリサイズと同じです 単位: メガバイト |
total_network | function_name function_name、バージョン |
rx_bytes と tx_bytes の合計。I/O タスクを実行しない関数の場合でも、Lambda ランタイムによって行われるネットワーク呼び出しのため、この値は通常 0 より大きくなります 単位: バイト |
used_memory_max | function_name function_name、バージョン |
関数サンドボックスの測定されたメモリ 単位: メガバイト |
残念ながらfd_use
はLambdaInsights
ネームスペースに登録されておらず、そのままでは可視化されません。可視化するには以下のように、CloudWatch Insightsで[可視化]タブに切り替え、クエリを実行します。
今回はLambdaの実行中にファイルディスクリプタ上限に達していたかどうかを確認したいため、1分毎のログフィールドfd_use
の最大値を集約関数を利用して表示したいので、以下のようなクエリを実行します。
stats max(fd_use) by bin(1m)
他の統計集計関数等についてはドキュメントを参照ください。
以下は検証環境の例ですが、ファイルディスクリプタ数の最大値が可視化されています。もし、この値が1024
に達しているようであれば、EMFILEの原因はファイルディスクリプタ上限に刺さったことだと特定できますので、次に、なぜファイルディクリプタ上限に刺さってしまったのか、、とトラブルシューティングを進めることができます。
その他のメトリクス
fd_use
の他にもLambdaInsights
ネームスペースに登録されないメトリクスは以下のとおりとなっています。Lambda Insightsを利用の際にはCloudWatchメトリクスで表示されていないメトリクスもある、ということを覚えておくと良いでしょう。
メトリクス名 | 説明 |
---|---|
cpu_system_time | CPU がカーネルコードの実行に費やした時間 単位: ミリ秒 |
cpu_user_time | CPU がユーザーコードの実行に費やした時間 単位: ミリ秒 |
fd_max | 使用可能なファイル記述子の最大数 単位: 個 |
fd_use | 使用中のファイル記述子の最大数 単位: 個 |
threads_max | 関数プロセスで使用中のスレッドの数。関数の作成者は、ランタイムによって作成されたスレッドの初期数を制御しません 単位: 個 |
tmp_max | /tmp ディレクトリで使用可能な領域の量単位: バイト |
tmp_used | /tmp ディレクトリ内で使用される領域の量単位: バイト |
以上、誰かのお役にたてば幸いです。