Re:boot!

35歳からのエンジニアWay

サーバレスアーキテクチャの入門をしてみた

さて、エンジニア復帰したのは良いものの、この2年ほどの技術的な知識が止まったままなので、ちょっとコツコツ勉強をしながらアウトプットしていきたいと思います。

ぼちぼちと技術系のサイトや雑誌を見た感じ、インフラで言えばAWSがますます一般的になって、大企業の採用事例もかなり増えているようですね。またWeb系の企業であれば、どの企業も多かれ少なかれInfrastructure as Codeに取りくんでる感じでしょうか。

完全にDockerコンテナ上でサービスを動かしてる所も、ぼちぼち出てきてるようです。

後は、Goを導入している事例も増えてきたな〜と思いました。インフラ的には並列処理が有効な処理の場合に、自前で作ってパフォーマンスを上げる!みたいな事例が多い感じでしょうか。

この様な「最適化のためにミドルウェア自前で作っちゃう」みたい領域は、Scalaが担っていくかもと思っていたのですがそうでもなかったですね。今後はGoが席巻していきそうな予感がします。

まあ、この辺の技術は当然のごとく勉強していかなければならないでしょう。しかし、ある程度は触った事もありますし、私は基本的にミーハーなので「まだみんながあんまり手を出してない所」が一番好みだったりもします。

そこで勉強の手始めとして目にとまったのが「サーバレスアーキテクチャ」でした。

サーバレスアーキテクチャは近年のアプリケーションレイヤでのキーワード「Microservices」を実現する一つの手段とも考えられているみたいです。そう考えれば、ちゃんと技術的なトレンドを汲んで生まれたものだと言えますね。

2017年のバスワード感が漂ってるので、ミーハーには持って来いなテーマです。

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

定義はあいまいなようですが、より実際の使い方を考慮して言うと

  • 事前にプロセスを立ち上げておかずに
  • ユーザアクセス(何かのイベント)をトリガとして
  • 事前に用意した処理を
  • 新しいプロセスを立ち上げた上で処理する

といった仕組みのようです。詳しくは次のエントリを見ると良いと思います。

www.publickey1.jp

thinkit.co.jp

いくつか記事を読んでいると現状「Amazon API GatewayAmazonAWS Lamdaを使って出来る仕組み」以外のなにものでもない感じですね。

AWS Lambdaを使えばアプリケーションサーバ(常駐プロセス)を自分で用意する必要がないので、そういった意味で「サーバレス」という事でしょう。(もちろん裏ではコンテナが起動して、プロセスが立ち上がって、処理がおわったらコンテナが捨てられるといった事が行われいるようですが)

この様な特徴を持っていることからFaaS(Function as a Service)という風にも呼ばれているようです。

サーバレスアーキテクチャの利用例

理解を深めるには実際にやってみるのが手っ取り早いので、具体的な例を探してみました。

典型的な例としては「画像処理」が良く挙げられるようですね。

blog.serverworks.co.jp

この例ではS3への画像アップロードをトリガにリサイズを行い、出来たサムネイルを再びS3に保存するといった事を行なっているようです。他にも動的にサムネイルを作っている例もありました。

qiita.com

確かに非同期で行いたい処理を投げたり、コンバーターみたいな使い方は相性が良さそうです。スケールアウトもしやすそうなので、スパイクな負荷がかかるような場合は特に向いてそう。パッと思いつくとこだと、プッシュ通知とか、スクレイピングAPIを叩いて結果をjsonに変換した上で返すといった風な使い方ができそうです。

サーバレスアーキテクチャHello World

では、まずは手始めにサーバレスでHello Worldをやってみたいと思います。

利用するのは以下の3つです。

SERVERLESS FRAMEWORKは一言でいえば、「AWS Lambda上で動くプログラムを、手元で作って管理するためのツールセット」です。

serverless.com

AWS Lambdaの設定を手元のコンフィグで管理したり、作ったプログラムをAWS Lambdaにデプロイするといった事が可能です。

また、SERVERLESS FRAMEWORK自体はnode.jsで動きますが、AWS Lambdaが2016年12月現在対応しているNode.js、PythonJava(Scala)、C#のそれぞれを管理するためのテンプレートも用意されているようです。つまり、好きな言語で作ってSERVERLESS FRAMEWORKで管理できるってことです。

さて、これ以上ぐだぐだ言っててもピンとこないので、早速Hello Worldしてみたいと思います。

大まかな流れとしては次の通りです。

  1. SERVERLESS FRAMEWORKのインストール
  2. IAMユーザのクレデンシャルの設定
  3. プロジェクトの作成
  4. デプロイ
  5. 実行テスト
  6. APIとして使えるようにする

