話題の Serverless Framework v1 の Quick Start を試してみた

最近よく聞く Serverless Framework の Quick Start を試してみた.

v1 系と v0 系 で全然違うデザインになっていて,Quick Start の手順すら違う.試してみた系の記事をいくつか読んでみたけど,結構 v0 系が多く,具体的に言うと serverless project create で初期化してる手順は v0 系.今回は v1 で試した.

github.com

Quick Start

README.md に書かれている Quick Start をそのまま試してみた.まずは npm でインストールすると,今回は最新の 1.0.0-beta.1.1 がインストールされた.

$ npm install -g serverless@beta
$ serverless --version
1.0.0-beta.1.1

次に service を初期化する.ちなみに事前に AWS 側で IAM ユーザーを作成しておく必要があって,今回は指示通りに AdministratorAccess 権限を付与したけど,運用を考えると良くないので,試した後に破棄した.

$ export AWS_ACCESS_KEY_ID=xxx
$ export AWS_SECRET_ACCESS_KEY=xxx
$ mkdir my-first-service && cd my-first-service
$ serverless create --template aws-nodejs
Serverless: Creating new Serverless service...
 _______                             __
|   _   .-----.----.--.--.-----.----|  .-----.-----.-----.
|   |___|  -__|   _|  |  |  -__|   _|  |  -__|__ --|__ --|
|____   |_____|__|  \___/|_____|__| |__|_____|_____|_____|
|   |   |             The Serverless Application Framework
|       |                           serverless.com, v1.0.0-beta.1.1
 -------'

Serverless: Successfully created service in the current directory
Serverless: with template: "aws-nodejs"
Serverless: NOTE: Please update the "service" property in serverless.yml with your service name

Quick Start には書かれていないけど,デフォルトだとバージニアリージョンでリソースが立ち上がってしまうため,serverless.yml に以下を追記する.そうすると,東京リージョンでリソースが立ち上がるようになる.実は環境変数 AWS_DEFAULT_REGION も設定して試してみたけど,そこは考慮されてなく,あくまで serverless.yml の値だけを参照している感じだった.

defaults:
  region: ap-northeast-1

さっそくデプロイをすると,Stack とあるように CloudFormation を使って,S3 や Lambda を操作していることがわかる.スゴイ…!

$ serverless deploy
Serverless: Creating Stack...
Serverless: Checking stack creation progress...
.................
Serverless: Stack successfully created.
Serverless: Zipping service...
Serverless: Uploading .zip file to S3...
Serverless: Updating Stack...
Serverless: Checking stack update progress...
...
Serverless: Deployment successful!

Service Information
service: aws-nodejs
stage: dev
region: ap-northeast-1
endpoints:
  None
functions:
  aws-nodejs-dev-hello: arn:aws:lambda:ap-northeast-1:${account-id}:function:aws-nodejs-dev-hello

さらに serverless invoke で Lambda 関数を実行することができた.

$ serverless invoke --function hello
{
    "message": "Go Serverless v1.0! Your function executed successfully!",
    "event": {}
}

追加で API Gateway もデプロイしてみる

せっかくなら API Gateway もデプロイしてみたいなと思って,Quick Start からは脱線して,serverless.yml に以下の events を追記した.

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

同じようにデプロイをすると,今度は endpoints に URL が入っていた.こんなに簡単に API Gateway を操作できるのか!

$ serverless deploy
Serverless: Zipping service...
Serverless: Uploading .zip file to S3...
Serverless: Updating Stack...
Serverless: Checking stack update progress...
.....
Serverless: Deployment successful!

Service Information
service: aws-nodejs
stage: dev
region: ap-northeast-1
endpoints:
  GET - https://6po128b3qe.execute-api.ap-northeast-1.amazonaws.com/dev/hello
functions:
  aws-nodejs-dev-hello: arn:aws:lambda:ap-northeast-1:${account-id}:function:aws-nodejs-dev-hello

