はじめに
こんにちは、クラウド事業部の梅本です。
普段は AWS 以外のクラウドでコンテナや k8s を業務で扱っているのですが、最近になって改めて AWS の資格勉強や Lambda での開発を行っています。
Lambda での機能開発を行っていく中で、チーム内で同様の機能を実装する場合がありました。 そこで今回は共通コンポーネントの管理として Lambda の Layer 機能について取り上げようと思います。また、Lambda の Sampleコードが提供されているのでそちらを利用させてもらい、Layer について学んでいきます。
Lambda の Layer についての詳細は以下 AWS のドキュメントを参考にしてください。
環境情報
- 開発環境:Cloud9
- 今回は AWS 開発環境の Cloud9 を使いますが、ローカル環境の AWS CLI を使って試すこともできます。
- AWS CLI:2.13.12
- 言語:Python 3.7(左記は Cloud9内、Lambda は 3.8)
- コード:以下参照
AWS CLI が v2 の場合は、~/.aws/config
に以下を設定してください。
詳細は README 参照。
[default] cli_binary_format=raw-in-base64-out
Lambda関数のデプロイ
最初にサンプルのアプリを Lambda にデプロイしていきます。 README を参考にコードの準備をしていきます。
$ git clone https://github.com/awsdocs/aws-lambda-developer-guide.git ... $ cd aws-lambda-developer-guide/sample-apps/blank-python $ ls 0-run-tests.sh 1-create-bucket.sh 2-build-layer.sh 3-deploy.sh 4-invoke.sh 5-cleanup.sh event.json function images README.md template.yml
それでは実際にデプロイしていきます。
# コードをパッケージ化したファイルを配置する S3 バケット作成 $ ./1-create-bucket.sh make_bucket: lambda-artifacts-6fa52fd7d7bfb009 # バケット名はテキストファイルに出力される $ cat bucket-name.txt lambda-artifacts-6fa52fd7d7bfb009 # Lambda で使うパッケージのダウンロード $ ./2-build-layer.sh ... Successfully installed aws-xray-sdk-2.4.3 botocore-1.31.38 future-0.18.3 jmespath-1.0.1 jsonpickle-1.3 python-dateutil-2.8.2 six-1.16.0 urllib3-1.26.16 wrapt-1.15.0 # ダウンロード先は package/python/ $ ls package/python/ aws_xray_sdk botocore future jmespath-1.0.1.dist-info libfuturize __pycache__ six.py wrapt aws_xray_sdk-2.4.3.dist-info botocore-1.31.38.dist-info future-0.18.3-py3.7.egg-info jsonpickle libpasteurize python_dateutil-2.8.2.dist-info urllib3 wrapt-1.15.0.dist-info bin dateutil jmespath jsonpickle-1.3.dist-info past six-1.16.0.dist-info urllib3-1.26.16.dist-info # S3にパッケージ化したファイルを配置後、CloudFormation で Lambda デプロイ $ ./3-deploy.sh Uploading to 8d21c0ffb295cabe31730259cc166a1d 13709480 / 13709480.0 (100.00%) Successfully packaged artifacts and wrote output template to file out.yml. Execute the following command to deploy the packaged template aws cloudformation deploy --template-file /home/ec2-user/environment/aws-lambda-developer-guide/sample-apps/blank-python/out.yml --stack-name <YOUR STACK NAME> Waiting for changeset to be created.. Waiting for stack create/update to complete Successfully created/updated stack - blank-python # CloudFormation で blank-python でデプロイが実行されていることを確認 $ aws cloudformation describe-stacks --stack-name blank-python { "Stacks": [ { "StackId": "arn:aws:cloudformation:ap-northeast-1:xxxxx:stack/blank-python/8ea4a350-47d3-11ee-8e1d-0e7f3d7f773b", "StackName": "blank-python", "ChangeSetId": "arn:aws:cloudformation:ap-northeast-1:xxxxx:changeSet/awscli-cloudformation-package-deploy-1693468445/dc5c7224-6724-4e74-892c-3b74e2120872", "Description": "An AWS Lambda application that calls the Lambda API.", "CreationTime": "2023-08-31T07:54:05.230000+00:00", "LastUpdatedTime": "2023-08-31T07:54:10.558000+00:00", "RollbackConfiguration": {}, "StackStatus": "CREATE_COMPLETE", "DisableRollback": false, "NotificationARNs": [], "Capabilities": [ "CAPABILITY_NAMED_IAM" ], "Tags": [], "EnableTerminationProtection": false, "DriftInformation": { "StackDriftStatus": "NOT_CHECKED" } } ] }
デプロイされたリソースを見ると Lambda の他に IAM Role や Lambda の Layer もデプロイされていることが分かります。
$ aws cloudformation list-stack-resources --stack-name blank-python { "StackResourceSummaries": [ { "LogicalResourceId": "function", "PhysicalResourceId": "blank-python-function-OGv43clVdkHy", "ResourceType": "AWS::Lambda::Function", "LastUpdatedTimestamp": "2023-08-31T07:54:43.320000+00:00", "ResourceStatus": "CREATE_COMPLETE", "DriftInformation": { "StackResourceDriftStatus": "NOT_CHECKED" } }, { "LogicalResourceId": "functionRole", "PhysicalResourceId": "blank-python-functionRole-1VEWSGVMWS3AZ", "ResourceType": "AWS::IAM::Role", "LastUpdatedTimestamp": "2023-08-31T07:54:30.298000+00:00", "ResourceStatus": "CREATE_COMPLETE", "DriftInformation": { "StackResourceDriftStatus": "NOT_CHECKED" } }, { "LogicalResourceId": "libs0f93fca259", "PhysicalResourceId": "arn:aws:lambda:ap-northeast-1:xxxx:layer:blank-python-lib:1", "ResourceType": "AWS::Lambda::LayerVersion", "LastUpdatedTimestamp": "2023-08-31T07:54:18.215000+00:00", "ResourceStatus": "CREATE_COMPLETE", "DriftInformation": { "StackResourceDriftStatus": "NOT_CHECKED" } } ] }
AWS Console でも同様に確認できます。
動作確認
デプロイした Lambda 関数を Cloud9 から実行してみます。
※スクリプトは無限ループで実行されるので、2、3回ターミナルにログが表示されてから Ctrl+C
で停止しましょう。
$ ./4-invoke.sh { "StatusCode": 200, "ExecutedVersion": "$LATEST" } {"TotalCodeSize": 78695042, "FunctionCount": 70} { "StatusCode": 200, "ExecutedVersion": "$LATEST" } {"TotalCodeSize": 78695042, "FunctionCount": 70}
※以下エラーが出力された場合は、環境情報にも記載している ~/.aws/config
の設定を見直してみましょう。
$ ./4-invoke.sh Invalid base64: "{ "Records": [ ... ] } "
AWS Console で Lambda の対象関数(blank-python-function-xxxx
、xxxxはランダム)に移動し、以下の様に
AWS X-Ray の Service Map にリソースが表示されていることが確認できます。表示の繫栄に少し時間がかかる場合があります。
また、CloudWatch ログでは、関数内の処理で実行している logging で出力指示した文字列が記録されています。
以下、画像の赤枠から CloudWatch に遷移し、確認できます。
以下画像は INFO
でフィルタリングしています。
関数内で出力指示をしている環境情報や受け付けた JSON の情報が記録されています。
処理の詳細はコードを参照してください。
Lambda関数内にある3つファイルは以下の通りです。
・lambda_function.py:Lambda で実際に実行されるコード ・lambda_function.test.py:単体テスト用に利用するコード ・requirements.txt:Layer にインストールする Python パッケージ群
Layerの編集
既に Layer が利用されているので、まずはどのように定義されているか確認してみましょう。 CloudFormation のコードを確認します。
# 前章でシェルを実行したディレクトリで実行 $ cat template.yml AWSTemplateFormatVersion: '2010-09-09' ... Resources: function: Type: AWS::Serverless::Function Properties: ... Layers: - !Ref libs libs: Type: AWS::Serverless::LayerVersion Properties: LayerName: blank-python-lib Description: Dependencies for the blank-python sample app. ContentUri: package/. CompatibleRuntimes: - python3.8
libs
の ContentUri
にある通り、2-build-layer.sh
でダウンロードしたパッケージを Layer に配置し、Lambda 関数の Layer を参照するように定義されています。
今回は Layer にパッケージを追加して、コードからそのパッケージを利用して処理が実行できるように変更します。
まずは、Python の HTTP 向けライブラリである requests
をインストールするように function/requirements.txt
変更します。
※試した結果、urllib3
のバージョンの影響で動作しなかったため、別バージョンを指定しています。また、後続のビルドの処理前に、一度 urllib3
のディレクトリを削除しています。
jsonpickle==1.3 aws-xray-sdk==2.4.3 urllib3==1.26.6 requests==2.31.0
次に関数のコード(function/lambda_function.py
)を変更します。今回は https://example.com/
にリクエストを送って結果をログに出力します。
@@ -16,5 +17,11 @@ def lambda_handler(event, context): logger.info('## ENVIRONMENT VARIABLES\r' + jsonpickle.encode(dict(**os.environ))) logger.info('## EVENT\r' + jsonpickle.encode(event)) logger.info('## CONTEXT\r' + jsonpickle.encode(context)) + + res = requests.get("https://example.com") + logger.info('## Request URL' + res.url) + logger.info('## Response Code' + str(res.status_code)) + logger.info('## Response Text\r' + res.text) + response = client.get_account_settings() return response['AccountUsage']
それでは再度デプロイしていきましょう。
# そのままでは requests が動かなかったため、一度 urllib3 を削除して、再度ダウンロードするようにします。 $ rm -fr package/python/urllib3* # Build $ ./2-build-layer.sh ... Successfully installed aws-xray-sdk-2.4.3 botocore-1.31.38 certifi-2023.7.22 charset-normalizer-3.2.0 future-0.18.3 idna-3.4 jmespath-1.0.1 jsonpickle-1.3 python-dateutil-2.8.2 requests-2.31.0 six-1.16.0 urllib3-1.26.6 wrapt-1.15.0 # Deploy $ ./3-deploy.sh Uploading to 18882854a136e6c6b94ff77448a178c4 1277 / 1277.0 (100.00%) Successfully packaged artifacts and wrote output template to file out.yml. Execute the following command to deploy the packaged template aws cloudformation deploy --template-file /home/ec2-user/environment/aws-lambda-developer-guide/sample-apps/blank-python/out.yml --stack-name <YOUR STACK NAME> Waiting for changeset to be created.. Waiting for stack create/update to complete Successfully created/updated stack - blank-python
デプロイができたところで、再度動作確認していきます。
# 2、3回ターミナルにログが表示されてから Ctrl+Cで停止 $ ./4-invoke.sh { "StatusCode": 200, "ExecutedVersion": "$LATEST" } {"TotalCodeSize": 107345342, "FunctionCount": 70} { "StatusCode": 200, "ExecutedVersion": "$LATEST" } {"TotalCodeSize": 107345342, "FunctionCount": 70} ^C
実行後、CloudWatch ログで追加した処理の確認をします。
requests
を使った、アクセスの結果が表示されていることが確認できるかと思います。
片付け
以下で環境を削除できます。不要なリソースは極力削除しておきましょう。
$ ./5-cleanup.sh Deleted blank-python stack. Delete deployment artifacts and bucket (lambda-artifacts-6fa52fd7d7bfb009)? (y/n)y ★yを入力 ... Delete function log group (/aws/lambda/blank-python-function-OGv43clVdkHy)? (y/n)y ★yを入力
おわりに
今回は Lambda のサンプルコードを使って、Layer を試してみました。 Layer を使って共通のコンポーネントを作ることで、他のプログラムへのリサイクルにもなって良いかと思います。 Lambda は機能が豊富なので更に勉強してそれぞれの機能を活かしていければと思います。
APCはAWSセレクトティアサービスパートナー認定を受けております。
その中で私達クラウド事業部はAWSなどのクラウド技術を活用したSI/SESのご支援をしております。
また、AWSの運用自動化ツールも展開しております。
一緒に働いていただける仲間も募集中です!
今年もまだまだ組織規模拡大中なので、ご興味持っていただけましたらぜひお声がけください。