HTTP API で Lambda 関数を公開する

AWSBeginner
オンラインで実践に進む

はじめに

ステータスページには、アプリケーションが正常かどうかを報告する HTTP エンドポイントが必要です。このラボでは、提供された Lambda ハンドラーを HTTP API に接続し、リクエストを送って呼び出し権限を診断します。最後に、自分のリソースを削除します。

最初に JSON イベントで Lambda 関数を実行する と Lambda 関数を設定して診断する を、その前提となるロールとログの単元も含めて完了してください。この新しい環境には、ハンドラーと実行ロールが用意されています。

認定試験との関連

このラボでは、次の試験トピックに関連する実践的な演習を行います。

ヘルスチェック関数をデプロイする

このステップでは、提供されたヘルスチェックハンドラーをパッケージ化し、HTTP イベントに応答できる関数をデプロイします。

準備済みの作業ディレクトリに移動します。

cd /home/labex/project

Terminal の隣の AWS View を開き、コマンドと同じ関数、API、ログの状態を追ってください。最初は無関係な参照ログだけが存在します。それを保持してください。

パッケージ化する前に、提供されたアプリケーションを読みます。

cat app.py

ハンドラーはイベントを受け取り、その入力をログに記録し、statusCode、headers、body 内の JSON 文字列を持つプロキシレスポンスを返します。HTTP ペイロード形式 2.0 は、URL のクエリパラメータを queryStringParameters に入れます。任意の name パラメータで挨拶が変わります。RELEASE_LABEL は、それと一緒に返される環境設定です。

zip を使い、デプロイ用アーカイブの最上位に app.py を置きます。

zip health.zip app.py

準備済みのロール ARN をシェル変数に読み込みます。--query はレスポンスの 1 つのフィールドを選択し、--output text は次のコマンドで使える形式にします。

ROLE_ARN=$(aws iam get-role --role-name labex-a01-execution --query 'Role.Arn' --output text)

Python 3.12 と準備済みの実行ロールで、app.handler をデプロイします。fileb:// は Zip のバイトをアップロードします。JSON の Variables マップは、Lambda の設定のラボと同じ形式です。その文字列値で、最初のリリースを示します。

aws lambda create-function \
  --function-name labex-a01-health \
  --runtime python3.12 \
  --role "$ROLE_ARN" \
  --handler app.handler \
  --timeout 5 \
  --environment '{"Variables":{"RELEASE_LABEL":"initial"}}' \
  --zip-file fileb://health.zip \
  --query '{Name:FunctionName,Handler:Handler,Runtime:Runtime}'

レスポンスには labex-a01-health、app.handler、python3.12 が表示されるはずです。AWS View を開き、Function カードを確認します。関数をデプロイしただけでは、HTTP ルートはまだ提供されません。HTTP APIs カードは空のままです。

HTTP ルートを接続し、リクエストを送る

このステップでは、HTTP リクエストをデプロイ済みの関数に接続します。Amazon API Gateway は、HTTP の入口を提供します。ルートは、GET /health などのメソッドとパスに対して、バックエンドの統合を選択します。

HTTP API を作成し、生成された ID をシェル変数に保存します。コマンド置換の $(...) は、選択した API ID を表示せずに取り込みます。

API_ID=$(aws apigatewayv2 create-api --name labex-a01 --protocol-type HTTP --query ApiId --output text)

リソース一覧のために、その ID を記録します。リダイレクトの > は、値をファイルに書き込みます。

printf '%s\n' "$API_ID" > api-id.txt

ステージは、API のデプロイの入口です。$default ステージには、URL 内にステージ名の部分がありません。--auto-deploy は、変更を自動的に適用します。一重引用符で、ドル記号をそのまま保持します。

aws apigatewayv2 create-stage --api-id "$API_ID" --stage-name '$default' --auto-deploy --query '{Stage:StageName,AutoDeploy:AutoDeploy}'

デプロイ済みの関数 ARN を読み取り、AWS_PROXY 統合を作成します。以下のクライアントからのルートは GET ですが、Lambda 統合はバックエンドを呼び出すために POST を使います。ペイロード形式 2.0 は、提供されたハンドラーに一致します。

FUNCTION_ARN=$(aws lambda get-function-configuration --function-name labex-a01-health --query FunctionArn --output text)
INTEGRATION_ID=$(aws apigatewayv2 create-integration --api-id "$API_ID" --integration-type AWS_PROXY --integration-method POST --integration-uri "$FUNCTION_ARN" --payload-format-version 2.0 --query IntegrationId --output text)

ルートキーは、クライアントの HTTP メソッドとパスを組み合わせます。その参照先は、作成した統合を指します。

aws apigatewayv2 create-route \
  --api-id "$API_ID" \
  --route-key 'GET /health' \
  --target "integrations/$INTEGRATION_ID" \
  --authorization-type NONE \
  --query '{Route:RouteKey,Authorization:AuthorizationType}'

この公開のヘルスルートは NONE を使います。アプリケーションへのサインインは、後で学びます。実行ロールは関数のコードが何をできるかを制御し、関数のリソースポリシーは誰がそれを呼び出せるかを制御します。API Gateway には、まだ正確な呼び出し許可が必要です。

選択されたアカウント ID を読み取り、この API の default ステージの GET /health ルートに対する送信元 ARN を作成します。エスケープしたドル記号により、展開する文字列内で $default をそのまま保持します。

ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
SOURCE_ARN="arn:aws:execute-api:us-east-1:$ACCOUNT_ID:$API_ID/\$default/GET/health"

この API ルートだけに、関数の呼び出しを許可します。

