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とは

最近巷で騒がれているサーバーレスなアーキテクチャを容易に作成、管理できる一般的なフレームワークというよりツールに近いもの。

Severless FrameworkのAWS環境においては、CloudFormation・Lambda・API Gateway・DynamoDB・S3・IAMなどのサービスを組み合わせてサーバーレスなアーキテクチャを管理・構成している。

今回は、Severless Frameworkを用いてLambda・API Gateway・DynamoDBのアーキテクチャで簡単なREST APIを実装する。

cloudcraft - serverless.png

なお、本記事ではv1.0.xを対象としている。
v0.xと大きく構成・設定が変わっているので注意。

環境

今回はAWS上で、Severless Frameworkを用いて簡単なREST APIを作成する。

Severless Frameworkでは、1.0.x時点でNode.js、Python、Java、Scalaがサポートされている。

今回は、Node.jsを対象として進めていく。
なお、JavaScriptに関しては、ES6を使用する。

パッケージ バージョン
npm 3.10
node 6.2
Serverless Framework 1.0.3

GitHub

hello-serverless

セットアップ

Serverless Frameworkインストール

早速Serverless Frameworkのインストール。

$ sudo npm install -g serverless

AWS Credentialsの設定

まずはSeverless Frameworkで使用するIAMを作成する。
作成の仕方は下記の公式ドキュメントを参考にしてください。
一旦ここでは、管理ポリシーとして「AdministratorAccess」のポリシーをもつユーザーを作成する。

http://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_users_create.html

次に、AWSサービスを管理するためのコマンドラインインターフェース(CLI)のツールをインストールしてServerless Frameworkで使用するIAMの設定を行う。

http://docs.aws.amazon.com/ja_jp/streams/latest/dev/kinesis-tutorial-cli-installation.html

MacでPython>=2.6.3な方の例
$ curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py"
$ sudo python get-pip.py
$ sudo pip install awscli

今回は、DefaultのAWS Profilesを使っていきます。

$ aws configure
AWS Access Key ID [None]: 作成したIAMのアクセスキーID
AWS Secret Access Key [None]: 作成したIAMのシークレットアクセスキー
Default region name [None]: ap-northeast-1
Default output format [None]: ENTER

既にDefaultのAWS Profilesが設定されている方は、下記を参考にProfileを切り替えてください。

https://serverless.com/framework/docs/providers/aws/guide/credentials/

プロジェクトの作成

今回はnode.jshelloというプロジェクトを作成する。
serverlessコマンドはインストール時に追加されるslsエイリアスを使っていく。

$ mkdir hello-serverless
$ cd hello-serverless
$ sls create --template aws-nodejs --name hello

ファイルを確認してみる。

ファイル 用途
event.json 各Functionにeventで渡されるテストデータ
handler.js 実際のLambda Functionを記述する
serverless.yml 各種設定。Lambda Functionの設定、エンドポイントの設定、リソースの設定などを記述する

handler.js

デフォルトのhandler.jsの中身を確認してみる。

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 });
};

Go Serverless v1.0! Your function executed successfully!というメッセージとevent内容を出力をJSON形式で表示する簡単な処理になっている。
これらを実際に確認してみる。

デプロイ先のリージョン設定

デフォルトでは、デプロイ先のリージョンとしてes-east-1(バージニア北部)に指定されているのでap-northeast-1(東京)に変更する。

serverless.yml
provider:
  name: aws
  runtime: nodejs4.3

# you can overwrite defaults here
  stage: dev
  region: ap-northeast-1

デプロイ

$ sls deploy -v

実行

sls invoke -f hello -p event.json

メッセージとevent内容が表示されただろうか?

イベントソース

どのイベントをトリガーにして、それぞれのLambda functionを呼び出すかを定義する。イベントの種類に関しては、AWSでサポートしているLambda functionのトリガーの全てが定義可能。

https://serverless.com/framework/docs/providers/aws/events/

今回はHTTP APIを定義する。
これによりAPI Gatewayへのアクセスによりトリガーされる。

eventsを追記する。

serverless.yml
functions:
  hello:
    handler: handler.hello
    events:
       - http:
           path: hello
           method: get
           cors: true

再度デプロイ

$ sls deploy -v
Service Information
service: hello
stage: dev
region: ap-northeast-1
api keys:
  None
endpoints:
  GET - https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/greet
functions:
  hello-dev-hello: arn:aws:lambda:ap-northeast-1:xxxxxxxxxx:function:hello-dev-hello

実際に、上記endpointsに記載されているAPIを叩いてみる。

$ curl -X GET https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/greet

ES6の設定

Babel + WebPackを用いて、ES6の環境を作成する。

パッケージのインストール

$ npm install --save-dev babel-core babel-loader babel-plugin-transform-runtime babel-polyfill babel-preset-es2015 babel-preset-stage-0 serverless-webpack webpack
$ npm install --save babel-runtime babel-polyfill

Babelの設定

