報警神器 AlertManager 的使用
ofollow,noindex">上節課我們和大家一起學習了 Grafana 的使用 ,也測試了 Grafana 的報警功能,但是 Grafana 的報警功能目前還比較弱,只支援 Graph 的圖表的報警。今天來給大家介紹一個功能更加強大的報警工具:AlertManager。
簡介
之前我們學習 Prometheus 的時候就瞭解到 Prometheus 包含一個報警模組,就是我們的 AlertManager,Alertmanager 主要用於接收 Prometheus 傳送的告警資訊,它支援豐富的告警通知渠道,而且很容易做到告警資訊進行去重,降噪,分組等,是一款前衛的告警通知系統。

接下來我們就來學習下 AlertManager 的具體使用方法。
安裝
從官方文件https://prometheus.io/docs/alerting/configuration/中我們可以看到下載 AlertManager
二進位制檔案後,可以通過下面的命令執行:
$ ./alertmanager --config.file=simple.yml
其中 -config.file
引數是用來指定對應的配置檔案的,由於我們這裡同樣要執行到 Kubernetes 叢集中來,所以我們使用 docker
映象的方式來安裝,使用的映象是: prom/alertmanager:v0.15.3
。
首先,指定配置檔案,同樣的,我們這裡使用一個 ConfigMap 資源物件:(alertmanager-conf.yaml)
apiVersion: v1 kind: ConfigMap metadata: name: alert-config namespace: kube-ops data: config.yml: |- global: # 在沒有報警的情況下宣告為已解決的時間 resolve_timeout: 5m# 配置郵件傳送資訊 smtp_smarthost: 'smtp.163.com:25' smtp_from: '[email protected]' smtp_auth_username: '[email protected]' smtp_auth_password: '<郵箱密碼>' smtp_hello: '163.com' smtp_require_tls: false # 所有報警資訊進入後的根路由,用來設定報警的分發策略 route: # 這裡的標籤列表是接收到報警資訊後的重新分組標籤,例如,接收到的報警資訊裡面有許多具有 cluster=A 和 alertname=LatncyHigh 這樣的標籤的報警資訊將會批量被聚合到一個分組裡面 group_by: ['alertname', 'cluster'] # 當一個新的報警分組被建立後,需要等待至少group_wait時間來初始化通知,這種方式可以確保您能有足夠的時間為同一分組來獲取多個警報,然後一起觸發這個報警資訊。 group_wait: 30s# 當第一個報警傳送後,等待'group_interval'時間來發送新的一組報警資訊。 group_interval: 5m# 如果一個報警資訊已經發送成功了,等待'repeat_interval'時間來重新發送他們 repeat_interval: 5m# 預設的receiver:如果一個報警沒有被一個route匹配,則傳送給預設的接收器 receiver: default# 上面所有的屬性都由所有子路由繼承,並且可以在每個子路由上進行覆蓋。 routes: - receiver: email group_wait: 10s match: team: node receivers: - name: 'default' email_configs: - to: '[email protected]' send_resolved: true - name: 'email' email_configs: - to: '[email protected]' send_resolved: true
這是 AlertManager 的配置檔案,我們先直接建立這個 ConfigMap 資源物件:
$ kubectl create -f alertmanager-conf.yaml configmap "alert-config" created
然後配置 AlertManager 的容器,我們可以直接在之前的 Prometheus 的 Pod 中新增這個容器,對應的 YAML 資源宣告如下:
- name: alertmanager image: prom/alertmanager:v0.15.3 imagePullPolicy: IfNotPresent args: - "--config.file=/etc/alertmanager/config.yml" ports: - containerPort: 9093 name: http volumeMounts: - mountPath: "/etc/alertmanager" name: alertcfg resources: requests: cpu: 100m memory: 256Mi limits: cpu: 100m memory: 256Mi volumes: - name: alertcfg configMap: name: alert-config
這裡我們將上面建立的 alert-config 這個 ConfigMap 資源物件以 Volume 的形式掛載到 /etc/alertmanager
目錄下去,然後在啟動引數中指定了配置檔案 --config.file=/etc/alertmanager/config.yml
,然後我們可以來更新這個 Prometheus 的 Pod:
$ kubectl apply -f prome-deploy.yaml deployment.extensions "prometheus" configured
當然我們也可以將 AlertManager 的配置檔案內容直接放入到之前的 Prometheus 的 ConfigMap 的資源物件中,也可以用一個單獨的 Pod 來執行 AlertManager 這個容器,完整的資源清單檔案可以參考這裡:https://github.com/cnych/kubeapp/tree/master/prometheus
AlertManager 的容器啟動起來後,我們還需要在 Prometheus 中配置下 AlertManager 的地址,讓 Prometheus 能夠訪問到 AlertManager,在 Prometheus 的 ConfigMap 資源清單中新增如下配置:
alerting: alertmanagers: - static_configs: - targets: ["localhost:9093"]
更新這個資源物件後,稍等一小會兒,執行 reload 操作:
$ kubectl delete -f prome-cm.yaml configmap "prometheus-config" deleted $ kubectl create -f prome-cm.yaml configmap "prometheus-config" created# 隔一會兒後$ kubectl get svc -n kube-ops NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE prometheusNodePort10.102.74.90<none>9090:30358/TCP3d $ curl -X POST "http://10.102.74.90:9090/-/reload"
更新完成後,我們檢視 Pod 發現有錯誤,檢視下 alertmanager 容器的日誌,發現有如下錯誤資訊:
$ kubectl get pods -n kube-ops NAMEREADYSTATUSRESTARTSAGE prometheus-56d64bf6f7-rpz9j1/2CrashLoopBackOff4911d $ kubectl logs -f prometheus-56d64bf6f7-rpz9j alertmanager -n kube-ops level=info ts=2018-11-28T10:33:51.830071513Z caller=main.go:174 msg="Starting Alertmanager" version="(version=0.15.3, branch=HEAD, revision=d4a7697cc90f8bce62efe7c44b63b542578ec0a1)"level=info ts=2018-11-28T10:33:51.830362309Z caller=main.go:175 build_context="(go=go1.11.2, user=root@4ecc17c53d26, date=20181109-15:40:48)"level=error ts=2018-11-28T10:33:51.830464639Z caller=main.go:179 msg="Unable to create data directory" err="mkdir data/: read-only file system"
這個是因為新版本 dockerfile
中的預設 WORKDIR
發生了變化,變成了 /etc/alertmanager
目錄,預設情況下儲存路徑 --storage.path
是相對目錄 data/
,因此,alertmanager 會在我們上面掛載的 ConfigMap 中去建立這個目錄,所以會報錯,我們可以通過覆蓋 --storage.path
引數來解決這個問題,在容器啟動引數中新增該引數:
- name: alertmanager image: prom/alertmanager:v0.15.3 imagePullPolicy: IfNotPresent args: - "--config.file=/etc/alertmanager/config.yml" - "--storage.path=/alertmanager/data"
重新更新 Pod,可以發現 Prometheus 已經是 Running 狀態了:
$ kubectl apply -f prome-deploy.yaml deployment.extensions "prometheus" configured $ kubectl get pods -n kube-ops NAMEREADYSTATUSRESTARTSAGE prometheus-646f457455-gr8x52/2Running03m $ kubectl logs -f prometheus-646f457455-gr8x5 alertmanager -n kube-ops level=info ts=2018-11-28T11:03:16.054633463Z caller=main.go:174 msg="Starting Alertmanager" version="(version=0.15.3, branch=HEAD, revision=d4a7697cc90f8bce62efe7c44b63b542578ec0a1)"level=info ts=2018-11-28T11:03:16.054931931Z caller=main.go:175 build_context="(go=go1.11.2, user=root@4ecc17c53d26, date=20181109-15:40:48)"level=info ts=2018-11-28T11:03:16.351058702Z caller=cluster.go:155 component=cluster msg="setting advertise address explicitly" addr=10.244.2.217 port=9094 level=info ts=2018-11-28T11:03:16.456683857Z caller=main.go:322 msg="Loading configuration file" file=/etc/alertmanager/config.yml level=info ts=2018-11-28T11:03:16.548558156Z caller=cluster.go:570 component=cluster msg="Waiting for gossip to settle..." interval=2s level=info ts=2018-11-28T11:03:16.556768564Z caller=main.go:398 msg=Listening address=:9093 level=info ts=2018-11-28T11:03:18.549158865Z caller=cluster.go:595 component=cluster msg="gossip not settled" polls=0 before=0 now=1 elapsed=2.000272112s level=info ts=2018-11-28T11:03:26.558221484Z caller=cluster.go:587 component=cluster msg="gossip settled; proceeding" elapsed=10.009335611s
報警規則
現在我們只是把 AlertManager 容器執行起來了,也和 Prometheus 進行了關聯,但是現在我們並不知道要做什麼報警,因為沒有任何地方告訴我們要報警,所以我們還需要配置一些報警規則來告訴我們對哪些資料進行報警。
警報規則允許你基於 Prometheus 表示式語言的表示式來定義報警報條件,並在觸發警報時傳送通知給外部的接收者。
同樣在 Prometheus 的配置檔案中新增如下報警規則配置:
rule_files: - /etc/prometheus/rules.yml
其中 rule_files
就是用來指定報警規則的,這裡我們同樣將 rules.yml
檔案用 ConfigMap 的形式掛載到 /etc/prometheus
目錄下面即可:
apiVersion: v1kind: ConfigMapmetadata: name: prometheus-confignamespace: kube-opsdata: prometheus.yml: | ... rules.yml: | groups: - name: test-rule rules: - alert: NodeMemoryUsage expr: (node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)) / node_memory_MemTotal_bytes * 100 > 20 for: 2m labels: team: node annotations: summary: "{{$labels.instance}}: High Memory usage detected" description: "{{$labels.instance}}: Memory usage is above 20% (current value is: {{ $value }}"
上面我們定義了一個名為 NodeMemoryUsage
的報警規則,其中:
-
for
語句會使 Prometheus 服務等待指定的時間, 然後執行查詢表示式。 -
labels
語句允許指定額外的標籤列表,把它們附加在告警上。 -
annotations
語句指定了另一組標籤,它們不被當做告警例項的身份標識,它們經常用於儲存一些額外的資訊,用於報警資訊的展示之類的。
為了方便演示,我們將的表示式判斷報警臨界值設定為20,重新更新 ConfigMap 資源物件,由於我們在 Prometheus 的 Pod 中已經通過 Volume 的形式將 prometheus-config 這個一個 ConfigMap 物件掛載到了 /etc/prometheus
目錄下面,所以更新後,該目錄下面也會出現 rules.yml
檔案,所以前面配置的 rule_files
路徑也是正常的,更新完成後,重新執行 reload
操作,這個時候我們去 Prometheus 的 Dashboard 中切換到 alerts
路徑下面就可以看到有報警配置規則的資料了:

