PLG= Promtail+Loki+Grafana
Promtail: 代理,负责收集日志并将其发送给 loki
Loki: 日志记录引擎,负责存储日志和处理查询
Grafana: UI 界面
PLG 的官方介绍可参考:https://grafana.com/docs/loki/latest/overview/
Loki 和常用的 EFK(Elasticsearch + Filebeat + Kibana)日志采集方案类似,基本架构都是在应用节点有代理进程(Promtail)收集日志,传输到集中化的服务(Loki)存储和索引,最后通过图形化界面(Grafana)统一查询。
Loki 与 EFK 的区别
ELK 存在的问题
使用 ELK 会导致运维同学在使用时,通过监控报警查询日志,需要频繁的在监控平台和 kibana 之间切换,影响用户体验。所以 loki 的第一目的就是最小化度量和日志的切换成本,有助于减少异常事件的响应时间和提高用户的体验。
优点是功能丰富,允许复杂的操作。但是,这些方案往往规模复杂,资源占用高,操作苦难。很多功能往往用不上,大多数查询只关注一定时间范围和一些简单的参数(如host、service等),使用这些解决方案就有点杀鸡用牛刀的感觉了。因此,Loki的第二个目的是,在查询语言的易操作性和复杂性之间可以达到一个权衡。
还有一点是关于检索的方案,ES 索引的切分成本较高,Loki 的第三个目的是为了提高更具成本效益的解决方案。
- Loki不会对原始的日志数据进行全文索引,而是采用了 Prometheus 的标签思想,只对标签索引
- 日志数据本身也会被压缩,并以chunks(块)的形式存储在对象存储或者本地文件系统
- Loki能够以微服务模式运行,也就是把自身的各个模块分解出单个进程运行。比如,通过横向扩展多个查询器(Querier)模块可提高查询性能。
这几点区别使Loki更加轻量和灵活,特别适合云原生环境。
运行模式
Loki 有一组组件(distributor,ingester,querier,query-frontend
),它们在内部被称为模块。每个组件为内部流量生成一个 gRPC 服务器(用于组件之间调用),为外部 API 请求生成一个 HTTP/1 服务器。所有组件都带有一个 HTTP/1 服务器,但大多数只公开就绪、健康和指标端点。
Loki 运行哪个组件取决于命令行中的 -target
标志或 Loki 配置文件中的 target: <string>
部分。当 target 的值为 all 时,Loki 将在单个进程中运行其所有组件。这被称为“单进程”、“单二进制”或单片模式。当使用 Helm 安装 Loki 时,单体模式是 Loki 的默认部署。
当 target
未设置为 all(即被设置为 querier
、ingester
、query-frontend
或 distributor
)时,Loki 被称为以“水平可扩展”或微服务模式运行。
Loki 的每个组件,例如ingester
和distributor
,都使用 Loki 配置中定义的 gRPC 侦听端口,通过 gRPC 相互通信。当以单体模式运行组件时,这仍然是正确的:每个组件虽然运行在同一进程中,但将通过本地网络相互连接以进行组件间通信。
单进程模式非常适合本地开发、小工作负载和评估目的。单片模式可以通过多个进程进行扩展,但有以下限制:
当运行具有多个副本的单体模式时,当前无法使用本地索引和本地存储,因为每个副本必须能够访问相同的存储后端,并且本地存储对于并发访问是不安全的。
单个组件无法独立扩展,因此读取组件不可能多于写入组件。
Loki 整体架构
本文仅仅简单介绍每个组件的主要工作,想要了解深入的组件工作方式请参考:
Loki 的模块:distributor,ingester,querier,query-frontend
Distributor
distributor
服务负责处理客户端写入的日志,它本质上是日志数据写入路径中的第一站,一旦 distributor
收到日志数据,会将其拆分为多个批次,然后并行发送给多个 ingester
。
distributor
通过 gRPC 与 ingester
通信,它们都是无状态的,可以根据需要扩大或缩小规模。
Igester
ingester
服务负责将日志数据写入长期存储后端(DynamoDB、S3、Cassandra 等)。此外 ingester
会验证摄取的日志行是按照时间戳递增的顺序接收的(即每条日志的时间戳都比前面的日志晚一些),当 ingester
收到不符合这个顺序的日志时,该日志行会被拒绝并返回一个错误。
query-frontend
查询前端是一个可选的服务,提供 querier
的 API 端点,可以用来加速读取路径。当查询前端就位时,应将传入的查询请求定向到查询前端,而不是 querier
, 为了执行实际的查询,群集中仍需要 querier
服务。
Querier
Querier
查询器服务使用 LogQL 查询语言处理查询,从 ingesters
和长期存储中获取日志。
查询器查询所有 ingesters
的内存数据,然后再到后端存储运行相同的查询。由于复制因子,查询器有可能会收到重复的数据。为了解决这个问题,查询器在内部对具有相同纳秒时间戳、标签集和日志信息的数据进行重复数据删除。
1、promtail收集并将日志发送给loki的 Distributor 组件
2、Distributor会对接收到的日志流进行正确性校验,并将验证后的日志分批并行发送到Ingester(存储)
3、Ingester 接受日志流并构建数据块,压缩后存放到所连接的存储后端
4、Querier(查询器) 收到HTTP查询请求,并将请求发送至Ingester 用以获取内存数据 ,Ingester 收到请求后返回符合条件的数据 ;
5、如果 Ingester 没有返回数据,Querier 会从后端存储加载数据并遍历去重执行查询 ,通过HTTP返回查询结果
PLG部署
本文只做二进制部署的方式,以后需要其他方式再写新文章,部署之前请确认,每台服务器时区与时间是否一致
部署方式在官网有说明
下载链接,在链接中下载 loki-linux-amd64.zip 和 promtail-linux-amd64.zip 的二进制文件
Host | SoftWare | OS |
---|---|---|
192.168.1.10 | Promtail、Loki、Grafana | CentOS 7.5 |
192.168.1.12 | Promtail、nginx、mysql | CentOS 7.5 |
Loki
下载二进制包
wget https://github.com/grafana/loki/releases/download/v2.3.0/loki-linux-amd64.zip -O /tmp/loki.zip
准备安装目录
mkdir -p /usr/local/loki && cd $_
解压二进制包
unzip /tmp/loki.zip -d /usr/local/loki
ln -s /usr/local/loki/loki-linux-amd64 /usr/bin/loki
下载配置文件
wget https://raw.githubusercontent.com/grafana/loki/master/cmd/loki/loki-local-config.yaml
启动命令:/usr/bin/loki -config.file /usr/local/loki/loki-local-config.yaml
,该命令执行为前台运行,打断即停止服务。
这里不使用命令启动,会写脚本交给 systemctl
来管理
# 创建服务用户
useradd --system loki
# 创建loki.service文件
cat << EOF >> /etc/systemd/system/loki.service
[Unit]
Description=Loki service
After=network.target
[Service]
Type=simple
User=loki
ExecStart=/usr/bin/loki \
-config.file /usr/local/loki/loki-local-config.yaml
[Install]
WantedBy=multi-user.target
EOF
使用 systemctl
启动服务
systemctl --now enable loki
使用命令 systemctl status loki
,查看到服务没起来,查看 /var/log/messages
日志,报错信息为以下内容
Sep 7 17:19:56 localhost loki: failed parsing config: /usr/local/loki/loki-local-config.yaml: yaml: unmarshal errors:
Sep 7 17:19:56 localhost loki: line 50: field unordered_writes not found in type validation.plain
配置文件 /usr/local/loki/loki-local-config.yaml
第50行内容删除,重新启动即可
访问 http://192.168.1.10:3100/metrics
,记得放行防火墙端口
Promtail
下载二进制包
wget https://github.com/grafana/loki/releases/download/v2.3.0/promtail-linux-amd64.zip -O /tmp/promtail.zip
准备安装目录
mkdir -p /usr/local/promtail && cd $_
解压二进制包
unzip /tmp/promtail.zip -d /usr/local/promtail/
ln -s /usr/local/promtail/promtail-linux-amd64 /usr/bin/promtail
下载配置文件
wget https://raw.githubusercontent.com/grafana/loki/main/clients/cmd/promtail/promtail-local-config.yaml
配置文件修改
PS:Pormtail 推送的日志目录,必须具有读取权限
$ vim /usr/local/promtail/promtail-local-config.yaml
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml
clients:
# 这里要修改为loki的ip以及对应端口
- url: http://localhost:3100/loki/api/v1/push
scrape_configs:
- job_name: system
static_configs:
- targets:
- localhost # 代表收集promtail本机的日志目录
labels:
# 将会作为索引查询,除了"__path__"外,其他的"key: value"可以自定义
app: "1.10-varlog"
# 收集日志的目录
__path__: /var/log/*log
日志授权
# 目录授权755
chmod 755 /var/log/
# 文件授权644
chmod 644 /var/log/*log
$ vim /usr/local/promtail/promtail-local-config.yaml
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml
clients:
- url: http://192.168.1.10:3100/loki/api/v1/push
scrape_configs:
- job_name: nginx
static_configs:
- targets:
- localhost
labels:
job: "1.12-nginx"
__path__: /var/log/nginx/*log
- job_name: db
static_configs:
- targets:
- localhost
labels:
job: "1.12-mysql"
__path__: /var/log/mysqld.log
日志授权
# 目录授权755
chmod 755 /var/log/nginx/ /var/log/
# 文件授权644
chmod 644 /var/log/nginx/*log /var/log/mysqld.log
启动命令:/usr/bin/promtail -config.file /usr/local/promtail/promtail-local-config.yaml
这里也不使用命令启动,同样交给 systemctl
来管理
# 创建promtail服务用户
useradd --system promtail
# 创建promtail.service文件
cat << EOF >> /etc/systemd/system/promtail.service
[Unit]
Description=Promtail service
After=network.target
[Service]
Type=simple
User=promtail
ExecStart=/usr/bin/promtail \
-config.file /usr/local/promtail/promtail-local-config.yaml
[Install]
WantedBy=multi-user.target
EOF
使用 systemctl
启动服务
systemctl --now enable promtail
访问 http://192.168.1.12:9080
和 http://192.168.1.10:9080
可以通过查看 /tmp/positions.yaml
文件来验证是否收集到需要的日志。
Grafana
配置 grafana 的 yum 源
cat << EOF >> /etc/yum.repos.d/grafana.repo
[grafana]
name=grafana
baseurl=https://packages.grafana.com/oss/rpm
repo_gpgcheck=1
enabled=1
gpgcheck=1
gpgkey=https://packages.grafana.com/gpg.key
sslverify=1
sslcacert=/etc/pki/tls/certs/ca-bundle.crt
EOF
安装 Grafana
yum -y install grafana
启动服务
systemctl --now enable grafana-server
Grafana 与 Loki 结合
访问 http://192.168.1.10:3000
,默认用户密码为admin
,登录后进行如下操作
添加数据源
选择数据源为 loki
在URL处填写 Loki 的访问地址,点击下方 save & test
查看日志
如果没有日志,稍微等几分钟,日志有一个推送的时间间隔
查询条件可以在下图的位置找到,不需要自己写,红框中的 app
,就是在 promtail
中定义的字段
示例:查看 nginx 日志,根据 app
的索引找到对应的 filename
,点击 show logs
报错解决
watcher closed
无法获取到日志文件,原因是权限问题,这个问题找了我好久,因为没有直接报权限的错误,需要给到日志所在目录 755
权限
msg="filetarget: watcher closed, tailer stopped, positions saved" path=/var/log/nginx/*log
msg="filetarget: watcher closed, tailer stopped, positions saved" path=/var/log/mariadb/mariadb.log
解决
chmod 755 /var/log/nginx
chmod 755 /var/log/mariadb
重启服务即可
permission denied
如果直接报这个权限问题,给日志文件644
权限即可
level=debug ts=2019-11-25T10:28:54.257209775Z caller=filetarget.go:245 msg="watching new directory" directory=/var/log
level=error ts=2019-11-25T10:28:54.257677719Z caller=filetarget.go:247 msg="error adding directory to watcher" error="permission denied"
解决
chmod -R 644 [日志路径]
重启服务即可
推送日志到loki失败
服务没有报错的情况下,loki中也没有显示日志,确认当天是否有日志写入,在我的使用经验中发现,开始收集日志当天如果没有日志写入,loki中是不会显示的,即使你的日志文件能够看到之前的日志,但也是不会被收集的。具体可以查看 promtail 所在服务器的 positions ,默认路径是 /tmp/positions.yaml
,如下,尾部数字为 0 则不会在 grafana 界面显示
positions:
/var/log/mysqld.log: "3368"
/var/log/nginx/access.log: "187"
/var/log/nginx/error.log: "0"
日志量大推送错误
当你搭建完成 promtail,并且启动发送日志到 loki 的时候很有可能会碰到这个错误,因为你要收集的日志太多了,超过了 loki 的限制,所以会报429。
level=warn ts=2021-09-13T07:00:13.558415458Z caller=client.go:344 component=client host=10.101.249.246:5654 msg="error sending batch, will retry" status=429 error="server returned HTTP status 429 Too Many Requests (429): Ingestion rate limit exceeded (limit: 4194304 bytes/sec) while attempting to ingest '10793' lines totaling '1048548' bytes, reduce log volume or contact your Loki administrator to see if the limit can be increased"
loki 配置文件添加 ingestion_rate_mb: 15
,旧版添加 ingestion_rate: 25000
limits_config:
reject_old_samples: true
reject_old_samples_max_age: 168h
# 每秒允许promtail传输32MB,默认为4
ingestion_rate_mb: 32
ingestion_burst_size_mb: 64
Loki API 查看日志
# 'query={job=""}': job后的引号内填写Pormtail配置文件中指定的job值
curl -G -s "http://localhost:3100/loki/api/v1/query_range" --data-urlencode 'query={job=""}' | jq