0
Help us understand the problem. What are the problem?

posted at

updated at

serverlessFWにこんにちは serverlessでローカル上でAWS Lambdaを起動・呼び出してみる

はじめに

AWS SAMを使ってLambda(GO)のCICDをGitHub Actionsで構築でサーバーレスアプリケーションを構築するフレームワークとしてAWS SAMは触った事があったが、serverlessは触った事がなかったので、serverlessでHello World(Node.js)をやってみたので備忘録を残す。

やってみた事としては、以下の3つ。

  • cli上でseverlessコマンドからHello World的なLambda関数を作成するseverless.ymlの作成
  • ローカル環境でそのLambda関数を実行し、Lambdaを呼び出し
  • 【おまけ】実際にAWS環境にDeploy

ソースコード全体は以下。

はじめてのserverless

プロジェクトを作成

今回はVirtualboxのCentOS7.9上でserverlessのセットアップを行った(環境構築方法の詳細はWindows上にLinux(CentOS)のWebアプリ開発環境をvirtualboxで構築するを参照)。

[root@localhost serverless]# yarn init
yarn init v1.22.17
question name (serverless):
question version (1.0.0):
question description: serverlessFW-learning
question entry point (index.js):
question repository url:
question author (Yuta Katayama xxx@xxx.com>): yuta-katayama-23
question license (MIT):
question private:
success Saved package.json
Done in 36.32s.

serverlessをインストール

Get started with Serverless Framework Open Source & AWSにはグローバルにインストールする方法が書かれているが、グローバルだと他の人がプロジェクトを利用する際に前提条件が増えるので、素直にpackage.jsonで管理する方法を取る。

yarn add --dev serverless

こうする事で、npxコマンドでnpx sls --versionのようにserverless(省略形でsls)コマンドが使えるようになる。

※npxはローカルインストールしたコマンドを実行するために使うもの(この辺りはnpxコマンドとは? 何ができるのか?などを参照)

npx serverlessでserverlessプロジェクトを作成

公式に書かれている通り、serverlessコマンドを実行(ローカルパッケージの場合にはnpx serverless)する事でCLI上で対話をしながらプロジェクトを作成できる。

[root@localhost serverless]# npx serverless

Creating a new serverless project

? What do you want to make? AWS - Node.js - Starter
? What do you want to call this project? aws-node-project

✔ Project successfully created in aws-node-project folder

? Do you want to login/register to Serverless Dashboard? No

? No AWS credentials found, what credentials do you want to use? Skip

You can setup your AWS account later. More details available here: http://slss.io/aws-creds-setup

上記のようにする事でserverless.ymlやhandler.jsが作成され、後はdeployすればLambda関数が作成できる状態が作られる。

※上記においてAWSのアカウント設定をskipしているが、これはAWS環境に実際にDeployする時になってから改めて設定すれば問題ない(serverlessはUsing AWS Access KeysSetup with the aws-cliに書かれている通り、aws-cliの設定内容(実態は~/.aws以下のconfigとcredential)からユーザ情報を読み取ってくれる)

※今回のように既にプロジェクトを作成済みの場合、createコマンドを用いてserverlessプロジェクトを作成する事もできる。createコマンドの場合、serverless.yamlやhandler.jsの出力先を--pathオプションで指定できるので、既にプロジェクトがある時には便利だろう。

[root@localhost serverless]# npx serverless create --template aws-nodejs --path myService

✔ Project successfully created in "myService" from "aws-nodejs" template (6s)

[root@localhost serverless]# cd myService/
[root@localhost myService]# tree -a
.
├── .gitignore
├── handler.js
└── serverless.yml

ローカルでserverlessアプリケーションを実行できるようにする

今回はserverlessアプリケーションと言ってもLambda単体しかないのでLambdaをローカルで実行できるようにする。

方法は簡単で公式のPluginsにも書かれているserverless-offlineをインストールし、serverless.yamlに以下のように追記するだけ。

service: aws-node-project

frameworkVersion: '3'

provider:
  name: aws
  runtime: nodejs14.x

