en ja en nb ja nl ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja en ja ja ja ja ja ja ja en ja ja ja ja en ja en ja ja ja en ja ja ja ja ja en ja ja ja ja ja en nl ja pl en ja nb ja ja nb ja ja en ja ja en ja ja ja ja ja en ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja jbo en en ja nl nb en ko it en it it it en fr it ja ja en la fi ja it it it pt it de en ja en en it fr it en it nb it ja ja ja ja en ja ja ja ja ja ja ja ja ja
AWSにはSQS (Simple Queue Service) というサービスが用意されています。これは名前の通り、メッセージを入れたり出したりするためのキューを提供します。キューは単独で使うこともできて、たとえばシナリオはこんな風になります。
データを吐くシステムAと、そのデータを処理するシステムBがある
システムAは不定期にデータを出力し、そのデータを書いたメッセージをキューに格納する
システムBは暇になったときキューからメッセージを取得し、そこに書いてあるデータを処理する(処理したらメッセージを削除する)
AとBは直接メッセージのやり取りはしないが、協調して非同期に動作することができる
「メッセージ」はプレーンテキストで記述された任意のデータです。最大サイズは256KBまで。多くの用途には十分なサイズです。
Slack APIと繋げると
弊社で利用しているチャットシステム・SlackはAPIが充実しており、HTTP POSTで送信した内容をそのままチャットでbotに発言させるためのAPIがあります。この仕組みを使うと、簡単なバッチを走らせるだけでCloudWatchのアラートをSlackに送信することができます。
CloudWatchは警告発生時にSNSのトピックへメッセージを送信する
そのトピックはSQSのキューにメッセージを格納する(ここまではAWSのコンソールから設定可能)
EC2上に小さなバッチを用意する。このバッチは無限ループを回り、キューを監視する
キューにメッセージが入ってくると、バッチはSlackにその旨を通知する
というわけで、簡単に手順を書いてみます。今回はバッチにrubyを使いますが、PHPでもPowerShell(!!)でも手順はあまり変わりません。大まかな手順は次の通りです。 1. AWS APIを叩くためのユーザーと認証用キーを用意する 1. SQSのキューを作成する 1. SNSのトピックを作成してSQSとつなげる 1. CloundWatchのメトリクスを作成してSNSとつなげる 1. SQSを監視するスクリプトを用意する
API用のIAM Userを用意する
存在しなければ新しく作りましょう。ユーザー詳細画面のPermissionsタブの中にInline Policiesメニューがあるので、Policy Generatorで権限を作成します。Amazon SQSの下にある「Delete Message」「ReceiveMessage」だけ選択しておけば良いでしょう。
Add Statementボタンを押すのを忘れないように
SQSで新しいキューを作成します。Create New Queueを選び、設定(まあデフォルトで良いでしょう)を決めたらすぐに作成されます。
この時点でエンドポイントとARNが決まります
キューを作成すると、コンソールからテストメッセージを(手書きで)SQSへ送信したり、キューに入っているメッセージを確認・削除したりできます。右クリックメニューのSend a Messageを選択するとメッセージ送信ウィンドウが開きます。これをrubyで取得してみましょう。
gem install aws-sdkでSDKがインストールできるので、その後は次のようなコードを実行するだけです。ロングポーリングを勝手にやってくれるので、自分でウェイトをかけたりする必要はありません。
require 'aws-sdk' # 認証用キーのデフォルト値を設定する。今後APIを叩くたび毎回この値が送信される Aws.config = {:access_key_id => ' ', :secret_access_key => ' ', :region => 'ap-northeast-1'} # キューにアクセスするためのインスタンスを取得 poller = Aws::SQS::QueuePoller.new('https://sqs.ap-northeast-1.amazonaws.com/999999999999/HelloSQS') poller.poll(:wait_time_seconds => 20, :idle_timeout => 5) {|msg| p msg}
実行結果はこんな感じでした。「HELLO WORLD!」がコンソールから入力した値です。またpollメソッドの戻り値として、メッセージの取得件数、最終メッセージのタイムスタンプなどが取れます。また、pollで受信したメッセージは自動的に削除されるため、明示的な削除は不明です(この動作は:skip_deleteにfalseを指定してスキップできます)。
irb(main):013:0> poller.poll(:wait_time_seconds => 20, :idle_timeout => 5) {|msg| p msg} # "……", "ApproximateFirstReceiveTimestamp"=>"1427414334046", "ApproximateReceiveCount"=>"1", "SentTimestamp"=>"1427414334044"}, md5_of_message_attributes=nil, message_attributes={}>
キューにパーミッションを追加する
現時点ではこのキューにメッセージを送ることができるのは自分だけです。SNSからメッセージを送信することができるように、Add a Permissionメニューからパーミッションを追加しておきましょう。
エンドポイントを知っていればどこからでもこのキューにメッセージを投げ込むことができるようになる
次はSNSです。Create New Topicでトピックを作成したら、Create Subscriptionで購読設定の追加を行います。ついでに自分のメールアドレスを追加しても結構です。(メールアドレスの確認作業が必要になるため手順は省略します)
これでSNSとSQSがつながったことになります。SNSのトップに戻り、Publish to topicをクリックしてテストメッセージを送信してみましょう。
これをさっきのrubyのコードで取得してみます。結果のうち、bodyプロパティの値を示します。JSONエンコードされているのでちょっと整形しました。
{ "Type" : "Notification", "MessageId" : "77c5f3c4-679b-5931-aed7-be4f5619a1d2", "TopicArn" : "arn:aws:sns:ap-northeast-1:999999999999:HelloSNS", "Subject" : "HELLO SNS!", "Message" : "HELLO I AM NEKO", "Timestamp" : "2015-03-24T09:32:50.814Z", "SignatureVersion" : "1", "Signature" : "wZMZ....g==", "SigningCertURL" : "https://....amazonaws.com/SimpleNotificationService-....pem", "UnsubscribeURL" : "https://....amazonaws.com/?Action=Unsubscribe&SubscriptionArn=...", "MessageAttributes" : { "AWS.SNS.MOBILE.MPNS.Type" : {"Type":"String","Value":"token"}, "AWS.SNS.MOBILE.WNS.Type" : {"Type":"String","Value":"wns/badge"}, "AWS.SNS.MOBILE.MPNS.NotificationClass" : {"Type":"String","Value":"realtime"} } }
CloudWatchを用意する
AWS側はこれで最後です。CloudWatchのメトリクスを作成します。
Descriptionは後から好きに変更できますが、Nameは一度作成したら変えられません。また、現状ではメトリクスにタグを付けることもできないので、監視内容のレベルに応じて「WARN」や「ERROR」といったプレフィックスを付けておくと良いかもしれません。図ではステータスがALARMになったときのみ通知を行っていますが、OKになったときにも通知を行うこともできます。
作成したらテストをしてみます。次のコードで、作成したメトリクスのコードをむりやりALARMにセットしてみます(これはテスト用コマンドなので、現在の値(正常値)にあわせて勝手に元に戻ります)。
cloudwatch = Aws::CloudWatch::Client.new(region: 'ap-northeast-1') cloudwatch.set_alarm_state(alarm_name: "WARN_HelloCloudWatch", state_value: "ALARM", state_reason: "THIS IS NOT A DRILL")
こういう1回限りの操作はPowerShellでやってもいいです。
PS C:\>Initialize-AWSDefaults -accessKey ' ' -secretKey ' ' -Region ap-northeast-1 PS C:\>Set-CWAlarmState -AlarmName WARN_HelloCloudWatch -StateValue ALARM -StateReason "THIS IS NOT A DRILL"
メトリクスのステータスがALARMに変更され、SNS→SQSとメッセージが送信されてゆきます。
Slack botの準備をする
Slackへ何か送信するためには、事前にIncoming Webhookの設定画面でもらえるトークンが必要です。トークンがあれば、次の簡単なコードで送信ができます。
def saySomething(channel, text, username = nil, emoji = nil) token = ' ' uri = 'https://your-slack-domain-name.slack.com/services/hooks/incoming-webhook' params = {:channel => channel, :text => text, :username => 'SlackBot'} params[:username] = username if username params[:icon_emoji] = emoji if emoji query = {'payload' => params.to_json}.map{|k, v| "#{k}=#{v}"}.join('&') uri_parsed = URI.parse(uri) http = Net::HTTP.new(uri_parsed.host, 443) http.use_ssl = true http.post(uri_parsed.path + '?token=' + token, URI.escape(query)).body end
textは発言内容です。改行は\r\nを使ってください。
channelは送信先チャンネル名で、'chanelName', '#privateGroupName', '@userName'のように指定します。スペルミスがあるとそのメッセージは闇に飲まれて消えてしまいます。
emojiはbotのアイコンを指定するための値で、':heart:'のようにコロンでくくった形で渡します。
後は無限ループの中でキューをポーリングし、何かメッセージがあったらSlackにそのまま送信するだけです。
require 'aws-sdk' Aws.config = {:access_key_id => ' ', :secret_access_key => ' ', :region => 'ap-northeast-1'} poller = Aws::SQS::QueuePoller.new('https://sqs.ap-northeast-1.amazonaws.com/999999999999/HelloSQS') channelName = 'your_channel_name' while true poller.poll(:wait_time_seconds => 20, :idle_timeout => 5) {|msg| begin body = JSON.parse(msg.body) message = JSON.parse(body['Message']) rescue => e saySomething(channelName, "電文のパースに失敗しました: " + msg.body) return false end if message == nil || message['NewStateValue'] == nil || message['AlarmDescription'] == nil saySomething(channelName, "なんか変なメッセージが来てます: " + msg.body) return false end text = '' if message['NewStateValue'] == 'OK' saySomething(channelName, "[復帰] CloudWatchのアラートが解消しました。\r\n" + "Name : " + message['AlarmName'] + "\r\n" + "Descrpition : " + message['AlarmDescription'] + "\r\n" + "Reason : " + message['NewStateReason'], "よかったね", ":heart:") else saySomething(channelName, "[警告] CloudWatchのアラートが発生しました。\r\n" + "Name : " + message['AlarmName'] + "\r\n" + "Descrpition : " + message['AlarmDescription'] + "\r\n" + "Reason : " + message['NewStateReason'], "たいへんです", ":broken_heart:") end } end
Slackにこうして通知が来ます。
エラー通知は大切ですが、受け取る側が気付かないのでは意味がありません。このSlack連携を導入して以来、エラー発生から対応までの時間が激減(多くの場合は即時)しました。 これだけ簡単な設定とバッチで通知システムができるのは楽しいですし、(得られる価値÷書いたコードの行数)は自分の仕事人生の中でも最大値を記録しました。
今回はSlackに通知をするようにしましたが、重要度に応じて携帯電話に音声発信をするTwillioのAPIを呼べば電話通知もできるようになります。 これからもなるべく手抜きをして最大の成果が得られるように頑張っていこうと思います。
*1:要するにスマホに通知が出ます
nurenezumi 2015-03-27 18:40
API Gateway + LambdaでSlackのCustom Commandを作る
こんにちは、AWS担当のwakです。先日このような記事を書いたの…
Twilioを使ってWeb API経由で電話をかける(前編)
こんにちは、AWS担当のwakです。 【インデックス(予定)】 Clo…
AWS LambdaのパラメータをDynamoDBで管理する
こんにちは、AWS担当のwakです。前回に続きAWS Lambdaの話をし…
AWS LambdaでWebサイトの死活監視を行ってSlackに結果を流す
こんにちは、AWS担当のwakです。前回に続きAWS Lambdaの話をし…
CloudWatchの監視結果をSlackに流す(AWS Lambdaバージョン)
こんにちは、AWS担当のwakです。 自分が書いた過去の記事を読み…
« PowerShellの作業が捗るかもな小技4つ Go言語フレームワークRevelをCentOSで動作… »