Help us understand the problem. What is going on with this article?

Serverless Frameworkのインストールと初期設定

More than 3 years have passed since last update.

概要

Serverless Framework のインストールと初期設定のまとめ。
※執筆時のバージョンは1.4.0です。

前提条件

AWS Lambda + Nodeでの開発を想定しています。

環境構築

以下の環境で必要なミドルウェアのインストールを行っていきます。

Nodeのインストール

AWS LambdaのNode実行環境 に合わせてv4.3.2をインストールします。

今後AWSのNode実行環境のバージョンが上がった時の事も考えnodebrew を利用してNode自体のバージョンも簡単に変更出来るようにしておきます。

nodebrewのインストール(一般ユーザーでOK)
$ curl -L git.io/nodebrew | perl - setup
nodebrewのパスを通す
$ export PATH=$HOME/.nodebrew/current/bin:$PATH

$ source ~/.bashrc
Node4.3.2をインストール
$ nodebrew install-binary v4.3.2 --v8-options=--harmony
v4.3.2をデフォルトバージョンとして設定
$ nodebrew use v4.3.2

serverlessのインストール

Serverless Frameworkはserverlessという名前のnpmパッケージとして提供されています。

よってnpmを使ってインストールを行っていきます。
その前にnpmのバージョンがかなり古いので以下のコマンドで新しくしておきます。

npmのバージョンアップ
$ npm update -g

バージョンアップが終わったらserverlessをインストールしていきます。

serverlessのインストール
$ npm install -g serverless

※serverlessのインストール時に下記のようなエラーが出る事があります。

ServerlessError: ServerlessError: Signature not yet current: 20160316T092850Z is still later than 20160316T092236Z (20160316T091736Z + 5 min.)

これはサーバの時刻が大きくズレている時に起こるようです。
ntpdateをインストールして時刻を合わせましょう。

ntpdateのインストール
# yum install -y ntpdate
時刻合わせ
# ntpdate -b ntp.nict.jp

ここまでで最低限の環境整備はOKです。
環境に関しては作成方法等をAnsibleのplaybookにしてgithubに上げておきました。
※おまけとしてAWS CLIのインストールも行ってあります。

serverlessの管理を行う為のIAMユーザーを作成する

serverlessの管理を行う為にIAMマネジメントコンソールからIAMユーザーを作成します。

公式ドキュメントを見ると、以下の記載が確認出来ます。

Create or login to your Amazon Web Services Account and go to the Identity & Access Management (IAM) page.

Click on Users and then Create New Users. Enter a name in the first field to remind you this User is the Framework, like serverless-admin. Then click Create. Later, you can create different IAM Users for different apps and different stages of those apps. That is, if you don't use separate AWS accounts for stages/apps, which is most common.

View and copy the API Key & Secret to a temporary place. You'll need it in the next step.

In the User record in the AWS IAM Dashboard, look for Managed Policies on the Permissions tab and click Attach Policy.

In the next screen, search for and select AdministratorAccess then click Attach.

どうやら現状だとAdministratorAccessの権限を与える事が推奨されている模様です。
理由としてはserverless自体がまだ開発中で必要な権限が定まっていない為だと思われます。

これはセキュリティ上好ましくないので何とかしたいところです。
公式ドキュメントの情報が変わり適切な権限が分かり次第、修正していきます。

ユーザー作成ですが非常に簡単なので詳細な手順は省略させて頂きます。
アクセスキーやシークレットアクセスキー等の必要な情報を控えておく事だけ忘れないようにしましょう。

createadminuser.png

プロジェクトの雛形を作成

aws-serverless-prototypeという名前でディレクトリを作成しその中にプロジェクトの雛形を作成します。

プロジェクトの雛形作成
$ mkdir aws-serverless-prototype
$ cd aws-serverless-prototype
$ serverless create --template aws-nodejs --name aws-serverless-prototype

下記の3つのファイルが作成されます。

.npmignore
# package directories
node_modules
jspm_packages

# Serverless directories
.serverless
handler.js
'use strict';

module.exports.hello = (event, context, callback) => {
  const response = {
    statusCode: 200,
    body: JSON.stringify({
      message: 'Go Serverless v1.0! Your function executed successfully!',
      input: event,
    }),
  };

  callback(null, response);

  // Use this code if you don't use the http event with the LAMBDA-PROXY integration
  // callback(null, { message: 'Go Serverless v1.0! Your function executed successfully!', event });
};
serverless.yml
# Welcome to Serverless!
#
# This file is the main config file for your service.
# It's very minimal at this point and uses default values.
# You can always add more config options for more control.
# We've included some commented out config examples here.
# Just uncomment any of them to get that config option.
#
# For full config options, check the docs:
#    docs.serverless.com
#
# Happy Coding!

