Usual Software Engineer

よくあるソフトウェアエンジニアのブログ

サーバーレスアーキテクチャでモニタリングシステムを実現する

モニタリングシステムはサーバーレスアーキテクチャで実現されるべきである。

みなさんは自社のサービスを外のネットワークからE2Eで死活やパフォーマンスを確認したい場合に、どのようにそれを行っていますか?
モニタリングシステムと言えば今はZabbixが定番でしょうか。もしくはいくつかのOSSモニタリングツールを組み合わせていたり、今風に有料のSaaSを利用していたりするのでしょうか。
ここではサーバーレスでアプリケーションパフォーマンスモニタリングを行うアーキテクチャを紹介します。

何故サーバーレスが良いのでしょうか?
サイトの単純なレスポンスタイムならよいですが、status.github.comページにあるような"MEAN HOOK DELIVERY TIME"など、hook実行までの経過時間やpush通知が届くまでの経過時間、kafkaのstream上のrecordがconsumeされるまでの経過時間を定期的に計測監視したい場合に、自前で計測のスクリプトやアプリケーションを書かなければいけません。*1
そして当然そのスクリプトやアプリケーション自身も何かしらでうまく動いているかどうかを監視・管理することになります。 つまり、
”「アプリケーションをモニタリングするためのアプリケーション」をモニタリングするためのアプリケーションをモニタリング...”
というように無限ループになってしまいます。
この無限ループから脱却するためには、 モニタリングアプリケーションをサーバーレスアーキテクチャで実現することで、マネージドサービス内でモニタリングの仕組みを完結させる ように設計するのが最適なのではないかと考えたわけです。

言いたいことはほぼ言ってしまったのですが、一つの例としてserverlessを使って実装してみました。*2
ソースコードのサンプルはこちらです。とても小さく、計測部分はダミーのコードになっています。
ちなみにAPMはapplication performance managementの略です。

github.com

コードに関して特に説明することはないのですが、handler.pyに定義した2つの関数がメインになっています。

serverless-apm-example/handler.py at 9f074b7fdee0c8cc3719e9d1e98f01b3d06d3668 · innossh/serverless-apm-example · GitHub

def monitor_response_time(event, context):
    elapsed_time = -1
    try:
        elapsed_time = test_response()
        put_cloudwatch_metric('response_time', elapsed_time, get_current_region(context))
    except Exception as e:
        print("Exception: " + str(e))
    return elapsed_time


def monitor_hook_delivery_time(event, context):
    elapsed_time = -1
    try:
        elapsed_time = test_hook()
        put_cloudwatch_metric('hook_delivery_time', elapsed_time, get_current_region(context))
    except Exception as e:
        print("Exception: " + str(e))
    return elapsed_time

それぞれのパフォーマンス計測結果をCloudWatchのcustom metricsとして保存する流れになっています。
これらの関数はserverless.ymlに定義されており、それぞれLambda functionとしてデプロイされることになります。

serverless-apm-example/serverless.yml at 9f074b7fdee0c8cc3719e9d1e98f01b3d06d3668 · innossh/serverless-apm-example · GitHub

functions:
  monitor_response_time:
    handler: handler.monitor_response_time
    description: 'Monitor response time'
    events:
      - schedule:
          description: 'Calling monitor_response_time function every minute'
          rate: rate(1 minute)
  monitor_hook_delivery_time:
    handler: handler.monitor_hook_delivery_time
    description: 'Monitor hook delivery time'
    events:
      - schedule:
          description: 'Calling monitor_hook_delivery_time function every minute'
          rate: rate(1 minute)

またそれぞれCloudWatch EventsのScheduleが設定されているため、毎分Lambda functionが呼び出される仕組みになっています。
デプロイは対象のディレクトリで serverless deploy コマンドを実行するだけです。

$ serverless deploy
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 (8.11 KB)...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
......................
Serverless: Stack update finished...
Service Information
service: serverless-apm-example
stage: dev
region: ap-northeast-1
api keys:
  None
endpoints:
  None
