サーバーレスのメリット&本質を、AWS Lambdaを使って理解しよう

「サーバーレス」はここ数年の技術トレンドの一つです。サーバーレスアーキテクチャを2年運用してきたJX通信社の小笠原みつき(yamitzky)さんが、そのメリットや実際の業務における考え方を、ハンズオンを交えながら解説します。

サーバーレスのメリット&本質を、AWS Lambdaを使って理解しよう

JX通信社の小笠原みつき@yamitzkyと申します。ニュース速報アプリ「NewsDigest」の事業統括をしているエンジニアです。

JX通信社では、人手のかかる「報道」という分野を機械化・自動化することをミッションの一つとして掲げており、技術選択においても、なるべく運用を自動化できる方法を選ぶよう心掛けています。その過程で「サーバーレス」という技術に出会い、2016年ごろから本番運用してきました。

今回の記事のゴールは、サーバーレスという技術が「どんなものであるか?」を理解することです。サーバー監視ツールの制作を通じて、特定のサービスだけにフォーカスせず、実際の業務で生かせる「考え方」の習得を目指します。

サーバーレスの沿革

以下の図は、Googleトレンドで「serverless」という言葉の人気度を示したものです。

1

Googleトレンドにおける「serverless」の人気度の動向

2015年の終わりごろからよく使われ始めたようですが、その勢いは未だ衰えていません。AWS(Amazon Web Services)やGCP(Google Cloud Platform)のようなクラウドサービスでも、サーバーレスを押し出した製品が次々に発表されていますし、日本でもServerlessconfというカンファレンスが開かれるなど、技術トレンドの一つとなっています。

「serverless」という単語自体が初めて使われたのは、2012年ごろと言われています。「Why The Future Of Software And Apps Is Serverless」という記事では、クラウドサービス普及によるサーバーとメモリコストの低下と、コンピューティング環境の変化について述べています。

The phrase “serverless” doesn’t mean servers are no longer involved. It simply means that developers no longer have to think that much about them. Computing resources get used as services without having to manage around physical capacities or limits.

Ken Fromm「Why The Future Of Software And Apps Is Serverless」(2012年10月15日、ReadWrite)より

その後、2014年にサーバーレスコンピューティングサービスの「AWS Lambda(以下、Lambda)」が発表されました。

2AWS Lambda (サーバーレスでコードを実行・自動管理) | AWS

今となってはLambdaはサーバーレスの代表格ですが、当時のアナウンスブログでは「serverless」という言葉は使われず、「コードをクラウド上で実行する製品」という紹介がされていました。

Today we are launching a preview of AWS Lambda, a brand-new way to build and run applications in the cloud, one that lets you take advantage of your existing programming skills and your knowledge of AWS.

AWS News Blog「AWS Lambda ― Run Code in the Cloud」(2014年11月13日)より

2016年に入ると、AWS Lambdaに対抗するサービスとして、GoogleからCloud Functions、IBMからOpenWhisk、MicrosoftからAzure Functionsが立て続けに発表されています。

3Cloud Functions - Event-driven Serverless Computing | Google Cloud
4OpenWhisk サーバーレス・アーキテクチャー - IBM Bluemix
5Azure Functions―サーバーレス アーキテクチャ | Microsoft Azure

もはやサーバーレスは、主要なクラウド事業者では基本的なサービスという位置づけになっていることが分かります。

6

サーバーレスの歴史

また、既存のクラウドサービスも、サーバーレスの文脈で語られるようになってきています。

例えば、GoogleのBigQueryはビッグデータの分析基盤を提供するフルマネージドサービスで、2011年ごろから存在しますが、2016年半ばになってから「BigQuery is serverless」と紹介されるようになっています。

サーバーレスの沿革を紐解くと、次のようなことが分かります。

  • サーバーレスのコンセプト自体は、必ずしもAWS Lambdaによって最初に実現されたわけではない
  • サーバーレスという言葉が誕生する以前から、サーバーレスなサービスが存在した
  • BigQueryのようなマネージドサービスも、サーバーレスと呼ばれる

サーバーレスとは何か

AWS LambdaやCloud Functionsのようなサービスは、PythonやGoなどで書かれたロジック=関数をクラウド上で実行するものです。処理実行の際に必要なインフラは、クラウド側が管理してくれます。関数の実行をマネージドにしたサービスとも言えるので、Function-as-a-Service(FaaS)と呼ばれます。

個人的には、FaaSは「狭義のサーバーレスの定義」だと思っています。

先ほどBigQueryもサーバーレスとして紹介されていることを述べましたが、BigQueryはPythonやGoなどのコードを動かすためのサービスではありません。その代わり、SQLで指定した集計処理をフルマネージド環境で実行してくれます。

BigQueryはFaaSではありませんが、サーバーレスなサービスとしてAWS Lambdaなどと次のような共通点があります。

  • フルマネージドであり、インフラを管理する必要がない
  • 関数やSQLなど、何らかの処理を実行してくれるサービスである
  • リクエストが発生したときにだけ、クラウド側で計算リソース(CPUやメモリ)を柔軟に確保する