SERVERLESS FRAMEWORKのインストール

まずはSERVERLESS FRAMEWORKをインストールします。

インストール手順のページを見ると次の様に書いてあるので、node.js ver4以上が必要ですね。私はv4.7.0をインストールしました。

Note: Serverless runs on Node v4 or higher.

$ npm install serverless -g
$ serverless -v # => 1.4.0

v1.4.0がインストールされました。

IAMユーザのクレデンシャルの設定

SERVERLESS FRAMEWORKは手元の端末からAWSを操作するので、権限を持ったIAMユーザのクレデンシャル(アクセスキーとシークレット)を設定する必要があります。

やるべき事は大きく2つです。

  • IAMでSERVERLESSの利用に必要な権限を持ったユーザのクレデンシャルを取得
  • 取得したクレデンシャルをSERVERLESSに設定

ちなみにSERVERLESSを使うためにはAWS Lambda、Amazon API Gatewayの他にもCloud formationの権限が必要となるようです、今回はとりあえずIAMで「AdministratorAccess」を与えたユーザを作ってアクセスキーとシークレットを取得しました。

試してはいませんが、必要な権限にのみ絞りたい場合は「AWSLambdaFullAccess」「AmazonAPIGatewayInvokeFullAccess」と、下記の手順でCloud formationに関する権限を与えればよさそうです。

qiita.com

クレデンシャルを取得したら、SERVERLESSへのクレデンシャルの設定を行います。クレデンシャルの設定にはいくつかの方法があるようで、公式ページのCredentialsにまとまっています。好みに合わせて自分の好きな方法を選べば良いでしょう。

今回は他のサイトにも倣ってaws-cliでクレデンシャルを保存しておく方法を利用しました。aws-cliのインストールとクレデンシャルのセットアップ方法は公式サイトに説明があります。

docs.aws.amazon.com

公式サイトとは違い、手元のリージョンはap-northeast-1(東京リージョン)を選びました。

$ aws configure

# input
AWS Access Key ID [None]: XXXXXXXXXXXXXXXXXX
AWS Secret Access Key [None]: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Default region name [None]: ap-northeast-1
Default output format [None]: json

プロジェクトの作成

続いてプロジェクトの作成を行います。作成にはserverless createコマンドを使います。同時にディレクトリと、Lambda上で動かすプログラムの言語を指定します。

$ serverless create --template aws-nodejs --path hello-serverless

# output
Serverless: Generating boilerplate...
Serverless: Generating boilerplate in "/Users/xxxx/hello-serverless"
 _______                             __
|   _   .-----.----.--.--.-----.----|  .-----.-----.-----.
|   |___|  -__|   _|  |  |  -__|   _|  |  -__|__ --|__ --|
|____   |_____|__|  \___/|_____|__| |__|_____|_____|_____|
|   |   |             The Serverless Application Framework
|       |                           serverless.com, v1.4.0
 -------'

Serverless: Successfully generated boilerplate for template: "aws-nodejs"

今回はnode.jsを利用する事にしました。別の言語で作りたい場合は下記のページを参考に--templateの値を変えればOKです。

serverless.com

作成されたディレクトリの中身には「handler.js」「serverless.yml」の2ファイルがありました。前者がLamda上で動かすプログラム、後者が設定ファイルですね。

# handler.jsの中身

'use strict';

module.exports.hello = (event, context, callback) => {
  const response = {
    statusCode: 200,
    body: JSON.stringify({
      message: 'Go Serverless v1.0! Your function executed successfully!',
      input: event,
    }),
  };

  callback(null, response);

  // Use this code if you don't use the http event with the LAMBDA-PROXY integration
  // callback(null, { message: 'Go Serverless v1.0! Your function executed successfully!', event });
};

bodyの部分が実際にレスポンスとして返すないようでしょう。Go Serverless v1.0! Your function executed successfully!と、引数のeventの内容を文字列にして返す様です。

# serverless.ymlの中身(コメント部分は割愛)

service: aws-nodejs

provider:
  name: aws
  runtime: nodejs4.3

functions:
  hello:
    handler: handler.hello

hander.helloの部分が恐らくLamda上に配置する関数名でしょう。handler.jsのmodule.exports.helloの部分と対応している感じでしょうか。

デプロイ

まずは動作を確認してみたいので、とりあえずデプロイしてみます。

$ serverless deploy

# output
Serverless: Packaging service...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading service .zip file to S3 (583 B)...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
.........
Serverless: Stack update finished...
Service Information
service: aws-nodejs
stage: dev
region: us-east-1
api keys:
  None
