Edited at
MobingiDay 4

Lambda Layerを使ってAWS LambdaのCPU使用率を計測する


はじめに

lambdaでは通常CPU使用率を気にする必要はありませんが、パフォーマンスチューニングをして実行時間を短くしようとした際に、処理のボトルネックがIOなのかCPUなのか知りたいと思うケースがあります。

その際に簡単なデコレータを用意してCPU使用率を計測できるようにして、その機能をLambda Layerで使えるようにしてみました。

なおpython3の話です。


概要


  • psutilを使えばCPU時間を取得できる

  • 関数の実行前後でCPU時間を取得するデコレータ関数を作る

  • その関数をLambda Layerとしてデプロイする

  • Lambda関数でLayerをインポートしてのエントリポイントにデコテータをつける


詳細


psutilを使ったデコレータ関数を用意する

psutlを使えばCPU時間が取れます。

import psutil

psutil.cputimes()


結果

scputimes(user=344.21, nice=2.71, system=38.98, idle=39275.96, iowait=5.9, irq=0.0, softirq=7.84, steal=13.8, guest=0.0, guest_nice=0.0)


psutil.cpu_timesを使って関数実行前後にCPU時間を取得し、その差分からCPU使用率を取得するデコレータ関数を作成します。

from functools import wraps

from psutil import cpu_times

def cpu_trace(func):
@wraps(func)
def wrapper(*args, **kwargs):
before = cpu_times()._asdict()
result = func(*args, **kwargs)
after = cpu_times()._asdict()

increment = {}
for key in after.keys():
increment[key] = after[key] - before[key]
total = sum(increment.values())
rate = dict( (key, value / total ) for key, value in increment.items())
print(rate)

return result

return wrapper

cpu_timesの戻り値はnamed_tupleなので、そのままでは各要素の値を計算し辛いです。なので_asdict()を読んでdict型で保存しています。

後はafterの各状態からbeforeの各状態を引いて差分を計算すれば関数実行中にどれだけCPU時間が増加したかわかります。それを元に各状態(idle, nice, user, ...)の比率を計算します。


作ったデコレータをLambda Layerとしてデプロイする

https://github.com/kanga333/pylambda_cputrace

作ったデコレータをSAMでデプロイする設定を上のリポジトリにあげてます。

Lambda Layerで作るPython用パッケージはpythonという名前のディレクトリに配置されるようなディレクトリ構成にする必要があります。

workdir(ContentUri)

└── python
├── psutil
├── psutil-5.4.8.egg-info
└── traceutil
├── __init__.py
└── cpu_trace.py

Lambda LayerはCloud Formationのpackageとdeployでアップロード出来ます。

AWSTemplateFormatVersion: '2010-09-09'

Transform: AWS::Serverless-2016-10-31
Description: Lambda Layers to measure cpu in Python3 applications.

Resources:
CpuTrace:
Type: AWS::Serverless::LayerVersion
Properties:
LayerName: AWSLambda-Python3-TraceUtil
Description: Lambda Layers to measure cpu in Python3 applications.
ContentUri: workdir
CompatibleRuntimes:
- python3.6
- python3.7
RetentionPolicy: Retain

aws cloudformation package \

--template-file sam.yml \
--s3-bucket $S3_BACKET \
--s3-prefix $S3_PREFIX \
--output-template-file .template.yml

aws cloudformation deploy \
--template-file .template.yml \
--stack-name $STACK_NAME


lambdaのエントリポイントにデコレータを追加する

あとはLambdaFunctionsのLayersに先程のLayerを追加した後にエントリポイントにデコレータを記述するだけです。

import json

from traceutil import cpu_trace
import time

@cpu_trace
def lambda_handler(event, context):
time.sleep(1)

# TODO implement
return {
'statusCode': 200,
'body': json.dumps('Hello from Lambda!')
}


結果の確認

結果はCloudwatch Logsで見れます


結果

Function Logs:

START RequestId: eff78e17-e3ee-11e8-a1bb-97928c2e9342 Version: $LATEST
{'user': 0.0, 'nice': 0.0, 'system': 0.0, 'idle': 1.0, 'iowait': 0.0, 'irq': 0.0, 'softirq': 0.0, 'steal': 0.0, 'guest': 0.0, 'guest_nice': 0.0}
END RequestId: eff78e17-e3ee-11e8-a1bb-97928c2e9342
REPORT RequestId: eff78e17-e3ee-11e8-a1bb-97928c2e9342 Duration: 1001.90 ms Billed Duration: 1100 ms Memory Size: 128 MB Max Memory Used: 25 MB


終わりに

LambdaのCPU使用率を計算したかったのでデコレータを使って計測できるLambda Layerを作ってみました。

こういうユーティリティっぽいものにも使えてLambda Layer便利ですね。