LocalstackとDocker ComposeでCloud Native Applicationをローカルで動かす
WebアプリケーションがAWSサービスに依存するときに開発環境をどう整えるか、色々調べましたが一番有力と思ったのがLocalstackでした。
本記事で、簡単な紹介とサンプルを記載していきます。
- LocalstackをDocker-Composeで動かす
- Lambda関数を登録する
- WEBアプリケーションをDocker-Composeで動かす
- WEBアプリケーションからLambda関数を呼び出す
LocalstackをDocker-Composeで動かす
LocalstackはAWSアービスのモック・テストツールで、Lambda関数、SQSキュー、DynamoDBなどをローカル環境で動かしてテスト実行や開発環境として使えます。
Pipでインストールしたくないので、レポジトリをcloneしてDocker-composeで動かします。
$ git clone https://github.com/localstack/localstack.git
$ cd localstack
$ export TMPDIR=/private$TMPDIR
$ export SERVICES=lambda
$ docker-compose up
起動後、 http://localhost:8080 でコンソールをアクセスできます。
Lambda関数を登録する
登録はAWSでやるときとは変わらないので、Hello World程度のもので記載します。
環境変数を設定し、AWS CLIが使えるようにします。
$ export AWS_ACCESS_KEY_ID=dummy
$ export AWS_SECRET_ACCESS_KEY=dummy
$ export AWS_DEFAULT_REGION=ap-northeast-1
$ export AWS_DEFAULT_OUTPUT=text
次は簡単なPythonコードでLambda関数のソースを用意します。
def lambda_handler(event, context):
print(event)
$ FUNCTION_NAME=MY_LAMBDA_FUNCTION
$ PYTHON_RUNTIME=python2.7
$ zip "./lambda_function.zip" ./my_lambda_function.py
$ aws --endpoint-url=http://localhost:4574 lambda create-function \
--function-name="${FUNCTION_NAME}" \
--runtime="${PYTHON_RUNTIME}" \
--role=r1 \
--handler=my_lambda_function.lambda_handler \
--zip-file "fileb://lambda_function.zip"
WEBアプリケーションをDocker-Composeで動かす
WEBアプリケーションは別のDocker-Composeファイルで定義されていますが、そのままですとLocalstackのDockerネットワークが異なるので接続できません。
繋げるためには、WEBアプリケーションのDocker-Composeファイルにnetworkセクションを定義する必要があります。
...
networks:
default:
external:
name: localstack_default
WEBアプリケーションからLambda関数を呼び出す
WEBアプリケーションがRailsだとします。ポイントとしてはクライアントを作るときにendpoint
のキーでLocalstackのURLとポート番号を指定することです。
以下の例では cloud_deployed
の変数でローカル環境かどうかを判別しています。
lambda = if cloud_deployed?
Aws::Lambda::Client.new(region: ENV['REGION'],
access_key_id: ENV['ACCESS_KEY_ID'],
secret_access_key: ENV['SECRET_ACCESS_KEY'])
else
Aws::Lambda::Client.new(region: ENV['REGION'],
endpoint: 'http://localstack:4574',
access_key_id: ENV['ACCESS_KEY_ID'],
secret_access_key: ENV['SECRET_ACCESS_KEY'])
end
あとはいつもどおり呼び出せます。
lambda.invoke(function_name: 'MY_LAMBDA_FUNCTION', payload: message)