aws lambda add-permission \
  --function-name labex-a01-health \
  --statement-id ApiHealth \
  --action lambda:InvokeFunction \
  --principal apigateway.amazonaws.com \
  --source-account "$ACCOUNT_ID" \
  --source-arn "$SOURCE_ARN" \
  --query Statement \
  --output text

この作業環境での HTTP リクエストには、生成された API ID と、準備済みの API アドレスを使います。これは作業環境の API の入口であり、AWS の公開ホスト名ではありません。

API_URL="http://127.0.0.1:8081/api/$API_ID"

公式 Console には、同じ API ID、$default ステージ、Auto deploy 設定が表示されます。その Invoke URL は AWS のエンドポイントです。このラボでは、上記の作業環境の API_URL で続けてください。

API の詳細と default ステージの呼び出し URL を示す、公式 API Gateway Console

出典:AWS API Gateway。

curl -i は、HTTP ヘッダーとレスポンス本文の両方を表示します。URL のクエリパラメータは、実際の Lambda イベントの一部になります。

curl -i "$API_URL/health?name=Maya"

HTTP 200 と、以下の JSON 本文が返るはずです。

{"healthy": true, "message": "Hello, Maya", "release": "initial"}

AWS View に戻ります。HTTP APIs カードには、GET /health、NONE、$default · AutoDeploy true が表示されるはずです。CloudWatch Logs カードには、実際のルート、ステータス、レスポンスが表示されるはずです。Show logs をクリックし、name: Maya を持つ queryStringParameters を見つけます。この手動の観察によって、HTTP リクエストとデプロイ済み関数の入力を結び付けます。検証では、リモートの設定と実際の実行を確認します。

AWS View の HTTP ヘルスルートとその Lambda レスポンス

この例は、設定されたルートと、成功した Maya / initial のレスポンスを示しています。生成された API ID とコードのフィンガープリントは、実行ごとに異なります。

ヘルスルートが、そのリクエストに対する Lambda 統合を選択します。

呼び出し権限を診断し、新しいリリースを公開する

このステップでは、呼び出しの境界が壊れた状態を観察し、正確な許可を復旧して、変更した関数設定をテストします。

ID を指定してステートメントを削除します。API、統合、実行ロールはそのまま残ります。

aws lambda remove-permission --function-name labex-a01-health --statement-id ApiHealth

許可がない状態で、別のリクエストを送ります。

curl -i "$API_URL/health?name=Noah"

HTTP 502 と Invocation permission denied が返るはずです。API のデプロイとルートの設定だけでは、Lambda の呼び出し権限は付与されません。AWS View には、既存の成功した呼び出しが残ります。この拒否されたリクエストは、ハンドラーを実行していません。リクエストの前後でログを手動で比較してください。自動チェックは、最終的に復旧されたポリシーから、過去の拒否を推測しません。

同じアカウントとルートに限定した許可を復旧します。

aws lambda add-permission \
  --function-name labex-a01-health \
  --statement-id ApiHealth \
  --action lambda:InvokeFunction \
  --principal apigateway.amazonaws.com \
  --source-account "$ACCOUNT_ID" \
  --source-arn "$SOURCE_ARN" \
  --query Statement \
  --output text

準備完了のリリースを示すように、関数の環境設定を変更します。ハンドラーは、実行時にこの設定を読み取ります。

aws lambda update-function-configuration --function-name labex-a01-health --environment '{"Variables":{"RELEASE_LABEL":"ready"}}' --query 'Environment.Variables'

ルートは引き続き同じ関数を指しています。異なるクエリ値でリクエストを送ります。

curl -i "$API_URL/health?name=Noah"

HTTP 200 と、新しいクエリと環境設定の両方から計算されたレスポンスが返るはずです。

{"healthy": true, "message": "Hello, Noah", "release": "ready"}

AWS View で最新のレスポンスを確認し、そのログを展開します。入力が Noah、本文が ready を示すことを確認してください。以前の Maya のレスポンスも、引き続き確認できるはずです。これらの異なる結果は、統合が 1 つの固定されたヘルスメッセージを返すのではなく、デプロイ済みの関数を実行していることを示します。

自分の API と関数を削除する

このステップでは、作成したリソースを削除し、無関係な参照ログが残っていることを確かめます。

リソース一覧は、api-id.txt の API ID、labex-a01-health、/aws/lambda/labex-a01-health です。API は、そのルート、統合、default ステージを所有しています。それ以上のリクエストを受け取れないように、最初に API を削除します。

aws apigatewayv2 delete-api --api-id "$API_ID"

関数と、その所有する呼び出しポリシーを削除します。

aws lambda delete-function --function-name labex-a01-health

Lambda のロググループには、独自のライフサイクルがあります。この関数のグループだけを削除します。

aws logs delete-log-group --log-group-name /aws/lambda/labex-a01-health

サービスから正常に一覧を読み取ります。リクエストのエラーは、削除の証明にはなりません。

aws apigatewayv2 get-apis --query 'Items[].Name'
aws lambda list-functions --query 'Functions[].FunctionName'

両方のリストが空になるはずです。残っているロググループを読み取ります。

aws logs describe-log-groups --query 'logGroups[].logGroupName'

/labex/labex-a01-reference だけが残るはずです。それと、準備済みの実行ロールを保持してください。AWS View で、API、Function、呼び出しのカードが空で、Reference logs には INFO platform reference keep unchanged が残っていることを確認します。

まとめ

Python のヘルスチェックハンドラーをデプロイし、ペイロード 2.0 の統合を通じて HTTP API ルートを接続して、default ステージを有効にしました。API から Lambda への呼び出し許可の範囲を限定し、その削除を診断して、実際のクエリ値と関数設定による異なるレスポンスを観察しました。最後に、無関係なリソースを保持しながら、自分の API、関数、ロググループを削除しました。