Serverless Frameworkを本番導入した話

はじめに

約半年ぶりの投稿になります。エンジニアの鈴木(@yudppp)です。

TOP画像 photo by Infidelic

Serverless Frameworkのversion 1がつい先日リリースされました。 触ってみたかったのと、利用に適していそうな業務があり、Serverless Frameworkを本番環境で導入しました。

HAROiDでは以前からLambdaは利用していましたが、APIサーバのバックエンドとしてのLambdaやAPI Gatewayの利用は初めてになります。

作りたかったもの

外部サービスの一つのエンドポイントを叩く際にTokenや外部サービスAPIのエンドポイントを隠蔽するためのAPIを作る必要がありました。 またこちらのAPIは日に数十回程度しか叩かれない想定の利用頻度が極めて低いAPIでした。(利用頻度が低いからといって重要度が低いわけではない)

普段通り作るのであれば、クライアントで直接叩いてしまうと外部サービスのTokenが漏れてしまわないよう、Tokenをつけて外部サービスのAPIに投げるAPIを作成し、それをEC2などで冗長化した構成で運用する方法をとっていました。

今回はアクセスが極めて少ない想定だったので考えていませんが、アクセスが増えた時に対応するためオートスケーリングの設定をする場合もあります。

この方法が悪いわけではないと思うのですが、使われない時間が多いため無駄にお金を払ってしまっている感じと、サーバーに何か起きてしまった時に対応をする必要がありました。

そういった問題を解決するためサーバーレスアプリケーションを採用し、それを作成するためのServerless Frameworkを利用することにしました。

Serverless Frameworkとは

Serverless Frameworkとはサーバーレスアプリケーションを簡単に作成できるフレームワークになります。

将来的にはAWS以外にも対応しそうですが、現状はAWSのみに対応しています。

Serverless Frameworkを利用してAWSでサーバレスアプリケーションなAPIを作成するときの仕組みですが、Lambdaの登録に必要な情報をS3にあげ、API Gateway + LambdaをCloud Formationを利用して作成しています。

またAPIの利用以外にもcron処理やS3,KinessisなどをhookにFunctionを走らせることも可能です。 こちらに設定できるイベントの詳細が書いてありました。

Lambda自体は同時実行数がデフォルトで100なので、1秒以内に返すAPIであれば100[rps]は捌くことができます。

他のフレームワークとの比較

Apex

HAROiDではGo言語が公用語になりつつあるので、Go言語をbinに変換してLambdaで動かすことができるgo-apexの導入も考えたのですが、API Gatewayを管理する機能はありませんでした。

Go言語である理由が特別ない案件だったので、今回は簡単にAPI Gatewayの管理ができるものにしようと、導入しませんでした。

fluct

fluctはLambdaとAPI Gatewayを管理するフレームワークです。
Serverless Frameworkと異なり、s3やkinesissなどの他のイベントには対応していません。

Lambdaに使える言語はNodeJSのみをサポートしているようでした。

fluctとServerless Frameworkを比較させた時に、プラグインや利用実績が多く、他の利用方法でも使えそうなServerless Frameworkを選択することにしました。

導入について

npmで簡単にインストールすることができます。

$ npm install -g serverless
$ serverless create --template aws-nodejs --path serverless-node-app
$ cd serverless-node-app

また今回はNodeJSを採用しましたが、テンプレートを利用することによりPythonやJava、ScalaなどLambdaを使える言語であれば利用することが可能です。

テンプレートについてはこちらをご参照ください。

serverless-node-app $ tree  
.
├── event.json
├── handler.js
└── serverless.yml

0 directories, 3 files  

このような構成になっています。

serverless.ymlは全体の設定ファイルとなっていて、関数とそれに結びつけるイベントの設定を行うことができます。

handler.jsはLambdaに置かれる関数そのものが定義されているファイルになります。

'use strict';

module.exports.hello = (event, context, callback) => {  
  const response = {
    statusCode: 200,
    body: JSON.stringify({
      message: 'hello',
      input: event,
    }),
  };
  callback(null, response);
};

最後にevent.jsonはLambdaの関数の確認をlocal環境で行うための引数の設定を行います。

環境変数について

NodeJSではAPIトークンなどを利用する場合環境変数にして指定する場合が多いかと思います。serverless.yml

service: serverless-node-app  
custom:  
  stageVariables:
    token: ${env:HOGE_TOKEN}
provider:  
  name: aws
  runtime: nodejs4.3
  stage: dev
  region: ap-northeast-1
functions:  
  hello:
    handler: handler.hello
plugins:  
  - serverless-plugin-stage-variables

とstageVariablesに環境変数を設定することによってhandler.js側でevent.stageVariables.tokenで取得できるようになりました。 この際pluginにserverless-plugin-stage-variablesを利用する必要があります。

独自ドメイン/SSL対応

独自ドメインをSSLで実現するためにAPI Gatewayの前段にCloudFrontを設定するようにしました。

その際にAPI GatewayのURLがhttps://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/production/helloのようなstage(この場合production)が含まれていたURLになってしまっていたのでOrigin Pathproductionを指定することによってxxxxxxxxx.cloudfront.net/helloといったURLに変更することができました。

最終的な構成

料金について

料金がどれだけ安くなったか、簡単に計算してみたいと思います。

1日に30回、2KBのデータがPOSTされた時に200msでレスポンスを返す場合を考えて計算してみます。
またアプリケーションのサイズは6MBとします。

EC2:  
    - オンデマンド料金(t2.micro*2): $0.02/h * 24 * 2
    - データ転送: $0.140/GB * 60KB
ELB:  
    - ロードバランサー使用時間: $0.027/h * 24
total: $1.7  

となり1日あたり約$1.7となりました。これでも十分安いような気もします。

Serverless Frameworkを使った場合の料金を次に出してみます。

CloudFront:  
    - リクエスト料金: $0.0120/10,000req * 30req
    - データ転送: $0.140/GB * 60KB
API Gateway:  
    - API呼び出し: $0.0425/10,000req * 30req
    - データ転送: $0.140/GB * 60KB
Lambda:  
    - リクエスト: $0.0020/10,000req * 30
    - 時間: $0.00001667/100ms/GB * 200ms * 128MB * 30
S3:  
    - ストレージの価格: $0.0330/GB * 6MB
Total: $0.0005  

となり1日あたり約$0.0005となり、数千分の1と計算が間違っていそうなくらい安くなりました。

今回は30回という極端に少ない数で計算を行ったので差が大きく差が開きましたが、この条件であれば、1日あたり約十万回以上叩かれるAPIであればEC2を用いた構成の方が安上がりになりました。

※Route53/SSL証明書の料金については今回どちらでも利用しているため省略しています。

感想

費用が安くなりサーバーの管理も楽になるので簡単なもの作るタイミングがあれば、また利用したいと思います。 ただデータベース等をちゃんと利用した本格的なアプリケーションに導入するにはサーバーレスを意識して実装しなくてはならないので大変そうに感じました。 Lambdaが正式にGo言語のサポートしてほしいです。