このエントリーをはてなブックマークに追加

Serverless Framework でのAPI Gateway + Lambda + DynamoDB構成をローカル環境で開発する

Serverless Frameworkでの開発の際、ローカル環境で実行させることでAWSのコストを発生させないようにするセットアップ手順を紹介します。

Serverless Frameworkとは

自分でやろうとすると結構面倒な、API Gatewayの設定、Lambda関数の作成を自動的に行なってくれるフレームワークです。
アプリケーションのコードど簡単な設定ファイルを用意すればすぐにAWS上に公開することができ、より一層Serverlessが普及していくのではないかと思います。

内部的にはCloudFormationのスタックが作成されてデプロイが行われるようですが、あまり内部の動作は気にせずに使うことができます。

インストールはNPMから行います。

$ npm install serverless -g

AWSの認証情報を設定する

いくつか方法がありますが、AWS CLIを使うと簡単です。 AWS CLIがまだインストールされていない場合は、pipでインストールできます。(Python 2.6.5以上が必要です)

$ pip install awscli

Macの場合、すでにsixパッケージがインストールされている場合にエラーが発生するかもしれません。
その場合は以下のコマンドでインストールできます。

$ pip install awscli --upgrade --ignore-installed six

AWSコマンドで認証情報を設定します。以下のコマンドを実行するとアクセスキーなどの入力を求められるので、コピペします。

$ aws configure

Serverlessプロジェクトを作成する

AWS上で動作するNode.jsプロジェクトを作成する場合、このようなコマンドです。 (serverlessコマンドはslsという名前でも実行できます。これ以降はslsコマンドを使っていきます。)

$ serverless create --template aws-nodejs --path sample-app

image

sample-appディレクトリには2つのファイルが生成されます。

  • handler.js - Lambdaのハンドラ関数です。ここにアプリケーションのコードを追加していきます (別のファイルにすることもできます)
  • serverless.yml - Serverless Frameworkの設定ファイルです。

Hello World的な関数が含まれているので、試しにデプロイできる状態になっています。

デプロイする (注意: 今の時点ではローカルではなくAWSにデプロイされます)

試しにhello関数をAPI Gateway経由でアクセスできるようにして、デプロイしてみます。

functions:
  hello:
    handler: handler.hello

#    The following are a few example events you can configure
#    NOTE: Please make sure to change your handler code to work with those events
#    Check the event documentation for details
#    events:
#      - http:
#          path: users/create
#          method: get

functions.helloの下にあるeventsがコメントアウトされているので解除します。

functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: users/create
          method: get

この状態でデプロイします。デプロイは以下の通り簡単です。

$ sls deploy

image

image

image

image

AWSにデプロイしたものを一式削除する

以下のコマンドできれいさっぱり消すことができます。

$ sls remove

ローカル環境を構築する

ここからが本題になります。API Gatewayの動きを再現するための仕組みと、Amazonが提供しているDynamoDB Localをつかってローカルに動作環境を再現します。 Serverless Frameworkのプラグインを導入することで簡単に実現できます。

  • DynamoDB Local - ローカルでDynamoDBを動かすツールです。Javaが必要です。
  • serverless-dynamodb-local - Serverless Frameworkのプラグイン、上記DynamoDB Localのインストールやテーブル作成を自動的に行なってくれます。
  • serverless-offline - API Gatewayを模したローカルAPIサーバーを動かしてくれます。

yarnコマンドでインストールします。

$ yarn add --dev serverless-dynamodb-local serverless-offline

パッケージが追加されたらserverless.ymlを編集し、以下を追加します。

plugins:
  - serverless-dynamodb-local
  - serverless-offline

custom:
  serverless-offline:
    port: 4000
  dynamodb:
    start:
      port: 8000

注意点:

  • pluginsに記載する順番は、この例の通りserverless-dynamodb-localを先に指定するようにしてください。 それによりserverless-offline実行時に、自動的にDynamoDBも起動されます。
  • dynamodbのポートは8000番を指定してください。上記の連動の際に8000番ポート以外では動作しません。

この状態でslsコマンドを単体で実行すると、サブコマンドが増えていることがわかります。

image

DynamoDBのテーブル定義を設定する

Serverless Frameworkのドキュメントにあるサンプルの通り、DynamoDBのテーブル定義を設定してみます。
以下の内容をserverless.ymlに追加します。

resources:
  Resources:
    usersTable:
      Type: AWS::DynamoDB::Table
      Properties:
        TableName: usersTable
        AttributeDefinitions:
          - AttributeName: email
            AttributeType: S
        KeySchema:
          - AttributeName: email
            KeyType: HASH
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1

DynamoDB Localのインストール

次のコマンドで自動的にDynamoDB Localがダウンロードされます。
プロジェクト作成後に1度だけ行います。

$ sls dynamodb install

DynamoDB Localの起動とテーブルの作成

sls dynamodb startコマンドでDynamoDB Localが起動します。あわせてテーブルを作成する場合は--migrateオプションを指定します。

$ sls dynamodb start --migrate

API Gateway ローカルサーバーの起動

sls offline startでAPIサーバーと同時にDynamoDB Localも起動します。
終了する場合はCtl+Cを入力します。

$ sls offline start

別のターミナルを開き、APIのテストをしてみます。

$ curl http://localhost:4000/users/create

正しく動作していることがわかります。

{"message":"Go Serverless v1.0! Your function executed successfully!","input":{"headers":{"Host":"localhost:4000","User-Agent":"curl/7.43.0","Accept":"*/*"},"path":"/users/create","pathParameters":null,"requestContext":{"accountId":"offlineContext_accountId","resourceId":"offlineContext_resourceId","stage":"dev","requestId":"offlineContext_requestId_","identity":{"cognitoIdentityPoolId":"offlineContext_cognitoIdentityPoolId","accountId":"offlineContext_accountId","cognitoIdentityId":"offlineContext_cognitoIdentityId","caller":"offlineContext_caller","apiKey":"offlineContext_apiKey","sourceIp":"127.0.0.1","cognitoAuthenticationType":"offlineContext_cognitoAuthenticationType","cognitoAuthenticationProvider":"offlineContext_cognitoAuthenticationProvider","userArn":"offlineContext_userArn","userAgent":"curl/7.43.0","user":"offlineContext_user"},"authorizer":{"principalId":"offlineContext_authorizer_principalId"},"resourcePath":"/users/create","httpMethod":"GET"},"resource":"/users/create","httpMethod":"GET","queryStringParameters":null,"body":null,"stageVariables":null,"isOffline":true}}

ワークフロー

このようにまずはローカルで実行できる環境を整え、アプリケーションが実装できたタイミングでデプロイをすることで、(コスト的に)効率よく開発をすすめることができます。

  1. ローカルで開発 & テスト
  2. stage:devでデプロイ & テスト
  3. stage:prodへデプロイ & テスト

基本的にこのサイクルを繰り返していくことになります。もちろんCIとの相性もよろしいかと思います。