💪

Mac M1で Serverless Framework を使って AWS Lambda をデプロイする

2023/01/06に公開

M1 MacのPCで作業するため、Arm64でビルドできる形で作成しました。
Serverless Framework を使って、以下の機能を実装しました。

  • DynamoDBのテーブルを作成
  • データの取得・更新・削除の機能を持ったLambda関数
  • API GatewayからLambda関数を呼び出す

事前準備

準備として、以下のものが必要です。

  • Node.js: Serverless Framework を使用するには、Node.js が必要です。
  • AWS アカウント: Serverless Framework を使用するには、AWS のアカウントが必要です。
  • AWS アクセスキーとシークレットアクセスキー: Serverless Framework から AWS のサービスを呼び出すには、アクセスキーとシークレットアクセスキーが必要です。
    • IAMでロールを作成し、アクセスキーとシークレットアクセスキーを控えます。
      AWS CLI を設定する環境変数
    • コンソール上で以下のコマンドを実行することで環境変数の設定が可能です。
export AWS_ACCESS_KEY_ID=xxxxx
export AWS_SECRET_ACCESS_KEY=xxxxx
export AWS_DEFAULT_REGION=ap-northeast-1
  • Serverless Framework のインストール
    • Serverless Framework を使用するには、まず、Node.js をインストールし、その上で、以下のコマンドを実行して、Serverless Framework をグローバルインストールします。
npm install -g serverless

プロジェクトの作成

Serverless Framework では、プロジェクトを作成するとともに、必要なリソースを自動で構築することができます。以下のコマンドを実行することで、新しいプロジェクトを作成できます。

serverless create --template aws-python3 --path my-project

このコマンドを実行すると、my-project という名前のディレクトリが作成され、その中に、サンプルの AWS Lambda 関数のファイルhandler.pyや、serverlessの設定ファイルserverless.ymlなどが配置されます。

ファイルの編集

作成されたディレクトリ内の設定ファイルserverless.ymlを編集します。
Macのアーキテクチャはarmであるため、設定をしないとビルドができませんでした。
LambdaをDockerで動かす形で記述しました。
Coffee noteというアプリケーションを作成したため、テーブル名や関数名がcoffee_noteとなっています。

  • serverless.yml
service: serverless-coffee-note
useDotenv: true
frameworkVersion: "3.26.0"

provider:
  name: aws
  architecture: arm64  # デフォルトはx86_64
  runtime: python3.8
  lambdaHashingVersion: 20201221
  # you can overwrite defaults here
  #  stage: dev
  region: ap-northeast-1
  iam:
    role:
      statements:
        - Effect: "Allow"
          Action:
            - dynamodb:GetItem
            - dynamodb:PutItem
            - dynamodb:UpdateItem
            - dynamodb:DeleteItem
            - dynamodb:Query
          Resource: "arn:aws:dynamodb:*:*:table/coffee_note"
  ecr:
    images:
      coffee_note:
        file: Dockerfile
        path: handler/coffee_note
        platform: linux/arm64

resources:
  Resources:
    coffeeNote:
      Type: AWS::DynamoDB::Table
      Properties:
        TableName: coffee_note
        AttributeDefinitions:
          - AttributeName: user_id
            AttributeType: S
          - AttributeName: date
            AttributeType: N
        KeySchema: [
            {
              KeyType: "HASH",
              AttributeName: "user_id",
            },
            {
              KeyType: "RANGE",
              AttributeName: "date",
            }]
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1

functions:
  coffeeNote:
    image:
      name: coffee_note
      command: coffee_note.coffee_note_handler
    events:
      - http:
          path: /coffee_note
          method: get
          integration: lambda-proxy
          cors: true

      - http:
          path: /coffee_note
          method: post
          integration: lambda-proxy
          cors: true

      - http:
          path: /coffee_note
          method: delete
          integration: lambda-proxy
          cors: true

  • iam: AWS リソースへのアクセス権限を設定しています。

  • ecr: Docker イメージを AWS Elastic Container Registry (ECR) にプッシュするための設定を行うことができます。

  • image: Docker イメージを指定しています。

  • name: には、ECR リポジトリの名前、command には、Docker コンテナ内で実行されるコマンドを指定します。

  • functions: Lambdaの関数の定義を行なっています。

  • events: Lambda 関数をトリガーするイベントを定義しています。
    HTTP リクエストによってこの Lambda 関数がAmazon API Gatewayを通してトリガーされます。

  • handler/coffee_note/coffee_note.py

import json
import boto3
from decimal import Decimal

from boto3.dynamodb.conditions import Key, Attr

dynamodb = boto3.resource('dynamodb') ##Dynamodbアクセスのためのオブジェクト取得
table = dynamodb.Table("coffee_note") ##指定テーブルのアクセスオブジェクト取得

def coffee_note_handler(event, context):
    # TODO implement
    print(event)
    if event["body"] :
        body = json.loads(event["body"])
    if event['httpMethod']=='GET' :
        # scanData = table.scan()
        user_id = event['queryStringParameters']
        response = table.query(
        KeyConditionExpression=Key('userId').eq(user_id['user_id'])
        )
        items=response['Items']
        print(items)
        return {
            'statusCode': 200,
            'body': json.dumps(items ,default=decimal_to_int)
        }
    if event['httpMethod']=='POST' :
        putResponse = table.put_item(を設定
        Item={
            'user_id': body["user_id"],
            'memo': body["memo"],
            'date': body["date"],
            'star': body["star"],
        }
        )
        return {
        'statusCode': 200,
        'body': json.dumps('Hello from PostLambda!')
        }

    if event["httpMethod"] == "DELETE":
        delResponse = table.delete_item(
            Key={
           'user_id': body["user_id"],
           'date':  body["date"],
            }
        )
        return {
            'body': json.dumps('Hello from Lambda!'),
            'statusCode': 200
        }

    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }

def decimal_to_int(obj):
    if isinstance(obj, Decimal):
        return int(obj)
  • handler/coffee_note/DockerFile
FROM public.ecr.aws/lambda/python:3.8-arm64
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY *.py ./
  • handler/coffee_note/requirements.txt
requests
boto3

デプロイ

設定ファイルを編集したら、以下のコマンドを実行することで、プロジェクトをデプロイすることができます。

serverless deploy

Discussion