Help us understand the problem. What is going on with this article?

mackerelで監視した後の一次対応を自動化する話(作り方編)

More than 1 year has passed since last update.

はじめに

監視システムからアラートを受信した後におこなう対応のうち、手順が決まっていて誰がやっても同じ部分(一次対応)を自動化して、その後対応するエンジニアが楽になるようにお膳立てします。

mackerelで監視した後の一次対応を自動化する話」からの続きです。実際の作り方を説明していきます

作り方

  1. フローをインポート
  2. フローの設定調整
    • ホストログイン設定
    • Redmine接続設定
    • Slack通知設定
    • デプロイ
  3. Mackerel設定
    • Webhook設定
    • ログイン用IPアドレス設定

1. フローをインポート

  • 次のJSONテキストをコピーして、FrontOpsにインポートします。
[{"id":"525a248c.ecf56c","type":"tab","label":"mackerelトリガーの一次対応","disabled":false,"info":""},{"id":"9fda9de0.5023a","type":"redmine-config","z":"","name":"redmine","endPoint":"","insecure":false},{"id":"59e7c7e6.bcc338","type":"connection","z":"","name":"instance1","host":"TBD","username":"sakazuki"},{"id":"4e73938e.e6c11c","type":"slack-controller","z":"","name":"devops"},{"id":"11fabbe6.f66144","type":"http in","z":"525a248c.ecf56c","name":"","url":"/mackerel","method":"post","upload":false,"swaggerDoc":"","x":120,"y":200,"wires":[["fcb620bf.5a27f","47a817b1.175268","78fb5615.b314c8"]]},{"id":"3a4a2e94.e93b42","type":"redmine","z":"525a248c.ecf56c","redmineConfig":"9fda9de0.5023a","operation":"create","endPoint":"","issueId":"","projectId":"1","trackerId":"3","statusId":"1","assignedId":"","subject":"{{{subject}}}","description":"```\n{{{payload}}}\n```","updateDesc":false,"gonext":true,"name":"チケット作成","x":510,"y":360,"wires":[["e3e07e61.42fda"]]},{"id":"fcb620bf.5a27f","type":"debug","z":"525a248c.ecf56c","name":"","active":true,"console":"false","complete":"payload","x":310,"y":160,"wires":[]},{"id":"47a817b1.175268","type":"http response","z":"525a248c.ecf56c","name":"","statusCode":"","headers":{},"x":290,"y":200,"wires":[]},{"id":"72e2a3b6.ff1f1c","type":"function","z":"525a248c.ecf56c","name":"チケットステータス判定","func":"var session = flow.get(\"session\") || {};\nmsg.hostname = msg.data.host.name;\nmsg.hostid = msg.data.host.id;\nvar al = msg.data.alert;\nmsg.subject = `[${msg.data.orgName}] ${al.status} ${msg.hostname} ${al.monitorName}`;\nif (session[msg.hostid]){\n    msg.redmineIssue = {issueId: session[msg.hostid]};\n    if (msg.data.alert.status === \"ok\"){\n        delete session[msg.hostid];\n        return [null, null, msg];\n    }else{\n        return [null, msg, null];\n    }\n}else{\n    return [msg, null, null];\n}\n","outputs":"3","noerr":0,"x":310,"y":400,"wires":[["3a4a2e94.e93b42"],["1ccd750b.140c6b"],["d4e4d82d.b423b8"]]},{"id":"e3e07e61.42fda","type":"function","z":"525a248c.ecf56c","name":"チケットID保存","func":"var session = flow.get(\"session\") || {};\nsession[msg.hostid] = msg.redmineIssue.issueId;\nflow.set(\"session\", session);\nreturn msg;","outputs":1,"noerr":0,"x":690,"y":360,"wires":[["b7c31cc8.b9a5","d47fd746.75f5a8"]]},{"id":"1ccd750b.140c6b","type":"redmine","z":"525a248c.ecf56c","redmineConfig":"9fda9de0.5023a","operation":"update","endPoint":"","issueId":"","projectId":"1","trackerId":"","statusId":"2","assignedId":"","subject":"{{{subject}}}","description":"```\n{{{payload}}}\n```","updateDesc":true,"gonext":true,"name":"チケット更新","x":510,"y":400,"wires":[["d47fd746.75f5a8"]]},{"id":"b7c31cc8.b9a5","type":"debug","z":"525a248c.ecf56c","name":"","active":true,"console":"false","complete":"true","x":991.0000286102295,"y":361.0000114440918,"wires":[]},{"id":"fcda919d.864bb","type":"switch","z":"525a248c.ecf56c","name":"アラート種別","property":"data.alert.monitorName","propertyType":"msg","rules":[{"t":"regex","v":"CPU|loadavg","vt":"str","case":false},{"t":"regex","v":"Memory|Swap","vt":"str","case":false},{"t":"regex","v":"Filesystem","vt":"str","case":false}],"checkall":"false","outputs":3,"x":570,"y":240,"wires":[["72473be7.a024c4"],["966160ed.9e223"],["23893395.57cb7c"]]},{"id":"72473be7.a024c4","type":"command","z":"525a248c.ecf56c","connectionconfig":"59e7c7e6.bcc338","command":"hostname\nw\nlast -5\necho \"#### top -b -n 1\"\ntop -b -n 1\necho \"#### ps awwfx\"\nps awwfx","timer":"","oldrc":false,"env":"","pty":false,"trim":false,"field":"diag","fieldType":"msg","name":"プロセス情報","x":750,"y":200,"wires":[["9985cb64.97e0d8"]]},{"id":"23893395.57cb7c","type":"command","z":"525a248c.ecf56c","connectionconfig":"59e7c7e6.bcc338","command":"hostname\nw\nlast -5\necho \"#### df -hP\"\ndf  -hP","timer":"","oldrc":false,"env":"","pty":false,"trim":false,"field":"diag","fieldType":"msg","name":"ディスク情報","x":750,"y":280,"wires":[["9985cb64.97e0d8"]]},{"id":"d4e4d82d.b423b8","type":"redmine","z":"525a248c.ecf56c","redmineConfig":"9fda9de0.5023a","operation":"update","endPoint":"","issueId":"","projectId":"1","trackerId":"","statusId":"5","assignedId":"","subject":"{{{subject}}}","description":"```\n{{{payload}}}\n```","updateDesc":true,"gonext":true,"name":"チケット終了","x":510,"y":440,"wires":[["d47fd746.75f5a8"]]},{"id":"764d5da2.7b46a4","type":"change","z":"525a248c.ecf56c","name":"変数準備","rules":[{"t":"set","p":"data","pt":"msg","to":"payload","tot":"msg"},{"t":"set","p":"override.host","pt":"msg","to":"data.host.memo","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":420,"y":240,"wires":[["fcda919d.864bb"]]},{"id":"515328a0.076408","type":"yaml","z":"525a248c.ecf56c","name":"","x":150,"y":400,"wires":[["72e2a3b6.ff1f1c"]]},{"id":"78fb5615.b314c8","type":"switch","z":"525a248c.ecf56c","name":"フィルタ","property":"payload.host.status","propertyType":"msg","rules":[{"t":"eq","v":"working","vt":"str"}],"checkall":"true","outputs":1,"x":290,"y":240,"wires":[["764d5da2.7b46a4"]]},{"id":"4b13b699.cd3ef8","type":"link in","z":"525a248c.ecf56c","name":"Redmine","links":["9985cb64.97e0d8"],"x":55,"y":400,"wires":[["515328a0.076408"]]},{"id":"9985cb64.97e0d8","type":"link out","z":"525a248c.ecf56c","name":"","links":["4b13b699.cd3ef8"],"x":895,"y":240,"wires":[]},{"id":"6edc79f1.820878","type":"debug","z":"525a248c.ecf56c","name":"","active":true,"console":"false","complete":"true","x":990,"y":480,"wires":[]},{"id":"9c67f495.51e6a8","type":"slack-say","z":"525a248c.ecf56c","slack":"4e73938e.e6c11c","channel":"general","msg":"{{{redmineIssue.url}}}\n\n```\n{{{payload}}}\n```\n","output":"str","gonext":false,"name":"","x":1000,"y":440,"wires":[]},{"id":"d47fd746.75f5a8","type":"redmine","z":"525a248c.ecf56c","redmineConfig":"9fda9de0.5023a","operation":"update","endPoint":"","issueId":"","projectId":"1","trackerId":"","statusId":"2","assignedId":"","subject":"","description":"\n### stdout\n```\n{{{diag.stdout}}}\n```\n### stderr\n```\n{{{diag.stderr}}}\n```\n### code\n{{{diag.code}}}\n","updateDesc":false,"gonext":true,"name":"チケット更新","x":690,"y":440,"wires":[["f9610761.dc5068"]]},{"id":"f9610761.dc5068","type":"function","z":"525a248c.ecf56c","name":"添付準備","func":"var filename = msg.hostname + \"-\" + Date.now() + \".txt\"\n\nmsg.attachments = {\n    title: msg.hostname + \" info\",\n    content: msg.diag.stdout,\n    filename: filename,\n    filetype: \"txt\"\n};\n\nmsg.payload = \"#### \" + msg.diag.stdout.split(/^#### /m, 2)[1].split(/\\n/).slice(0, 20).join(\"\\n\");\n\nreturn msg;","outputs":1,"noerr":0,"x":840,"y":440,"wires":[["9c67f495.51e6a8","6edc79f1.820878"]]},{"id":"79da8148.bc89c","type":"comment","z":"525a248c.ecf56c","name":"MackerelのWebhook通知を契機に一次対応をおこなう","info":"","x":230,"y":80,"wires":[]},{"id":"8e8dbed7.b8c63","type":"catch","z":"525a248c.ecf56c","name":"例外時","scope":["23893395.57cb7c","72473be7.a024c4"],"x":850,"y":320,"wires":[["54c21552.af34ec"]]},{"id":"54c21552.af34ec","type":"slack-say","z":"525a248c.ecf56c","slack":"4e73938e.e6c11c","channel":"general","msg":"{{{error.message}}}\n{{{payload}}}","output":"str","gonext":false,"name":"","x":1000,"y":320,"wires":[]},{"id":"966160ed.9e223","type":"command","z":"525a248c.ecf56c","connectionconfig":"59e7c7e6.bcc338","command":"hostname\nw\nlast -5\necho \"#### top -b -n 1 -o +%MEM\"\ntop -b -n 1 -o +%MEM\necho \"#### free -h\"\nfree -h","timer":"","oldrc":false,"env":"","pty":false,"trim":false,"field":"diag","fieldType":"msg","name":"メモリ情報","x":740,"y":240,"wires":[["9985cb64.97e0d8"]]}]
  • 右メニューから[読み込み]-[クリップボード]を開き、JSONデータを貼り付けて、[読み込み]します。

2017-11-30 08_18_19-frontops-2900E64MSPX0mzjh.tmp - FrontOps.png

  • 以下のフローがインポートされます。
    • 実際は、後述する設定が未了なので、注意アイコンが表示されます。

2017-11-30 07_27_03-mackerel1.json - FrontOps.png

2. フローの設定調整

ホストログイン設定

ホストにSSHログインできるようにするために、ログイン認証設定をおこないます。

2017-11-30 07_12_29-mackerel1.json - FrontOps.png

  • [プロセス情報]ノードをダブルクリックして、[接続先]を設定します。

    2017-11-30 07_41_05-mackerel1.json - FrontOps.png

  • [ユーザ名]、[パスワード]、[秘密鍵]、[パスフレーズ]など、ログイン認証に必要な情報を設定します。

    • [ホスト]にはFQDNやIPアドレスを指定します。今回はMackerelから通知されるデータの中から動的にこの情報を設定するため、TBD(To Be Defined) としています > 2017-11-30 06_06_51-mackerel1.json - FrontOps.png
  • もっと詳細に知りたい方は[FrontOps] サーバにSSHログインしてコマンドを実行する方法を参照してください。

Redmine接続設定

Redmine APIでチケット作成・更新が行えるようにAPIキーを設定します。

2017-11-30 07_29_51-mackerel1.json - FrontOps.png

  • [チケット作成]ノードをダブルクリックして、接続設定をおこないます。

    2017-11-30 07_38_19-mackerel1.json - FrontOps.png

  • RedmineのAPIエンドポイント(URL)とAPIキーを設定します。

    2017-11-30 07_30_47-mackerel1.json - FrontOps.png

  • APIキーを取得するには、Redmineにログインして[個人設定]-[APIアクセスキー]-[表示]をクリックします。

    redmine-api-key.png

  • Redmineに合わせて適切な[プロジェクト]や[トラッカー]を選択します。

    • 更新対象は、[チケット作成][チケット更新][チケット終了][チケット更新]の4ノードです

mackerel-redmine.png

Slack通知設定

Slack通知をおこなうために、トークンの設定をおこないます。

2017-11-30 07_47_53-mackerel1.json - FrontOps.png

  • [Slack Say]ノードをダブルクリックして、[Slackトークン]の設定をおこないます。

2017-11-30 07_48_31-mackerel1.json - FrontOps.png

2017-11-30 07_50_19-mackerel1.json - FrontOps.png

  • トークンの取得方法は、Slack Appsのページから、アプリを選択し、Features/[OAuth & Permissions] をクリック。Bot User OAuth Access Token を取得して設定します。

  • 適切なSlackチャンネルを設定します。

    2017-11-30 08_45_39-frontops-2900E64MSPX0mzjh.tmp - FrontOps.png

デプロイ

設定調整が終わったら、[デプロイ]します。これで設定が保存されます。

2017-11-30 08_53_18-frontops-2900E64MSPX0mzjh.tmp - FrontOps.png

3. Mackerel設定

Webhook設定

Webhook通知でFrontOpsと連携させます

  1. FrontOps、[Endpoint]-[ngrok Connect] をクリックして、フローを公開します。取得したURLがポップアップで表示されるのでコピーします。

    2017-11-30 05_36_53-frontops-20205sCRh4AGldX2.tmp - FrontOps.png

    * 注)ポップアップが消えた後は、[Endpoint]-[Open a public URL]でURLを確認できます。
    2017-11-30 05_37_17-frontops-20205sCRh4AGldX2.tmp - FrontOps.png

  2. mackerelにサインインして [Monitors] - [チャンネル設定] - [通知グループ/通知チャンネルを追加] をクリック

  3. Webhookを選択して、設定をおこないます。

    webhook-settings

ログイン用IP設定

SSH接続に使用するIPをホストのメモ欄に登録します

2017-11-30 08_58_17-.png

その他

ホスト登録、Monitoring設定、Slack通知の設定については割愛します。
このフローを試すには、Monitoring設定で CPU|loadavg|Memory|Swap|Filesystem のいずれかで通知が行われるようにしてください。

おわりに

これで設定完了です。
テストのために閾値を変更するなどしてアラートを発生させて、前回記事で書いたように、mackerelからのSlack通知に続けて、情報収集結果が通知されることを確認してください。

GUIなので直感的に情報収集用コマンドを変更したり、チケットの内容を変更したりができると思います。
いろいろ試してみてください。
FrontOpsを使えば、一次対応が自動化できることを知って貰えれば嬉しいです。

尚、実際これを実運用しようと思ったら、デスクトップやNgrokはちょっと・・・となると思います。
でも安心してください。FrontOpsはdockerやlambdaでも動きます。
この方法については、また別の機会に投稿できればと思います。

追伸

今回のケースでは使っていませんが、FrontOpsでは、SlackボタンやTwilioを使った対話操作も簡単です。
これを使うと一次対応のかなり広い範囲を自動化することができるようになります。
以下のQiita記事と短い動画でも紹介しています。ご覧いただき興味を持ったらFrontOpsを試してみてください。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした