Blog

Serverless Frameworkの使い方まとめ

概要

Serverless Framework はServerless Applicationを構成管理デプロイするためのツールです。この記事ではその使い方をまとめています。

※ 2022.4.17 – Serverless Framework v3 の変更内容を反映しました。

インストール

Node.jsのインストール

ServerlessはNode.jsで作られたCLIツールです。
なので、あなたのマシンにNode.jsをインストールする必要があります。Node.jsの公式サイトからあなたのPCにNode.jsをインストールしましょう。ServerlessはNode.jsのv12以上が必要となります。

Serverlessのインストール

Serverlessはnpmのパッケージとして公開されています。ターミナルを開いて、npm install -g serverlessでインストールが完了します。

正しくインストールされているかは、serverless --versionにて確認しましょう。以下のようにバージョンが表示されれば正しくインストールされています。

$ serverless --version
Framework Core: 3.14.0
Plugin: 6.2.1
SDK: 4.3.2

プロバイダーアカウントのセットアップ

プロバイダーのアカウントをセットアップします。プロバイダーとは、要はどのクラウドサービスを使用してServerlessを動かすかということです。AWS, GCP, Azure, IBM Cloudなど様々なクラウドプロバイダーに対応しています。
ここではAWSにスポットを当てて解説します。

AWS account Setup
ここにAWSアカウントのセットアップ方法が書いていますが、要は以下を実施すればOKです。

  • Serverless用のIAMユーザを発行
  • IAMユーザにAdministratorAccessの管理ポリシーを与える

基本的にはこのアカウントを使って、ServerlessのデプロイやLambdaファンクションのローカルからの実行を行います。
また、IAMロールを制限したい場合は、serverless-puresec-cliNarrowing the Serverless IAM Deployment Policyを参考に独自でセットアップしましょう。

サービスの作成

Serverlessはサービスという単位で実行環境を作っていきます。
まずはサービスを開設してみましょう。

AWSをプロバイダーとしてNode.jsでサービスを開設する場合は以下の手順になります。

$ serverless create --template aws-nodejs --name my-special-service --path my-special-service

すると、my-special-serviceのディレクトリが作られ、その配下に以下のファイルが出来ているはずです。
これでサービスの作成は完了です。

  • serverless.yml
  • handler.js

また、サービス用のテンプレートですが、以下の言語用のテンプレートが用意されています。

  • aws-nodejs
  • aws-nodejs-typescript
  • aws-nodejs-ecma-script
  • aws-python
  • aws-python3
  • aws-kotlin-jvm-maven
  • aws-kotlin-jvm-gradle
  • aws-kotlin-nodejs-gradle
  • aws-groovy-gradle
  • aws-java-maven
  • aws-java-gradle
  • aws-scala-sbt
  • aws-csharp
  • aws-fsharp

どの言語でLambdaを動かしたいかによってテンプレートを変更してあげましょう

serverless.yml

serverless.ymlは各サービス全体の設定を行うためのファイルです。
AWSでは、以下のような設定が可能です。

  • サービス内のLambdaファンクション群の設定
  • Lambdaに設定されるIAMロールの設定
  • デプロイ時にどのファイル/ディレクトリを含めるか/含めないかの設定
  • 使用するプラグインの定義
  • Lambdaファンクションごとのトリガーとなるイベントの定義
  • Lambdaファンクションが他のAWSリソースを連携する場合は、そのIAMを含めた定義

まずは、serverless.ymlの設定をしてあげましょう。もろもろサービスを実行するために必要な設定はすべてこのファイルで行います。

handler.js

これはファンクションを定義するためのスケルトンとして生成されるファイルです。これを参考に実際のファンクションのプログラムを作っていきます。

event.json

ServerlessのCLIでファンクションを実行する際に入力値となるデータを定義するファイルです。Lambdaファンクション内でevent変数に展開されます。

※2022年4月時点ではテンプレートに event.json が含まれないので、後述しますがサンプルで event.json を利用する場合別途作成しておく必要があります。

Githubに上がった既存のサービスをインポートする場合

既にGithubに上がっている既存サービスの開発を行う場合は、そのままローカルにインポートしてServerlessをセットアップしたいと思います。その際はserverless install -u [GITHUB URL OF SERVICE]でインポート可能です。

サービスのデプロイ

次に作ったサービスをプロバイダー上へデプロイしましょう。

serverless deploy -vでデプロイは開始されます。-vオプションを付けるとverboseというモードでデプロイが実施され、途中経過がターミナル上で確認できます。

また、Serverlessのデプロイにはstageという概念が導入されています。いわゆる本番環境とテスト環境といった環境をstageという単位で切り分けています。

