
AWS SAM CLI と localstack を利用して Lambda をローカル実行してみよう
はじめに
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