CLOVER🍀

That was when it all began.

LocalStackを使って、AWS Lambdaを試してみる(Python版)

これは、なにをしたくて書いたもの?

AWS Lambdaを動かすのに、以前LocalStackを使ってNode.jsで作成した関数を動かしてみました。

LocalStackを使って、AWS Lambdaを試してみる - CLOVER🍀

今回は、ちょっとPythonで作成してみたいと思います。

お題

お題も前のエントリと似たようなもので、ペイロードで与えられたパラメーターを含めて、メッセージを返却する関数を作成します。

pipでインストールするパッケージも利用します。

環境

LocalStackは、Docker Composeで起動。LocalStackは、「192.168.0.3」というホストで動作しているものとします。
docker-compose.yml

version: "3"
services:
  localstack:
    image: localstack/localstack:0.9.1
    ports:
      - "4567-4593:4567-4593"
      - "8080:8080"
    environment:
      - SERVICES=${SERVICES- }
      - DEBUG=${DEBUG- }
      - DATA_DIR=${DATA_DIR- }
      - PORT_WEB_UI=${PORT_WEB_UI- }
      - LAMBDA_EXECUTOR=docker-reuse
      - KINESIS_ERROR_PROBABILITY=${KINESIS_ERROR_PROBABILITY- }
      - DOCKER_HOST=unix:///var/run/docker.sock
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"

AWS Lambdaの実行は、「docker-reuse」にしてコンテナを使いまわすようにしています。

      - LAMBDA_EXECUTOR=docker-reuse

これを、単に「docker」にするとコンテナが増え続けていきます…。

コードは、以下の環境で作成。

$ python3 -V
Python 3.6.7

Lambda関数を作成する

では、Lambda関数を作成しましょう。

AWS Lambdaで利用可能なPythonのバージョン一覧は以下ですが、今回はローカルに合わせてPython 3.6を使うことにしましょう。

Python による Lambda 関数のビルド - AWS Lambda

ライブラリの利用には、仮想環境を使用します。

$ python3 -m venv venv
$ . venv/bin/activate

ライブラリには、PyYAMLを使用しました。

$ pip3 install pyyaml

バージョン。

$ pip3 freeze
...
PyYAML==5.1

PyYAML

用途に特に意味はないのですが…。

で、こちらのドキュメントを見ながら、関数を作成。

Python の AWS Lambda 関数ハンドラ - AWS Lambda

my-lambda-function.py

import yaml

def my_handler(event, context):
    print('event = {}'.format(event))
    print('context = {}'.format(context))

    return yaml.dump({ 'word': event['word'] })

これを、zipファイルにパッケージングします。

Python の AWS Lambda デプロイパッケージ - AWS Lambda

仮想環境がある場合

今回は、こんなコマンドで作成しました。

$ cd venv/lib/python3.6/site-packages && \
    zip -r ../../../../function.zip . && \
    cd ../../../../ && \
    zip -g function.zip my-lambda-function.py 

Lambda関数のデプロイ

では、Lambda関数をデプロイしましょう。

AWS CLIの設定。

$ aws configure
AWS Access Key ID [None]: my-access-key
AWS Secret Access Key [None]: my-secret-access-key
Default region name [None]: us-east-1
Default output format [None]:

作成したパッケージをデプロイ。作成したLambdaの関数名は、「--handler」オプションで「ファイル名(拡張子なし):関数名」で
登録するようです。

$ aws --endpoint-url http://192.168.0.3:4574 lambda create-function \
    --function-name my-lambda \
    --runtime python3.6 \
    --handler my-lambda-function.my_handler \
    --role test-role \
    --zip-file fileb://function.zip
{
    "FunctionName": "my-lambda",
    "FunctionArn": "arn:aws:lambda:us-east-1:000000000000:function:my-lambda",
    "Runtime": "python3.6",
    "Role": "test-role",
    "Handler": "my-lambda-function.my_handler",
    "VpcConfig": {
        "SubnetIds": [
            null
        ],
        "SecurityGroupIds": [
            null
        ]
    },
    "Environment": {
        "Variables": {},
        "Error": {}
    },
    "TracingConfig": {}
}

LocalStackに登録するので、「--endpoint-url」での指定が必要です。

$ aws --endpoint-url http://192.168.0.3:4574 lambda create-function \

登録した関数の一覧。

$ aws --endpoint-url http://192.168.0.3:4574 lambda list-functions
{
    "Functions": [
        {
            "FunctionName": "my-lambda",
            "FunctionArn": "arn:aws:lambda:us-east-1:000000000000:function:my-lambda",
            "Runtime": "python3.6",
            "Handler": "my-lambda-function.my_handler",
            "CodeSize": 50,
            "Version": "$LATEST",
            "Environment": {
                "Variables": {}
            }
        }
    ]
}

Lambdaの呼び出し。結果は、引数で指定したファイルに出力されます。

$ aws --endpoint-url http://192.168.0.3:4574 lambda invoke --function-name my-lambda --payload '{"word": "Hello Lambda"}' result.log
{
    "StatusCode": 200
}

確認。とりあえず改行は気にしない…。

$ cat result.log 
"word: Hello Lambda\n"

ここで、ちょっと関数の定義を変更してみます。

def my_handler(event, context):
    print('event = {}'.format(event))
    print('context = {}'.format(context))

    return yaml.dump({ 'word': event['word'], 'foo': 'bar' })

更新には、「update-function-code」を使用。

$ aws --endpoint-url http://192.168.0.3:4574 lambda update-function-code --function-name my-lambda --zip-file fileb://function.zip
{
    "FunctionName": "my-lambda"
}

再度呼び出し。

$ aws --endpoint-url http://192.168.0.3:4574 lambda invoke --function-name my-lambda --payload '{"word": "Hello Lambda"}' result.log
{
    "StatusCode": 200
}

確認。

$ cat result.log 
"foo: bar\nword: Hello Lambda\n"

ちゃんと、結果が反映されましたね。

最後に、関数を削除しておしまい。

$ aws --endpoint-url http://192.168.0.3:4574 lambda delete-function --function-name my-lambda

確認。

$ aws --endpoint-url http://192.168.0.3:4574 lambda list-functions
{
    "Functions": []
}

ログを確認したい

「docker container logs」で確認したい…ところですが、Lambdaの実行が「docker-reuse」だとログが残らず…。

      - LAMBDA_EXECUTOR=docker-reuse

これを「docker」にするとログが残るのですが、代わりにLambdaの実行とともに終了したコンテナが増えていく…。

      - LAMBDA_EXECUTOR=docker

うーん…。