Serverless Framework で Hello World を作る

https://serverless.com/ の　V1.0 は、V0.5以前と比べるとディレクトリ構成も作りもシンプルになって、マイクロサービスを作るっという気分にさせてくれそうです。

まずは、慣れるために Hello World を作ってみました。

# ソースリポジトリ

ソースは以下のリポジトリを参照してください。
https://github.com/katsuhiko/sls-hello

# バージョン

| version | 
|:---|
| node 6.8.1 |
| npm 3.10.8 |
| serverless framework 1.0.3 |


# Hello World!

https://github.com/serverless/serverless#quick-start

ここに従って、作業していきます。
node　/ npm はインストールされている前提です。

## Serverless Framework のインストール

以下のコマンドで npm を利用してインストールします。
とりあえずは global へのインストールで良いと思いますが、真剣に開発するようになったら、サービスごとに Serverless Framework の指定したバージョンを local に入れることになると思います。

```
$ npm install -g serverless
```

## AWS credentials の設定

AWS コンソールからユーザー「serlverless-admin」を作り、「アクセス許可」＞「管理ポリシー」＞「ポリシーのアタッチ」ボタンクリックで「AdministratorAccess」を割り当てます。
そして「認証情報」＞「アクセスキー」＞「アクセスキーの作成」ボタンクリックでアクセスキーを作成します。

今回は、AWS creadentials に上記アクセスキーの情報を設定します。
開発、本番で切り替えるときに Profile を切り替えたほうが良いかなっと思ったので、Profile名「devSls」で設定しています。

```
$ mkdir ~/.aws
$ vi ~/.aws/credentials
[devSls]
aws_access_key_id = XXXXXXX
aws_secret_access_key = XXXXXXXXXXXXXX

```

Region を ` ~/.aws/config ` で設定したかったのですが、2016/11/01　時点ではまだ対応されていないようです。　Issue は上がっていたので、すぐに対応されると思います。

` ~/.aws/config ` で Region を設定する場合は以下です。 ` profile ` という prefix をつけるのがポイントです。

```
$ vi ~/.aws/config
[profile devSls]
region = ap-northeast-1
```

## Service の作成

コマンドで簡単に作成できます。

```
$ serverless create --template aws-nodejs --path sls-hello
$ cd sls-hello
```

## ソースの変更

テンプレートで作成したときに Lambda Proxy に対応する　` response ` になっています。
CORS として、　` headers ` を追加しています。

```js
'use strict';

module.exports.hello = (event, context, callback) => {
  const response = {
    statusCode: 200,
    headers: {
      'Access-Control-Allow-Origin' : '*'
    },
    body: JSON.stringify({
      message: 'Hello World!',
    }),
  };

  callback(null, response);
};

module.exports.helloHoge = (event, context, callback) => {
  const response = {
    statusCode: 200,
    headers: {
      'Access-Control-Allow-Origin' : '*'
    },
    body: JSON.stringify({
      message: `Hello ${event.pathParameters.name}!`,
    }),
  };

  callback(null, response);
};
```

パスパラメーターは ` event.pathParameters.name ` で受け取れます。 ` name ` の部分は、　` serverless.yml ` にて設定しています。

```yaml
service: sls-hello

provider:
  name: aws
  runtime: nodejs4.3
  stage: ${opt:stage, self:custom.defaultStage}
  profile: ${self:custom.profiles.${self:provider.stage}}
  region: ap-northeast-1

custom:
  defaultStage: dev
  profiles:
    dev: devSls
    prod: prodSls

functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: hello
          method: get
          cors: true
  helloHoge:
    handler: handler.helloHoge
    events:
      - http:
          path: hello/{name}
          method: get
          cors: true
```

stage、profile は、開発dev と本番prod を想定してドキュメント通りに設定してみました。

- https://github.com/serverless/serverless/blob/master/docs/providers/aws/guide/credentials.md#per-stage-profiles

Lambda Proxy のおかげで、 http のイベントをシンプルに表現できています。

` region ` は、本当は　`　~/.aws/config ` で　profile ごとに指定したほうが良いと思います。2016/11/01　時点では、　 ` ~/.aws/config ` では指定できなかったので ` serverless.yml ` で指定しています。

## デプロイ

以下のコマンドでデプロイできます。

