はじめに
Lambda 開発する中で、動作確認をローカルで実行できないかと思ったことはありませんか?
そんな疑問も、AWSが提供している SAM CLI を活用することで、ローカル実行することができます。さらに、dockerを使ったlocalstack も使用することで、S3 や SecretsManager などのAWSリソースを、ローカル環境でエミュレートすることも可能です。
それでは、ローカル環境でLambdaの実行ができるように環境を作ってみましょう。
実施環境
・mac OS(10.14.5)
・SAM CLI(1.6.2)
・AWS CLI(2.0.59)
・python (3.7)
・docker-compose(1.25.4)
事前準備
dockerインストール
homebrewインストール
は割愛します。こちらは、AWSのSAM CLIドキュメントに記載されています。
- SAM CLIインストール
brew tap aws/tap brew install aws-sam-cli sam --version SAM CLI, version 1.6.2
- AWS CLIインストール
localstackコンテナでのAWSリソースデータを作る際使うのでインストールしておきます。
brew install awscli
- AWS認証情報設定
localstack用プロファイルとしてダミー値で登録します。
aws configure --profile=localstack AWS Access Key ID [None]: dummy AWS Secret Access Key [None]: dummy Default region name [None]: ap-northeast-1 Default output format [None]: json
設定入ったか確認します。
$ cat ~/.aws/credentials [localstack] aws_access_key_id = dummy aws_secret_access_key = dummy [default] aws_access_key_id = dummy aws_secret_access_key = dummy $ cat ~/.aws/config [profile localstack] region = ap-northeast-1 output = json [default] region = ap-northeast-1 output = json
localstack profileとして登録されていますね。
Lambda モジュール作成
- Lambda雛形作成
SAM CLIコマンドで、Lambda用のファイルの雛形が楽に作成できます。
今回はPythonで作ってみます。
今回はハローワールドとして作成してみます。(2つとも「1」を選択)
色々テンプレートが用意されているようですね。
$ sam init --runtime python3.7 --name lambda_test Which template source would you like to use? 1 - AWS Quick Start Templates 2 - Custom Template Location Choice: 1 Cloning app templates from https://github.com/awslabs/aws-sam-cli-app-templates.git AWS quick start application templates: 1 - Hello World Example 2 - EventBridge Hello World 3 - EventBridge App from scratch (100+ Event Schemas) 4 - Step Functions Sample App (Stock Trader) Template selection: 1 ----------------------- Generating application: ----------------------- Name: lambda_test Runtime: python3.7 Dependency Manager: pip Application Template: hello-world Output Directory: . Next steps can be found in the README file at ./lambda_test/README.md $
そうすると下記のように雛形が作成されています。
$ ls -l drwxr-xr-x 9 hoge hoge 288 5 15 20:44 lambda_test $ ls -l lambda_test -rw-r--r-- 1 hoge hoge 8180 5 15 20:44 README.md -rw-r--r-- 1 hoge hoge 0 5 15 20:44 __init__.py drwxr-xr-x 3 hoge hoge 96 5 15 20:44 events drwxr-xr-x 5 hoge hoge 160 5 15 20:44 hello_world -rw-r--r-- 1 hoge hoge 1631 5 15 20:44 template.yaml drwxr-xr-x 4 hoge hoge 128 5 15 20:44 tests
このように雛形ファイルができてました。
Lambda 実行ファイルは、hello_world/app.py になります。
それでは各設定ファイルを設定していきましょう。
- template.yaml
Lambdaの環境変数をここに定義します。Environmentのところを追加します。
コンソールにある環境変数の設定がこれに該当します。
Resources: HelloWorldFunction: Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction Properties: CodeUri: hello_world/ Handler: app.lambda_handler Runtime: python3.7 Events: HelloWorld: Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api Properties: Path: /hello Method: get Environment: Variables: S3_BUCKET: 'test-bucket' S3_PATH: '/sample_data/test.csv' SECRET_KEY: 'test_key' TZ: 'Asia/Tokyo'
- requirements.txt
hello_worldフォルダ配下にファイルがあります。
こちらのファイルに、処理で使うpythonのモジュールを記載します。
AWSリソース使うため boto3 を入れます。
requests boto3
- docker-compose.yml
こちらにlocalstackを設定します。雛形では作られないのでファイル作成して設定しましょう。
version: '3' services: localstack: image: localstack/localstack ports: - "4566:4566" - "4571:4571" - "8080:8080" environment: - SERVICES=s3,lambda,secretsmanager,logs - DATA_DIR=/tmp/localstack/data - DEFAULT_REGION=ap-northeast-1 volumes: - "${TMPDIR:-/tmp/localstack}:/tmp/localstack"
environment の SERVICES のところに起動したいサービス(上記は、s3/lambda/secretsmanager/logsを指定)を設定します。
それでは、docker-compose を起動してみましょう。
- docker-compose 起動
$ TMPDIR=/private$TMPDIR docker-compose up -d Creating network "lambda_test_default" with the default driver Creating lambda_test_localstack_1 ... done
下記URLでサービスが立ち上がったか見れます。(runningであれば起動済み)
それでは、実際にデータ設定してみましょう。
テスト用データ作成
AWS CLIで、profileに「localstack」を指定、endpoint-urlに「http://localhost:4566」を指定して実行することで、localstackにデータ設定することができます。
- S3バケット作成
$ aws s3 mb s3://test-bucket --endpoint-url=http://localhost:4566 --profile=localstack make_bucket: test-bucket
- S3ファイルアップロード
サンプルとしてtest.csvを用意し、アップロードします。
$ aws s3 --endpoint-url=http://localhost:4566 cp tests/test.csv s3://test-bucket/sample_data/ --profile=localstack upload: tests/test.csv to s3://test-bucket/sample_data/test.csv
- S3アップロード確認
$ aws s3 ls --endpoint-url=http://localhost:4566 s3://test-bucket/sample_data/ --profile=localstack 2021-05-15 21:56:47 17 test.csv
S3にアップロードしたということをエミュレートできていいますね。
- SecretsManagerキー作成
キーとしてサンプルファイルを用意して登録します。
$ aws --endpoint-url=http://localhost:4566 secretsmanager create-secret --name test_key --description "secrets key" --secret-string file://tests/samplekey --profile localstack { "ARN": "arn:aws:secretsmanager:ap-northeast-1:1234567890:secret:test_key-dVhXa", "Name": "test_key", "VersionId": "44647d9c-f589-4aa7-bfed-5e4d77f2a355" }
- SecretsManagerキー取得して確認
$ aws --endpoint-url=http://localhost:4566 secretsmanager get-secret-value --secret-id test_key --profile localstack { "ARN": "arn:aws:secretsmanager:ap-northeast-1:1234567890:secret:test_key-dVhXa", "Name": "test_key", "VersionId": "44647d9c-f589-4aa7-bfed-5e4d77f2a355", "SecretString": "# sample\ntest key\n", "VersionStages": [ "AWSCURRENT" ], "CreatedDate": "2021-05-15T22:03:05+09:00" }
※キーファイルはサンプルなので下記のようにしています。
# sample
test key
コマンド結果のSecretStringの箇所に、設定したキーが確認できますね。
これでモックデータも作成できましたので、実際にLambdaのMain処理を用意します。
Lambda処理作成
雛形作成時に hello_world/app.py が作成されているので、こちらにコーディングします。
S3、SecretsManagerを使用する際、endpoint-urlに「`http://localstack:4566`」を指定することで localstackのデータを参照することができます。
import os import io import json import boto3 import logging # logger logger = logging.getLogger() logger.setLevel(logging.INFO) # S3ファイル取得 def getS3File(bucketName, s3FilePath, dlName): try: # s3 client s3Cli = boto3.client('s3', endpoint_url='http://localstack:4566') tmpFile = os.path.join('/tmp/', dlName) logger.info(tmpFile) s3Cli.download_file(bucketName, s3FilePath, tmpFile) # ファイルダウンロードできていること確認 logger.info(os.listdir(path='/tmp/')) except (Exception) as e: logger.error(str(e)) raise e # シークレットキー取得 def getSecret(secretsName): session = boto3.session.Session() client = session.client( service_name='secretsmanager', region_name='ap-northeast-1', endpoint_url='http://localstack:4566' ) try: response = client.get_secret_value(SecretId=secretsName) except Exception as e: logger.error(str(e)) raise e else: if 'SecretString' in response: secret = response['SecretString'] else: secret = base64.b64decode(response['SecretBinary']) return secret def lambda_handler(event, context): # 環境変数取得 s3Bucket = os.environ['S3_BUCKET'] s3Path = os.environ['S3_PATH'] secretKey = os.environ['SECRET_KEY'] # 環境変数設定できているか確認 logger.info(s3Bucket) logger.info(s3Path) logger.info(secretKey) # S3ファイル取得 getS3File(s3Bucket, s3Path, 'lambda-test.csv') # SecretsManager値取得 keyData = getSecret(secretKey) logger.info(keyData) return { "statusCode": 200, "body": json.dumps({ "message": "hello world", }), }
これでソースも用意できたので実際に Lambda 実行してみましょう。
Lambda実行
- ビルド
$ sam build --use-container Starting Build inside a container Building codeuri: hello_world/ runtime: python3.7 metadata: {} functions: ['HelloWorldFunction'] Fetching amazon/aws-sam-cli-build-image-python3.7 Docker container image....... Mounting /Users/hogehoge/hello_world as /tmp/samcli/source:ro,delegated inside runtime container Build Succeeded Built Artifacts : .aws-sam/build Built Template : .aws-sam/build/template.yaml Commands you can use next ========================= [*] Invoke Function: sam local invoke [*] Deploy: sam deploy --guided Running PythonPipBuilder:ResolveDependencies Running PythonPipBuilder:CopySource
- localstackネットワークID確認
$ docker network ls NETWORK ID NAME DRIVER SCOPE 1234abcd lambda_test_default bridge local
NETWORK IDをメモしておきましょう。Lambda実行時に指定します。
- Lambdaローカル実行
docker-network に上記でメモしました NETWORK ID を指定します。
log-file を指定すると実行ログがファイル出力できます。
$sam local invoke HelloWorldFunction --docker-network <上記で確認したNETWORK ID> --log-file check.log --profile=localstack
これで、Lambdaをローカルで実行できました。
実行ログを見てみると正常に実行できていることが確認できます。
START RequestId: 54f546e0-af12-1b3f-0a4f-255106ac6bf9 Version: $LATEST [INFO] 2021-05-15T15:56:37.581Z 54f546e0-af12-1b3f-0a4f-255106ac6bf9 test-bucket [INFO] 2021-05-15T15:56:37.581Z 54f546e0-af12-1b3f-0a4f-255106ac6bf9 sample_data/test.csv [INFO] 2021-05-15T15:56:37.581Z 54f546e0-af12-1b3f-0a4f-255106ac6bf9 test_key [INFO] 2021-05-15T15:56:37.605Z 54f546e0-af12-1b3f-0a4f-255106ac6bf9 Found credentials in environment variables. [INFO] 2021-05-15T15:56:38.425Z 54f546e0-af12-1b3f-0a4f-255106ac6bf9 /tmp/lambda-test.csv [INFO] 2021-05-15T15:56:38.491Z 54f546e0-af12-1b3f-0a4f-255106ac6bf9 ['lambda-test.csv'] [INFO] 2021-05-15T15:56:38.518Z 54f546e0-af12-1b3f-0a4f-255106ac6bf9 Found credentials in environment variables. [INFO] 2021-05-15T15:56:39.24Z 54f546e0-af12-1b3f-0a4f-255106ac6bf9 # sample test key END RequestId: 54f546e0-af12-1b3f-0a4f-255106ac6bf9 REPORT RequestId: 54f546e0-af12-1b3f-0a4f-255106ac6bf9 Init Duration: 1069.92 ms Duration: 1447.93 ms Billed Duration: 1500 ms Memory Size: 128 MB Max Memory Used: 43 MB {"statusCode":200,"body":"{\"message\": \"hello world\"}"}
他にもS3 PUTトリガーなどのイベント情報設定も下記コマンドで作れます。
$sam local generate-event s3 put --bucket test-bucket --key sample_data/test.csv > events/event.json
下記のようにeven.jsonができます。
{ "Records": [ { "eventVersion": "2.0", "eventSource": "aws:s3", "awsRegion": "ap-northeast-1", "eventTime": "1970-01-01T00:00:00.000Z", "eventName": "ObjectCreated:Put", "userIdentity": { "principalId": "EXAMPLE" }, "requestParameters": { "sourceIPAddress": "127.0.0.1" }, "responseElements": { "x-amz-request-id": "EXAMPLE123456789", "x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH" }, "s3": { "s3SchemaVersion": "1.0", "configurationId": "testConfigRule", "bucket": { "name": "test-bucket", "ownerIdentity": { "principalId": "EXAMPLE" }, "arn": "arn:aws:s3:::test-bucket" }, "object": { "key": "sample_data/test.csv", "size": 1024, "eTag": "0123456789abcdef0123456789abcdef", "sequencer": "0A1B2C3D4E5F678901" } } } ] }
いかがでしたでしょうか。Lambda処理をローカル環境にて実行できました。
SAM CLI とlocalstackを使用することで、AWSコンソールにデプロイせずとも、AWSリソースをエミュレートし、Lambda処理をローカル実行できるのは開発捗りますよね。
- (補足)docker-compose 停止
終わったら起動したdocker-compose停止しておきましょう。
docker-compose down
参考
SAM CLIインストールのドキュメント
SAM CLIコマンドコマンドリファレンス
localstackのgithub
https://github.com/localstack/localstack