API を叩いてみたら,普通に返ってきた.

$ curl -s https://6po128b3qe.execute-api.ap-northeast-1.amazonaws.com/dev/hello | jq .
{
  "message": "Go Serverless v1.0! Your function executed successfully!",
  "event": {
    "body": {},
    "method": "GET",
    "principalId": "",
    "headers": {
      "Accept": "*/*",
      "CloudFront-Forwarded-Proto": "https",
      "CloudFront-Is-Desktop-Viewer": "true",
      "CloudFront-Is-Mobile-Viewer": "false",
      "CloudFront-Is-SmartTV-Viewer": "false",
      "CloudFront-Is-Tablet-Viewer": "false",
      "CloudFront-Viewer-Country": "JP",
      "Host": "6po128b3qe.execute-api.ap-northeast-1.amazonaws.com",
      "User-Agent": "curl/7.43.0",
      "Via": "1.1 159df6785a5f32228becb5f676dbc4f3.cloudfront.net (CloudFront)",
      "X-Amz-Cf-Id": "1BsBy0g7mLx3VFYL4rBI04m50GiRaYP614wmcW5yH-6mwYc_ZpeaTw==",
      "X-Forwarded-For": "xxx.xxx.xxx.xxx, xxx.xxx.xxx.xxx",
      "X-Forwarded-Port": "443",
      "X-Forwarded-Proto": "https"
    },
    "query": {},
    "path": {},
    "identity": {
      "cognitoIdentityPoolId": "",
      "accountId": "",
      "cognitoIdentityId": "",
      "caller": "",
      "apiKey": "",
      "sourceIp": "xxx.xxx.xxx.xxx",
      "cognitoAuthenticationType": "",
      "cognitoAuthenticationProvider": "",
      "userArn": "",
      "userAgent": "curl/7.43.0",
      "user": ""
    },
    "stageVariables": {}
  }
}

API Gateway の管理コンソールを見たら,ちゃんとリソースが定義されていた.

f:id:kakku22:20160814044035p:plain

リソース削除も簡単にできる

リソースが残ってしまう心配もしなくて良くて,remove サブコマンドであっさりと消えてしまった.IAM で AdministratorAccess を与えてたから,実行するときはちょっと怖かったけど.

$ serverless remove
Serverless: Getting all objects in S3 bucket...
Serverless: Removing objects in S3 bucket...
Serverless: Removing Stack...
Serverless: Checking stack removal progress...
.........
Serverless: Resource removal successful!

という感じに,何もハマることなく,Quick Start を試すことができた.

CloudFormation のスタックを見てみた

最初の serverless deploy で生成された AWS リソースは以下だった.

  • AWS::IAM::Policy
  • AWS::IAM::Role
  • AWS::S3::Bucket
  • AWS::Lambda::Function

適用されていた CloudFormation テンプレートは以下だった.S3 にアップロードされた aws-nodejs-1471105389531.zip を Lambda にデプロイしていることもわかった.

