Serverless Framework で AWS Lambda と API Gateway をデプロイする

Serverless Meetup Tokyo #16 (オンライン開催) を拝見していて、ちょうど今、AWS への各種リソースのデプロイの自動化が課題だったので、よい機会と思い Serverless Framework を使ってみた。 尚、Serlverless Framwork については知っていたが、offline-start しか使ったことがなかった程度の人間です。

やったこと

弊社では Webシステムを、

  • フロントエンド: SPA(Angular)
  • バックエンド:
    • REST っぽいAPI: Lambda + API Gateway
    • DB: PostgreSQL 他

で組むことが増えてきていて、プロジェクトの取っ掛かり時の環境構築を手動でやっているのがダルくなってきた。 今回は、「REST っぽいAPI: Lambda + API Gateway」のところを Serverless Framework で自動化してみた。

手順

1. 環境構築用 IAM の作成

IAM コンソールで「serverless_deployment」という名前で作成。以下のポリシーをアタッチした。

  • AWSLambdaFullAccess
  • AmazonS3FullAccess
  • AmazonAPIGatewayAdministrator
  • AWSCloudFormationFullAccess

さらに、以下のようなインラインポリシーを追加した。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "iam:DeleteRolePolicy",
                "iam:CreateRole",
                "iam:DeleteRole",
                "iam:PutRolePolicy"
            ],
            "Resource": "*"
        }
    ]
}

IAMFullAccess はさすがにヤバいかなと思い必要な権限だけ抽出したものだが、たぶん他の xxxFullAccess も必要なものだけにした方がよいだろう。

作成した IAM のアクセスキーなどを自PCの ~/.aws/credentials に追加した。ちなみに環境は Windows 10 内の WSL(Ubuntu)。

.aws/credentials

[serverless-deployment]
aws_access_key_id = AKIAxxxxx
aws_secret_access_key = 49s9xxxxxxxxxxxxxxxxxxxxxxxxxxxx

2. ツールのインストール

awscli は現在最新の v2 をインストール

node は 12.14.1

そして Serverless Framework をインストールする。

npm install serverless -g

source ~/.bash_profile
serverless --version

> Framework Core: 1.65.0
> Plugin: 3.4.1
> SDK: 2.3.0
> Components: 2.22.3

global じゃなくてもいいけど、パス通すのが面倒なので。

3. テンプレートからプロジェクトの作成

serverless create --template aws-nodejs

で、nodejs のテンプレートから Serverless Framework のプロジェクトを作成。

また、npm init で適当に package.json を作って、

npm install --save-dev serverless-plugin-custom-binary

を実行しておく。これは後に必要になるプラグイン。

ls すると以下のようなファイルとディレクトリがある。

handler.js  node_modules  package-lock.json  package.json  serverless.yml

4. serverless.yml を編集する

serverless.yml を開いて次のように編集する(これだと最早テンプレートの意味ないが)。

service: my-awesome-service

plugins:
  - serverless-plugin-custom-binary

custom:
  apiGateway:
    binaryMediaTypes:
      - image/jpeg

provider:
  name: aws
  runtime: nodejs12.x
  stage: ${opt:stage, 'dev'}
  region: ap-northeast-1
  apiName: ${self:service}-${self:provider.stage}

functions:
  api:
    handler:  handler.hello
    name: ${self:service}-api-${self:provider.stage}
    events:
      - http:
          path: /{proxy+}
          method: get
          integration: lambda

まず service: my-awesome-service、これが AWS に作成されるリソース名の元になるのでちゃんと考えて命名しよう。重複したらどうなちゃうのかは不明。 kebab-case を採用しておくと良いと思われる。例えばサービス名で S3 Bucket を作りたいとき、Bucket 名は CamelCase(大文字) を許可してないため。

次に Plugins と binaryMediaTypes。これを行うために先に serverless-plugin-custom-binary をインストールしておいた。

provider-apiName。これは API Gateway の名前なんだけど、これをしない場合 <stage名>-<service名> になる。Lambda とかは <service名>-<stage名> となり逆で気持ち悪いので、他の同じになるように直している。

stage: ${opt:stage, 'dev'}。単純に stage: dev とするだけだと、 --stage prod を引数で指定された値が ${self:provider.stage} に代入されないので注意。

functions-api。Labmda に <service名>-<stage名>-api という名前の関数が作成される。"api" は任意の名称で ok。

functions-api-name。既定だと <service名>-<stage名>-api になるが、<service名>-api-<stage名>にしたい(stage名は最後尾に統一したい) ので name: ${self:service}-api-${self:provider.stage} とした。

handler: handler.hello。 handles.js の hello 関数を呼び出すの意。

path: /{proxy+}。呼び出し URL のパス部分を全てスルーする。https://hoge.net/dev/fuga/piyo/gege とか。

integration: lambda。既定で ON ぽいので要らないかも。

5. AWS にデプロイする

serverless deploy --aws-profile serverless-deployment

を実行する。--aws-profile serverless-deployment で AWSプロファイルを指定している事に注意。

Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
........
Serverless: Stack create finished...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service MyAwesomeService.zip file to S3 (1.13 KB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
...............................
Serverless: Stack update finished...
Service Information
service: MyAwesomeService
stage: dev
region: ap-northeast-1
stack: MyAwesomeService-dev
resources: 11
api keys:
  None
endpoints:
  GET - https://xxxx.execute-api.ap-northeast-1.amazonaws.com/dev/{proxy+}
functions:
  api: MyAwesomeService-dev-api
layers:
  None
Serverless: Run the "serverless" command to setup monitoring, troubleshooting and testing.

なんやかんや実行されてデプロイされたみたい。

6. デプロイされたか確認

API Gateway

image.png

image.png

Lambda

image.png

image.png

cURL で呼び出してみる。

 curl https://xxxx.execute-api.ap-northeast-1.amazonaws.com/dev/aaa/bbb/ccc
{"statusCode":200,"body":"{\n  \"message\": \"Go Serverless v1.0! Your function executed successfully!\"...

うまくいったみたい。

7. 後片付け(削除)

serverless remove --aws-profile serverless-deployment

ですべてのリソースがキレイさっぱり消えます。これはこれで怖いので IAM の権限で制限したほうが良さそう。

トラブルシューティング

Q: デプロイが全然終わらない

A: Console から CloudFormation の該当スタックを削除してリトライ

Q: sls deploy や remove が「S3 のバケットが無い」とかでエラーになる

A: 該当バケット(my-awesome-service-veri-serverlessdeploymentbucke-rzr9e2jjdrlv のようなごちゃごちゃしたやつ) を手動作成するか、Console から CloudFormation の該当スタックを削除してリトライ

今後やりたいこと

  • Lambda へ VPC の設定
  • Lambda タイムアウト値の設定
  • スクリプトでビルドとか Webpack した結果を Serverless でデプロイ
  • S3 Bucket の作成
  • S3 に SPA をデプロイ
  • リソース権限周りをもっと深堀り

参考