```
$ sls deploy -v
Serverless: Creating Stack…
Serverless: Checking Stack create progress…
CloudFormation - CREATE_IN_PROGRESS - AWS::CloudFormation::Stack - sls-hello-dev
CloudFormation - CREATE_IN_PROGRESS - AWS::S3::Bucket - ServerlessDeploymentBucket
CloudFormation - CREATE_IN_PROGRESS - AWS::S3::Bucket - ServerlessDeploymentBucket
CloudFormation - CREATE_COMPLETE - AWS::S3::Bucket - ServerlessDeploymentBucket
CloudFormation - CREATE_COMPLETE - AWS::CloudFormation::Stack - sls-hello-dev
Serverless: Stack create finished…
Serverless: Packaging service…
Serverless: Uploading CloudFormation file to S3…
Serverless: Uploading service .zip file to S3…
Serverless: Updating Stack…
Serverless: Checking Stack update progress…
CloudFormation - CREATE_COMPLETE - AWS::CloudFormation::Stack - sls-hello-dev
CloudFormation - UPDATE_IN_PROGRESS - AWS::CloudFormation::Stack - sls-hello-dev
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::RestApi - ApiGatewayRestApi
CloudFormation - CREATE_IN_PROGRESS - AWS::IAM::Role - IamRoleLambdaExecution
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::RestApi - ApiGatewayRestApi
CloudFormation - CREATE_COMPLETE - AWS::ApiGateway::RestApi - ApiGatewayRestApi
CloudFormation - CREATE_IN_PROGRESS - AWS::IAM::Role - IamRoleLambdaExecution
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Resource - ApiGatewayResourceHello
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Resource - ApiGatewayResourceHello
CloudFormation - CREATE_COMPLETE - AWS::ApiGateway::Resource - ApiGatewayResourceHello
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Method - ApiGatewayMethodHelloOptions
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Method - ApiGatewayMethodHelloOptions
CloudFormation - CREATE_COMPLETE - AWS::ApiGateway::Method - ApiGatewayMethodHelloOptions
CloudFormation - CREATE_COMPLETE - AWS::IAM::Role - IamRoleLambdaExecution
CloudFormation - CREATE_IN_PROGRESS - AWS::IAM::Policy - IamPolicyLambdaExecution
CloudFormation - CREATE_IN_PROGRESS - AWS::IAM::Policy - IamPolicyLambdaExecution
CloudFormation - CREATE_COMPLETE - AWS::IAM::Policy - IamPolicyLambdaExecution
CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Function - HelloLambdaFunction
CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Function - HelloLambdaFunction
CloudFormation - CREATE_COMPLETE - AWS::Lambda::Function - HelloLambdaFunction
CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Permission - HelloLambdaPermissionApiGateway
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Method - ApiGatewayMethodHelloGet
CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Permission - HelloLambdaPermissionApiGateway
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Method - ApiGatewayMethodHelloGet
CloudFormation - CREATE_COMPLETE - AWS::ApiGateway::Method - ApiGatewayMethodHelloGet
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Deployment - ApiGatewayDeployment1478007545799
CloudFormation - CREATE_COMPLETE - AWS::Lambda::Permission - HelloLambdaPermissionApiGateway
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Deployment - ApiGatewayDeployment1478007545799
CloudFormation - CREATE_COMPLETE - AWS::ApiGateway::Deployment - ApiGatewayDeployment1478007545799
CloudFormation - UPDATE_COMPLETE_CLEANUP_IN_PROGRESS - AWS::CloudFormation::Stack - sls-hello-dev
CloudFormation - UPDATE_COMPLETE - AWS::CloudFormation::Stack - sls-hello-dev
Serverless: Stack update finished…

Service Information
service: sls-hello
stage: dev
region: ap-northeast-1
api keys:
  None
endpoints:
  GET - https://xxxx.execute-api.ap-northeast-1.amazonaws.com/dev/hello
  GET - https://xxxx.execute-api.ap-northeast-1.amazonaws.com/dev/hello/{name}
functions:
  sls-hello-dev-helloHoge: arn:aws:lambda:ap-northeast-1:9999:function:sls-hello-dev-helloHoge
  sls-hello-dev-hello: arn:aws:lambda:ap-northeast-1:9999:function:sls-hello-dev-hello
```

下のほうにでる「Service Information」を見ると、どの　region にデプロイされたかがわかります。
よくある ` us-east-1 `　に意図せずにデプロイされた場合

```
$ sls remove
```

で関連するAWSのサービスをキレイに削除してくれます。

どの　region　にデプロイされているかわからなくなったら以下のコマンドで確認できます。

```
$ sls info

Service Information
service: sls-hello
stage: dev
region: ap-northeast-1
api keys:
  None
endpoints:
  GET - https://xxxx.execute-api.ap-northeast-1.amazonaws.com/dev/hello
  GET - https://xxxx.execute-api.ap-northeast-1.amazonaws.com/dev/hello/{name}
functions:
  sls-hello-dev-helloHoge: arn:aws:lambda:ap-northeast-1:9999:function:sls-hello-dev-helloHoge
  sls-hello-dev-hello: arn:aws:lambda:ap-northeast-1:999:function:sls-hello-dev-hello

```

CloudFormation経由でいろいろと作っているので、いざというときは AWS Console から Stack を削除すればキレイに消えます。

## 動作確認

endpoints で表示された URL にアクセスすれば確認できます。

# 感想

触りだけを試したのみですが、素で扱うとリポジトリ管理やデプロイしづらいと感じる Lambda を、管理しつつ気軽に扱えそうです。
ディレクトリ構成の指定がないのも良いと思いました。

ローカルで動かすことや　DynamoDB　を利用するとなると、Hello World のように単純にはいかないと思うので、勉強しながら良い構成を見つけていきたいです。

特に [serverless-dynamodb-local Plugin](https://github.com/99xt/serverless-dynamodb-local/) は、シンプルになった Serverless Framework を複雑にしている気がするけど、やはり最低限、このようにする必要があるかもなっとも思うし、、、

つぎは serverless-dynamodb-local Plugin を触る必要がありそうです！