サーバーレスを一言で定義するのは難しいのですが、私は、リクエストに応じて、必要なだけの計算リソースをほぼリアルタイムで確保するアーキテクチャという認識をしています。ただし、OpenFaaSのような「オープンソースのFaaS基盤」もあり、必ずしもクラウド上のマネージドサービスだけとは言えません。

本稿ではこれから実際にAWS Lambdaを触ってみますが、サーバーレスの重要なポイントであるリソース確保の柔軟性を理解するためにも「いつリソースが確保されているのか?」を意識してみましょう。

サーバーレスのメリットとデメリット

サーバーレスアーキテクチャの大きなメリットの一つは、リソース確保の柔軟性です。

サーバーレスなAPIでは、1つのAPIへのリクエストが「1回のLambda関数の実行」に該当します。そのため、各リクエストの処理に対して、最低128MBのメモリが確保されます。その場合、同時に100リクエストが発生したら、合計12.8GBのメモリが即座に確保されることになります。この際に重要なことは「各リクエストが確保したメモリ量で処理できるか?」であって、システム全体で確保すべきメモリに関してはあまり考える必要がありません。

逆に、1リクエストもないときにはメモリは確保されず、費用も発生しないので、ユーザー数の少ない新規のWebサービスなどでは安価な運用ができるでしょう。後ほど詳しく見ていきますが、これから作ってみる監視システムも、Lambdaの費用は1カ月で1円もかからないはずです。

さらに、マネージドサービスを組み合わせていけば運用コストも下げることができ、エンジニアはロジックに集中できます。

サーバーレスアーキテクチャには、デメリットもあります。

サーバーレスでは、基本的にはクラウド依存が前提になります。クラウド事業者の設定している上限を超えるような使い方はできませんし(Lambdaの場合、同時実行数などに制約がありスケールが難しい場合もあります)、まれにサーバーレス環境自体に障害が起きることもあります。

そのため、あらかじめ特定のサーバーレス環境に依存しない設計で作っておくことが重要です。本稿でも、ハンズオンの途中で「コードを良くする」工程を入れ、AWS Lambda以外の環境でも使える設計を目指します。

サーバー監視ツールを作ってみよう

それでは、実際にサーバーレスなシステムを作ってみて、その仕組みを理解しましょう。題材として、次のようなサーバー監視ツールをAWS Lambdaを使ってサーバーレスに作ってみます。

  • 1分間に1回、Webサービスへアクセスし、レスポンスタイムを監視する
  • 監視状況を、Amazon CloudWatch Metricsに保存する

今回は「Webサービスのレスポンスタイムの監視」という例ですが、「SQLを使ったデータベースの状態監視」や「ECサイトの販売状況の監視」や「天気データの監視」など、趣味でも実務でもいろいろと応用の効くシステムです。

全体のアーキテクチャーは、以下の図のようになります。

7

サーバーレスな監視のアーキテクチャー

3つのサービスはそれぞれ異なる役割を持ちます。

CloudWatch Events
イベントに基づいて、AWSのさまざまなサービスを起動させるマネージドサービスです。Lambdaを定期的に動かします
Lambda
マネージドなFaaSです。Webサービスを監視して、その結果をCloudWatch Metricsに書き込みます
CloudWatch Metrics
メトリクスを保存して表示するマネージドサービスです。監視結果を保存します

事前準備

一般的に、FaaSの関数をデプロイするには、次の3つの方法があります(使用するプログラミング言語やクラウドサービスによって多少の差はあります)

  • 管理画面上で直接コードを書く
  • 公式のツール(AWS CLIなど)を使ってデプロイする
  • 外部のツールを使ってデプロイする

今回は、Serverless Frameworkという外部のツールを使います。Serverless Frameworkは、AWS Lambdaだけでなく、GCPやMicrosoft Azureなど、いくつかのサービスに対応しています。

Serverless Frameworkのセットアップは、公式のクイックスタートを参照してください。

本稿は、2018年6月現在のAWSで動作検証しています。Pythonは3.6.1、Serverless Frameworkのバージョンは1.27.2です。

Lambda関数のデプロイ

それでは、実際にサーバーレス環境を触ってみましょう。まずは「service-watcher」という名前でプロジェクトを作ります。--templateの引数は、どのようなテンプレートでプロジェクトを初期化するかを指定したもので、aws-python3はランタイムをPython 3.6としたAWS Lambda向けのテンプレートです。

$ serverless create --template aws-python3 --path service-watcher
$ cd service-watcher

このプロジェクトには、「handler.py」と「serverless.yml」という2つのファイルが含まれています。handler.pyがソースコードです。ただのPythonスクリプトなので、python handler.pyと実行してみても、特にエラーなく終了します。

