Serverless Framework on AWS入門

触ることになったので新年からお勉強。

プロジェクトの作成

まずは、serverlessコマンド(slsコマンド)のインストール。
$ npm install -g serverless

$ serverless -v
Framework Core: 2.70.0
Plugin: 5.5.2
SDK: 4.3.0
Components: 3.18.1

$ sls -v
Framework Core: 2.70.0
Plugin: 5.5.2
SDK: 4.3.0
Components: 3.18.1

IAMユーザーの作成

serverlessコマンドでAWSに操作を行うためのIAMユーザーを作成。
IAMに与える権限は公式ガイドよさそうなサンプルがあったのでこれを拝借してポリシーを作成。

作成したIAMをserverlessコマンドに設定します。
要はAWS CLIでプロファイルを設定するのと同じことです。
# デフォルトプロファイルに設定
$ serverless config credentials --provider aws --key 1234 --secret 5678
# 名前を付けてプロファイル設定
$ serverless config credentials --provider aws --key 1234 --secret 5678 --profile serverless --overwrite
# 作成したプロファイルはAWS CLIと共用
$ aws s3 ls --profile serverless

サービスの作成

デプロイの単位であるサービスを作成します。
今回はPython3のテンプレートを使って作成します。

$ serverless create --template aws-python3 --name MyServerlessService --path MyServerlessService
$ cd MyServerlessService/
$ ls -a
.  ..  .npmignore  handler.py  serverless.yml
出来上がったサービスのファイルをそれぞれ見ていく。
「.npmignore」はNPMパッケージを公開するときに使うものなので割愛。
import json


def hello(event, context):
    body = {
        "message": "Go Serverless v1.0! Your function executed successfully!",
        "input": event
    }

    response = {
        "statusCode": 200,
        "body": json.dumps(body)
    }

    return response

    # Use this code if you don't use the http event with the LAMBDA-PROXY
    # integration
    """
    return {
        "message": "Go Serverless v1.0! Your function executed successfully!",
        "event": event
    }
    """
これはデプロイされるLambdaの実体になるファイルだろう。
# Welcome to Serverless!
#
# This file is the main config file for your service.
# It's very minimal at this point and uses default values.
# You can always add more config options for more control.
# We've included some commented out config examples here.
# Just uncomment any of them to get that config option.
#
# For full config options, check the docs:
#    docs.serverless.com
#
# Happy Coding!

service: MyServerlessService
# app and org for use with dashboard.serverless.com
#app: your-app-name
#org: your-org-name

# You can pin your service to only deploy with a specific Serverless version
# Check out our docs for more details
frameworkVersion: '2'

provider:
  name: aws
  runtime: python3.8
  lambdaHashingVersion: 20201221

# you can overwrite defaults here
#  stage: dev
#  region: us-east-1

# you can add statements to the Lambda function's IAM Role here
#  iam:
#    role:
#      statements:
#        - Effect: "Allow"
#          Action:
#            - "s3:ListBucket"
#          Resource: { "Fn::Join" : ["", ["arn:aws:s3:::", { "Ref" : "ServerlessDeploymentBucket" } ] ]  }
#        - Effect: "Allow"
#          Action:
#            - "s3:PutObject"
#          Resource:
#            Fn::Join:
#              - ""
#              - - "arn:aws:s3:::"
#                - "Ref" : "ServerlessDeploymentBucket"
#                - "/*"

# you can define service wide environment variables here
#  environment:
#    variable1: value1

# you can add packaging information here
#package:
#  patterns:
#    - '!exclude-me.py'
#    - '!exclude-me-dir/**'
#    - include-me.py
#    - include-me-dir/**

functions:
  hello:
    handler: handler.hello
#    The following are a few example events you can configure
#    NOTE: Please make sure to change your handler code to work with those events
#    Check the event documentation for details
#    events:
#      - httpApi:
#          path: /users/create
#          method: get
#      - websocket: $connect
#      - s3: ${env:BUCKET}
#      - schedule: rate(10 minutes)
#      - sns: greeter-topic
#      - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000
#      - alexaSkill: amzn1.ask.skill.xx-xx-xx-xx
#      - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx
#      - iot:
#          sql: "SELECT * FROM 'some_topic'"
#      - cloudwatchEvent:
#          event:
#            source:
#              - "aws.ec2"
#            detail-type:
#              - "EC2 Instance State-change Notification"
#            detail:
#              state:
#                - pending
#      - cloudwatchLog: '/aws/lambda/hello'
#      - cognitoUserPool:
#          pool: MyUserPool
#          trigger: PreSignUp
#      - alb:
#          listenerArn: arn:aws:elasticloadbalancing:us-east-1:XXXXXX:listener/app/my-load-balancer/50dc6c495c0c9188/
#          priority: 1
#          conditions:
#            host: example.com
#            path: /hello