service: aws-serverless-prototype

# You can pin your service to only deploy with a specific Serverless version
# Check out our docs for more details
# frameworkVersion: "=X.X.X"

provider:
  name: aws
  runtime: nodejs4.3

# you can overwrite defaults here
#  stage: dev
#  region: us-east-1

# you can add statements to the Lambda function's IAM Role here
#  iamRoleStatements:
#    - Effect: "Allow"
#      Action:
#        - "s3:ListBucket"
#      Resource: { "Fn::Join" : ["", ["arn:aws:s3:::", { "Ref" : "ServerlessDeploymentBucket" } ] ]  }
#    - Effect: "Allow"
#      Action:
#        - "s3:PutObject"
#      Resource:
#        Fn::Join:
#          - ""
#          - - "arn:aws:s3:::"
#            - "Ref" : "ServerlessDeploymentBucket"

# you can define service wide environment variables here
#  environment:
#    variable1: value1

# you can add packaging information here
#package:
#  include:
#    - include-me.js
#    - include-me-dir/**
#  exclude:
#    - exclude-me.js
#    - exclude-me-dir/**

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
#      - s3: ${env:BUCKET}
#      - schedule: rate(10 minutes)
#      - sns: greeter-topic
#      - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000
#      - alexaSkill

#    Define function environment variables here
#    environment:
#      variable2: value2

# you can add CloudFormation resource templates here
#resources:
#  Resources:
#    NewResource:
#      Type: AWS::S3::Bucket
#      Properties:
#        BucketName: my-new-bucket
#  Outputs:
#     NewOutput:
#       Description: "Description for the output"
#       Value: "Some output value"

サンプルAPIの構築

今回サンプルで作成するのはClientsという名前のDynamoDBのテーブルを用意して、そこに1件データを登録するAPIと、生成したClientリソースをIDを指定して1件取得する2つのAPIを作成してします。

設定ファイルの準備

serverless.ymlとpackage.jsonを以下のように変更します。
ここでは以下の設定を行っています。

  • サービス名の設定
  • デフォルトで利用するregionを設定
  • stage(環境変数的な物)の設定
  • dynamodbに対する必要な権限の設定
  • Lambda関数の設定(clientCreate等が関数の名前になります、ここではAPI GatewayからLambda関数を実行出来る設定も行っています。)
  • DynamoDbのテーブルの作成(Clientsテーブル)
serverless.yml
service: prototype

provider:
  name: aws
  runtime: nodejs4.3
  region: ap-northeast-1
  stage: dev
  iamRoleStatements:
    - Effect: Allow
      Action:
        - dynamodb:DescribeTable
        - dynamodb:Query
        - dynamodb:Scan
        - dynamodb:GetItem
        - dynamodb:PutItem
        - dynamodb:UpdateItem
        - dynamodb:DeleteItem
      Resource: "arn:aws:dynamodb:ap-northeast-1:*:*"

functions:
  clientCreate:
    handler: src/functions/client/handler.create
    events:
      - http:
          path: clients
          method: post
          cors: true
  clientFind:
    handler: src/functions/client/handler.find
    events:
      - http:
          path: clients/{id}
          method: get
          cors: true

resources:
  Resources:
    ClientsDynamoDbTable:
      Type: 'AWS::DynamoDB::Table'
      DeletionPolicy: Retain
      Properties:
        AttributeDefinitions:
          -
            AttributeName: id
            AttributeType: S
        KeySchema:
          -
            AttributeName: id
            KeyType: HASH
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1
        TableName: 'Clients'

package.jsonです。dependenciesの中身以外は適当で構いません。

package.json
{
  "name": "prototype",
  "version": "0.1.0",
  "description": "Serverless CRUD prototype",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "MIT",
  "dependencies": {
    "aws-sdk": "^2.7.19",
    "uuid": "^3.0.1"
  }
}

Lambda関数本体のコードの作成

下記のコードを作成します。

src/functions/client/client-create.js
'use strict';

const AWS = require('aws-sdk');
const dynamoDb = new AWS.DynamoDB.DocumentClient();
const uuid = require('uuid');