# 以下を追記
plugins:
  - serverless-offline

functions:
  hello:
    handler: handler.hello

後は、npx sls offlineコマンドを叩けばLambdaをローカルで起動できる。

[root@localhost serverless]# npx sls offline

Starting Offline at stage dev (us-east-1)

Offline [http for lambda] listening on http://localhost:3002
Function names exposed for local invocation by aws-sdk:
           * hello: aws-node-project-dev-hello
(λ: hello) RequestId: cl1suu1g00000f0pvg2b8cg52  Duration: 4.05 ms  Billed Duration: 5 ms

※上記では実際にLambda関数を実行した際のログも出でいる((λ: hello) RequestId: cl1suu1g00000f0pvg2b8cg52 Duration: 4.05 ms Billed Duration: 5 ms)が、それは次の章で見ていく。

※serverless-offlineを入れずとも、AWS - Invoke Localに書かれているように、以下のようにローカルでLambdaを実行する事は可能。API Gatewayなど他のAWSサービスと連携するサーバーレスアプリケーションをローカルでエミュレーションするにはserverless-offlineが必要になる。

study@localhost:~/workspace/serverless (main *)
$ yarn invoke:local
yarn run v1.22.19
$ npx sls invoke local --function hello
{
    "statusCode": 200,
    "body": "{\n  \"message\": \"Go Serverless v3.0! Your function executed successfully!\",\n  \"input\": \"\"\n}"
}
Done in 3.00s.

aws-cliでLambdaを実行する(呼び出す)

まずはaws-cliのインストールが必要なのでAWS CLI の最新バージョンをインストールまたは更新します。を参照しインストールする。

あとはinvokeに書かれているようにコマンドを実行するだけ。

[root@localhost serverless]# aws lambda invoke --payload '{ "name": "Bob" }' --endpoint-url http://localhost:3002 --function-name aws-node-project-dev-hello --cli-binary-format raw-in-base64-out response.json
{
    "StatusCode": 200
}
response.json
{
	"statusCode": 200,
	"body": "{\n  \"message\": \"Go Serverless v3.0! Your function executed successfully!\",\n  \"input\": {\n    \"name\": \"Bob\"\n  }\n}"
}

※2点、注意点があるのでそれについて補足する。

aws-cliを利用する際には、ダミーでもAWSのクレデンシャル情報が必要

AWSクレデンシャルがない状態でコマンドを実行すると、以下のようにエラーになる。

[root@localhost serverless]# aws lambda invoke --payload '{ "name": "Bob" }' --endpoint-url http://localhost:3002 --function-name aws-node-project-dev-hello --cli-binary-format raw-in-base64-out response.json

You must specify a region. You can also configure your region by running "aws configure".

という事でダミーでもいいので以下のようにしてAWSクレデンシャルを設定する。

[root@localhost serverless]# aws configure
AWS Access Key ID [None]: dumy
AWS Secret Access Key [None]: dumy
Default region name [None]: ap-northeast-1
Default output format [None]: json

※後々AWS環境にDeployする(serverless.ymlに基づいてAWS環境にLambdaを作成する)際には、aws-cliのprofileを切り替えて本物のクレデンシャルを利用する必要があるので注意(詳細はAWS CLI の名前付きプロファイルを参照)

aws-cli v2 から、cliの引数のパラメータはbase64エンコードされた文字列を渡す必要がある

詳細はAWS CLI バージョン 2 では、デフォルトでバイナリパラメータが base64 でエンコードされた文字列として渡されるようになりましたを参照。

やる事としては、cliのコマンド実行時に--cli-binary-format raw-in-base64-outオプションを追加する or ~/.aws/configcli_binary_format=raw-in-base64-outを追記すればいい。
ちなみに、何もしないで実行すると以下のようにエラーになる。

[root@localhost serverless]# aws lambda invoke --payload '{ "name": "Bob" }' --endpoint-url http://localhost:3002 --function-name aws-node-project-dev-hello response.json

Invalid base64: "{ "name": "Bob" }"

まとめとして