#    Define function environment variables here
#    environment:
#      variable2: value2

# you can add CloudFormation resource templates here
#resources:
#  Resources:
#    NewResource:
#      Type: AWS::S3::Bucket
#      Properties:
#        BucketName: my-new-bucket
#  Outputs:
#     NewOutput:
#       Description: "Description for the output"
#       Value: "Some output value"
でこっちがサービスの設定ファイル。

サービスのデプロイ

設定を紐解きつつ「serverless.yml」を修正してみる。
# サービス名
service: MyServerlessService

# Serverlessフレームワークのバージョン
frameworkVersion: '2'

# サービスのプロバイダ
provider:
  name: aws
  runtime: python3.8
  lambdaHashingVersion: 20201221
  stage: api # API Gatewayのステージ名
  region: ap-northeast-1 # デプロイリージョン
  # Lambdaが使用するIAMロール
  iamRoleStatements:
    - Effect: "Allow"
      Action:
      - "s3:ListBucket"
      Resource: { "Fn::Join" : ["", ["arn:aws:s3:::", { "Ref" : "ServerlessDeploymentBucket" } ] ]  }
    - Effect: "Allow"
      Action:
      - "logs:CreateLogGroup"
      - "logs:CreateLogStream"
      - "logs:PutLogEvents"
      Resource: "*"

# Lambdaの設定
functions:
  hello:
    handler: handler.hello
    timeout: 29
    events:
      # API Gateway(REST)の紐づけ
      - http:
          path: /hello
          method: get
          integration: lambda_proxy # Lambdaプロキシ統合の利用
    # Lambda環境変数
    environment:
      TZ: Asia/Tokyo

デプロイの実行

defaultプロファイルで実行しないよう、プロファイルを指定して実行。
# --verboseオプションで詳細なデプロイの経過が見れる
$ serverless deploy --verbose --aws-profile serverless
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Ensuring that deployment bucket exists
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service MyServerlessService.zip file to S3 (641 B)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
~~~中略~~~
CloudFormation - UPDATE_COMPLETE_CLEANUP_IN_PROGRESS - AWS::CloudFormation::Stack - MyServerlessService-api
CloudFormation - UPDATE_COMPLETE - AWS::CloudFormation::Stack - MyServerlessService-api
Serverless: Stack update finished...
Service Information
service: MyServerlessService
stage: api
region: ap-northeast-1
stack: MyServerlessService-api
resources: 11
api keys:
  None
endpoints:
  GET - https://XXXXXXXX.execute-api.ap-northeast-1.amazonaws.com/api/hello
functions:
  hello: MyServerlessService-api-hello
layers:
  None

# 得られたエンドポイントにアクセス
$ curl https://XXXXXXXX.execute-api.ap-northeast-1.amazonaws.com/api/hello
{"message": "Go Serverless v1.0! Your function executed successfully!", "input": {"resource": "/hello", "path": "/hello"~~~
CloudFormationスタックが作成され諸々必要なリソースを作成してくれます。
更新するときも同じコマンドを再実行すればOK。

サービスの削除

$ serverless remove --verbose --aws-profile serverless
Serverless: Getting all objects in S3 bucket...
Serverless: Removing objects in S3 bucket...
Serverless: Removing Stack...
Serverless: Checking Stack delete progress...
CloudFormation - DELETE_IN_PROGRESS - AWS::CloudFormation::Stack - MyServerlessService-api
~~中略~~~
CloudFormation - DELETE_IN_PROGRESS - AWS::Logs::LogGroup - HelloLogGroup
Serverless: Stack delete finished...
Serverless: Stack delete finished...
不要になったサービスの削除もコマンド一つで行えます。
内部的にはCloudFormationスタックを削除しているだけなのでServerlessコマンド外で作ったものに関しては関与できなさそうです。
取り急ぎ基本的なことは理解できたので、ここからさらに複雑なものを作っていくにはどうすればいいかってところを学んでいきたいと思います。