ようへいの日々精進XP

よかろうもん

alerty プラグイン三種盛り - いくつかの知見を添えて

tl;dr

alerty という cron の失敗の結果を各種通知先にアラートを飛ばすツールを sonots さんが作られた。

blog.livedoor.jp

このツールのイイなあと思うところの一つはプラグインによって通知先を増やせる点。また、README にも書かれているが、プラグインの実装がとても簡単に出来るようになっている点も自分のような Ruby HelloWorld 検定准一級(初心者)には嬉しい配慮だと思う。

ということで、alerty の公開に便乗して勉強を兼ねてエイヤで三種のプラグインを作ったので、作成の過程で得た知見等を雑にメモる。 尚、alerty の詳細については上記の Blog 記事や以下の Github リポジトリをご一読あれ。

github.com


プラグイン三種盛り

三種のプラグインについてリンクと図で。

alerty-plugin-datadog_event

以下、ざっくり処理の流れ。Datadog の Monitor からさらに担当者への通知も定義することが出来る。(そこまでやるかは置いといて)

f:id:inokara:20150909055836p:plain

以下、スクショ。

f:id:inokara:20150908234238p:plain

alerty-plugin-amazon_cloudwatch_logs

以下、ざっくり処理の流れ。CloudWatch Logs から Metric Filter → Alert という流れで更に担当者への通知も定義することが出来る。(同じくそこまでやるかは置いといて)

f:id:inokara:20150909062417p:plain

以下、スクショ。

f:id:inokara:20150908234909p:plain

alerty-plugin-post_im_kayac

以下、ざっくりと処理の流れ。Cron の実行結果は S3 に放り込んでおいて Presigned URL を発行、発行してみて気付いたが URL が長すぎたので bitly で短縮して im.kayac の handler に含めて通知、URL を踏んで詳細のログは S3 で確認するという流れ。尚、せっかくの Presigned URL なので bitly 上で短縮した URL は非公開設定しておく。

f:id:inokara:20150909070119p:plain

おや、アラートだ。

f:id:inokara:20150909054005p:plain

どれどれ。

f:id:inokara:20150909054032p:plain

赤丸で囲まれたリンクをクリックすると既定のブラウザで開く。

f:id:inokara:20150909054359p:plain

詳細のログが記録された S3 バケットにアクセスしてログを確認することが出来る。

f:id:inokara:20150909054129p:plain


いくつかの知見

Datadog Events API の Source Type について

知見というわけではないが、alerty-plugin-datadog_event を作るにあたっての調査報告。

Datadog Events API において Event を Put する際に source_type_name というオプションがあるので、これを指定して Event を put することでダッシュボードの Events において sources: foo,bar 等のようにイベントを絞り込むことが出来る。

f:id:inokara:20150908222705p:plain

ところが...以下の実験にて source_type_name 以下では source に該当は任意の値を指定しても My Apps として登録させてしまうのではないか疑惑。

#
# Event を put する
#
% curl  -X POST -H "Content-type: application/json" \ 
-d '{
      "title": "foo",                         
      "text": "bar",    
      "priority": "normal",
      "tags": ["environment:test"],
      "alert_type": "info",
      "source": "baz"    
  }' \
'https://app.datadoghq.com/api/v1/events?api_key=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
{"status": "ok", "event": {"priority": "normal", "date_happened": 1441719048, "handle": null, "title": "foo", "url": "https://app.datadoghq.com/event/event?id=2977220706502185346", "text": "bar", "tags": ["environment:test"], "related_event_id": null, "id": 2977220706502185346}}
#
# put した Event を確認
#
% ruby test.rb
["200", {"events"=>[{"date_happened"=>1441719048, "alert_type"=>"info", "is_aggregate"=>false, "title"=>"foo", "url"=>"/event/event?event_id=2977220706502185346", "text"=>"bar", "tags"=>["environment:test"], "comments"=>[], "device_name"=>nil, "priority"=>"normal", "source"=>"My Apps", "host"=>nil, "resource"=>"/api/v1/events/2977220706502185346", "id"=>2977220706502185346}]}]

Event を put する際には "source": "baz" で put しているにも関わらず Event を get してみると "source"=>"My Apps" でレスポンスが返ってきている。これは、そもそも source には任意を自由に定義出来るのではなく、決められた値のみ指定することが可能でそれ以外を指定した場合には My Apps と返ってくるのか、それとも source への任意の値を指定することはまだ未対応なのか個人的な七不思議の一つとして引き続き調査を進めたい。

CloudWatch Logs の Timestamp

alerty-plugin-amazon_cloudwatch_logs を作る際に得た知見という程ではないがドキュメントをちゃんと読みませう的な内容。AWS SDK for Ruby V2 を利用して、以下のように put_log_events メソッドでメッセージを送信する

resp = @client.put_log_events({
  log_group_name: @log_group_name,
  log_stream_name: @log_stream_name,
  log_events: [
    {
      timestamp: timestamp,  
      message: event_message.to_json,
    },
  ]
})