今回はserverlessコマンドで初期プロジェクトを作成して、それをローカルで起動・実行するだけをやってみた。今後はNode.jsでもES6で実装したり、ESLintを導入したり、色々やってみたいと思う。

おまけ

AWS環境にDeployしてみる

aws-cliのクレデンシャルを追加で設定する

上記のaws-cliを利用する際には、ダミーでもAWSのクレデンシャル情報が必要ではダミーの実際のAWSアカウントで払い出したものではないクレデンシャル情報を設定していたので、今度は本物のAWSアカウントのクレデンシャル情報を設定する。

やり方は簡単で、公式の名前付きプロファイルを使用するに書かれている通り、aws configure --profile {新しく作成するprofile名}というコマンドで設定できる。

追加のプロファイルを設定するには、--profile オプションで aws configure を使用する

[root@localhost serverless]# aws configure --profile yuta_katayama
AWS Access Key ID [None]: ******************** ← マスクのために*で表記している
AWS Secret Access Key [None]: **************************************** ← マスクのために*で表記している
Default region name [None]: ap-northeast-1
Default output format [None]: json

設定すると以下のように新しくprofileが作成できている事が確認できる。

[root@localhost serverless]# aws configure list-profiles
default
yuta_katayama

profileを切り替えるには名前を指定されたプロファイルを使用するに書かれているようにLinuxであれば環境変数を設定すればいい。設定後にaws configure listを実行すれば、現在aws-cliで使用されているクレデンシャル情報が確認できる。

[root@localhost serverless]# export AWS_PROFILE=yuta_katayama
[root@localhost serverless]# aws configure list
      Name                    Value             Type    Location
      ----                    -----             ----    --------
   profile            yuta_katayama              env    ['AWS_PROFILE', 'AWS_DEFAULT_PROFILE']
access_key     ****************CQID shared-credentials-file    
secret_key     ****************NJo8 shared-credentials-file    
    region           ap-northeast-1      config-file    ~/.aws/config

npx sls deployでAWS環境にDeployしてみる

公式のAWS - deployに書かれている通り、npx sls deploy --region ap-northeast-1コマンドを実行するだけ(--regionオプションがないとデフォルトのus-east-1にDeployされる)。実行すると以下のようになる。

[root@localhost serverless]# yarn deploy
yarn run v1.22.17
$ npx sls deploy --region ap-northeast-1

Deploying aws-node-project to stage dev (ap-northeast-1)

✔ Service deployed to stack aws-node-project-dev (185s)

functions:
  hello: aws-node-project-dev-hello (108 kB)

Toggle on monitoring with the Serverless Dashboard: run "serverless"
Done in 189.37s.

この状態になると実際にAWS環境のLambdaを呼び出す事もできる。
※注意として--functionオプションに渡すLambda関数名はservrless.ymlに定義している関数名を指定する必要がある

[root@localhost serverless]# npx serverless invoke --function hello --data "hello world" --stage dev --region ap-northeast-1
{
    "statusCode": 200,
    "body": "{\n  \"message\": \"Go Serverless v3.0! Your function executed successfully!\",\n  \"input\": \"hello world\"\n}"
}

npx sls deployでやっている事

AWS - deploy

The sls deploy command deploys your entire service via CloudFormation.(sls deployコマンドは、CloudFormation経由でサービス全体をデプロイします。)

と書かれているように、裏で実行されている事としては、CloudFormationのスタックを作成しAWSサービスの各リソースを構築するという事が行われている。

実際にマネジメントコンソールからCloudFormationのスタックを確認してみると、以下のようにserverlessによって作成されたスタックが確認できる。
image.png

また、スタックの中を見てみると以下の画像のようにLambda関数以外にも複数のリソースが自動で作成されている事が分かる。
image.png

これがserverlessフレームワークがサーバレスアプリケーションの構築のためのフレームワークと言われる所以。つまり、アプリケーションとして成り立つために必要になるログ出力など最低限のものを自動で構築してくれ、一般的にLambdaのDeployというとS3にzipを格納してそれをLambda関数に適用させるがそれに必要になるS3も自動で作成してくれる。
この辺りが便利になっている部分だろう。