我們可以看到頁面中出現了我們剛剛定義的報警規則資訊,而且報警資訊中還有狀態顯示。一個報警資訊在生命週期內有下面3種狀態:
-
inactive: 表示當前報警資訊既不是firing狀態也不是pending狀態
-
pending: 表示在設定的閾值時間範圍內被激活了
-
firing: 表示超過設定的閾值時間被激活了
我們這裡的狀態現在是 firing
就表示這個報警已經被激活了,我們這裡的報警資訊有一個 team=node
這樣的標籤,而最上面我們配置 alertmanager 的時候就有如下的路由配置資訊了:
routes: - receiver: email group_wait: 10s match: team: node
所以我們這裡的報警資訊會被 email
這個接收器來進行報警,我們上面配置的是郵箱,所以正常來說這個時候我們會收到一封如下的報警郵件:
我們可以看到收到的郵件內容中包含一個 View In AlertManager
的連結,我們同樣可以通過 NodePort 的形式去訪問到 AlertManager 的 Dashboard 頁面:
$ kubectl get svc -n kube-ops NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE prometheusNodePort10.102.74.90<none>9093:31788/TCP,9090:30358/TCP34d
然後通過 <任一Node節點>:31788
進行訪問,我們就可以檢視到 AlertManager 的 Dashboard 頁面:
在這個頁面中我們可以進行一些操作,比如過濾、分組等等,裡面還有兩個新的概念:Inhibition(抑制)和 Silences(靜默)。
-
Inhibition:如果某些其他警報已經觸發了,則對於某些警報,Inhibition 是一個抑制通知的概念。例如:一個警報已經觸發,它正在通知整個叢集是不可達的時,Alertmanager 則可以配置成關心這個叢集的其他警報無效。這可以防止與實際問題無關的數百或數千個觸發警報的通知,Inhibition 需要通過上面的配置檔案進行配置。
-
Silences:靜默是一個非常簡單的方法,可以在給定時間內簡單地忽略所有警報。Silences 基於 matchers配置,類似路由樹。來到的警告將會被檢查,判斷它們是否和活躍的 Silences 相等或者正則表示式匹配。如果匹配成功,則不會將這些警報傳送給接收者。
由於全域性配置中我們配置的 group_interval: 5m
,也就是每5分鐘一個分組的報警將會重新觸發,所以正常來說,上面的測試報警如果一直滿足報警條件(CPU使用率大於20%)的話,那麼每5分鐘我們就可以收到一條報警郵件。
現在我們新增一個 Silences,如下圖所示,匹配 node02 節點的記憶體報警:

新增完成後,等下一次的報警資訊觸發後,我們可以看到報警資訊裡面已經沒有了節點 node02 的報警資訊了:

由於我們上面新增的 Silences 是有過期時間的,所以在這個時間段過後,node02 的報警資訊就會恢復了。
webhook接收器
上面我們配置的是 AlertManager 自帶的郵件報警模板,我們也說了 AlertManager 支援很多中報警接收器,比如 slack、微信之類的,其中最為靈活的方式當然是使用 webhook 了,我們可以定義一個 webhook 來接收報警資訊,然後在 webhook 裡面去進行處理,需要傳送怎樣的報警資訊我們自定義就可以。
比如我們這裡用 Flask 編寫了一個簡單的處理釘釘報警的 webhook 的程式:
import os import requests from flask import Flask from flask import request app = Flask(__name__) @app.route('/', methods=['POST', 'GET']) def send(): if request.method == 'POST': post_data = request.get_data() send_alert(post_data) return 'success' else: return 'weclome to use prometheus alertmanager dingtalk webhook server!'def send_alert(data): token = os.getenv('ROBOT_TOKEN') if not token: print('you must set ROBOT_TOKEN env') return url = 'https://oapi.dingtalk.com/robot/send?access_token=%s' % token send_data = { "msgtype": "text", "text": { "content": data } } req = requests.post(url, json=send_data) result = req.json() if result['errcode'] != 0: print('notify dingtalk error: %s' % result['errcode']) if __name__ == '__main__': from waitress import serve serve(app, listen='0.0.0.0:5000')
程式碼非常簡單,通過一個 ROBOT_TOKEN 的環境變數傳入群機器人的 TOKEN,然後直接將 webhook 傳送過來的資料直接以文字的形式轉發給群機器人。
大家可以根據自己的需求來定製報警資料,上述程式碼倉庫地址:github.com/cnych/alertmanager-dingtalk-hook
當然我們得將上面這個服務部署到叢集中來,對應的資源清單如下:(dingtalk-hook.yaml)
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: dingtalk-hook namespace: kube-ops spec: template: metadata: labels: app: dingtalk-hook spec: containers: - name: dingtalk-hook image: cnych/alertmanager-dingtalk-hook:v0.2 imagePullPolicy: IfNotPresent ports: - containerPort: 5000 name: http env: - name: ROBOT_TOKEN valueFrom: secretKeyRef: name: dingtalk-secret key: token resources: requests: cpu: 50m memory: 100Mi limits: cpu: 50m memory: 100Mi --- apiVersion: v1 kind: Service metadata: name: dingtalk-hook namespace: kube-ops spec: selector: app: dingtalk-hook ports: - name: hook port: 5000 targetPort: http
要注意上面我們聲明瞭一個 ROBOT_TOKEN 的環境變數,由於這是一個相對於私密的資訊,所以我們這裡從一個 Secret 物件中去獲取,通過如下命令建立一個名為 dingtalk-secret 的 Secret 物件,然後部署上面的資源物件即可:
$ kubectl create secret generic dingtalk-secret --from-literal=token=替換成釘釘群聊的機器人TOKEN -n kube-ops secret "dingtalk-secret" created $ kubectl create -f dingtalk-hook.yaml deployment.extensions "dingtalk-hook" createdservice "dingtalk-hook" created $ kubectl get pods -n kube-ops NAMEREADYSTATUSRESTARTSAGE dingtalk-hook-c4fcd8cd6-6r2b61/1Running045m......
部署成功後,現在我們就可以給 AlertManager 配置一個 webhook 了,在上面的配置中增加一個路由接收器
routes: - receiver: webhook match: filesystem: node receivers: - name: 'webhook' webhook_configs: - url: 'http://dingtalk-hook:5000' send_resolved: true
我們這裡配置了一個名為 webhook 的接收器,地址為: http://dingtalk-hook:5000
,這個地址當然就是上面我們部署的釘釘的 webhook 的接收程式的 Service 地址。
然後我們也在報警規則中新增一條關於節點檔案系統使用情況的報警規則,注意 labels 標籤要帶上 alertname=NodeFilesystem
,這樣報警資訊就會被 webook 這一個接收器所匹配:
- alert: NodeFilesystemUsage expr: (node_filesystem_size_bytes{device="rootfs"} - node_filesystem_free_bytes{device="rootfs"}) / node_filesystem_size_bytes{device="rootfs"} * 100 > 10 for: 2m labels: filesystem: node annotations: summary: "{{$labels.instance}}: High Filesystem usage detected" description: "{{$labels.instance}}: Filesystem usage is above 10% (current value is: {{ $value }}"
更新 AlertManager 和 Prometheus 的 ConfigMap 資源物件(先刪除再建立),更新完成後,隔一會兒執行 reload 操作是更新生效:
$ kubectl get svc -n kube-ops NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE prometheusNodePort10.102.74.90<none>9093:31788/TCP,9090:30358/TCP34d $ curl -X POST "http://10.102.74.90:9093/-/reload"$ curl -X POST "http://10.102.74.90:9090/-/reload"
AlertManager 和 Prometheus 都可以通過上面的 reload 操作進行重新載入
都完成更新後,再次去 Prometheus 的 Alert 路徑下面檢視報警資訊:

隔一會兒關於這個節點檔案系統的報警就會被觸發了,由於這個報警資訊包含一個 filesystem=node
的 label 標籤,所以會被路由到 webhook
這個接收器中,也就是上面我們自定義的這個 dingtalk-hook,觸發後可以觀察這個 Pod 的日誌:
$ kubectl logs -f dingtalk-hook-cc677c46d-gf26f -n kube-ops * Serving Flask app "app" (lazy loading) * Environment: production WARNING: Do not use the development server in a production environment. Use a production WSGI server instead. * Debug mode: off * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)10.244.2.217 - - [28/Nov/2018 17:14:09] "POST / HTTP/1.1" 200 -
可以看到 POST 請求已經成功了,同時這個時候正常來說就可以收到一條釘釘訊息了:

由於我們程式中是用一個非常簡單的文字形式直接轉發的,所以這裡報警資訊不夠友好,沒關係,有了這個示例我們完全就可以根據自己的需要來定製訊息模板了,可以參考釘釘自定義機器人文件:https://open-doc.dingtalk.com/microapp/serverapi2/qf2nxq
到這裡我們就完成了完全手動的控制 Prometheus、Grafana 以及我們的 AlertManager 的報警功能,接下來我們會給大家講解 Kubernetes 中更加自動化的監控方案:Prometheus-Operator。
掃描下面的二維碼(或微信搜尋 k8s技術圈
)關注我們的微信公眾帳號,在微信公眾帳號中回覆 加群 即可加入到我們的 kubernetes 討論群裡面共同學習。