{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Description": "The AWS CloudFormation template for this Serverless application's resources outside of Lambdas and Api Gateway",
    "Resources": {
        "ServerlessDeploymentBucket": {
            "Type": "AWS::S3::Bucket"
        },
        "IamRoleLambda": {
            "Type": "AWS::IAM::Role",
            "Properties": {
                "AssumeRolePolicyDocument": {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Effect": "Allow",
                            "Principal": {
                                "Service": [
                                    "lambda.amazonaws.com"
                                ]
                            },
                            "Action": [
                                "sts:AssumeRole"
                            ]
                        }
                    ]
                },
                "Path": "/"
            }
        },
        "IamPolicyLambda": {
            "Type": "AWS::IAM::Policy",
            "Properties": {
                "PolicyName": "dev-aws-nodejs-lambda",
                "PolicyDocument": {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Effect": "Allow",
                            "Action": [
                                "logs:CreateLogGroup",
                                "logs:CreateLogStream",
                                "logs:PutLogEvents"
                            ],
                            "Resource": "arn:aws:logs:ap-northeast-1:*:*"
                        }
                    ]
                },
                "Roles": [
                    {
                        "Ref": "IamRoleLambda"
                    }
                ]
            }
        },
        "hello": {
            "Type": "AWS::Lambda::Function",
            "Properties": {
                "Code": {
                    "S3Bucket": {
                        "Ref": "ServerlessDeploymentBucket"
                    },
                    "S3Key": "aws-nodejs-1471105389531.zip"
                },
                "FunctionName": "aws-nodejs-dev-hello",
                "Handler": "handler.hello",
                "MemorySize": 1024,
                "Role": {
                    "Fn::GetAtt": [
                        "IamRoleLambda",
                        "Arn"
                    ]
                },
                "Runtime": "nodejs4.3",
                "Timeout": 6
            }
        }
    },
    "Outputs": {
        "ServerlessDeploymentBucketName": {
            "Value": {
                "Ref": "ServerlessDeploymentBucket"
            }
        },
        "Function1Arn": {
            "Description": "Lambda function info",
            "Value": {
                "Fn::GetAtt": [
                    "hello",
                    "Arn"
                ]
            }
        }
    }
}

追加で API Gateway をデプロイした後の serverless deploy で生成された AWS リソースは以下だった.そのまんまだけど,API Gateway のリソースが追加されていた.

  • AWS::ApiGateway::Deployment
  • AWS::ApiGateway::Method
  • AWS::IAM::Policy
  • AWS::IAM::Role
  • AWS::ApiGateway::Resource
  • AWS::ApiGateway::RestApi
  • AWS::S3::Bucket
  • AWS::Lambda::Function
  • AWS::Lambda::Permission

IAM に関して感じたこと

Quick Start だとしても IAM に AdministratorAccess 権限を与えるのは危険だと感じたけど,ドキュメントには一応理由が書かれていた.絶賛開発中とは言え,もうそれなりに認知されてるフレームワークだし,必要な権限を洗い出すことぐらいはできそうとは思った.AdministratorAccess 権限が与えられてる IAM で aws configure をしてしまうと,無意識に全ての操作ができてしまうので,上に書いた通り,一時的に環境変数を export して使う方が良さそう.

  • 本番環境では権限を絞って欲しい
  • 開発が早すぎて,必要な権限を伝えられない
  • API Keys は安全に管理して欲しい

Note: In a production environment we recommend reducing the permissions to the IAM User which the Framework uses. Unfortunately, the frameworks functionality is growing so fast, we can’t yet offer you a finite set of permissions it needs. In the interim, ensure that your AWS API Keys are kept in a safe, private location. https://github.com/serverless/serverless/blob/master/docs/guide/provider-account-setup.md

まとめ

Serverless Framework の Quick Start を試してみた.ここまで簡単に Lambda + API Gateway の環境が構築できてしまうと,今までのオペレーションは何だったのかと思ってしまうし,かなり良いのではないかなと思った.実際にドキュメントを見ると,設定可能な項目がたくさんあるし,プラグインもあるらしい.あと,現在は Lambda だけだけど, Azure Functions / Google CloudFunctions / IBM OpenWhisk も対応する予定らしく,クラウドプラットフォームを横断的に使えるフレームワークを目指していることがわかった.スゴイ…!(2回目)

関連記事

この前の「実践 SERVERLESS 勉強会」で知った Apex / Chalice / Flourish など類似の管理ツールもあって,比較できるぐらいには使ってみようと思う.

kakakakakku.hatenablog.com

クラウドインフラ Podcast でも Serverless アーキテクチャの話があって,Serverless Framework のことも言及されていた.個人的にクラウドインフラ Podcast が超勉強になってて,数回聞き直したりしているほどのお気に入り!

cloudinfra.audio

あと秋に ServerlessConf Tokyo が開催される!絶対行こうと思ってる :)

tokyo.serverlessconf.io