實現基於 Grafana Loki 的日誌報警
對於生產環境以及一個有追求的運維人員來說,哪怕是毫秒級別的宕機也是不能容忍的。對基礎設施及應用進行適當的日誌記錄和監控非常有助於解決問題,還可以幫助優化成本和資源,以及幫助檢測以後可能會發生的一些問題。前面我們學習使用了 Prometheus 來進行監控報警,但是如果我們使用 Loki 收集日誌是否可以根據採集的日誌來進行報警呢?答案是肯定的,而且有兩種方式可以來實現:Promtail 中的 metrics 階段和 Loki 的 ruler 組件。
測試應用
比如現在我們有一個如下所示的 nginx 應用用於 Loki 日誌報警:
# nginx-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- name: nginx
port: 80
protocol: TCP
selector:
app: nginx
type: NodePort
爲方便測試,我們這裏使用 NodePort 類型的服務來暴露應用,直接安裝即可:
$ kubectl apply -f nginx-deploy.yaml
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-5d59d67564-ll9xf 1/1 Running 0 16s
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 91d
nginx NodePort 10.99.153.32 <none> 80:31313/TCP 22s
我們可以通過如下命令來來模擬每隔 10s 訪問 Nginx 應用:
$ while true; do curl --silent --output /dev/null --write-out '%{http_code}' http://192.168.0.106:31313; sleep 10; echo; done
200
200
metrics 階段
前面我們提到在 Promtail 中通過一系列 Pipeline 來處理日誌,其中就包括一個 metrics 的階段,可以根據我們的需求來增加一個監控指標,這就是我們需要實現的基於日誌的監控報警的核心點,通過結構化日誌,增加監控指標,然後使用 Prometheus 結合 Alertmanager 完成之前我們非常熟悉的監控報警。
首先我們需要安裝 Prometheus 與 Alertmanager,可以手動安裝,也可以使用 Prometheus Operator 的方式,可以參考監控報警章節相關內容,比如這裏我們選擇使用 Prometheus Operator 的方式。
前面我們介紹了幾種 Loki 的部署方式,這裏我們就保留上節微服務模式的 Loki 集羣,接下來我們需要重新配置 Promtail,爲其添加一個 metrics
處理階段,使用如下所示的 values 文件重新安裝。
# ci/metrics-values.yaml
rbac:
pspEnabled: false
config:
clients:
- url: http://loki-loki-distributed-gateway/loki/api/v1/push
snippets:
pipelineStages:
- cri: {}
- match:
selector: '{app="nginx"}'
stages:
- regex:
expression: '.*(?P<hits>GET /.*)'
- metrics:
nginx_hits:
type: Counter
description: "Total nginx requests"
source: hits
config:
action: inc
serviceMonitor:
enabled: true
additionalLabels:
app: prometheus-operator
release: prometheus
上面最重要的部分就是爲 Promtail 添加了 pipelineStages
配置,用於對日誌行進行轉換,在這裏我們添加了一個 match
的階段,會去匹配具有 app=nginx
這樣的日誌流數據,然後下一個階段是利用正則表達式過濾出包含 GET 關鍵字的日誌行。
在 metrics 指標階段,我們定義了一個 nginx_hits
的指標,Promtail 通過其 /metrics
端點暴露這個自定義的指標數據。這裏我們定義的是一個 Counter
類型的指標,當從 regex 階段匹配上後,這個計數器就會遞增。
爲了在 Prometheus 中能夠這個指標,我們通過 promtail.serviceMonitor.enable=true
開啓了一個 ServiceMonitor。接下來重新更新 Loki 應用,使用如下所示的命令即可:
$ helm upgrade --install loki -n logging -f ci/metrics-values.yaml .
更新完成後會創建一個 ServiceMonitor 對象用於發現 Promtail 的指標數據:
$ kubectl get servicemonitor -n logging
NAME AGE
loki-promtail 10s
如果你使用的 Prometheus-Operator 默認不能發現 logging 命名空間下面的數據,則需要創建如下所示的一個 Role 權限:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
labels:
app.kubernetes.io/component: prometheus
app.kubernetes.io/name: prometheus
app.kubernetes.io/part-of: kube-prometheus
app.kubernetes.io/version: 2.26.0
name: prometheus-k8s
namespace: logging
rules:
- apiGroups:
- ""
resources:
- services
- endpoints
- pods
verbs:
- get
- list
- watch
- apiGroups:
- extensions
resources:
- ingresses
verbs:
- get
- list
- watch
- apiGroups:
- networking.k8s.io
resources:
- ingresses
verbs:
- get
- list
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: prometheus-k8s
namespace: logging
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: prometheus-k8s
subjects:
- kind: ServiceAccount
name: prometheus-k8s
namespace: monitoring
正常在 Prometheus 裏面就可以看到 Promtail 的抓取目標了:
如果你使用的是 Prometheus Operator 自帶的 Grafana,則需要手動添加上 Loki 的數據源,前面微服務模式中我們已經在 Grafana 中配置了 Loki 的數據源,現在當我們訪問測試應用的時候,在 Loki 中是可以查看到日誌數據的:
而且現在在 Prometheus 中還可以查詢到我們在 Promtail 中添加的 metrics 指標數據:
因爲現在已經有監控指標了,所以我們就可以根據需求來創建報警規則了,我們這裏使用的 Prometheus Operator,所以可以直接創建一個 PrometheusRule
資源對象即可:
# nginx-prometheus-rule.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
labels:
prometheus: k8s
role: alert-rules
name: promtail-nginx-hits
namespace: logging
spec:
groups:
- name: nginx-hits
rules:
- alert: LokiNginxHits
annotations:
summary: nginx hits counter
description: 'nginx_hits total insufficient count ({{ $value }}).'
expr: |
sum(increase(promtail_custom_nginx_hits[1m])) > 2
for: 2m
labels:
severity: critical
這裏我們配置了名爲 nginx_hits
的報警規則,這些規則在同一個分組中,每隔一定的時間間隔依次執行。觸發報警的閾值通過 expr
表達式進行配置。我們這裏表示的是 1 分鐘之內新增的總和是否大於 2,當 expor
表達式的條件持續了 2 分鐘時間後,報警就會真正被觸發,報警真正被觸發之前會保持爲 Pending 狀態。
然後具體想要把報警發送到什麼地方去,可以根據標籤去配置 receiver,比如可以通過 WebHook 來接收。我們在 AlertManager 中也是可以看到接收到的報警事件的。
Ruler 組件
上面的方式雖然可以實現我們的日誌報警功能,但是還是不夠直接,需要通過 Promtail 去進行處理,那麼我們能否直接通過 Loki 來實現報警功能呢?其實在 Loki2.0 版本就提供了報警功能,其中有一個 Ruler 組件可以持續查詢一個 rules 規則,並將超過閾值的事件推送給 AlertManager 或者其他 Webhook 服務,這也就是 Loki 自帶的報警功能了,而且是兼容 AlertManager 的。
首先我們需要開啓 Loki Ruler 組件,更新 loki-distributed
安裝的 Values 文件,在前面微服務模式的基礎上增加 ruler 組件配置:
# ci/alert-values.yaml
loki:
structuredConfig:
ingester:
max_transfer_retries: 0
chunk_idle_period: 1h
chunk_target_size: 1536000
max_chunk_age: 1h
storage_config: # 存儲的配置,定義其他組件可能用到的存儲
aws: # s3 / s3 兼容的對象存儲
endpoint: minio.logging.svc.cluster.local:9000
insecure: true
bucketnames: loki-data
access_key_id: myaccessKey
secret_access_key: mysecretKey
s3forcepathstyle: true
boltdb_shipper:
shared_store: s3
schema_config:
configs:
- from: 2022-06-21
store: boltdb-shipper # index
object_store: s3 # chunks
schema: v12
index:
prefix: loki_index_
period: 24h
ruler:
storage:
type: local
local:
directory: /etc/loki/rules
ring:
kvstore:
store: memberlist
rule_path: /tmp/loki/scratch
alertmanager_url: http://alertmanager-main.monitoring.svc.cluster.local:9093
external_url: http:/192.168.0.106:31918
distributor:
replicas: 2
ingester: # WAL(replay)
replicas: 2
persistence:
enabled: true
size: 1Gi
storageClass: local-path
querier:
replicas: 2
persistence:
enabled: true
size: 1Gi
storageClass: local-path
queryFrontend:
replicas: 2
gateway: # nginx容器 -> 路由日誌寫/讀的請求
nginxConfig:
httpSnippet: |-
client_max_body_size 100M;
serverSnippet: |-
client_max_body_size 100M;
# Configuration for the ruler
ruler:
enabled: true
kind: Deployment
replicas: 1
persistence:
enabled: true
size: 1Gi
storageClass: local-path
# -- Directories containing rules files
directories:
tenant_no:
rules1.txt: |
groups:
- name: nginx-rate
rules:
- alert: LokiNginxRate
expr: sum(rate({app="nginx"} |= "error" [1m])) by (job)
/
sum(rate({app="nginx"}[1m])) by (job)
> 0.01
for: 1m
labels:
severity: critical
annotations:
summary: loki nginx rate
description: high request latency
我們首先通過 loki.structuredConfig.ruler
對 Ruler 組件進行配置,比如指定 Alertmanager 的地址,規則存儲方式等,然後通過 ruler
屬性配置了組件的相關信息以及報警規則,重新使用上面的 values 文件安裝 Loki:
$ helm upgrade --install loki -n logging -f ci/alert-values.yaml .
$ kubectl get pods -n logging
NAME READY STATUS RESTARTS AGE
grafana-55d8779dc6-gkgpf 1/1 Running 2 (66m ago) 3d21h
loki-loki-distributed-distributor-56959cc548-xpv6d 1/1 Running 0 3m36s
loki-loki-distributed-distributor-56959cc548-zjfsb 1/1 Running 0 2m52s
loki-loki-distributed-gateway-6f4cfd898c-p9xxf 1/1 Running 0 21m
loki-loki-distributed-ingester-0 1/1 Running 0 2m32s
loki-loki-distributed-ingester-1 1/1 Running 0 3m34s
loki-loki-distributed-querier-0 1/1 Running 0 2m48s
loki-loki-distributed-querier-1 1/1 Running 0 3m29s
loki-loki-distributed-query-frontend-5bcc7949d-brzg6 1/1 Running 0 3m30s
loki-loki-distributed-query-frontend-5bcc7949d-g2wwd 1/1 Running 0 3m35s
loki-loki-distributed-ruler-5d4b8cd889-m2vbd 1/1 Running 0 3m35s
minio-548656f786-mjd4c 1/1 Running 2 (66m ago) 3d21h
promtail-ddz27 1/1 Running 0 19m
promtail-lzr6v 1/1 Running 0 20m
promtail-nldqx 1/1 Running 0 20m
Loki 的 rulers 規則和結構與 Prometheus 是完全兼容,唯一的區別在於查詢語句(LogQL)不同,在 Loki 中我們用 LogQL
來查詢日誌,一個典型的 rules 配置文件如下所示:
groups:
# 組名稱
- name: xxxx
rules:
# Alert名稱
- alert: xxxx
# logQL查詢語句
expr: xxxx
# 產生告警的持續時間 pending.
[ for: | default = 0s ]
# 自定義告警事件的label
labels:
[ : ]
# 告警時間的註釋
annotations:
[ : ]
比如我們這裏配置的規則 sum(rate({app="nginx"} |= "error" [1m])) by (job) / sum(rate({app="nginx"}[1m])) by (job) > 0.01
表示通過日誌查到 nginx 日誌的錯誤率大於 1% 就觸發告警,同樣重新使用上面的 values 文件更新 Loki:
更新完成後我們查看 Ruler 組件的日誌可以看到一些關於上面我們配置的報警規則的信息:
$ kubectl logs -f loki-loki-distributed-ruler-5d4b8cd889-m2vbd -n logging
......
level=info ts=2022-06-25T10:10:07.445554993Z caller=metrics.go:122 component=ruler org_id=tenant_no latency=fast query="((sum by(job)(rate({app=\"nginx\"} |= \"error\"[1m])) / sum by(job)(rate({app=\"nginx\"}[1m]))) > 0.01)" query_type=metric range_type=instant length=0s step=0s duration=25.306079ms status=200 limit=0 returned_lines=0 throughput=0B total_bytes=0B queue_time=0s subqueries=1
level=info ts=2022-06-25T10:11:03.196836972Z caller=pool.go:171 msg="removing stale client" addr=10.244.2.165:9095
level=info ts=2022-06-25T10:11:07.423644116Z caller=metrics.go:122 component=ruler org_id=tenant_no latency=fast query="((sum by(job)(rate({app=\"nginx\"} |= \"error\"[1m])) / sum by(job)(rate({app=\"nginx\"}[1m]))) > 0.01)" query_type=metric range_type=instant length=0s step=0s duration=3.234499ms status=200 limit=0 returned_lines=0 throughput=0B total_bytes=0B queue_time=0s subqueries=1
同樣在 1m
之內如果持續超過閾值,則會真正觸發報警規則,觸發後我們在 Alertmanager 也可以看到對應的報警信息了:
到這裏我們就完成了使用 Loki 基於日誌的監控報警。
k8s 技術圈 專注容器、專注 kubernetes 技術......
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/8JAQxUQSfIggAIArokeL9g