本ブログは「Monitoring your AWS Lambda functions inside and out」の抄訳となります。
AWS Lambdaをよく知らないという方のために書くと、それは、イベントに応答したり自動的にコンピュートリソースを管理してくれるコンピュートサービスです。それは、サーバの準備や管理、メンテナンスを、あなた自身がする必要がないことを意味します。これが、AWS Lambdaや類似のサービスが "サーバーレス "と呼ばれる理由です。
これはちょっとした難問もたらします。サーバがないのに、どうやってサーバをNew Relicで監視するのでしょう?これには、2つの方法があります。
- サーバーレス監視:
- これにより、AWS Lambda関数の内部を確認することができます。詳細な継続時間、コールドスタート、例外、トレースバックなどのパフォーマンスデータを含む、すべての呼び出しを監視します。
- Synthetics:
- ウェブサイトで人が生成したデータをオーガニックトラフィックとすると、ロボットが生成されたデータはSyntheticです。New Relic OneのSynthetic監視を使用すると、テストをスクリプト化し、AWS Lambda関数が外部イベントに対してどのように応答するかを監視することができます。
まず最初に、テスト用のLambda関数を作成しましょう。
デモをすぐに試したい場合は、ソースをGitHubで公開していますので、そちらをご利用ください。このアプリケーションをローカルで実行するには、Python 3.9.0とvirtualenvがアクティブであることを確認してから、ターミナルで以下のコマンドを実行します。
pip install -r requirements.txt
uvicorn main:app
serverless CLIを設定し、serverless.yaml
ファイルを完成させたら、次のコマンドで、AWS Lambdaにアプリケーションをデプロイします。
npm install
sls deploy --stage staging
serverless.yaml
ファイルを設定していない場合は、このブログの後半でその方法を紹介します。
PythonとFastAPIによるAWS Lambda関数の作成
HTTP GETリクエストに対して、JSON形式でランダムなUUID(Universally Unique Identifier)を返す FastAPI アプリケーションを作成します。
import uuid
from fastapi import FastAPI
from mangum import Mangum
app = FastAPI(title="InsideOutDemoApp")
@app.get("/uuid")
def index():
return {"uuid": uuid.uuid4()}
handler = Mangum(app)
上のコードでは、InsideOutDemoApp
というFastAPIを作成しており、このFastAPIは単一のエンドポイント /uuid
を持っています。そしてこのアプリを、AWS LambdaやAPI GatewayでASGI(Asynchronous Server Gateway Interface)アプリケーションを使用するためのPythonパッケージであるMangumでラップしています。
このアプリケーションをローカルで実行し、ブラウザでアクセスすると、JSONの文字列が表示され、更新する都度、新たに生成されたUUIDが表示されます。
AWS Lambdaへデプロイ
これで、コードをAWS Lambdaにデプロイする準備が整いました。PythonコードをAWS Lambdaにデプロイするには、様々な方法があります。この例では、serverless.comのCLIツールを使用します。これはJavaScriptのアプリケーションで、npm経由でインストールできます。コマンドラインで以下を実行します。
npm install -g serverless
また、コマンドラインで以下を実行して、AWSの認証情報でserverlessを設定する必要があります。
serverless config credentials --provider aws --key <YOUR_KEY> --secret <YOUR_SECRET>
serverlessがインストールされ、AWSアカウントにアクセスできるようになったので、続いてAWS Lambda関数を設定します。
service: inside-out-demo-app
package:
individually: true
provider:
name: aws
runtime: python3.8
region: eu-west-1
stage: ${opt:stage, "dev"}
plugins:
- serverless-python-requirements
custom:
pythonRequirements:
dockerizePip: true
layer:
name: inside-out-app-demo-layer
description: Inside Out Demo App
compatibleRuntimes:
- python3.8
functions:
app:
package:
include:
- "main.py"
exclude:
- "requirements.txt"
- "package.json"
- "package-lock.json"
- ".serverless/**"
- "__pycache__/**"
- "node_modules/**"
handler: main.handler
environment:
STAGE: ${self:provider.stage}
layers:
- { Ref: PythonRequirementsLambdaLayer }
events:
- http:
method: any
path: /uuid
上記のYAMLファイルでは、コードが実行されるAWSリージョン、使用するPythonランタイム、サポートするイベントなど、必要なものをすべて指定できていることがわかります。利用可能なオプションの詳細については、サーバーレスのドキュメントを確認してください。
また、このデプロイメントにはserverless-python-requirements
プラグインが必要であることに気付いたかもしれません。このプラグインもnpmからインストールできます。
npm install serverless-python-requirements
これで、デプロイを実行することができます。
sls deploy --stage staging
AWS Lambda関数のURLがターミナルに出力されているはずです。ブラウザでアクセスすると、ローカル版と全く同じように見えるはずです。ページを読み込むたびに変化するUUIDを持つJSON文字列です。
あなたのコードはAWS Lambda上で実行されていますが、まだインストルメントされていません。
サーバーレスモニタリングによるAWS Lambdaのモニタリング
プロセスを効率化するために、New Relic は サーバーレスフレームワークのプラグインと Setup AWS Lambda monitoring Nerdlet を用意しています。
NerdletはNew Relic Oneの「Add more data」>「Cloud and platform technologies」>「Lambda」からアクセスできます。画面に表示される確認にしたがってServerlessの監視を有効にし、以下のような回答をしてください。
- Are you using the Serverless framework?
- Yes
- Do you have a Node or Python Lambda Function to instrument?
- Yes
- Do you want to deliver your function’s telemetry with our Lambda Extension, or through a Cloudwatch Logs subscription?
- Lambda Extension
Nerdletが完成したら、新しい値をserverless.yaml
にコピーしてください。完成したファイルは以下のようになります。
service: inside-out-demo-app
package:
individually: true
provider:
name: aws
runtime: python3.8
region: eu-west-1
stage: ${opt:stage, "dev"}
plugins:
- serverless-python-requirements
- serverless-newrelic-lambda-layers
custom:
pythonRequirements:
dockerizePip: true
layer:
name: inside-out-app-demo-layer
description: Inside Out Demo App
compatibleRuntimes:
- python3.8
newRelic:
accountId: <NR_ACCOUNT_ID>
apiKey: <NR_API_KEY>
enableExtension: true
enableIntegration: true
logEnabled: true
functions:
app:
package:
include:
- "main.py"
exclude:
- "requirements.txt"
- "package.json"
- "package-lock.json"
- ".serverless/**"
- "__pycache__/**"
- "node_modules/**"
handler: main.handler
environment:
STAGE: ${self:provider.stage}
layers:
- { Ref: PythonRequirementsLambdaLayer }
events:
- http:
method: any
path: /uuid
次のコマンドを実行し、New Relic Serverless framework プラグインをインストールします。
npm install serverless-newrelic-lambda-layers
そうすれば、コードを再びデプロイすることができます。
sls deploy --stage staging
デプロイの都度、関数のURLが変わることに注意してください。
AWS Lambda関数のパフォーマンスとヘルスをNew Relicで表示する
コードのデプロイが完了した後、URLにトラフィックが発生すると、New Relicに関数に関する情報が表示されるようになります。
それぞれの起動については豊富な情報が用意されており、それらはすべてNRQLでクエリー可能なので、アラートの作成など、必要に応じて利用することができます。
ここでは、実行に2ミリ秒以上かかる呼び出しが、標準偏差で3を超えた場合にトリガーされるアラートを作成しています。私のアプリケーションは高いパフォーマンスを発揮したいと思っています。
AWS Lambdaのエラー処理とスタックトレース
パフォーマンスデータだけでなく、AWS Lambda関数でエラーが発生した場合は、スタックトレースとともにNew Relicに取り込まれます。
Syntheticsでのテスト
しかし、すべてのバグやエラーが例外を発生させるわけではありません。例えば、アプリケーションが突然「自分はティーポットである」と宣言したとしても、例外は発生しませんが、それでも知っておきたいことではあるでしょう。
Syntheticモニターの最もシンプルなタイプはPingです。pingモニターは、指定されたURLに対してHEADリクエストを行い、成功したか(HTTP 200)、失敗したか(その他のHTTPステータス)を記録します。しかし、お使いのAWS Lambda関数はHEADリクエストをサポートしていません。pingモニターの「Advanced options」にある「Bypass HEAD request」オプションを変更して、代わりにGETリクエストを送信するようにします。
Syntheticテストの結果は、データエクスプローラーにも表示されるので、素早くクエリーしたり、他のデータと比較したり、アラートを作成したりすることができます。
Scripted API tests
最後に考慮すべき状況は、AWS Lambda関数やその他の監視すべきAPIが、例外を発生させず、HTTPステータス200を送信しているが、予想外のレスポンスボディを返している場合だ。200 OKのステータスを受け取り、ボディにエラーメッセージが含まれていることは、GraphQLの開発者であればよく知っていることでしょう。
@app.get("/uuid")
def index():
return {"uuid": "n0t-a-val1d-uu1d"}
ここでは、常に無効な UUID を返すように FastAPI アプリの URL ハンドラを変更しました。例外は発生せず、有効な応答であるため、FastAPI は HTTP 200 OK ステータスを返します。この例では、トレースに何の問題も見られず、Ping Syntheticが失敗を記録することもありません。
その代わり、レスポンスボディを調べて、正しいデータが返されていることを確認する必要があります。これには、スクリプト API Syntheticモニタを使用します。
var assert = require('assert');
$http.get('https://nr.execute-api.eu-west-1.amazonaws.com/staging/uuid',
function (err, response, body) {
assert.equal(response.statusCode, 200, 'Expected a 200 OK response');
data = JSON.parse(body)
assert.equal(data.uuid.length, 36, "Expected a 36 character UUID")
assert.equal(/^[0-9A-F]{8}-[0-9A-F]{4}-[4][0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i.test(data.uuid), true, "Expected a valid UUID")
}
);
このSynthetics用のコードでは、AWS Lambda関数にGETリクエストを発行した後、関数が有効な値を返していることを確認するためにいくつかのアサーションを行っています。
- HTTP 200ステータスコードを返していますか?
- レスポンスボディは、有効なJSONとして解析できていますか?
- レスポンスボディのJSONはオブジェクトであり、UUIDという属性を持っていますか?
- UUID属性の値は、UUID4として正しい長さですか?
- UUID属性の値は、有効な文字で構成された正しい長さのオクテット数を含んでいますか?
これらのチェックを経て、APIが有効なUUIDを返していることを合理的に確認することができます。
まとめ
Syntheticモニターは、人間のユーザーと同じように、HTTP GETリクエストを介してAWS Lambda関数をトリガーしているので、Syntheticモニターのリクエストをサーバーレスのモニタリングイベントで確認することができます。
次のステップ
AWS Lambdaを使っていない?New Relicは、Google Cloud FunctionsやAzure Functionsなど、さまざまなサーバーレスプロバイダーをサポートしています。
また、証明書チェック、リンク切れモニター、ステップモニター、そして完全にスクリプト可能なヘッドレスブラウザなど、さらにいくつかのSyntheticモニタータイプをお試しいただけます。詳しくはドキュメントをご覧ください。
本ブログに掲載されている見解は著者に所属するものであり、必ずしも New Relic 株式会社の公式見解であるわけではありません。また、本ブログには、外部サイトにアクセスするリンクが含まれる場合があります。それらリンク先の内容について、New Relic がいかなる保証も提供することはありません。