{
  "plugins": ["transform-runtime"],
  "presets": ["es2015", "stage-0"]
}

webpackの設定

今回は、Babelとローカルの実行環境を作成するためにwebpackを用いる。

serverless.yml
plugins:
  - serverless-webpack
webpack.config.js
module.exports = {
  entry: './handler.js',
  target: 'node',
  module: {
    loaders: [{
      test: /\.js$/,
      loaders: ['babel'],
      include: __dirname,
      exclude: /node_modules/,
    }]
  },
  externals: {
    'aws-sdk': 'aws-sdk'
  }
};

ES6

handler.jsをまるっとES6へ

handler.js
export const 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-webpackによりローカルでの実行も可能となる。

$ sls webpack serve

表示されたローカルのエンドポイントにcurlコマンドでリクエストしてみる。

$ curl -X GET http://localhost:8000/hello

DynamoDBの設定

AWSのNoSQLなDynamoDBを使って、名前を登録、Hello, {登録名}とレスポンスを返すように変更していく。

API概要

ここでは、下記のHTTP APIを用意していく。

パス メソッド リクエストJSONフォーマット 用途
/names POST {"id":ID, "name":"登録名"} 名前のDynamoDBへの登録
/hello/{id} GET "Hello, {登録名}"メッセージの取得

パッケージのインストール

$ npm install --save-dev  aws-sdk

DynamoDBの設定

Severless Frameworkでは、serverless.ymlresourcesという項目でDynamoDBはじめ、S3などの各種リソースの設定を行える。特にAWSのコンソールを用いる必要はない。

今回は、単純に「名前」を扱うidとnameカラムをもつnamesテーブルの設定を行う。
(nameカラムに関してはここでは定義しないことにする)

serverless.yml
  Resources:
    hello:
      Type: "AWS::DynamoDB::Table"
      Properties:
        TableName: names
        AttributeDefinitions:
          - AttributeName: id
            AttributeType: N
        KeySchema:
          - AttributeName: id
            KeyType: HASH
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1

また、合わせてデプロイ時にDynamoDBを扱うIAMを作成するためにproviderにRoleを設定する。

serverless.xml
provider:
  name: aws
  runtime: nodejs4.3
# you can overwrite defaults here
  stage: dev
  region: ap-northeast-1
  iamRoleStatements:
    - Effect: Allow
      Resource: "arn:aws:dynamodb:${self:provider.region}:*:table/*"
      Action:
        - "dynamodb:*"

Lambda処理

DynamoDBに名前を登録するregistメソッドと指定されたIDに対して、Hello, {登録名}を返すhelloメソッドを定義する。

handler.js
import AWS from 'aws-sdk'

AWS.config.update({region: 'ap-northeast-1'})

const db = new AWS.DynamoDB()

export const hello = (event, context, callback) => {
  const params = {
    TableName: 'names',
    Key: {
      id: {N: event.pathParameters.id}
    }
  }

  try {
    db.getItem(params, (error, data) => {
      if (error) {
        callback(null, {statusCode: 400, body: JSON.stringify({message: 'Failed.', error: error})})
      }
      callback(null, {statusCode: 200, body: JSON.stringify({message: `Hello, ${data.Item.name.S}.`})});
    })
  } catch (error) {
    callback(null, {statusCode: 400, body: JSON.stringify({message: 'Failed.', error: error})})
  }
}

export const regist = (event, context, callback) => {
  const body = JSON.parse(event.body)
  const params = {
    TableName: 'names',
    Item: {
      id: {N: String(body.id)},
      name: {S: body.name}
    },
    Expected: {
      id: {Exists: false}
    }
  }


  try {
    db.putItem(params, (error, data) => {
      if (error) {
        callback(null, {statusCode: 400, body: JSON.stringify({message: 'Failed.', error: error})})
      }
      callback(null, {statusCode: 200, body: JSON.stringify({message: 'Succeeded!', params: params})});
    })
  } catch (error) {
    callback(null, {statusCode: 400, body: JSON.stringify({message: 'Failed.', error: error})})
  }
}

Lambda Functionの設定

serverless.xml
functions:
  hello:
    handler: handler.hello
    events:
      - http:
           path: hello/{id}
           method: get
           cors: true

  create:
    handler: handler.regist
    events:
      - http:
           path: names
           method: post
           cors: true

確認

デプロイしてcurlコマンドで確認してみる。

まずはID1でTaroを登録する。

$ curl -H "Accept: application/json" -H "Content-type: application/json" -X POST -d '{"id":1, "name":"Taro"}'  https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/hello

メッセージの確認

$ curl https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/hello/1

レスポンスJSONにmessageHello, Taro.と表示されればok :)

次回

今回は、Serverless Frameworkを使ってREST APIを作成した。
次回は、QraphQLに対応してみる。

最後に

サイバー・バズでは新しいことにチャレンジしつつサービスをつくっていくエンジニアを募集しています。

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
ユーザーは見つかりませんでした