AWS
lambda
ServerlessFramework
サーバーレスアーキテクチャ

サーバーレス初心者でも簡単に出来た!「Serverless Framework」を使って「AWS Lambda」を試してみる

2018年現在、 既に「サーバレスアーキテクチャ」を無視できない時代になっている ので、今更ながら実際に手を動かして試してみました。 自身のように 「『サーバレス』って聞いたことはあるけど、ハードルが高そうだし、実際に何ができて、どんなメリットがあるかよく分からない」 といった方々にとって、本稿が理解のきっかけになれば幸いです。

本稿では以下の流れに沿って話を進めます。

  • 「サーバーレスアーキテクチャ」とは
  • サーバレスアーキテクチャは「現代版CGI」である
  • サーバーレス化によるメリット
  • サーバレスが効果的なシステム
  • 「AWS Lambda」とは
  • 「Serverless Framework」とは
  • ハンズオン「Serverless FrameworkでREST APIを作ってみよう」

「サーバーレスアーキテクチャ」とは

サーバーレスアーキテクチャとは、関数が呼び出された時のみ、 その関数を実行するための「コンテナ」と呼ばれる仮想OS環境を生成し、関数の実行後はコンテナを破棄することによって 常駐プロセスを必要としないアプリケーション実行環境を実現すること です。

またサーバレスアーキテクチャによってサーバーの管理を不要とするマネージドサービスのことを 「FaaS(Function as a Service)」 と呼び 、代表的なものとして「AWS Lambda」、「Google Cloud Functions」、「Apache OpenWhisk」、「Azure Functions」があります。

サーバレスアーキテクチャは「モダンCGI」である

サーバレスアーキテクチャと似たような従来技術として「CGI(Common Gateway Interface)」があります。このことから、サーバレスアーキテクチャのことを 「モダンCGI(現代版CGI)」 と呼ぶ人もいるようです。

CGIではプログラムの実行が終了するとプロセスが破棄されます。しかし、CGIはプログラムを実行する度にPerlなどのインタプリタのプロセスを起動する必要があり、コンピュータリソースに対してオーバーヘッドが大きいことが課題でした。一方でサーバレスアーキテクチャはコンテナによってコンピュータリソースの柔軟なスケーリングを可能とし、この課題を解決しています。

またCGIではWebサーバーとの入出力を環境変数や標準入出力を通じて行われていたのに対し、サーバーレスアーキテクチャではアプリケーションとの入出力をJSONで行うことが特徴です。

アーキテクチャ CGI サーバーレス
実行単位 プロセス コンテナ
入出力 環境変数、標準入出力 JSON

(『What Is "Serverless"? An Alternative Take – Hassy Veldstra – Medium』より)

サーバーレス化によるメリット

ではなぜサーバレスアーキテクチャが注目されているのでしょうか。サーバーレスアーキテクチャが普及した最大の理由は以下の二つにあります。

  • サーバーの管理が不要となる
    アプリケーションの実行環境はマネージドサービスが全て自動で用意してくれるため、開発者はフロントエンドの開発のみに集中することができる。
  • 処理のコストを抑えることができる
    関数を実行していない状態ではコンピュータリソースを一切使用しないため、従来のオンプレミスサーバーやAmazon EC2などのIaaSよりもコストを抑えることができる。

AWSではその他のメリットとして、柔軟なスケーリングや高可用性を挙げています。詳しくは『サーバーレスコンピューティング(サーバー管理が不要なアプリケーション構築)|AWS』をご覧ください。

サーバレスが効果的なシステム

上記のようなメリットのあるサーバレスアーキテクチャですが、現時点で全てのシステムに適用できる「銀の弾丸」ではありません。サーバレスアーキテクチャが効果的だと考えられるシステムには以下のようなものが挙げられます。

  • 時間帯や季節によって、サービスやシステムへのアクセス数や負荷が大幅に上がるもの
  • 各々の処理自体は短時間で終わるが、大量のユーザーを捌く必要のあるもの

