2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

LocalStack を使って dockerise した Lambda を実行する方法

Posted at

はじめに

AWS上で動かしているアプリをどうにかしてローカルで実行できないかと思って
色々試してみてどうにか Lambda を動かすことができたのでそのまとめ。

LocalStack を使った構成

使っている技術など

  • Docker
  • Docker Compose
  • LocalStack
  • Python3

ディレクトリ構成

.
├── docker-compose.yml
├── app  # ここにAPIのコードが入っている感じ。今回は説明対象外
└── docker
    ├── api
    │   └── Dockerfile # 今回は説明対象外
    ├── lambda
    │   └── Dockerfile # AWS で実行するやつ。今回は説明対象外
    └── localstack
        ├── create_lambda.sh
        ├── dummy_function.py # 別にここにある必要はないけど、同じところにあった方が管理しやすいので
        └── dummy_function.zip

ソースコード

全部載せると量が半端ないので、必要なところだけ抜粋。

docker-compose.yml
version: '3'
services:
  db:
    container_name: db
    image: postgres:14.2-alpine
    volumes:
      - postgres:/var/lib/postgresql
    environment:
      POSTGRES_USER: test
      POSTGRES_PASSWORD: password
      POSTGRES_DB: test
      PGPASSWORD: password
  api:
    container_name: api
    build:
      context: ./docker/api
      dockerfile: Dockerfile
    environment:
      - LAMBDA_FUNCTION_NAME: dummy_function
      - AWS_LAMBDA_ENDPOINT: http://localstack:4566
      - AWS_ACCESS_KEY_ID: test # localstack内部で使っている KEY ID
      - AWS_SECRET_ACCESS_KEY: test # localstack内部で使っている ACCESS KEY
      - AWS_DEFAULT_REGION: us-east-1  # localstackと一致していればどこでもよい
  localstack:
    container_name: localstack
    image: localstack/localstack:0.14.2
    volumes:
      - ./docker/localstack:/docker-entrypoint-initaws.d
      - localstack:/localstack
    environment:
      - DEBUG=${DEBUG- }
      - DEFAULT_REGION=us-east-1
      - SERVICES=${SERVICES- }
      - HOSTNAME_EXTERNAL=localstack
      - LS_LOG=info
      - DATA_DIR=/localstack
      - LAMBDA_HOST=hoge_lambda:8080
  hoge_lambda:
    container_name: hoge_lambda
    build:
      context: ./lambda
      dockerfile: Dockerfile # 実際に AWS 上で動かす Lambda の Dockerfile
    environment:
      AWS_ACCESS_KEY_ID: test
      AWS_SECRET_ACCESS_KEY: text
      AWS_DEFAULT_REGION: us-east-1
volumes:
  localstack:
  postgres:
dummy_function.py
import http.client
import json
import os

LAMBDA_HOST = os.getenv("LAMBDA_HOST", "hoge_lambda:8080")


def lambda_handler(event, context):
    conn = http.client.HTTPConnection(LAMBDA_HOST)
    conn.request(
        "POST", "/2015-03-31/functions/function/invocations", json.dumps(event), {"Content-type": "application/json"}
    )
    response = conn.getresponse()
    if response.status != 200:
        print(
            "Failed to execute request. Status: {}, Message: {}, data: {}".format(
                response.status, response.reason, response.read().decode("utf8")
            )
        )
        raise Exception()

    body = response.read().decode("utf8")
    return json.loads(body)

LAMBDA_HOSTdocker-compose.ymlenvironment などで設定すると、
任意の Lambda にリクエストを送ることが可能。
あと、本当なら requests などより使いやすい http クライアントを使った方がよいのだと思うのだけど、
単なるリクエストの proxy で且つ外部ライブラリのインストールは極力避けたかったのでこの方法を採用。

create_lambda.sh
#!/bin/bash

awslocal lambda create-function \
  --function-name dummy_function \
  --zip-file fileb:///docker-entrypoint-initaws.d/dummy_function.zip \
  --handler dummy_function.lambda_handler \
  --runtime python3.8 \
  --role test-role

echo "Finished."

ここで指定している dummy_function.zipdummy_function.py だけを入れた Zip ファイルで、
予め作成して ./docker/localstack などに置いておく。

aws_lambda_service.py
import json
import os
import boto3


class AwsLambdaService:
    LAMBDA_FUNCTION_NAME = os.getenv("LAMBDA_FUNCTION_NAME")
    AWS_LAMBDA_ENDPOINT = os.getenv("AWS_LAMBDA_ENDPOINT")

    def invoke(self, payload):
        client = self._get_client()
        client.invoke(
            FunctionName=self.LAMBDA_FUNCTION_NAME, InvocationType="Event", Payload=json.dumps(payload),
        )

    @staticmethod
    def _get_client():
        return boto3.client(
            "lambda",
            endpoint_url=AWS_LAMBDA_ENDPOINT,
        )

AWS_LAMBDA_ENDPOINT を指定することで localstack 側の Lambda にリクエストを送ることが可能で、
ここが None だと AWS の Lambda にリクエストを送る。
なので、ローカルと本番で同じコードを使うことが可能。

おわりに

この方法だと、アプリと Lambda の間に proxy 用の Lambda が入るだけで他は同じものを使える点がよいと思っている。

あと、今回はアプリケーションから Lambda を呼び出すパターンに対応したけど、
S3 のファイル作成をトリガーとして実行されるパターンも基本的には問題ないはず
( localstack で実行できれば。。。)

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?