Serverlessはデフォルトで、devというステージかつus-east-1リージョンにデプロイされるようになっています。
もしこれを変更したければ serverless.ymlに以下のように設定します。

serverless.yml 

service: service-name
provider:
  name: aws
  stage: beta
  region: us-west-2

これでbetaステージのus-west-2リージョンにデプロイされるようになります。

serverless.ymlで定義したものと異なるサービス及びリージョンへのデプロイ

また、deployコマンドはデプロイするstageやregionを引数で指定することも出来ます。

productionステージのap-northeast-1リージョンに上げたければ、serverless deploy --stage production --region ap-northeast-1とすれば、引数通りの指定でデプロイが実施されます。

ファンクション単位のデプロイ

serverless deploy -vはサービス全体のデプロイを行います。ファンクションの一部を修正したなどのケースで、サービス全体がデプロイされるのは大変です。
Serverlessはファンクション単位でのデプロイもserverless deploy function -f <yourfunction>にて可能です。

ファンクションの実行

デプロイしたファンクションを実行してみましょう。

event.json の作成

ServerlessのCLIでファンクションを実行する際に入力値となるデータを定義したファイルを作成します。Lambdaファンクション内でevent変数に展開されます。

{
  "key1": "value1",
  "key2": "value2",
  "key3": "value3"
}

helloファンクションの実行

サービス作成時にhandler.jsとして作成されるhelloファンクションを実行する場合を考えてみましょう。serverless invoke --function hello -p event.jsonで実行できます。

実行時にログを表示させる

ファンクションの実行時にログを見たいを思います。Lambdaの実行時にCloudwatchのログが同時に見れれば便利ですよね? Serverlessはそれにも対応しています。serverless invoke --function Yourfunction -p event.json --logでlogオプションを付与するとこで以下の通りログが確認できます。

$ serverless invoke --function hello -p event.json --log
{
    "statusCode": 200,
    "body": "{\n  \"message\": \"Go Serverless v1.0! Your function executed successfully!\",\n  \"input\": {\n    \"key1\": \"value1\",\n    \"key2\": \"value2\",\n    \"key3\": \"value3\"\n  }\n}"
}
--------------------------------------------------------------------
START
END RequestId: 4767edc0-b1cc-4dcb-9128-2474967ec40a
END Duration: 2.09 ms Memory Used: 56 MB

body の中身をパースすると event.json の内容が含まれた以下のオブジェクトに展開されます。

{
  message: 'Go Serverless v1.0! Your function executed successfully!',
  input: {
    key1: 'value1',
    key2: 'value2',
    key3: 'value3'
  }
}

各ファンクションのログの履歴を確認する

後からファンクション毎のログを確認する場合はserverless logs --function Yourfunctionというコマンドを実行します。

すると以下の通りログがリストで表示されます。

$ serverless logs --function hello
START
END Duration: 2.47 ms (init: 167.55 ms) Memory Used: 55 MB
START
2022-04-16 02:16:58.039	{ key1: 'value1', key2: 'value2', key3: 'value3' }
END Duration: 4.56 ms (init: 155.54 ms) Memory Used: 55 MB

※確認のため handler.js 関数内で console.log(event) のようにログ出力を追加しています。

他のAWSリソースにIAMでポリシーを設定する

実行時に他のAWSリソースを参照する場合は、serverless.yml内のprovider.iamを使用して必要なポリシーを設定します。

DynamoDBのexampleテーブルに対する全権限を付与する場合は以下のような設定をします。

# serverless.yml 
provider: 
  iam:
    role:
      statements:
        - Effect: "Allow"
          Action:
            - "dynamodb:*"
          Resource:
            - arn:aws:dynamodb:us-east-1:*:table/example

イベントトリガーの設定

Serverless Frameworkはファンクションにイベントを設定することでイベント駆動のアーキテクチャを実現することをサポートしています。

イベントにはHTTPリクエスト(AWSではAPI Gateway)やS3 bucket、スケジュールイベントなど、設定を行うことが出来ます。

今回は、AWS Gatewayを使用したhttpイベントの設定を例にあげます。

HTTP eventを追加する

greetというパスでhttpリクエストを受けた場合にhelloファンクションを実行させたい場合は以下のようにserverless.ymlを設定します。

# serverless.yml
functions:
  hello:
    handler: handler.hello
    events:
      # HTTP API endpoint (API Gateway v2)
      - httpApi:
          path: /greet
          method: GET
      # REST API endpoint (API Gateway v1)
      - http:
          path: /greet
          method: GET

この設定を行い、serverless deployを実行してデプロイに成功すれば以下のようなEndpointがターミナル上に表示されるはずです。 上が REST API、下が HTTP API のエンドポイントです。

endpoints:
  GET - https://xxx.execute-api.us-east-1.amazonaws.com/dev/greet
  GET - https://yyy.execute-api.us-east-1.amazonaws.com/greet