この際の timestamp については Time.now.to_i で得られる Epoch Time ではなくて Epoch Time のミリ秒までが必要となる為、こちらの記事を参考にさせて頂いて以下のようにミリ秒までを取得した。

timestamp = Time.now.instance_eval { self.to_i * 1000 + (usec/1000) }

以下、API ドキュメント。

A point in time expressed as the number of milliseconds since Jan 1, 1970 00:00:00 UTC.

上記の記述の通りミリ秒での指定が必要となる。

CloudWatch Logs にログを放り込む場合には token 必須

こちらもログを放り込む際のネタ。

ロググループ、ログストリーム作りたての一発目は以下のように put_log_event でログを放り込むことが出来る。

resp = @client.put_log_events({
  log_group_name: @log_group_name,
  log_stream_name: @log_stream_name,
  log_events: [
    {
      timestamp: timestamp,  
      message: event_message.to_json,
    },
  ]
})

一発目にログを放り込んだ際のレスポンスに以下のような nextSequenceToken が含まれる(以下は AWS CLI でのレスポンス例で AWS SDK for Ruby の場合にはレスポンスオブジェクトから next_sequence_token というメソッドで取得可能)ので、次にログを放り込む際にはこの nextSequenceToken の値を sequence_token: キーの値として定義した上で put_log_events する。

{
    "nextSequenceToken": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}

以下は二回目以降の put_log_events のパラメータ。

resp = @client.put_log_events({
  log_group_name: @log_group_name,
  log_stream_name: @log_stream_name,
  log_events: [ 
    {
      timestamp: timestamp,  
      message: event_message.to_json,
    },
  ],
  sequence_token: next_token
})

今回のプラグインではログを放り込む際に取得した next_sequence_token をローカルのファイルに書き込んで、次のタイミングで放り込む際に書き込んだトークンを取得して利用するという方法を取ってみた。

AWS SDK for Ruby V2 で S3 Presigned URL を使って...

alerty-plugin-post_im_kayac を作る際に cron の実行結果を im.kayac の message に載せると見辛かったので message の中身を S3 にアップロードしておいて URL だけメッセージに載っけて飛ばそうと思ったけど、出来るだけセキュアにやりたかったので署名付き URL を発行することにした。AWS SDK for Ruby V2 では以下の手順で署名付き URL を発行することが出来る。

def put_log_to_s3(message)
   obj = @s3.bucket(@s3_bucket).object('KeyName')
   url = URI.parse(obj.presigned_url(:put))
   Net::HTTP.start(url.host) do |http|
     http.send_request("PUT", url.request_uri, message, {
        "content-type" => "",
      })   
   end  
   obj.presigned_url(:get, expires_in: 600) 
end
  • Aws::S3::Resource クラスのインスタンスを作成
  • Aws::S3::Resource クラスインスタンスの bucket メソッドと object メソッドを呼び出して、バケット名とオブジェクトキーを指定する
  • URI クラスのインスタンスを作成し、これを使用して Aws::S3::Resource クラスインスタンス.presigned_url メソッドを利用して署名付き URL を生成する(:put:get で取得出来る URL が異なるようだ→未検証、また :expires_in でオブジェクトの期限を設定することが出来る)

こちらも以下のドキュメントに記載されている。

docs.aws.amazon.com

余談だが、AWS のドキュメントが読みやすくなっているのはとても嬉しい。

Ruby で im.kayac.com にメッセージを送る

以下の Gem を利用すると簡単だった。

github.com

今回のプラグインはパスワード認証のみ対応しているが、上記の Gem は秘密鍵認証にも対応している。

bitly で短縮 URL を生成する

署名付き URL が長すぎたので短縮した方が見栄えがイイと思ったので bitly で 署名付き URL を短縮することにした。こちらは以下の Gem を利用した。

github.com

以下のように書くだけで簡単に短縮 URL を取得出来た。

Bitly.use_api_version_3
bitly = Bitly.new(config.bitly_user_name, config.bitly_api_key)
bitly.shorten("http://xxxx.xxx.com").short_url

事前に bitly の User Name と API キーを取得しておく。

f:id:inokara:20150908232728p:plain

また、bitly ではデフォルトで短縮した URL は公開されてしまうので公開されないよう上記のように非公開設定にしておくことをお忘れなく...。


todo

todo こそモチベーション。

alerty-plugin-datadog_event

  • source_type_name を使えるようにする

alerty-plugin-amazon_cloudwatch_logs

  • log_group_namelog_stream_name が存在しない場合には自動で追加するようにする
  • Windows 環境で token を書き込むファイルへの書き込みが上手く動作しないことがあったので調べる

alerty-plugin-post_im_kayac

  • 秘密鍵認証の対応
  • cron の実行ログを保存する S3 キーの名前を日付、ホスト名を合わせたキー名にする
  • Presigned URL の expire を設定ファイルで指定出来るようにする

全般的に

  • 各種エラー処理

以上。

もし、よろしければプラグインを使って頂いてフィードバック等頂けるとありがたや、ありがたや。