APC 技術ブログ

株式会社エーピーコミュニケーションズの技術ブログです。

株式会社 エーピーコミュニケーションズの技術ブログです。

AWS Lambda でお試し Layer

はじめに

こんにちは、クラウド事業部の梅本です。

普段は AWS 以外のクラウドでコンテナや k8s を業務で扱っているのですが、最近になって改めて AWS の資格勉強や Lambda での開発を行っています。

Lambda での機能開発を行っていく中で、チーム内で同様の機能を実装する場合がありました。 そこで今回は共通コンポーネントの管理として Lambda の Layer 機能について取り上げようと思います。また、Lambda の Sampleコードが提供されているのでそちらを利用させてもらい、Layer について学んでいきます。

Lambda の Layer についての詳細は以下 AWS のドキュメントを参考にしてください。

docs.aws.amazon.com

環境情報

  • 開発環境:Cloud9
    • 今回は AWS 開発環境の Cloud9 を使いますが、ローカル環境の AWS CLI を使って試すこともできます。
  • AWS CLI:2.13.12
  • 言語:Python 3.7(左記は Cloud9内、Lambda は 3.8)
  • コード:以下参照

github.com

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 関数内のファイル

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

libsContentUri にある通り、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セレクトティアサービスパートナー

その中で私達クラウド事業部はAWSなどのクラウド技術を活用したSI/SESのご支援をしております。

www.ap-com.co.jp

また、AWSの運用自動化ツールも展開しております。

www.ap-com.co.jp

一緒に働いていただける仲間も募集中です!
今年もまだまだ組織規模拡大中なので、ご興味持っていただけましたらぜひお声がけください。

www.ap-com.co.jp

本記事の投稿者: 梅本
コンテナや k8s をメインにインフラ系のご支援を担当しています。
AWS は現在学び直し中! 普段は QiitaZenn に k8s を中心とした記事を投稿しております。よろしければ。