やりたいこと
-
Grafanaで発生したアラートをチャットワークに送信したいが、標準では対応していないため、Webhookを用いて、Chatworkに通知したい
-
GrafanaのWebhookと、AlertmanagerのWebhookの構造が同じなため、Alertmanagerでも使用したい
-
スクリプトはGoogle Apps Scriptに設置する
-
フロー図
[Grafana] → [Google Apps Script] → [Chatwork] [Alertmanager] ↑
構成技術
- Google Apps Script
- Chatwork API
- Grafana Contact points Webhook
流れ
-
Chatwork側の準備
-
チャットワークAPIで必要になるので発行する
-
通知先となるグループチャットを作成する。既存のものを使用する場合、省略可
-
チャットワークAPIで必要になるので取得する
-
-
Google Apps Scriptの準備
-
Google Apps Script APIの有効化
Claspで外部からGoogle Apps Scriptを操作できるように、ここを参考に、Apps Scriptの設定画面を開きGoogle Apps Script APIのアクセスを有効にする
-
Claspをインストール
プロジェクトディレクトリ(projdirは適当な名前)を作成して、パッケージを追加する
$ mkdir <projdir> $ cd <projdir> $ npm init -f $ npm install -D @google/clasp
-
-
clasp login
でGoogle Apps Scriptと関連付け$ npx clasp login
出力されているURLをブラウザで開いてGoogleアカウントでログイン、claspによるGoogleアカウントへのサクセスを許可する
-
clasp create
でスクリプトプロジェクトを新規作成$ npx clasp create --type webapp --title <title>
<title>
はプロジェクトのタイトルを任意に設定 自分のプロジェクトに設定したタイトルでプロジェクトが作成される -
Chatwork Client for Google Apps Scriptをインストール
-
自分のプロジェクトから作成したプロジェクトを開く
-
ライブラリ横の+を選択
-
ライブラリの追加ダイアログが開くので、Chatwork Client for Google Apps ScriptにあるGoogle App Script スクリプト IDを入力して、検索ボタンを押下
-
ChatWorkClientライブラリがヒットするので、最新バージョンを選択して、追加ボタンを押下
-
変更を反映するため、
clasp pull
を実行$ npx clasp pull
appsscript.jsonが更新されて以下のようになる
{ "timeZone": "America/New_York", "dependencies": { "libraries": [ { "userSymbol": "ChatWorkClient", "version": "18", "libraryId": "1nf253qsOnZ-RcdcFu1Y2v4pGwTuuDxN5EbuvKEZprBWg764tjwA5fLav" } ] }, "exceptionLogging": "STACKDRIVER", "runtimeVersion": "V8" }
-
-
appsscript.jsonを編集してタイムゾーンの変更とWEBアプリ用の設定を追加
@@ -1,5 +1,5 @@ { - "timeZone": "America/New_York", + "timeZone": "Asia/Tokyo", "dependencies": { "libraries": [ { @@ -10,5 +10,9 @@ ] }, "exceptionLogging": "STACKDRIVER", - "runtimeVersion": "V8" -} \ No newline at end of file + "runtimeVersion": "V8", + "webapp": { + "access": "ANYONE_ANONYMOUS", + "executeAs": "USER_DEPLOYING" + } +}
-
TypeScriptで開発を行いたいので、ここを参考に、必要な設定を追加
-
Apps Script用の型定義をインストール
$ npm install -D @types/google-apps-script
-
TypeScriptの機能を有効にするためにtsconfig.jsonの作成
{ "compilerOptions": { "lib": ["esnext"], "experimentalDecorators": true } }
-
-
index.tsを作成
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersfunction doPost(e: GoogleAppsScript.Events.DoPost): void { // Chatwork APIトークン const token = "<Chatwork APIトークン>"; // 通知先ルームID const room_id = "<Chatwork ルームID>"; // チャットワークAPI const client = ChatWorkClient.factory({ token: token }); // 通知内容データ const grafanaAlert = JSON.parse(e.postData.contents); // 通知本文 let body = "[toall]"; grafanaAlert.alerts.forEach((alert) => { body += `[info][title][${alert.status}] ${alert.labels.alertname}[/title]\n`; body += "Labels:\n"; for (const property in alert.labels) { body += ` ${property}: ${alert.labels[property]}\n`; } if (alert.annotations) { body += "Annotations:\n"; for (const property in alert.annotations) { body += ` ${property}: ${alert.annotations[property]}\n`; } } if (alert.silenceURL) { body += `Silence alert: ${alert.silenceURL}\n`; } if (alert.dashboardURL) { body += `Go to dashboard: ${alert.dashboardURL}\n`; } body += "[/info]\n"; }); // Chatworkへ送信 client.sendMessage({ room_id: room_id, body: body, }); } body部は、Message templating | Grafana LabsのCustom template examplesとChatworkのメッセージ記法を組み合わせた
-
clasp push
でコードをプッシュ$ npx clasp push ? Manifest file has been updated. Do you want to push and overwrite? Yes └─ /<projdir>/appsscript.json └─ /<projdir>/grafanToChatwork/index.ts Pushed 2 files.
-
clasp deploy
でコードをデプロイ$ npx clasp deploy Created version 1. - XXXXXXXXXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX @1.
- XXXXXXXXXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXはデプロイID
- アクセスするURL は、 https://script.google.com/macros/s/<デプロイID>/exec となる
このWEBアプリは、appsscript.jsonで定義したとおり、ユーザー「自分」として実行し、URLへのアクセスは全員で公開される
-
プロジェクトを開き、デバッグボタンを押下して、ChatWorkClientライブラリへ権限を与える
デバッグは、doPost()のパラメータeがundefinedのためエラーになる
-
テスト
Contact points | Grafana LabsのJSONファイルをテストデータとしてアクセスする
-
JSONファイルをsample.jsonとして保存
-
curlでPOSTリクエストを発生させる
$ curl -X POST -v -H "Content-Type: application/json" --data @sample.json https://script.google.com/macros/s/<デプロイ ID>/exec
エラーがなければ、「スクリプトが完了しましたが、何も返されませんでした。」と返る
<!DOCTYPE html><html><head><link rel="shortcut icon" href="//ssl.gstatic.com/docs/script/images/favicon.ico"><title>エラー</title><style type="text/css" nonce="ag2ugnBDNHytzn8ZSzwMNg">body {background-color: #fff; margin: 0; padding: 0;}.errorMessage {font-family: Arial,sans-serif; font-size: 12pt; font-weight: bold; line-height: 150%; padding-top: 25px;}</style></head><body style="margin:20px"><div><img alt="Google Apps Script" src="//ssl.gstatic.com/docs/script/images/logo.png"></div><div style="text-align:center;font-family:monospace;margin:50px auto 0;max-width:600px">スクリプトが完了しましたが、何も返されませんでした。</div></body></html>
-
Chatworkに投稿されていることを確認
-
-
GrafanaのWebhookに登録
- Grafanaを開き、AlertingのContant pointsに移動
- New contact pointを押下
- Contant point typeをWebhookに設定
- UrlをWEBアプリのURLに設定
- Testを押下してChatworkに通知が行くことを確認
- Save contact pointを押下して保存
- AlertingのNotification policiesで先ほど追加したContact pointを使って通知するように設定
-
AlertmanagerのWebhookに登録
-
alertmanager.ymlに設定を追加
route: receiver: 'chatwork' receivers: - name: 'chatwork' webhook_configs: - send_resolved: true url: 'https://script.google.com/macros/s/<デプロイ ID>/exec'
-
Alertmanagerを再起動
-
テストアラートを発生させて通知をテスト
<alertmanager_bin>/amtool --alertmanager.url=<alertmanager_url> alert add foo node=bar \ --annotation=runbook='http://runbook.biz' \ --annotation=summary='summary of the alert' \ --annotation=description='description of the alert' \ --end=$(date -Iseconds --date "5 minutes")
-
-
動作と通知が確認できたら、通知用のChatworkアカウントを別で作成し、チャットグループに加え、そのアカウントで発行したAPIトークンに変更しpush・再デプロイ
作成者と通知者が同じでも問題ないが、Chatwork上で自分自身がチャットすることになるため、通知がないので、アカウントを分ける
再デプロイについて
コードを変更するたび、GASサーバにpushしてデプロイする必要があるが、デプロイするたびにURLが変わってしまうため、デプロイする際にはデプロイIDをパラメータに加える
$ npx clasp redeploy -i XXXXXXXXXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
デプロイIDがわからない場合は、
-
プロジェクトを開いて、デプロイボタンを押下
-
デプロイを管理ダイアログの、アクティブにあるデプロイIDの下のコピーをクリック
-
もしくは、
clasp deployment
を使う$ npx clasp deployments 2 Deployments. - XXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX @HEAD - XXXXXXXXXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX @1
の@数字の方を選択。数字はバージョン番号なので、一番大きいのを選択
その他
デバッグについて
- ウェブアプリとしてデプロイしたGASをブラウザからAPIとして呼ぶ際のCORSエラー – sambaiz-net
- debugging – How to debug web development in Google Application Script? – Stack Overflow
制作中にデバッグ出力として、Loggerクラスやconsole.log()を使用したかったが、どちらもcurlで実行した際には何も出力されなかった。対処方法として、スプレッドシートに出力、メール、Apps Script プロジェクトからGoogle Cloud Platform(GCP)プロジェクトの使用に切り替えるなどがある
APIトークンなどの保存について
今回は、便宜上APIトークンや、ルームIDをコードに直接書いているが、プロパティに保存して取り出す形にしたほうがいい
- 【GAS】プロパティストア・プロパティサービスとは?使ってみた!【概要から使い方まで解説!】 – だいきの小屋
- 【初心者向けGAS】プロパティストアの概要とスクリプトプロパティの編集方法
- Google Apps Script (GAS) で Slack 連携を実装する前に知っておくとよい 5 つのこと – Qiita
- Properties Service | Apps Script | Google Developers
NPMモジュールの利用
今回は、1つのファイルで完結しているが、アラートテンプレート(Goテンプレート製)をそのまま使いたいとかだとnpmライブラリを用いたほうが便利です。その際にはrollupやwebappなどでnode_modulesなども含めて1つのgsファイルにする必要があります