はじめに
監視システムからアラートを受信した後におこなう対応のうち、手順が決まっていて誰がやっても同じ部分(一次対応)を自動化して、その後対応するエンジニアが楽になるようにお膳立てします。
「mackerelで監視した後の一次対応を自動化する話」からの続きです。実際の作り方を説明していきます
作り方
- フローをインポート
- フローの設定調整
- ホストログイン設定
- Redmine接続設定
- Slack通知設定
- デプロイ
- 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データを貼り付けて、[読み込み]します。
- 以下のフローがインポートされます。
- 実際は、後述する設定が未了なので、注意アイコンが表示されます。
2. フローの設定調整
ホストログイン設定
ホストにSSHログインできるようにするために、ログイン認証設定をおこないます。
- [プロセス情報]ノードをダブルクリックして、[接続先]を設定します。
- [ユーザ名]、[パスワード]、[秘密鍵]、[パスフレーズ]など、ログイン認証に必要な情報を設定します。
- [ホスト]にはFQDNやIPアドレスを指定します。今回はMackerelから通知されるデータの中から動的にこの情報を設定するため、TBD(To Be Defined) としています
- もっと詳細に知りたい方は[FrontOps] サーバにSSHログインしてコマンドを実行する方法を参照してください。
Redmine接続設定
Redmine APIでチケット作成・更新が行えるようにAPIキーを設定します。
- [チケット作成]ノードをダブルクリックして、接続設定をおこないます。
- RedmineのAPIエンドポイント(URL)とAPIキーを設定します。
- APIキーを取得するには、Redmineにログインして[個人設定]-[APIアクセスキー]-[表示]をクリックします。
- Redmineに合わせて適切な[プロジェクト]や[トラッカー]を選択します。
- 更新対象は、[チケット作成][チケット更新][チケット終了][チケット更新]の4ノードです
Slack通知設定
Slack通知をおこなうために、トークンの設定をおこないます。
- [Slack Say]ノードをダブルクリックして、[Slackトークン]の設定をおこないます。
-
トークンの取得方法は、Slack Appsのページから、アプリを選択し、Features/[OAuth & Permissions] をクリック。Bot User OAuth Access Token を取得して設定します。
- もっと詳細に知りたい方は、[FrontOps] Slackでメッセージを送受信する方法を参照してください。
-
適切なSlackチャンネルを設定します。
デプロイ
設定調整が終わったら、[デプロイ]します。これで設定が保存されます。
3. Mackerel設定
Webhook設定
Webhook通知でFrontOpsと連携させます
-
FrontOps、[Endpoint]-[ngrok Connect] をクリックして、フローを公開します。取得したURLがポップアップで表示されるのでコピーします。
-
mackerelにサインインして [Monitors] - [チャンネル設定] - [通知グループ/通知チャンネルを追加] をクリック
-
Webhookを選択して、設定をおこないます。
ログイン用IP設定
SSH接続に使用するIPをホストのメモ欄に登録します
その他
ホスト登録、Monitoring設定、Slack通知の設定については割愛します。
このフローを試すには、Monitoring設定で CPU|loadavg|Memory|Swap|Filesystem
のいずれかで通知が行われるようにしてください。
おわりに
これで設定完了です。
テストのために閾値を変更するなどしてアラートを発生させて、前回記事で書いたように、mackerelからのSlack通知に続けて、情報収集結果が通知されることを確認してください。
GUIなので直感的に情報収集用コマンドを変更したり、チケットの内容を変更したりができると思います。
いろいろ試してみてください。
FrontOpsを使えば、一次対応が自動化できることを知って貰えれば嬉しいです。
尚、実際これを実運用しようと思ったら、デスクトップやNgrokはちょっと・・・となると思います。
でも安心してください。FrontOpsはdockerやlambdaでも動きます。
この方法については、また別の機会に投稿できればと思います。
追伸
今回のケースでは使っていませんが、FrontOpsでは、SlackボタンやTwilioを使った対話操作も簡単です。
これを使うと一次対応のかなり広い範囲を自動化することができるようになります。
以下のQiita記事と短い動画でも紹介しています。ご覧いただき興味を持ったらFrontOpsを試してみてください。