具体的な導入事例としては以下のようなものがあります。本稿では後述のハンズオンとしてREST APIを作成します。

  • REST API
  • Single Page Application (SPA)
  • 従来はcronで実行していた定期的な処理や運用管理のバッチジョブ
  • アップロードされた画像のリサイズ
  • スマートスピーカーからのリクエスト処理(「Alexa Skill」)

「AWS Lambda」とは

AWS Lambda」はサーバレスアーキテクチャの代表的なマネージドサービスの一つです。LambdaはAWSが2014年に開催したカンファレンス「AWS re:Invent 2014」で初公開されました。2017年12月にCloud Native Computing Foundationが実施した調査によると、FaaSを導入している企業のうち、 Lambdaのシェアは70% であり、その他のFaaSと大きく差をつけています。

FaaS シェア
AWS Lambda 70%
Google Cloud Functions 13%
Apache OpenWhisk 12%
Azure Functions 12%

(『Cloud Native Technologies Are Scaling Production Applications - Cloud Native Computing Foundation』より)

Lambdaは「Amazon Linux」と呼ばれる仮想OS上で動作し、以下の言語をサポートしています。(2018年3月時点)

  • Node.js – v4.3.2 および 6.10.3
  • Java – Java 8
  • Python – Python 3.6 および 2.7
  • .NET Core – .NET Core 1.0.1 (C#)
  • Go – Go 1.x

(『Lambda Execution Environment and Available Libraries - AWS Lambda 』より)

上記のいずれかの言語でLambda関数と呼ばれるコードを設定することで多様なサービスを実現することができます。本稿では後述のハンズオンとしてPythonを使用します。

「Serverless Framework」とは

Serverless Framework」とはサーバレス・アプリケーションを簡単に構築するためのツールです。Serverless Frameworkを使えば、設定ファイルに必要な設定を記述し、 たった一行のコマンドを打つ だけでFaaS上に必要なリソースを生成してくれます。2018年3月時点で対応しているFaaSは以下の通りとなります。

  • AWS Lambda
  • Google Cloud Functions
  • Apache OpenWhisk
  • Azure Functions

Serverless Frameworkはオープンソースソフトウェアであり、ライセンスは「MIT License」(『serverless/LICENSE.txt』)となっています。

ハンズオン「Serverless FrameworkでREST APIを作ってみよう」

それでは実際にServerless Frameworkを使ってAWS上にREST APIを作ってみましょう。

前提条件は以下の通りです。

  • Docker CEをインストールしていること
  • AWSアカウントを所有していること

本稿の動作環境は以下の通りです。EC2上のCentOSインスタンスを使ってServerless Frameworkの動作環境を構築しました。

分類 項目 バージョン
Amazon EC2 AMI ID ami-29d1e34e(CentOS Linux 7 x86_64)
Amazon EC2 インスタンスタイプ t2.medium
Amazon EC2 OSバージョン CentOS Linux release 7.3.1611 (Core)
Docker CE バージョン 17.06.0-ce

1. Dockerコンテナを構築する

まずはEC2上のインスタンスへログインして作業用のディレクトリを作成しましょう。

$ mkdir serverless
$ mv serverless

Serverless FrameworkがGitHub上で公開しているリポジトリ「GitHub - serverless/serverless」からDockerfileを取得します。

$ curl -O https://raw.githubusercontent.com/serverless/serverless/master/Dockerfile

以下はDockerfileの内容です。Serverless Frameworkをインストールするための環境が記述されています。

FROM node:latest

# install yarn
RUN curl -o- -L https://yarnpkg.com/install.sh | bash

# install python tooling
RUN apt-get update -y && apt-get install -y python-dev python-pip && pip install --upgrade pip

# install other utils
RUN apt-get update -y && apt-get install -y screen

# install aws-cli
RUN pip install awscli

続いて、以下のdocker-compose.ymlを作成します。

version: '3'
services:
  serverless:
    build: .
    tty: true
    image: serverless
    container_name: serverless
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

ディレクトリ構成は以下の通りです。

path/to/your/directry/serverlss

.
├── docker-compose.yml
└── Dockerfile

Dockerイメージを作成し、コンテナを起動しましょう。

$ docker-compose up -d

2. Dockerコンテナにログインする

手順1. で作成したコンテナにログインします。

$ docker exec -it serverless bash

ログイン後、vimをインストールしておきましょう。(後述の操作でファイルの編集が必要となります。)

# apt-get update && apt-get install -y vim

3. Serverless Frameworkをインストールする

Serverless Frameworkをインストールします。

# npm install -g serverless

動作確認として、Serverless Frameworkのバージョンを参照します。

# serverless -v
1.26.1

4. Serverless FrameworkにAWSアカウントを設定する

IAMユーザーの追加

Serverless Frameworkで使うIAMユーザーを「IAM Management Console」から追加します。設定は以下の通りです。

  • ユーザー名: sls_user(任意)
  • アクセスの種類: 「プログラムによるアクセス」をチェック
  • アクセス権限: AdministratorAccess

IAMユーザー作成後は以下の内容を必ず控えてください。特に「シークレットアクセスキーは」IAMユーザー作成時のみしか取得することができません。

  • アクセスキーID
  • シークレットアクセスキー(アクセスキーの)

IAMユーザーを追加する詳しい手順については「AWS アカウント内での IAM ユーザーの作成 - AWS Identity and Access Management」を参考としてください。

AWSアカウントの設定

生成したIAMユーザーの「アクセスキーID」、「シークレットアクセスキー」を設定します。slsserverlessコマンドのエイリアスです。

  • YOUR_ACCESS_KEY_ID: アクセスキーID
  • YOUR_SECRET_ACCESS_KEY: シークレットアクセスキー
# sls config credentials --provider aws --key YOUR_ACCESS_KEY_ID --secret YOUR_SECRET_ACCESS_KEY
Serverless: Setting up AWS...
Serverless: Saving your AWS profile in "~/.aws/credentials"...
Serverless: Success! Your AWS access keys were stored under the "default" profile.

AWSアカウントの設定が正しく反映されているかを確認します。

# cat ~/.aws/credentials
[default]
aws_access_key_id = YOUR_ACCESS_KEY_ID
aws_secret_access_key = YOUR_SECRET_ACCESS_KEY

5. 新規サービスを作成する

これから新規のサービスを作成します。まずは作業用のディレクトリを作成しましょう。本稿では/home/serverlessを作業用ディレクトリとしました。

# cd home/
# mkdir serverless
# cd serverless/

続いてテンプレートからサービスを生成します。

# sls create -t aws-python -p slstest
Serverless: Generating boilerplate...
Serverless: Generating boilerplate in "/home/serverless/slstest"
 _______                             __
|   _   .-----.----.--.--.-----.----|  .-----.-----.-----.
|   |___|  -__|   _|  |  |  -__|   _|  |  -__|__ --|__ --|
|____   |_____|__|  \___/|_____|__| |__|_____|_____|_____|
|   |   |             The Serverless Application Framework
|       |                           serverless.com, v1.26.1
 -------'

Serverless: Successfully generated boilerplate for template: "aws-python"
  • -t aws-python: テンプレートを指定します。
  • -p slstest: サービスを生成するディレクトリを指定します。

(『Serverless Framework Commands - AWS Lambda - Create』より)

上記コマンド入力後のディレクトリ構成は以下の通りとなっています。

/home/serverless

.
`-- slstest
    |-- handler.py
    `-- serverless.yml

handler.pyの内容は以下の通りです。このファイルではLamda関数helloの処理が記述されています。

handler.py

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
    }
    """

serverless.ymlの内容は以下の通りです。このファイルではデプロイ先であるFaaSプロバイダ(本稿ではAWS)やhandler.pyで設定した関数のインターフェースなどの設定が記述されています。

serverless.yml

# 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: slstest

# You can pin your service to only deploy with a specific Serverless version
# Check out our docs for more details
# frameworkVersion: "=X.X.X"

provider:
  name: aws
  runtime: python2.7

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

# you can add statements to the Lambda function's IAM Role here
#  iamRoleStatements:
#    - 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:
#  include:
#    - include-me.py
#    - include-me-dir/**
#  exclude:
#    - exclude-me.py
#    - exclude-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:
#      - http:
#          path: users/create
#          method: get
#      - 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
#      - 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

#    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"

上記の操作で生成された以下のディレクトリへ移動します。

# cd slstest/

6. serverless.ymlを編集する

serverless.ymlに記述されている以下の項目を書き換えます。

  • provider:region:
    us-east-1(バージニア北部)からap-northeast-1(東京)へ変更します。
  • functions:hello:events:
    Lambda関数のエンドポイントとして「Amazon API Gateway」を設定します。

以下がその修正内容です。ここでserverless.ymlはYAMLフォーマットに準ずるため、コメントアウトを外す際は スペースの数に注意してください。

diff -u serverless.yml.orig serverless.yml

--- serverless.yml.orig    2018-03-10 18:06:49.201949814 +0000
+++ serverless.yml    2018-03-10 18:07:56.764442772 +0000
@@ -23,7 +23,7 @@

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

 # you can add statements to the Lambda function's IAM Role here
 #  iamRoleStatements:
@@ -61,10 +61,10 @@
 #    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:
-#      - http:
-#          path: users/create
-#          method: get
+    events:
+      - http:
+          path: users/create
+          method: get
 #      - s3: ${env:BUCKET}
 #      - schedule: rate(10 minutes)
 #      - sns: greeter-topic

7. AWSへデプロイする

上記の修正が完了したら、AWSへデプロイしましょう。 たった1行のコマンドでデプロイが可能です。

# sls deploy
......
......
......
Serverless: Stack update finished...
Service Information
service: slstest
stage: dev
region: ap-northeast-1
stack: slstest-dev
api keys:
  None
endpoints:
  GET - https://w3okvsf78f.execute-api.ap-northeast-1.amazonaws.com/dev/users/create
functions:
  hello: slstest-dev-hello

Stack Outputs
HelloLambdaFunctionQualifiedArn: arn:aws:lambda:ap-northeast-1:752411445417:function:slstest-dev-hello:1
ServiceEndpoint: https://w3okvsf78f.execute-api.ap-northeast-1.amazonaws.com/dev
ServerlessDeploymentBucketName: slstest-dev-serverlessdeploymentbucket-1pm5qhmvou61b

8. 動作確認する

動作確認のため、デプロイしたhello関数を呼び出します。

# sls invoke -f hello
{
    "body": "{\"input\": {}, \"message\": \"Go Serverless v1.0! Your function executed successfully!\"}",
    "statusCode": 200
}
  • -f hello: 呼び出したいLambda関数を指定します。

(『Serverless Framework Commands - AWS Lambda - Invoke』より)

デプロイ実行時に表示されたendpointsのURLをcurlコマンドからコールすることが可能となります。

# curl https://w3okvsf78f.execute-api.ap-northeast-1.amazonaws.com/dev/users/create
{"input": {"body": null, "resource": "/users/create", "requestContext": {"requestTime": "10/Mar/2018:18:17:46 +0000", "protocol": "HTTP/1.1", "resourceId": "l7xq21", "apiId": "w3okvsf78f", "resourcePath": "/users/create", "httpMethod": "GET", "requestId": "551c47d7-248f-11e8-b0aa-e9ee457da36c", "path": "/dev/users/create", "accountId": "752411445417", "requestTimeEpoch": 1520705866164, "identity": {"userArn": null, "cognitoAuthenticationType": null, "accessKey": null, "caller": null, "userAgent": "curl/7.38.0", "user": null, "cognitoIdentityPoolId": null, "cognitoIdentityId": null, "cognitoAuthenticationProvider": null, "sourceIp": "52.196.88.40", "accountId": null}, "stage": "dev"}, "queryStringParameters": null, "httpMethod": "GET", "pathParameters": null, "headers": {"Via": "1.1 8cf37620e1bbd4588a3405f59a3b4da9.cloudfront.net (CloudFront)", "CloudFront-Is-Desktop-Viewer": "true", "CloudFront-Is-SmartTV-Viewer": "false", "CloudFront-Forwarded-Proto": "https", "X-Forwarded-For": "52.196.88.40, 52.46.20.63", "CloudFront-Viewer-Country": "JP", "Accept": "*/*", "User-Agent": "curl/7.38.0", "X-Amzn-Trace-Id": "Root=1-5aa4214a-50632754f3c75448f71dca34", "Host": "w3okvsf78f.execute-api.ap-northeast-1.amazonaws.com", "X-Forwarded-Proto": "https", "X-Amz-Cf-Id": "21sakbqugdIRgEP61MvWnH6dlthDrUSfnhV4i6NR5oWwcUp3ZnR6OA==", "CloudFront-Is-Tablet-Viewer": "false", "X-Forwarded-Port": "443", "CloudFront-Is-Mobile-Viewer": "false"}, "stageVariables": null, "path": "/users/create", "isBase64Encoded": false}, "message": "Go Serverless v1.0! Your function executed successfully!"}

もちろん、ブラウザからリクエストすることもできます。

スクリーンショット 2018-03-11 03.33.26.png

AWS上でサービスがどのように構築されているかを確認したい場合は、「CloudFormation マネジメントコンソール」から、上記でデプロイしたサービス(「スタック」)を参照することができます。

スクリーンショット 2018-03-11 03.36.38.png

スタック名をクリックすると、スタックの中でどのようなAWSリソースが使われているかを確認することができます。

スクリーンショット 2018-03-11 03.40.17.png

9. サービスを削除する

デプロイしたサービスは以下のコマンドを入力することで削除できます。

# sls remove
Serverless: Getting all objects in S3 bucket...
Serverless: Removing objects in S3 bucket...
Serverless: Removing Stack...
Serverless: Checking Stack removal progress...
................
Serverless: Stack removal finished...

以上、ハンズオン「Serverless FrameworkでREST APIを作ってみよう」でした。

今まで何となく「『サーバレス』はハードルが高そう」と感じていましたが、 サーバーレスでやっていることといえば、 関数を記述したソースコードをクラウドへアップロードする程度 です。それなら90年代にFTPを使って レンタルサーバーへCGIスクリプトをアップロードしていたこと と大して変わりありません。

私と同じように「サーバレス」を何となく敬遠している方がいれば、是非、ハンズオンを試してみてください。30分もあれば十分にサーバーレス・アプリケーションを構築できると思います。

まとめ

  • 「サーバーレスアーキテクチャ」とは、関数を実行する度にコンテナを生成することによって、 常駐プロセスを必要としないアプリケーション実行環境を実現すること である。
  • サーバレスアーキテクチャによってサーバーの管理を不要とするマネージドサービスのことを 「FaaS(Function as a Service)」 と呼ぶ。
  • サーバレスアーキテクチャは 「モダンCGI」 である。
  • サーバレスアーキテクチャの場合、関数を実行していないときはコンピュータリソースを一切使用しないため、 コストを抑えることができる。
  • サーバレスアーキテクチャの代表的な導入事例として「REST API」や「Single Page Application (SPA)」、バッチジョブなどがある。
  • 2017年12月時点で Lambdaのシェアは70% であり、他のFaaSと大きく差をつけている。
  • 「Serverless Framework」 を使えばサーバレス・アプリケーションを簡単に構築できる。

参考ウェブサイト

本稿の執筆にあたって、以下のウェブサイトを参考とさせて頂きました。