环境介绍及组件版本
我这里是做实验,所有组件全部署在一台机器上(包括日志收集端),生产部署根据需求将各组件分别部署(promtail日志收集端是需要每个需要日志收集的服务器都部署的)。
组件信息
服务名称
版本
备注
Promtail
v2.7.0
服务端收集日志agent输出到loki存储,类似filebeat
Loki
v2.7.0
存储日志和查询日志
Grafana
v9.2.7
用于查询日志及配置日志告警规则,将告警发送至webhook
PrometheusAlert
v4.8.2
告警通知中心用于暴露webhook,将Grafana推送的告警信息模板格式化后发送至飞书
Loki介绍 特点
围绕日志标签
构建索引,而不是像es
一样进行全文索引
多租户:通过tenant ID
实现多租户,如果关闭多租户, 则默认
唯一租户为fake
部署模式
单进程模式:所有的组件运行在一个进程
中,适用于测试环境
或者较小
的生产环境
微服务可扩展模式:各组件单独运行
,可水平伸缩
扩展
组件
Distributor
负责处理客户端
的日志写入
,负责接收
日志数据,然后将其拆分成多个块
,并行
的发送给ingester
Distributor
通过GRPC协议
与Ingester
进行通信
Hashing
Distributor
通过一致性哈希
和可配置因子
来确定哪些Ingester
服务的实例应该接收
日志数据
hash
基于日志标签
和tenant ID
console
中的hashring
用于实现一致性hash
,所有Ingester
都使用自己拥有的一组令牌注册到console
中,Distributor
通过找到日志hash值最匹配的令牌并将日志数据发送给该令牌的所有者
Ingester
负责将日志数据写入持久化后端(S3,OSS)
Ingester负责所有的日志行有序
Ingester负责所有的日志行按升序排序,如果收到乱序的日志行,将拒绝并报错
来自每一组唯一标签的日志在内存中被构建为“块”,然后被刷新到备份存储后端。
如果ingester进程奔溃,内存中构建的块的数据未刷写到磁盘,则会丢失
Querier:LogQL首先尝试查询所有Ingester的内存数据,然后再从后端存储加载数据。
Chunk Store
块存储是Loki长期数据存储,支持交互式查询和持续写入包含(块索引
以及块数据本身的键值存储
)
注意: 块存储
不是单独
的服务,而是嵌入
到需要访问的Loki数据
的服务中的库:Querier和Ingester
Loki架构图
数据写入
Distributor
负责接收日志数据,然后拆分为多个块,并行的发送给Ingester
Ingester
接收Distributor
发送的数据块,缓存在内存中, 同时定时刷写进持久化存储Chunk Store
中
数据查询
Ingester接收Querier查询请求,根据块索引查询指定的块,如果内存中没有,将从持久化存储chunk Store中查找数据,并返回
开始安装服务 本文档部署使用单进程模式
进行部署的,官网有K8s下Helm
部署、Docker
部署等其他
部署模式,请自行参考
学习。
安装supervisor 我这里二进制部署各个服务,使用了supervisor
来管理各个服务
的启停
,所以我这里安装下supervisor
,如果你使用二进制直接启停此步骤可以忽略,详细的supervisor使用文档点我查看博客
安装supervisor
1 $ yum install -y epel-release supervisor unzip
修改supervisor配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 $ vim /etc/supervisord.conf [unix_http_server] file=/var/run/supervisor/supervisor.sock ; (the path to the socket file) chmod =0700 ; sockef file mode (default 0700)[inet_http_server] ; inet (TCP) server disabled by default port=*:9001 ; (ip_address:port specifier, *:port for all iface) username=admin ; (default is no username (open server)) password=admin ; (default is no password (open server)) [supervisord] logfile=/var/log/supervisor/supervisord.log ; (main log file;default $CWD /supervisord.log) logfile_maxbytes=50MB ; (max main logfile bytes b4 rotation;default 50MB) logfile_backups=10 ; (num of main logfile rotation backups;default 10) loglevel=info ; (log level;default info; others: debug,warn,trace) pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid) nodaemon=false ; (start in foreground if true ;default false ) minfds=1024 ; (min. avail startup file descriptors;default 1024) minprocs=200 ; (min. avail process descriptors;default 200) [rpcinterface:supervisor] supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface [supervisorctl] serverurl=unix:///var/run/supervisor/supervisor.sock ; use a unix:// URL for a unix socket serverurl=http://127.0.0.1:9001 ; use an http:// url to specify an inet socket username=admin ; should be same as http_username if set password=admin ; should be same as http_password if set [include] files = supervisord.d/*.ini
启动supervisord服务
,服务启动成功后
后期托管的服务
可以通过web来管理
,访问http://本机ip:9001
账号密码默认admin/admin
1 $ systemctl enable supervisord && systemctl start supervisord
安装Loki 自行在Github下载 对应架构和版本的二进制服务包即可
创建loki的数据
目录,下载二进制
包解压
并重命名
1 2 3 4 5 6 7 8 $ mkdir /data/loki/{data,log } -p && cd /data/loki/ $ wget https://github.com/grafana/loki/releases/download/v2.7.0/loki-linux-amd64.zip $ unzip loki-linux-amd64.zip && mv loki-linux-amd64 loki
创建loki配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 $ cat > loki-config.yaml<<EOF auth_enabled: false server: http_listen_port: 3100 grpc_listen_port: 9096 common: path_prefix: /data/loki/data storage: filesystem: chunks_directory: /data/loki/data/chunks rules_directory: /data/loki/data/rules replication_factor: 1 ring: instance_addr: 127.0.0.1 kvstore: store: inmemory schema_config: configs: - from: 2020-10-24 store: boltdb-shipper object_store: filesystem schema: v11 index: prefix: index_ period: 24h ruler: alertmanager_url: http://localhost:9093 EOF
创建supervisor托管启停Loki服务的配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 $ cat >/etc/supervisord.d/loki.ini<<EOF ;项目名 [program:loki] ;脚本目录 directory=/data/loki ;脚本执行命令 command=/data/loki/loki --config.file=/data/loki/loki-config.yaml ;supervisor启动的时候是否随着同时启动,默认True autostart=true ;当程序exit的时候自动重启,可选值:[unexpected,true,false],默认为unexpected,表示进程意外杀死后才重启,false是永不重启,true是自动重启 autorestart=true ; 启动失败自动重试次数,默认是3 startretries=3 ;这个选项是子进程启动多少秒之后,此时状态如果是running,则我们认为启动成功了。默认值为1 startsecs=1 ;程序启动使用的用户身份 user = root ;stdout 日志文件,需要注意当指定目录不存在时无法正常启动,所以需要手动创建目录(supervisord 会自动创建日志文件) stdout_logfile=/data/loki/log/loki.out ;把stderr重定向到stdout,默认 false redirect_stderr = true ;stdout日志文件大小,默认 50MB stdout_logfile_maxbytes = 20MB ;stdout日志文件备份数 stdout_logfile_backups = 10 EOF
supervisor启动loki服务,启动成功后loki服务
的地址就是http://本机ip:3100
1 2 $ supervisorctl update && supervisorctl start loki && supervisorctl status loki RUNNING pid 18598, uptime 0:00:16
安装Promtail 自行在Github下载 对应架构和版本的二进制
服务包即可
创建Promtail
的数据目录,下载二进制包
解压并重命名
1 2 3 4 5 6 7 8 $ mkdir /data/promtail/{data,log } -p && cd /data/promtail/ $ wget https://github.com/grafana/loki/releases/download/v2.7.0/promtail-linux-amd64.zip $ unzip promtail-linux-amd64.zip && mv promtail-linux-amd64 promtail
创建promtai配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 $ cat >>promtail-local-config.yaml<<EOF server: # http端口,默认即可 http_listen_port: 9080 grpc_listen_port: 0 # 解决大量日志报trying to send message larger than max错误问题 grpc_server_max_concurrent_streams: 500 grpc_server_max_recv_msg_size: 10000000 grpc_server_max_send_msg_size: 10000000 positions: # 监听的日志文件已经读取的行偏移量存储文件地址 filename: /tmp/positions.yaml clients: # 这里是你的loki服务器地址 - url: http://192.168.0.191:3100/loki/api/v1/push # 配置要收集的日志 scrape_configs: # 配置收集的任务名称(自定义即可,后面可以根据这个名称过滤查看日志),我这里收集的是系统日志。 - job_name: system_log static_configs: - targets: - localhost # 给这个日志收集任务打上一些标签,后面可以根据这些标签来过滤日志查看,或者来做告警。 labels: job: system_log host: ops-test-191 team: ops app: system # 被收集的日志路径,这里配置了/var/log/*.log,还有/var/log/cron这种指定的文件,不以.log结尾的日志文件。 __path__: /var/log/{*.log,btmp,cron,dmesg,lastlog,messages,secure,wtmp} # 我这里配置一个收集kafka日志的任务 - job_name: kafka_server static_configs: - targets: - localhost # 给这个任务打标签,方便查看过滤日志 labels: job: kafka_server host: ops-test-191 app: kafka team: ops # kafka日志文件的路径 __path__: /data/kafka/logs/server.log # 这个配置解决kafka这个任务在收集日志时,某条日志出现换行后会分别收集到多行中导致日志丢失解决方法。 pipeline_stages: - match: # 匹配哪些标签 selector: '{job="kafka_server"}' stages: - multiline: # 已这个开头的才认为是新一行 firstline: '^\[' # 等待三秒钟非[开头的行收集到一行中,否则收集到新一行里面去 max_wait_time: 3s EOF
创建supervisor
托管启停promtail服务
的配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 $ cat >/etc/supervisord.d/promtail.ini<<EOF ;项目名 [program:promtai] ;脚本目录 directory=/data/promtail ;脚本执行命令 command=/data/promtail/promtail --config.file=/data/promtail/promtail-local-config.yaml ;supervisor启动的时候是否随着同时启动,默认True autostart=true ;当程序exit的时候自动重启,可选值:[unexpected,true,false],默认为unexpected,表示进程意外杀死后才重启,false是永不重启,true是自动重启 autorestart=true ; 启动失败自动重试次数,默认是3 startretries=3 ;这个选项是子进程启动多少秒之后,此时状态如果是running,则我们认为启动成功了。默认值为1 startsecs=1 ;程序启动使用的用户身份 user = root ;stdout 日志文件,需要注意当指定目录不存在时无法正常启动,所以需要手动创建目录(supervisord 会自动创建日志文件) stdout_logfile=/data/promtail/log/promtail.out ;把stderr重定向到stdout,默认 false redirect_stderr = true ;stdout日志文件大小,默认 50MB stdout_logfile_maxbytes = 20MB ;stdout日志文件备份数 stdout_logfile_backups = 10 EOF
supervisor启动promtail
服务
1 2 3 $ supervisorctl update && supervisorctl status loki RUNNING pid 18598, uptime 0:21:50 promtai RUNNING pid 19709, uptime 0:00:05
查看promtail
的日志,无报错
即启动成功
了
1 2 3 4 5 6 $ tail -f /data/promtail/log/promtail.out level=info ts=2022-12-01T03:02:07.777216395Z caller =promtail.go:123 msg="Reloading configuration file" md5sum =939a5b17c2c41c6ac525dcf45e9db654 level=info ts=2022-12-01T03:02:07.778163605Z caller =server.go:323 http=[::]:9080 grpc=[::]:43841 msg="server listening on addresses" level=info ts=2022-12-01T03:02:07.778324042Z caller =main.go:171 msg="Starting Promtail" version="(version=HEAD-1b627d8, branch=HEAD, revision=1b627d880)" level=warn ts=2022-12-01T03:02:07.795982266Z caller =promtail.go:220 msg="enable watchConfig" level=info ts=2022-12-01T03:02:12.796214081Z caller =filetargetmanager.go:352 msg="Adding target" key="/var/log/{*.log,btmp,cron,dmesg,lastlog,messages,secure,wtmp}:{app=\"system\", host=\"ops-test-191\", job=\"system_log\", team=\"ops\"}"
安装Grafana 自行在官网下载 对版本的二进制服务包即可
创建grafana
的数据目录,下载二进制
包解压
并重命名
1 2 3 4 5 6 7 8 $ wget https://dl.grafana.com/enterprise/release/grafana-enterprise-9.2.7.linux-amd64.tar.gz $ tar xzvf grafana-enterprise-9.2.7.linux-amd64.tar.gz && mv grafana-9.2.7 /data/grafana $ mkdir -p /data/grafana/log
创建supervisor托管启停grafana服务的配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 $ cat >/etc/supervisord.d/grafana.ini<<EOF ;项目名 [program:grafana] ;脚本目录 directory=/data/grafana ;脚本执行命令 command=/data/grafana/bin/grafana-server ;supervisor启动的时候是否随着同时启动,默认True autostart=true ;当程序exit的时候自动重启,可选值:[unexpected,true,false],默认为unexpected,表示进程意外杀死后才重启,false是永不重启,true是自动重启 autorestart=true ; 启动失败自动重试次数,默认是3 startretries=3 ;这个选项是子进程启动多少秒之后,此时状态如果是running,则我们认为启动成功了。默认值为1 startsecs=1 ;程序启动使用的用户身份 user = root ;stdout 日志文件,需要注意当指定目录不存在时无法正常启动,所以需要手动创建目录(supervisord 会自动创建日志文件) stdout_logfile=/data/grafana/log/grafana.out ;把stderr重定向到stdout,默认 false redirect_stderr = true ;stdout日志文件大小,默认 50MB stdout_logfile_maxbytes = 20MB ;stdout日志文件备份数 stdout_logfile_backups = 10 EOF
supervisor启动grafana服务
,如有启动失败查看/data/grafana/log/grafana.out
日志。
1 2 3 4 5 $ supervisorctl update && supervisorctl status grafana RUNNING pid 20601, uptime 0:00:45 loki RUNNING pid 18598, uptime 0:39:23 promtai RUNNING pid 19709, uptime 0:17:38
grafana服务启动成功后可以通过 http://本机ip:3000
访问,默认账号密码admin/admin
,首次登录
会提示重置密码
,你可以选择Skip跳过
或者设置一个新密码
都可以。
下面配置Grafana
添加Loki数据源
,然后查看promtail
收集到loki
中的日志数据。
选择loki数据源
输入刚刚安装的loki地址
,测试并添加数据源
根据日志标签查询
日志
安装PrometheusAlert实现告警通知到飞书 自行在Github下载 对应架构
和版本的二进制服务
包即可,下载二进制
包解压
并重命名
1 2 3 4 5 6 7 8 $ wget https://github.com/feiyu563/PrometheusAlert/releases/download/v4.8.2/linux.zip $ unzip linux.zip && mv linux /data/PrometheusAlert $ cd /data/PrometheusAlert && chmod +x PrometheusAlert
修改PrometheusAlert配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 $ cd /data/PrometheusAlert $ vim conf/app.conf ------------- 【下面展示的是我修改的配置,未展示的就是使用的默认配置】 ------------- login_user=admin login_password=putianhui open-feishu=1 fsurl=https://open.feishu.cn/open-apis/bot/hook/xxxxxxxxx
创建supervisor托管启停PrometheusAlert服务的配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 $ cat >/etc/supervisord.d/prometheus-alter.ini<<EOF ;项目名 [program:prometheus-alter] ;脚本目录 directory=/data/PrometheusAlert ;脚本执行命令 command=/data/PrometheusAlert/PrometheusAlert ;supervisor启动的时候是否随着同时启动,默认True autostart=true ;当程序exit的时候自动重启,可选值:[unexpected,true,false],默认为unexpected,表示进程意外杀死后才重启,false是永不重启,true是自动重启 autorestart=true ; 启动失败自动重试次数,默认是3 startretries=3 ;这个选项是子进程启动多少秒之后,此时状态如果是running,则我们认为启动成功了。默认值为1 startsecs=1 ;程序启动使用的用户身份 user = root ;stdout 日志文件,需要注意当指定目录不存在时无法正常启动,所以需要手动创建目录(supervisord 会自动创建日志文件) stdout_logfile=/data/PrometheusAlert/logs/PrometheusAlert.out ;把stderr重定向到stdout,默认 false redirect_stderr = true ;stdout日志文件大小,默认 50MB stdout_logfile_maxbytes = 20MB ;stdout日志文件备份数 stdout_logfile_backups = 10 EOF
supervisor启动PrometheusAlert服务
1 2 3 4 5 $ supervisorctl update && supervisorctl status grafana RUNNING pid 20601, uptime 1:56:44 loki RUNNING pid 18598, uptime 2:35:22 prometheus-alter RUNNING pid 26324, uptime 0:00:04 promtai RUNNING pid 19709, uptime 2:13:37
查看PrometheusAlert
的日志,无报错即启动成功了,访问地址是http://主机ip:8080
,账号密码
是你配置文件里面配置的
。
1 2 3 4 5 6 7 $ cat /data/PrometheusAlert/logs/PrometheusAlert.out 2022/12/01 13:15:40.808 [I] [proc.go:255] [main] 构建的Go版本: go1.17.5 2022/12/01 13:15:40.809 [I] [proc.go:255] [main] 应用当前版本: 'v4.8.2' 2022/12/01 13:15:40.809 [I] [proc.go:255] [main] 应用当前提交: eb8600a0597ab4f011c123083e2a457a11ea536e 2022/12/01 13:15:40.809 [I] [proc.go:255] [main] 应用构建时间: 2022-06-24T15:42:46+0800 2022/12/01 13:15:40.809 [I] [proc.go:255] [main] 应用构建用户: root@bogon 2022/12/01 13:15:40.865 [I] [asm_amd64.s:1581] http server Running on http://0.0.0.0:8080
登录平台测试
下刚刚配置的飞书webhook
是否正常。
配置日志告警到飞书 配置prometheusAlert上Grafana日志告警模板 登录到PrometheusAlert平台
,点击添加自定义模板
填写模板信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 {{if eq .state "ok" }} {{ range $k ,$v :=.alerts}}** ======== 💚 日志告警恢复 💚 ========** **告警名称**:[{{$v .labels.alertname}}]({{.generatorURL}}) **告警级别**:严重 **告警状态**:{{$v .status}} **开始时间**:{{GetCSTtime $v .startsAt}} **结束时间**:{{GetCSTtime $v .endsAt}} **当前**:**恢复正常** **===========【END】============** {{ end }} {{else }} {{ range $k ,$v :=.alerts}}** ======== 💔 日志告警 💔 ========** **告警名称**:[{{$v .labels.alertname}}]({{.generatorURL}}) **告警级别**:严重 **告警状态**:{{$v .status}} **开始时间**:{{GetCSTtime $v .startsAt}} **结束时间**:{{GetCSTtime $v .endsAt}} **静默告警**:[点我跳转静默]({{.silenceURL}}) [Description内容] {{$v .annotations.description}} **标签列表** {{ range $kk ,$vv :=$v .labels}} - {{$kk }} = {{$vv }} {{end}}**===========【END】============** {{ end }} {{end}}
测试
下模版是否正常
,使用下面的测试Json数据
填写到消息协议JSON内容
中,点击模板测试
验证飞书是否
收到格式化的告警消息
。
1 { "receiver" : "grafana-promealert-mysql" , "status" : "firing" , "alerts" : [ { "status" : "firing" , "labels" : { "alertname" : "Kafka出现错误日志" , "grafana_folder" : "ops" } , "annotations" : { "description" : "**最新值**:3\n**所属团队**:ops\n**所属Job**:kafka_server\n**所在主机**:k8s-master-21\n**日志路径**:/var/log/kafka_server.log" } , "startsAt" : "2022-11-30T08:03:00Z" , "endsAt" : "0001-01-01T00:00:00Z" , "generatorURL" : "http://localhost:3000/alerting/grafana/Fmq-FVF4z/view" , "fingerprint" : "2b0c4c09a891c428" , "silenceURL" : "http://localhost:3000/alerting/silence/new?alertmanager=grafana\u0026matcher=alertname%3DKafka%E5%87%BA%E7%8E%B0%E9%94%99%E8%AF%AF%E6%97%A5%E5%BF%97\u0026matcher=grafana_folder%3Dops" , "dashboardURL" : "" , "panelURL" : "" , "valueString" : "" } ] , "groupLabels" : { "alertname" : "Kafka出现错误日志" , "grafana_folder" : "ops" } , "commonLabels" : { "alertname" : "Kafka出现错误日志" , "grafana_folder" : "ops" } , "commonAnnotations" : { "description" : "**最新值**:3\n**所属团队**:ops\n**所属Job**:kafka_server\n**所在主机**:k8s-master-21\n**日志路径**:/var/log/kafka_server.log" } , "externalURL" : "http://localhost:3000/" , "version" : "1" , "groupKey" : "{}:{alertname=\"Kafka出现错误日志\", grafana_folder=\"ops\"}" , "truncatedAlerts" : 0 , "orgId" : 1 , "title" : "[FIRING:1] Kafka出现错误日志 ops " , "state" : "alerting" , "message" : "" }
找到我们刚刚
添加的自定义模板webhook通知
地址,记录
下来等下在Grafana里面
添加告警联络点
时要用到。
1 2 http://192.168.0.191:8080/prometheusalert?type =fs&tpl=grafana-fs-loki&fsurl=飞书机器人地址&at=zhangsan@xxx.com
配置Grafana上日志告警 登录Grafana
平台进入到告警中心
,新建一个webhook的联络点
,webhook的地址
就写刚刚复制
的自定义模板路径
。
我们修改通知策略
,把默认
的通知渠道
改成刚刚添加的飞书告警联络点
我们前面使用promtail
收集了kafka的日志
,现在配置一个kafka错误
日志的告警规则示例
。
需求就是:30分钟内
当kafka
服务的日志
出现ERRO关键词
> 1次
就发送告警信息。
注意
:在点击New alert rule
按钮来新建告警
规则前,先在Search by data source
中选择
你的loki数据源
。
添加告警规则的详情
第一步:设置查询指标和告警触发的条件
这里纠正一下
:把第一步中查询A中
的Sum
这个参数删掉
,不然无法获取
告警数据的labels信息
。告警发送过来时Description内容
里面只有最新值
,其他都是空的
。
配置告警触发的抑制和告警的详细信息
第四步:告警通知
的配置,可以配置分组告警
,我这里虽然给这个告警规则打了一个team=ops
的标签,但是我是没有做分组告警
的,默认
都发送给告警策略里面配置的默认告警联络点
中。
配置完成
后就可以在告警规则列表
里面看到刚刚创建的规则
了
我们现在手动
往kafka日志
文件中写入两条
包含ERRO关键词
的日志,然后等一分钟
后观察
是否有告警发送到飞书
这边来
1 2 3 $ echo '[2022-11-30 06:12:35,761] ERRO [GroupMetadataManager brokerId=1] Removed 0 expired offsets in 0 milliseconds. (kafka.coordinator.group.GroupMetadataManager)' >> /data/kafka/logs/server.log $ echo '[2022-11-30 06:12:35,761] ERRO [GroupMetadataManager brokerId=1] Removed 0 expired offsets in 0 milliseconds. (kafka.coordinator.group.GroupMetadataManager)' >> /data/kafka/logs/server.log
等待30s钟
后,刚刚创建的告警规则就会从ok
变成pending
,然后持续一分钟
后从pending
变成firing
,飞书就能收到日志的告警信息
了