ServerlessインストールからLambdaへのデプロイ

## はじめに
サーバーレスアプリケーション（以後SLA）の開発のためのフレームワーク[Serverless](https://github.com/serverless/serverless)を使って、nodejsベースのAWS Lambda上で動く関数作成からデプロイまでやります。公式のQuick startが微妙だなと思ったので、備忘録として書いておきます。

## なぜSLA開発にフレームワークを使うのか
**コードの変更はローカルで、ビルドはEC2やDockerで、デプロイはS3で、ハンドラの設定はLambdaで、APIの設定はAPI Gatewayで、ログの確認はCloudWatchで、という開発プロセスは辛いので、自前で開発環境を整えるか、フレームワークを使ったほうが良い**と思うからです。

小規模なアプリケーションだったり、一回書いておしまいということであれば、Lambdaのインラインエディタとテスト機能だけで用が済むかもしれません。しかし、アプリケーションが少し大きくなったり、少し手の込んだことをしようとするだけで、SLA開発の性質や、Lambdaの制限によって、CI/CDのコストが跳ね上がります。例えば、

- コード中で外部のパッケージを利用する場合、デプロイが辛くなる。関数ファイルと外部パッケージをzipしてLambdaにアップロードする必要がある
- その場合、Lambdaと開発環境をそろえようと思うと、EC2やDockerを利用することになる
- アップロードするzipファイルサイズが3MBを越えた場合、インラインエディタで編集できなくなる
- zipファイルサイズが10MBを越えた場合、コードを変更するたびにzipファイルをS3にアップロード -> S3からLambdaにインポートという手順を踏む必要がある

Serverlessを使うことによって、

- `sls deploy`コマンドだけでデプロイができる
- 裏側でCloudFormationのスタックを利用することによって、S3とLambdaのコードの管理だけでなく、APIやデータベースの設定も一括してやってくれるので、開発に一貫性が生まれる

というメリットがあると思います。

## 流れ
- IAM Policyの作成
- IAM Userの作成
- .aws/credentialsの編集
- Serverlessのインストール
- serverless.ymlの編集
- デプロイ

まず、ServerlessのプロジェクトがAWS上のサービスを利用するための権限が必要です。必要な権限のセット(IAM Policy)を持ったAWSユーザー(IAM User)のcredentialをプロジェクトにひもづけてやる必要があります。これがIAM PoicyとIAM Userの作成にあたります。

次に、ひもづけるcredentialは、serverless.ymlの中にベタ書きしたくないので、ローカルの .aws/credentialsの中で管理します。

そして、serverless.ymlで.aws/credentials内の目的のcredentialを参照するよう設定します。その他、AWSリージョンや関数名、nodejsのバージョンの設定などを行い、デプロイします。

## 注意点
- 公式のQuick startにある設定動画は古いし、ちゃんとしてないのであてにならない
- AdministratorAccessを献上する必要はない

## IAMでカスタムポリシーの作成

ServerlessのGithub上のQuick startでは、AdministratorAccessのポリシーを持ったIAMユーザーのcredentialをセットするように言われているのですが、AdministratorAccessを献上するのはあまり良い気分はしないですよね。別のところでは、公式にこう言われています。

In a production environment, we recommend reducing the permissions to the IAM User which the Framework uses. Unfortunately, the Framework's functionality is growing so fast, we can't yet offer you a finite set of permissions it needs (we're working on this). Consider using a separate AWS account in the interim, if you cannot get permission to your organization's primary AWS accounts.
(https://github.com/serverless/serverless/blob/master/docs/providers/aws/guide/credentials.md)

今回は、ガチガチに管理するわけでもないけど、AdministratroAccessよりは限定して、かつLambdaへのデプロイに十分なカスタムポリシーを作ります。AWSの他のサービスも使う場合は、それに応じてActionに追加します。

AWSのコンソールから、
`IAM`
->　`Policies`
-> `Create policy`
-> `JSON`タブ
と進み、以下のような形に変更します。S3、Lambdaなど、サービスは限定しつつ、それぞれのサービスのアクションについてはワイルドカードにして限定しない形です。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt20180730",
            "Effect": "Allow",
            "Action": [
                "s3:*",
                "logs:*",
                "iam:*",
                "apigateway:*",
                "lambda:*",
                "cloudformation:*",
                "events:*"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}
```

`"Sid"`は自分が識別しやすいように、任意のIDを入れます。ガチガチにしたいなら、`"Action"`や`"Resource"`をもっと限定します。でも個人使用では、これぐらいが手軽に管理しやすい落とし所のような気がします。

-> `Review policy`
-> `Name`に`ServerlessFrameworkBasic`と入力
-> `Create policy` で完了です。

## IAM Userの作成
AWSコンソールで、 
`IAM`
-> `Users`
-> `User name`に`my-app-user`と入力
-> `Programmatic access`にチェックを入れる
-> `Next:Permissions`
-> `Attach existing policies directly`
-> 先程作成した`ServerlessFrameworkBasic`をポリシーの中から検索してポリシー名左にチェックを入れる 
-> `Next:Review`
-> `Create user`
-> `Download .csv`でcredentialをダウンロードしておく

ここで表示されている `Access key ID`と`Secret access key`はあとで使います。

## .aws/credentialsの編集
作成したIAM Userのcredentialをローカルの`~/.aws/credential`にテキストエディタで追加します。なかったら作ります。 `serverless`コマンドで入れることもできるようですが、コマンドの中に直接アクセスキーを入れるのはおすすめしないです。

```:credentials
[任意のプロファイル名]
aws_access_key_id = 先程のAccess key IDをここに入れる
aws_secret_access_key = 先程のSecret access keyをここに入れる
```

例えば、こんな感じになります。すでに他のcredentialがある場合は、それを上書きするのではなく追加します。

```:credentials
[default]
aws_access_key_id = ...
aws_secret_access_key = ...

[my-app-user]
aws_access_key_id = 1234567890ABCDEFGHIJ
aws_secret_access_key = 1234567890abcdefghijklmnopqrstuvwxyzABCD

[another-credential]
aws_access_key_id = ...
aws_secret_access_key = ...
```

ただし、AWS CLIを使っていて、ここで用いるプロファイルが、AWS CLIでデフォルトで使用されているものと異なる場合、後々お気をつけください。参考までに、公式ではこう言われています。

Be aware! Due to the way AWS IAM and the local environment works, if you invoke your lambda functions locally using the CLI command serverless invoke local -f ... the IAM role/profile could be (and probably is) different from the one set in the serverless.yml configuration file. Thus, most likely, a different set of permissions will be in place, altering the interaction between your lambda functions and other AWS resources.

## Serverlessのインストールとプロジェクトの作成
nodejsは入っている前提です。2018/07/30現在、[AWS Lambdaでサポートされているnodejsのバージョン](https://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html)は主に6.10と8.10なので、8.10を入れておくと、ちょっとしたことをnodeコンソールで試せて便利だと思います。今回serverlessはグローバルにインストール、プロジェクトのルートディレクトリはホームディレクトリ直下に作るとします。

```zsh:serverlessのインストール
npm i -g serverless
```

```zsh:プロジェクト作成
# cd ~ && sls create -t aws-nodejs -p プロジェクト名
cd ~ && sls create -t aws-nodejs -p my-app
```

プロジェクトのルート・ディレクトリを見ると、`handler.js`と`serverless.yml`ができているのが確認できると思います。

```zsh:プロジェクトディレクトリの確認
ls ~/my-app
# => handler.js     serverless.yml
```

## serverless.ymlの編集
最低限必要な記述はこんな感じです。[serverless.ymlのリファレンス](https://serverless.com/framework/docs/providers/aws/guide/serverless.yml/)を見ると良いと思います。

```yaml:serverless.yml
service:
  name: プロジェクト名

provider:
  name: aws
  runtime: nodejsのバージョン
  profile: credentialのプロファイル名
  region: リージョン

functions:
 関数名:
    handler: handler.関数名
```

例えば、
- プロジェクト名は`my-app`
- 先程作成した `my-app-user`のcredentialを使う
- Lambdaのnodejsのバージョンは8.10
- 以下の様な`handler.js`を持っている

```handler.js
'use strict';

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

  callback(null, response);
};
```
とすると、`serverless.yml`はこうなります。

```yaml:serverless.yml
service:
  name: my-app

provider:
  name: aws
  runtime: nodejs8.10
  profile: my-app-user
  region: ap-northeast-1

functions:
  main:
    handler: handler.main
```

注意したいところは、
- `stage`は`dev`がデフォルト。デプロイ時にLambda上では関数名にstage名がsuffixされる。
- nodejsのバージョンは`6.10`がデフォルトで記述されているので、`8.10`を使用する場合は、見間違えずに変更する。
- `region`は`us-east-1`リージョンがデフォルト。デプロイ時にCloudFormationのStackがus-east-1で作成されるので、使用するリージョンがus-east-1でない場合はちゃんと指定しないとエラーが起きる可能性がある。

## デプロイ

```zsh
cd ~/my-app
sls deploy -v
```

いまのところ、`v`(verbose)オプションの有無で出力があまり変わらないので、`sls deploy`だけでも良いと思いますが、`Starck Outputs`を確認したいときはつけても良いと思います。


## 参考
https://github.com/serverless/serverless
https://serverless.com/framework/docs/providers/aws/guide/serverless.yml/