簡単Serverless Frameworkコトハジメ

# 概要
ServerlessFrameworkは
Lambda / Lambda@Edge / GatewayAPI / CloudFormation 等のサーバーレス時代のための環境構築をCodeで管理できるツール群です。
使いこなせると対IoTデバイス向けの複雑なシステムをサーバー管理ナシで組めますし、
簡単なシングルファンクションをLambdaに設置するようなことも可能です。

今回はコトハジメということで、簡単なシングルファンクションをLambdaに設置することでコマンド等を整理していきたいと思います。

# インストール
```bash:terminal
npm install -g serverless
```

をするとslsコマンドが使えるようになります。

# sls createでプロジェクトの雛形を作成する

```bash
mkdir tutorial
cd tutorial
sls create --template aws-nodejs
```
でプロジェクトを作成します。


プロジェクトの雛形が作成されます。

```bash:雛形ファイル構成
.
├── handler.js
└── serverless.yml
```


handler.jsを見てみると、HelloWorldをしてくれそうなコードが雛形として作成されています。

```javascript:handler.js

module.exports.hello = async event => {
  return {
    statusCode: 200,
    body: JSON.stringify(
      {
        message: 'Go Serverless v1.0! Your function executed successfully!',
        input: event,
      },
      null, 2
    ),
  };
};
```

## 手元ですぐにHelloWorldを試す

手元で試したいので、`sls　offline`を利用しましょう

```bash:terminal

npm install --save-dev serverless-offline
```
でインストールして・・・

serverless.ymlに以下を追記します。

```yml:serverless.yml
plugins:
  - serverless-offline

functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: hello
          method: get
```

※ functionsはsls createしたときに雛形生成されたものを少しカスタマイズしています。

ターミナルで`sls offline` を実行すると次のようなメッセージが表示されます。

```bash:terminal
Serverless: GET /hello
Serverless: Offline [HTTP] listening on http://localhost:3000
```

localhost:3000/helloが用意されたようなのでブラウザで打ってみると早速実行してくれました。

![スクリーンショット 2019-12-09 19.43.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40127/cc66c532-d13d-b6e1-8943-3e87a7826b3e.png)

HelloWorld終了です。ローカル環境で簡易的なAPIサーバーを作ることができました。

# デプロイしてサーバーサイドでHelloWorldを行なう

```yml:serverless.yml

provider:
  name: aws
  runtime: nodejs12.x
  region: ap-northeast-1
  memorySize: 512
  timeout: 10
```
provider.runtimeをnodejs12.xに変更
region / memorySize / timeoutを追加しました。
東京リージョンにデプロイしたいときは、`region: ap-northeast-1`を忘れずに。

# デプロイコマンドを実行


`sls deploy -v` でAWS上に実行コードを上げます。 -v オプションは詳細ログを出力します。

<img width="563" alt="スクリーンショット_2019-12-09_20_37_28.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40127/a0f2118e-4dfd-0c82-0fc1-85dac1218aea.png">
endpointsにURLが記載されているので、コピーしてブラウザで確認します。
sls offlineでローカルで実行したものと同じものが、
AWS上で動いているのを確認することができます。(同じ図になるので画像は省略)
これでHelloWorldは完了しました。

# [GUIで確認] CloudFormation
CloudFormationを見てみると、tutorial-devというものが作成されていました。
 -devというのは環境変数です。後で変更することにします。
![スクリーンショット 2019-12-09 20.34.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40127/fe66d431-234d-58a6-2324-ccd4c8b3016e.png)

# [GUIで確認] Lambda
![スクリーンショット 2019-12-09 20.39.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40127/7dbe17b1-47e9-73eb-b9d5-d9556e167493.png)
APIGatewayをトリガーにLambdaが動作するよう自動的に設定されます。
※ serverless.ymlのfunctions.hello.events.httpに追記したことで、Gateway APIが自動生成されます。

# sls offlineでローカルデバッグしながら開発する
```javascript:handler.js

module.exports.hello = async event => {
  console.log(event); // 1. console.logでofflineだとターミナルに, deployだとCloudWatchに値を出力
  return {
    statusCode: 200,
    body: JSON.stringify(
      {
        path: event.path,
        useragent: event.headers['User-Agent'], // 2. Requestに応じたレスポンスを作る
      },
      null, 2
    ),
  };
};
```

1. console.logでsls offlineだとターミナルに出力されます。sls deployだとCloudWatchに値を出力します。
   本番時に大量ログを吐くようにすると、CloudWatch料金が掛かりますので、sls deployするときはconsole.logを外したり、軽量化したほうが良いかもしれません。
2. Requestに応じたレスポンスを作る
といったことが sls offlineで試しつつ、sls deployで本番デプロイできます。GUI上でやるよりずっと楽に開発できると思います。

# 環境変数で挙動を変える

sls deployを何もオプションなしで実行すると、 `dev` というstageが付けられます。
dev以外のステージを使い、且つ、環境変数を設定していきます。
今回は、`sitea` という環境と `siteb` という環境を作ります。

```yml:serverless.yml
service: tutorial

provider:
  name: aws
  runtime: nodejs12.x
  region: ap-northeast-1
  memorySize: 512
  timeout: 10
  stage: ${opt:stage, self:custom.defaultStage}

custom:
  defaultStage: sitea
  environment:
    sitea: ${file(env/sitea.yml)}
    siteb: ${file(env/siteb.yml)}

plugins:
  - serverless-offline

functions:
  hello:
    handler: handler.hello
    environment: ${self:custom.environment.${self:provider.stage}}
    events:
      - http:
          path: hello
          method: get

```

```yml:sitea.yml
URL: "https://www.sitea.com"
```
```yml:siteb.yml
URL: "https://www.siteb.com"
```

```javascript:handler.js
module.exports.hello = async event => {
  console.log(event);
  return {
    statusCode: 200,
    body: JSON.stringify(
      {
        path: event.path,
        useragent: event.headers['User-Agent'],
        site: process.env.URL, // 1.process.env.****がsiteb.ymlから定義される
      },
      null, 2
    ),
  };
};

```

▼実行結果：環境変数が利用できている
![スクリーンショット 2019-12-09 21.06.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40127/cd1074d5-5988-1b5a-e69b-ee78fc86aa6b.png)


`sls deploy --stage siteb` を実行して、
lambdaで確認したときに、`dev` のステージが `siteb`に変わったタスクが追加されました。
![スクリーンショット 2019-12-09 21.07.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40127/a227a385-a1d3-c719-02a5-b9534c49c02c.png)

環境変数は
dev / stg / prdで分けても良いですし、サイトで分けても良いですし、その組み合わせで分けても良いかもしれません。

# 作ったものを消したい
sls removeで消していきます。
GUIから消すときはCloudFormationから消していきましょう。
CloudWatchにゴミのログが残ります。ログを完全に消したいときはCloudWatchを最後に確認してください。
Roleとかを作っていたときもゴミが残るときがあります。

### 今日はここまで。

AdventCalendarも続くので続きを書くかもしれません。
ネタとして
・dashboard.serverless.com
・Lambda@Edge
・作ってみたもののサンプル
・Lambda上でChromeを立ち上げてテストしたりスクリーンショットさせたり
あたりがあるかなぁと。