Alertmanagerからの通知先に自作システムを利用したいが、webhookだとなんかダサいかなと思っているので、タイトルの通りに最近巷でアツいPagerDutyの互換APIを自作システムに持たせてアラートを送りたくなったのでやってみました。
其の壱のゴール
Alertmanagerのpagerduty_configで互換APIを指定し、アラート通知が届くこと
Alertmanagerとは
メールやSlack、webhookなどと言った先に通知を送るシステムです。Prometheusと併せてよく使われますが、別のプロダクトで単体でも使えます。
また、以下のような特徴を持ちます。
- グルーピング:類似のアラートを、単一の通知に分類
- インヒビション:他のアラートが発生している時に、特定のアラート通知を抑制する
- サイレンス:一定時間、アラートをミュートにする
PagerDutyとは
システム管理者およびサポートチーム向けのアラーム集約およびディスパッチサービスです。監視ツールからアラートを収集し、すべての監視アラームを全体的に表示し、問題がある場合は担当者に警告します。
本編
TL;DR
できた!ソースはこちら(Transnano / pagerduty-api · GitLab)
互換API
最近はGoにハマっているのでGo/Ginで書いてみました。(サブタイトルを其の壱と付けたようにまだまだここは適当です。とりあえずエンドポイントとエラーコードを揃えたくらいです。)
内容としてはAlertmanagerからの通知を受け取ってそのまま出力します。
...
r.POST("/v2/enqueue", func(c *gin.Context) {
buf := make([]byte, 2048)
n, _ := c.Request.Body.Read(buf)
b := string(buf[0:n])
fmt.Println(b)
num := len(b)
switch {
case num == 0:
c.JSON(http.StatusBadRequest, gin.H{
"status": "invalid event",
"message": "Event object is invalid",
"errors": "Length of 'routing_key' is incorrect (should be 32 characters)",
})
case num > 500:
c.AbortWithStatus(http.StatusTooManyRequests)
default:
c.JSON(http.StatusAccepted, gin.H{
"status": "success",
"message": "Event processed",
"dedup_key": "samplekeyhere",
})
}
})
r.Run()
...
Alertmanagerの設定
通知に関する設定は以下のようなymlファイルになります。
---
receivers:
- name: 'team-X-pager'
pagerduty_configs:
- routing_key: dummykey_length_is_32_characters
url: http://pagerduty-api:8080/v2/enqueue
---
ポイントはPagerDutyのV2のAPIを呼び出すため、routing_key
を使っていることと自作したシステムのエンドポイントをurl
で指定していることです。
サンプル
こちらのソースsample · master · Transnano / pagerduty-api · GitLabにも用意してまが、以下の手順でAlertmangerにアラート発生をリクエストすると自作システムが出力したログを確認できます。
# Alertmangerにアラート発生をリクエストする
$ curl -d '[{"labels": {"Alertname": "PagerDuty Test"}}]' http://localhost:9093/api/v1/alerts
# 自作システムのログ
[GIN-debug] POST /v2/enqueue --> main.main.func3 (3 handlers)
[GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
...(障害発生)
{"routing_key":"dummykey_length_is_32_characters","dedup_key":"<dedup_key>","event_action":"trigger","payload":{"summary":"[FIRING:1] PagerDuty Test ","source":"AlertManager","severity":"error","custom_details":{"firing":"Labels:\n - alertname = PagerDuty Test\nAnnotations:\nSource: \n","num_firing":"1","num_resolved":"0","resolved":""}},"client":"AlertManager","client_url":"http://alertmanager:9093/#/alerts?receiver=team-X-pager"}
[GIN] 2019/03/22 - 11:00:49 | 202 | 1.3033ms | 172.25.0.4 | POST /v2/enqueue
...(障害復旧)
{"routing_key":"dummykey_length_is_32_characters","dedup_key":"<dedup_key>","event_action":"resolve","payload":{"summary":"[RESOLVED] PagerDuty Test ","source":"AlertManager","severity":"error","custom_details":{"firing":"","num_firing":"0","num_resolved":"1","resolved":"Labels:\n - alertname = PagerDuty Test\nAnnotations:\nSource: \n"}},"client":"AlertManager","client_url":"http://alertmanager:9093/#/alerts?receiver=team-X-pager"}
[GIN] 2019/03/22 - 11:15:31 | 202 | 207.8µs | 172.25.0.4 | POST /v2/enqueue