endpoints:
  None
functions:
  aws-nodejs-dev-hello: arn:aws:lambda:us-east-1:xxxxxxxxxxxx:function:aws-nodejs-dev-hello

この様に進捗が出た後に、デプロイされた内容に関する情報が出力されるようです。「Serverless: Stack update finished...」以降の部分ですね。この値は「serverless info」コマンドで確認する事ができます。

$ serverless info

# output
Service Information
service: aws-nodejs
stage: dev
region: us-east-1
api keys:
  None
endpoints:
  None
functions:
  serverless-dev-hello: arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxx:function:serverless-dev-hello

値を見てみるとaws-cliで東京リージョンを指定しましたが「us-east-1」になっています。endpointsはこのプログラムを呼び出すためののURLが入る所ですが、現在は「None」になっています。この辺りの値の変更はserver.xmlで設定を追加することで行うことが出来ます

実行テスト

実際にAPIとして使うためには、server.xmlに設定を追加してAmazon API Gatewayを使える様にする必要がありますが、「serverless invoke」コマンドを使うことでプログラムの実行結果を見ることができます。

$ serverless invoke -f hello

{
    "statusCode": 200,
    "body": "{\"message\":\"Go Serverless v1.0! Your function executed successfully!\",\"input\":{}}"
}

手元で実行するとこの様な結果が帰ってきました。正しく動作していそうですね。

APIとして使える様にする

さて、手元での動作が確認できたので、Amazon API Gatewayと組み合わせてエンドポイントを追加し、URLを叩いたら実行されるようにしたいと思います。

この変更は下記の様にserverless.ymlのfunctionsにeventsの項目を追加することで可能です。合わせてリージョンを東京に変更してあります。

# serverless.yml

 provider:
   name: aws
   runtime: nodejs4.3
+  region: ap-northeast-1

 functions:
   hello:
     handler: handler.hello
+    events:
+     - http:
+          path: hello
+          method: get

変更が出来たらデプロイして反映させます。

$ serverless deploy

# output
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
.....
Serverless: Stack create finished...
Serverless: Packaging service...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading service .zip file to S3 (583 B)...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
..............................
Serverless: Stack update finished...
Service Information
service: aws-nodejs
stage: dev
region: ap-northeast-1
api keys:
  None
endpoints:
  GET - https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/hello
functions:
  aws-nodejs-dev-hello: arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxx:function:aws-nodejs-dev-hello

この様にendpointsにURLが表示されました(URLのサブドメインは一部マスクしてあります。)後はこのURLにアクセスしてみるだけです。

$ curl https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/hello

# output
{"message":"Go Serverless v1.0! Your function executed successfully!","input":{"resource":"/hello","path":"/hello","httpMethod":"GET","headers":
以下省略 

この様にちゃんと出力が帰ってきます。入門としてはこのくらいで十分でしょう。テスト後には「serverless remove」コマンドを実行することでデプロイした内容を削除することが出来ます。

$ serverless remove

# output
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もあるので、Hello Worldする程度なら非常に手軽に試すことができました。ただ、あまりサーバレスアーキテクチャ(Lamda)の良さという点では実感が少ないですね。

ユーザアクセス以外のトリガになる処理を利用してみたり、実際に業務で利用してみればそのありがたさが実感できるのかもしれません。

今回は良さを実感は出来ませんでしたが、改めてサーバレスアーキテクチャのメリット・デメリットを簡単にまとめると以下の感じでしょうか。

メリット

  • 実行されるそれぞれの処理がステートレスで、お互いにシェアードナッシングなので安全安心。
  • 同様に上記の理由から疎結合でスケール(アウト)しやすい。
  • 常駐プロセスがいないので可用性が高く、本当に必要な分のリソースにのみ確保すれば良いので安くあがる

要は安全かつ拡張性が高くて安いってことですね。

デメリット

  • 都度プロセスが立ち上がるのでオーバーヘッドが大きい
  • 処理が疎結合になって分離するため、デバッグ(シナリオテスト)などのコストが高い

やはりオーバーヘッドが高いのは大きなデメリットのようです。実際に解説記事をいくつか読んでいても、まだ全てをサーバレスアーキテクチャにするのは(負荷の高さを考えると)時期尚早といった感じでした。

処理をあまり疎結合にしすぎると今度は開発コストが増大しそうなので、仮にオーバーヘッドが減ったとしても結局全てサーバレスアーキテクチャでという考えは、いずれにせよ難しいのかもしれませんね。