handler.pyを次のように編集してください。このコードは、example.comにアクセスして、レスポンスが返ってくるまでにかかった時間を表示するものです。

import datetime
import urllib.request

def hello(event, context):
    started_at = datetime.datetime.now()
    with urllib.request.urlopen("https://example.com"):
        ended_at = datetime.datetime.now()
        elapsed = (ended_at - started_at).total_seconds()
        return f'Time: {elapsed} seconds'

これをデプロイしてみましょう。

$ serverless deploy

AWS Lambdaの管理画面にログインすると、service-watcher-dev-helloという関数が作成されているはずです。Lambda関数の詳細を見ると、「関数コード」の項目の中にhandler.pyというファイルが存在していて、先ほど変更したコードがデプロイされていることが分かります。

8

デプロイされたLambda関数

Lambda関数の動作確認

試しに、管理画面上で「テスト」のボタンを押してLambda関数を実行してみてください。すると実行結果が表示され、サービスのレスポンスタイムの監視ができていることが分かります。

9

実行結果

課金期間の「100 ms」は、わずか100ミリ秒のみが課金対象となっていることを示しています。つまり、1回実行しても0.000000208円分の費用しか発生していないということです。

同様の監視ツールをEC2のようなIaaSで作るには、あらかじめインスタンスを借りておかないといけません。しかし、Lambdaの場合は必要なときに必要なリソースのみをほぼリアルタイムに確保できているため、Lambdaを動かしている間の100ミリ秒分だけ費用を支払えば良いことが分かります。

Lambda関数のテスト実行は、ターミナル上からもServerless Frameworkを使って行なうことができます。

$ serverless invoke -f hello
"Time: 0.056416 seconds"

ここまでで、Lambda関数の基本的なデプロイと動作確認が完了したことになります。このままでは手動でLambda関数を実行したときにしか監視ができませんので、定期的に実行されるようにしてみます。

Lambda関数を定期的に実行する

serverless.ymlを開いて、次のように変更してください。events:以下の部分が、今回の変更です。

functions:
  hello:
    handler: handler.hello
    events:
      - schedule: rate(1 minute)

再度、デプロイを行います。

$ serverless deploy

管理画面を確認してみると、左側にCloudWatch Eventsが追加され、スケジュール式のところに「rate(1 minute)」と表示されています。

10

CloudWatch Events

この左側の欄はAWS上では「トリガー」と呼ばれていますが、「どのようなイベントを起点として、Lambda関数が起動するか」を表しています。この場合は、「1分に1回」というイベントです。例えば、サーバーレスなAPIの場合、「API Gatewayへのリクエストが発生すること」がトリガーとなります。

監視結果を保存する

最後に、監視の結果をCloudWatch Metricsに書き込みます。次のようにhandler.pyを変更してください。

import datetime
import urllib.request
import boto3

def hello(event, context):
    cloudwatch = boto3.client('cloudwatch')
    started_at = datetime.datetime.now()
    with urllib.request.urlopen("https://example.com"):
        ended_at = datetime.datetime.now()
        elapsed = (ended_at - started_at).total_seconds()
        cloudwatch.put_metric_data(
            Namespace='Watcher',
            MetricData=[{
                'MetricName': 'ResponseTime',
                'Unit': 'Milliseconds',
                'Value': elapsed,
                'Dimensions': [
                    {'Name': 'URL', 'Value': 'https://example.com'}
                ]
            }]
        )

これをデプロイして、実行してみましょう。

$ serverless deploy
$ serverless invoke -f hello

すると、次のようなエラーが出て失敗します。

{
    "errorMessage": "An error occurred (AccessDenied) when calling the PutMetricData operation(略)",
    ...
}

これは、今デプロイしたAWS Lambdaが、CloudWatch Metricsに対して書き込みをする権限を持っていないために起きているエラーです。

AWSなどのクラウドサービスに慣れていないうちは、どうしてもクラウド特有の権限設定などで詰まったりすることもあるので要注意です。

serverless.ymlを変更して、Lambda関数にCloudWatch Metricsに書き込むための権限を与えます。iamRoleStatements:以下の行が今回の変更分です。

provider:
  name: aws
  runtime: python3.6
  iamRoleStatements:
    - Effect: "Allow"
      Action:
       - "cloudwatch:PutMetricData"
      Resource: "*"

Lambda関数のソースコード内のcloudwatch.put_metric_dataという処理に権限が足りていなかったので、cloudwatch:PutMetricDataの権限を許可しています。

再度、デプロイと実行をしてみましょう。

$ serverless deploy
$ serverless invoke -f hello

今度はエラーが表示されず、正常にCloudWatch Metricsへの書き込みが行われたはずです。

監視結果を確認する

エンジニアHubに会員登録すると
続きをお読みいただけます(無料)。
登録のメリット
  • すべての過去記事を読める
  • 過去のウェビナー動画を
    視聴できる
  • 企業やエージェントから
    スカウトが届く