functions:
  serverless-apm-example-dev-monitor_response_time: arn:aws:lambda:ap-northeast-1:************:function:serverless-apm-example-dev-monitor_response_time
  serverless-apm-example-dev-monitor_hook_delivery_time: arn:aws:lambda:ap-northeast-1:************:function:serverless-apm-example-dev-monitor_hook_delivery_time

とても簡単ですが、初めてAWS Lamdbaを使う場合は何が起きているかよくわからなくて逆に難しく感じるかもしれません。
裏側ではCloudFormation、S3、IAM Role、Lambda、CloudWatch Eventsなどを利用して関数がサーバーレスで動く環境が作られています。

Lambda

f:id:innossh:20170122170320p:plain

CloudWatch Events

f:id:innossh:20170122170326p:plain

ここまで自身で用意したのは handler.pyserverless.yml だけですね。
これだけでアプリケーションのパフォーマンス監視ができてしまうといっても過言ではありません。
さらにLambda自体も簡単にCloudWatchで監視・ログ確認・アラート通知などができますので、
この全体の仕組自体の監視がAWSのマネージドサービス上で完結できるわけです!

それでは保存したcustom metricsをCloudWatchのダッシュボードを作成し確認してみましょう。

f:id:innossh:20170122170345p:plain

必要であればしきい値を設定してアラート通知の設定をすることもできます。アラート通知はserverlessのツールでは設定できないのでマネージメントコンソールかAWS CLIなどを使ってください。

f:id:innossh:20170122170332p:plain

CloudWatchのcustom metricsに入れてしまいさえすれば、可視化にしろ通知にしろモニタリングとしては十分かなと思います。
少し話がそれますが、Grafanaを使うとよりcoolなダッシュボードを作ることができます。
GrafanaはデフォルトでCloudWatchをデータソースとして扱えるようになっており、 EC2にGrafanaをインストールする場合はそのインスタンスにCloudWatchにアクセス可能なIAM Roleを設定してあげるだけで、 CloudWatchの全てのmetricsのデータをGrafana上で可視化できます。
試しにローカルのDocker上でGrafanaを動かして先ほどのCloudWatchのデータを取り込むことで、このようなダッシュボードを作ることができます。しかも数分で。

f:id:innossh:20170122170339p:plain

やったのは docker run -i -p 3000:3000 grafana/grafana:4.1.1 とwebブラウザから http://localhost:3000 にアクセスしてadmin/adminでログインしてちょろっと設定しただけです。
前からGrafanaはただのグラフ化ツールだろうとあまり触れてこなかったのですが、 いざまともに使ってみるとデータソースの設定はとっても簡単でCloudWatchもZabbixもすぐ連携できますし、 LDAPログインの設定も簡単、そして何より単純なグラフがよりcoolに見えるだけですごく気持ちが良いというなかなか良いツールだと気づきました。
UI/UXだけでも選ぶ価値があるんだなということを再認識しました。

話をもとに戻して、今回サーバーレスアーキテクチャでアプリケーションパフォーマンスのモニタリングを実現する仕組みを紹介しました。
一言で言うならば、モニタリングする仕組みなんて面倒だから自分で管理したくないんだよ!ということでした。
サーバーレスアーキテクチャに興味がありつつなかなかどのシステムから導入してよいか迷って二の足を踏んでいる方は、 プロダクションコードとは別の庭であるモニタリングの部分からサーバーレスアーキテクチャを導入してみると、 相性が良くてうまくいきやすいのではないかなと思います。
それでは良いサーバーレスライフを。

*1:計測したい機能自体のログに経過時間を出力している場合は一般的にログ監視をするだけなのでみなさん慣れているかと思いますが、Microservicesで構成されたサービスの機能をE2Eで計測したい場合はそう簡単にいかないものですよね

*2:一番有名なserverlessの他にapexもあります。どちらもpythonの他にnodejsにも対応しています。またpythonであればAWSが開発しているchaliceというシンプルなツールが便利ですが、こちらはどちらかというとAPI Gatewayを使ったWebサービスを作るのに向いています。