ちなみに、自分でLambda関数のDeployをするためにCloudFormationを書くとCloud Formationを使ってGolang製LambdaのCICDをGitHub Actionsで構築でやってみたように色々AWSリソースの定義を書く必要があり大変。

※今回serverlessの方で自動的に作成してくれたCloudFormationのtemplate.yamlの全体としては以下(これだけの定義をserverless.yml
に書いたたった数行でできる)。

template.yaml(クリックで開きます)
template.yaml
AWSTemplateFormatVersion: 2010-09-09
Description: The AWS CloudFormation template for this Serverless application
Resources:
  ServerlessDeploymentBucket:
    Type: 'AWS::S3::Bucket'
    Properties:
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
  ServerlessDeploymentBucketPolicy:
    Type: 'AWS::S3::BucketPolicy'
    Properties:
      Bucket: !Ref ServerlessDeploymentBucket
      PolicyDocument:
        Statement:
          - Action: 's3:*'
            Effect: Deny
            Principal: '*'
            Resource:
              - !Join 
                - ''
                - - 'arn:'
                  - !Ref 'AWS::Partition'
                  - ':s3:::'
                  - !Ref ServerlessDeploymentBucket
                  - /*
              - !Join 
                - ''
                - - 'arn:'
                  - !Ref 'AWS::Partition'
                  - ':s3:::'
                  - !Ref ServerlessDeploymentBucket
            Condition:
              Bool:
                'aws:SecureTransport': false
  HelloLogGroup:
    Type: 'AWS::Logs::LogGroup'
    Properties:
      LogGroupName: /aws/lambda/aws-node-project-dev-hello
  IamRoleLambdaExecution:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - 'sts:AssumeRole'
      Policies:
        - PolicyName: !Join 
            - '-'
            - - aws-node-project
              - dev
              - lambda
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - 'logs:CreateLogStream'
                  - 'logs:CreateLogGroup'
                Resource:
                  - !Sub >-
                    arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/aws-node-project-dev*:*
              - Effect: Allow
                Action:
                  - 'logs:PutLogEvents'
                Resource:
                  - !Sub >-
                    arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/aws-node-project-dev*:*:*
      Path: /
      RoleName: !Join 
        - '-'
        - - aws-node-project
          - dev
          - !Ref 'AWS::Region'
          - lambdaRole
  HelloLambdaFunction:
    Type: 'AWS::Lambda::Function'
    Properties:
      Code:
        S3Bucket: !Ref ServerlessDeploymentBucket
        S3Key: >-
          serverless/aws-node-project/dev/1649769206618-2022-04-12T13:13:26.618Z/aws-node-project.zip
      Handler: handler.hello
      Runtime: nodejs14.x
      FunctionName: aws-node-project-dev-hello
      MemorySize: 1024
      Timeout: 6
      Role: !GetAtt 
        - IamRoleLambdaExecution
        - Arn
    DependsOn:
      - HelloLogGroup
  HelloLambdaVersioncUWuCRlBIs9VT7ZlYopUEw3ADBvd3GjqQ5P9jF0YN8:
    Type: 'AWS::Lambda::Version'
    DeletionPolicy: Retain
    Properties:
      FunctionName: !Ref HelloLambdaFunction
      CodeSha256: ragOnPdr8ZNOSxsHRpByhvtsKyMt7qYT6H3gJcsuBoM=
Outputs:
  ServerlessDeploymentBucketName:
    Value: !Ref ServerlessDeploymentBucket
    Export:
      Name: sls-aws-node-project-dev-ServerlessDeploymentBucketName
  HelloLambdaFunctionQualifiedArn:
    Description: Current Lambda function version
    Value: !Ref HelloLambdaVersioncUWuCRlBIs9VT7ZlYopUEw3ADBvd3GjqQ5P9jF0YN8
    Export:
      Name: sls-aws-node-project-dev-HelloLambdaFunctionQualifiedArn

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
0
Help us understand the problem. What are the problem?