これに対して実際にリクエストを送ってみるとLambdaで定義した返り値が返ってくるはずです。

$ curl https://xxx.execute-api.us-east-1.amazonaws.com/dev/greet
$ curl https://yyy.execute-api.us-east-1.amazonaws.com/greet

https://serverless.com/framework/docs/providers/aws/events/apigateway/
API Gatewayを使ったhttpイベントは様々なパラメータを設定することが可能です。詳しくは公式のドキュメントを確認してみてください。個人的にはlambda-proxyの機能が、手軽にLambdaからステータスコードの設定が出来て便利だと感じています。

また、現段階でAWSをプロバイダーとして以下のイベントが設定可能です。

  • API Gatewayを使用したhttpイベント
  • S3の操作をトリガーとしたイベント
  • Lambdaのスケジュールイベント
  • SNSの通知をトリガーとしたイベント
  • DynamoDB / Kinesis Streamsイベント
  • Allexa Skill / Alexa Smart Homeイベント
  • AWS IoTイベント
  • CloudWatch Event
  • CloudWatch Log
  • Cognito User Poolのトリガーファンクション
  • SQS

https://serverless.com/framework/docs/providers/aws/events/

Serverlessで使用できる変数

Serverless Frameworkはserverless.yml内で変数を定義することで柔軟なサービス設定が可能となっています。

環境変数を参照する

環境変数を参照する場合は${env:SOME_VAR}というシンタックスをserverless.ymlに記述します。以下がその例です。serverless.yml

service: new-service
provider: aws
functions:
  hello:
      name: ${env:FUNC_PREFIX}-hello
      handler: handler.hello
  world:
      name: ${env:FUNC_PREFIX}-world
      handler: handler.world

この例ではあなたのPC内のFUNC_PREFIXという環境変数を参照するようになりました。

CLIオプションを参照する

serverlessコマンド実行時のオプションを参照させたい場合は${opt:SOME_VAR}というシンタックスをserverless.ymlに記述します。以下がその例です。

serverless.yml

service: new-service
provider: aws
functions:
  hello:
      name: ${opt:stage}-hello
      handler: handler.hello
  world:
      name: ${opt:stage}-world
      handler: handler.world

serverless deploy --stage devとした場合は、${opt:stage}はdevが返ります。また、serverless deploy --stage productionとした場合は、productionが返ります。

自身で定義した変数を参照する

serverless.yml内で自身で定義した変数を参照させることも出来ます。${self:someProperty}というシンタックスで定義可能です。以下がその例です。

serverless.yml 

service: new-service
provider: aws
custom:
  globalSchedule: rate(10 minutes)

functions:
  hello:
      handler: handler.hello
      events:
        - schedule: ${self:custom.globalSchedule}
  world:
      handler: handler.world
      events:
        - schedule: ${self:custom.globalSchedule}

他のファイルで定義した変数を参照する

serverless.ymlとは別で定義したファイルを読み込んで、その変数を参照することも出来ます。${file(../myFile.yml):someProperty}というシンタックスでserverless.ymlに定義してください。以下がその例になります。

myCustomFile.yml 

globalSchedule: rate(10 minutes)
serverless.yml 

service: new-service
provider: aws
custom: ${file(../myCustomFile.yml)} # You can reference the entire file
functions:
  hello:
      handler: handler.hello
      events:
        - schedule: ${file(../myCustomFile.yml):globalSchedule} # Or you can reference a specific property
  world:
      handler: handler.world
      events:
        - schedule: ${self:custom.globalSchedule} # This would also work in this case

変数のネスト

以下のように変数をネストさせることも可能です。

serverless.yml 

service: new-service
provider: aws
custom:
  myFlexibleArn: ${env:${opt:stage}_arn}

functions:
  hello:
      handler: handler.hello

変数の上書き

以下のような記述を例にあげます。

serverless.yml

service: new-service
provider:
  name: aws
  stage: dev
custom:
  myStage: ${opt:stage, self:provider.stage}

functions:
  hello:
      handler: handler.hello

この場合にserverless deployを発行したとします。この時、opt:stageは定義されていないため、self:provider.stageに定義されているdevが参照されます。

serverless deploy --stage productionが指定されている場合は、opt:stageが定義されているためopt:stageが優先して参照されます。

サービスの削除

あなたのつくったサービス内で、serverless removeを実行してください。これでサービスがプロバイダー上から削除されます。

Tips

アクセスキーなど見せたくない情報をserverless.ymlで扱う

SSM秘匿パラメータの機能を使いましょう
https://serverless.com/framework/docs/providers/aws/guide/variables/#reference-variables-using-the-ssm-parameter-store