RareJob Tech Blog

レアジョブのエンジニア・デザイナーによる技術ブログです

サーバレスで外形監視を実装しオンプレとの違いを検証してみた

はじめに

レアジョブのプラットフォームチームでチームリーダーをさせていただいている塚田です。 Microservicesで共通基盤を作るチームですが、チームの特性上コンテナやLambdaを触ることが多いチームです。 今回はLambdaについての記事です。

課題

サービス提供会社として、自身が提供しているサービスが問題なく動いているか監視する必要があります。 監視という観点でいうと、サーバのリソースを描写し、閾値を超えた場合通知するよう作るのも必要ですが、 実際ユーザーはどのくらいの体感速度だったのかという観点は非常に重要です。

ユーザーの体感を定量的に可視化するため、我々は昔からオンプレのZabbixを用いて外形監視の一つとしてユーザー視点でレスポンスタイムを観測してきました。 しかし、そろそろオンプレの運用も脱出したい。願わくばサーバ(IaaS)を使わず運用したい。

そんな中、Lambdaでカスタムランタイムをサポートしたというニュースが2018年に流れたので、 シェルを動かせばソッコー実装できるのではと思い試してみることにしました。

やってみた

まず、LambdaのLayersを作成します。 ここでLayersを作る目的は、大きく2つありました。

  • シェルを実行できるようにする
  • awscliを使えるようにする

という理由です。Layer化しておけば外部ライブラリを簡単にインポートできるようになります。

ソースコードは公式が公開 チュートリアル – カスタムランタイムの公開 - AWS Lambda しているものを少し調整しています。 以下のような感じです

#!/bin/sh
set -euo pipefail

# tmpフォルダ内にawscliを配置するためHOMEとPATHを設定
export HOME="/tmp"
export PATH="$HOME/.local/bin:$PATH"

# install pip
cd /tmp
curl -sSL https://bootstrap.pypa.io/get-pip.py -o get-pip.py
python get-pip.py --user

# install awscli using pip
pip install awscli --user 

# ハンドラ関数の読み込み
# Initialization - load function handler
source $LAMBDA_TASK_ROOT/"$(echo $_HANDLER | cut -d. -f1).sh"

# Processing
while true
do
    HEADERS="$(mktemp)"
     
    # Get an event
    EVENT_DATA=$(curl -sS -LD "$HEADERS" -X GET "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next")
    REQUEST_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2)
     
    # ハンドラ関数を実行
    # Execute the handler function from the script
    RESPONSE=$($(echo "$_HANDLER" | cut -d. -f2) "$EVENT_DATA")
     
    # 結果を返却
    # Send the response
    curl -kL -X POST "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$REQUEST_ID/response" -d "$RESPONSE"  -o /dev/null 2> /dev/null
done

その後、zip化。zip awscli.zip bootstrap runtimes は python3.7 を指定します。

コマンドでLambdaへuploadする際は、以下のようなコマンドで実施できます。

aws lambda publish-layer-version \
    --layer-name awscli \
    --zip-file fileb://awscli.zip \
    --compatible-runtimes python3.7

もし、コンソール画面から行う場合はここから登録します。

f:id:sumito1984:20190807105432p:plain
Lambda-Layers

ここまでは準備段階です。

その後、Lambda関数用にシェルを作成します。

function handler () {
    res=$(curl -kL 'https://www.rarejob.com/dna' -o /dev/null -w "%{time_total}" 2> /dev/null)
    aws cloudwatch put-metric-data --metric-name responseTime --namespace responseTime --value $res --dimensions Name=Time,Value=second 
}

運用時はURLをここにベタに書かず、DynamoDBから参照させたりした方が良いと思いますが、今回は便宜上ベタ書きにします。

今回の監視対象はDaily News Article。レアジョブで最も人気のある、日替わりのニュース教材です。 www.rarejob.com

その後、 function.shをzip化。zip function.zip function.sh Lambda関数を登録します。

f:id:sumito1984:20190807112235p:plain
lambda関数

ランタイムはカスタムランタイム、 ハンドラーはfunction.handlerをしてください。

Lambdaの実行IAMに

  • AWSLambdaBasicExecutionRole
  • CloudWatchの書き込み権限

を与えてください。

レイヤーには準備としてuploadしたLayersのawscliを指定します。

f:id:sumito1984:20190807112533p:plain
Layers

トリガーには、CloudWatch Eventsで、スケジュール式: rate(3 minutes) を設定。 今回は3分単位で監視するようにします。

あとは、CloudWatchのダッシュボードをよしなに設定すれば、描写を確認することができます。

f:id:sumito1984:20190807113034p:plain
CloudWatch

既存の監視(オンプレのzabbix)と比較してみる

f:id:sumito1984:20190807113112p:plain
zabbix

異常値を無視すると、 オンプレのzabbixは0.1 秒程度のレスポンスタイムを確認 一方で今回のLambdaで実施したレスポンスタイムは0.3秒程度でした。

考察

レスポンスタイムが、オンプレのzabbixとLambdaで一致しているわけではないというのが注意です。

  • 単純に処理のタイミング。
  • zabbixに組み込まれているレスポンスタイム処理と、今回curlコマンドでのレスポンスタイム処理自体の違い。
  • zabbixはオンプレで動いているが、Lambdaは毎回起動される度に同一サーバではない可能性があるという点。

などなど、考えられるので原因は複合的かもしれません。 現在は検証段階なので、外形監視の本格移行などは予定はしておりません。

今回の検証は、いちエンジニアの「やりたいこと」に過ぎなかったのですが、レアジョブでは「やりたいこと」が推奨されている会社です。 その中では、サービスの一部としてとして形になっているもの、検証だけで終わってしまったもの様々です。

しかしどんどんチャレンジして、世の中にアウトプットを出して行こうと思っています。