module.exports = (event, callback) => {
  const data = JSON.parse(event.body);
  const nowDate = new Date().getTime();

  const clientCreateParams = {};

  clientCreateParams.id = uuid.v1();
  clientCreateParams.secret = uuid.v4();
  clientCreateParams.name = data.body.name;
  clientCreateParams.redirectUri = data.body.redirectUri;
  clientCreateParams.createdAt = nowDate;
  clientCreateParams.updatedAt = nowDate;

  const params = {
    TableName: 'Clients',
    Item: clientCreateParams
  };

  return dynamoDb.put(params, (error, data) => {
    if (error) {
      callback(error);
    }
    callback(error, params.Item);
  });
};
src/functions/client/client-find.js
'use strict';

const AWS = require('aws-sdk');
const dynamoDb = new AWS.DynamoDB.DocumentClient();

module.exports = (event, callback) => {
  const params = {
    TableName: 'Clients',
    Key: {
      id: event.pathParameters.id
    }
  };

  return dynamoDb.get(params, (error, data) => {
    if (error) {
      callback(error);
    }
    callback(error, data.Item);
  });
};
src/functions/client/handler.js
'use strict';

const clientCreate = require('./client-create.js');
const clientFind = require('./client-find.js');

module.exports.create = (event, context, callback) => {
  clientCreate(event, (error, result) => {
    const response = {
      statusCode: 201,
      headers: {
        "Access-Control-Allow-Origin" : "*"
      },
      body: JSON.stringify(result),
    };

    context.succeed(response);
  });
};

module.exports.find = (event, context, callback) => {
  clientFind(event, (error, result) => {
    const response = {
      statusCode: 200,
      headers: {
        "Access-Control-Allow-Origin" : "*"
      },
      body: JSON.stringify(result),
    };

    context.succeed(response);
  });
};

デプロイ&動作確認

プロジェクトルートで以下のコマンドでデプロイを行います。

パッケージのインストール
$ npm install
デプロイの実行
$ serverless deploy

AWSのコンソールで確認すると、それぞれ下記のような設定が行われている事を確認出来ます。

作成されたDynamoDBのテーブル

Dynamodb.png

作成されたAPI Gatewayの設定

apigateway.png

作成されたLambda関数

Lambdafunction.png

動作確認

作成されたエンドポイントに対してcurlでリクエストを行い動作確認を行います。

Clientを作成するリクエスト
curl -X POST -kv \
-d \
'
{
  "body": {
    "name":"neko",
    "redirectUri":"https://example.com"
  }
}
' \
https://XXXX.execute-api.ap-northeast-1.amazonaws.com/dev/clients
Client作成のレスポンス(レスポンスヘッダは一部省略)
HTTP/1.1 201 Created
Content-Type: application/json
Content-Length: 195
Connection: keep-alive

{
  "id":"f8a403b0-d294-11e6-9e68-8f8adb71c002",
  "secret":"dd812d74-89e0-412f-85f7-6fb53d1b7dbc",
  "name":"neko",
  "redirectUri":"https://example.com",
  "createdAt":1483544794986,
  "updatedAt":1483544794986
}

作成されたClientリソースが返却されました。続いて作成されたリソースをGETリクエストで取得してみます。

GETリクエスト
curl -kv https://XXXX.execute-api.ap-northeast-1.amazonaws.com/dev/clients/f8a403b0-d294-11e6-9e68-8f8adb71c002

以下のように正常にレスポンスが返却されました。

GETリクエストのレスポンス(レスポンスヘッダは一部省略)
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 195

{
  "id":"f8a403b0-d294-11e6-9e68-8f8adb71c002",
  "createdAt":1483544794986,
  "name":"neko",
  "updatedAt":1483544794986,
  "redirectUri":"https://example.com",
  "secret":"dd812d74-89e0-412f-85f7-6fb53d1b7dbc"
}

先程作成したリソースが返却されているかと思います。

デプロイしたリソースを削除するには?

以下のコマンドを実行します。

デプロイしたリソースを削除する
serverless remove

ただしDynamoDBのテーブルやCloudWatchのロググループ等は削除されずに残ります。(少なくともこの記事のやり方の場合では)

これらは手動でAWSのコンソール等で削除する必要があります。

まとめ

Serverless Frameworkを利用すると簡単にAWS上にLambda関数をデプロイする事が出来ました。

本格的に利用するには以下の点を習得する必要があると感じました。

  • デプロイ時の環境変数を何からの形で置換えられるようにする
  • Babelやwebpack等を利用した新しいJavascriptの構文を用いての開発方法
  • DynamoDBのテーブル定義を自由に設定出来るように構文を覚える

一応この記事で作成したコードはgithubに上げておきました。

※現状も色々と施行錯誤を行っている段階なのでコードは変化していく予定です。
また新しい発見があったら記事を書こうと思います。

最後まで読んで頂きありがとうございました。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした