Loki + Promtail + Grafana(简称LPG),LPG日志收集方案内存占用很少,它不像ELK日志系统那样为日志建立索引,而是为每个日志流设置一组标签。下面分别介绍下它的核心组件:
Promtail:日志收集器,负责收集日志并将其发送给Loki,对标ELK中的Logstash。
Loki:聚合并存储日志数据和处理查询,可以作为Grafana的数据源,为Grafana提供可视化数据,对标ELK中的Elasticsearch
Grafana:从Loki中获取日志信息,进行可视化展示,对标ELK中的Kibana
LPG相较于ELK Stack有以下优势:
Elasticsearch中的数据作为非结构化JSON对象存储在磁盘上,Loki以二进制的形式存储。
Elasticsearch采用全文索引,倒排索引的切分和共享的成本较高。Loki仅索引元数据,比如标签。
和Prometheus无缝集成。
官方文档:Grafana Loki docs
扩展阅读:
架构
读写 日志数据的写主要依托的是 Distributor 和 Ingester 两个组件,整体的流程如下:
Distributor 一旦 Promtail 收集日志并将其发送给 Loki,Distributor 就是第一个接收日志的组件。
由于日志的写入量可能很大,所以不能在它们传入时将它们写入数据库,这会毁掉数据库,需要批处理和压缩数据。
Loki 通过构建压缩数据块来实现这一点,方法是在日志进入时对其进行 Gzip 操作,组件 Ingester 是一个有状态的组件,负责构建和刷新 Chunck,当 Chunk 达到一定的数量或者时间后,刷新到存储中去。
每个流的日志对应一个 Ingester,当日志到达 Distributor 后,根据元数据和 Hash 算法计算出应该到哪个 Ingester 上面。
此外,为了冗余和弹性,会将其复制 n(默认情况下为 3)次。
Ingester Ingester 接收到日志并开始构建 Chunk:
将日志进行压缩并附加到 Chunk 上面。一旦 Chunk“填满”(数据达到一定数量或者过了一定期限),Ingester 将其刷新到数据库。
Loki对块和索引使用单独的数据库,因为它们存储的数据类型不同。
刷新一个 Chunk 之后,Ingester 然后创建一个新的空 Chunk 并将新条目添加到该 Chunk 中。
Querier 由 Querier 负责给定一个时间范围和标签选择器,Querier 查看索引以确定哪些块匹配,并通过 greps 将结果显示出来。它还从 Ingester 获取尚未刷新的最新数据。
对于每个查询,一个查询器将显示所有相关日志。实现了查询并行化,提供分布式 grep,使即使是大型查询也是足够的
部署
使用docker compose 统一部署需Loki、Promtail、Grafana这些服务
工作目录 创建工作目录
1 2 3 4 5 mkdir -p lpg/loki/data mkdir -p lpg/promtail/logs mkdir -p lpg/grafana/data/ chmod -R 777 lpg cd lpg
docker-compose.yml 创建docker-compose.yml
将以下内容复制进去
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 version: "3" networks: loki: services: loki: container_name: lpg-loki image: grafana/loki:latest restart: always networks: - loki ports: - "3100:3100" volumes: - $PWD/loki/:/etc/loki/ - $PWD/loki/data/:/tmp/loki/ command: -config.file=/etc/loki/loki.yaml promtail: container_name: lpg-promtail image: grafana/promtail:latest restart: always networks: - loki volumes: - $PWD/promtail:/etc/promtail/ - $PWD/promtail/logs/:/var/log/ command: -config.file=/etc/promtail/promtail.yml grafana: container_name: lpg-grafana image: grafana/grafana:latest restart: always ports: - "3000:3000" depends_on: - loki - promtail networks: - loki environment: - GF_SECURITY_ADMIN_PASSWORD=bb123456 volumes: - $PWD/grafana/grafana.ini:/etc/grafana/grafana.ini - $PWD/grafana/data/:/var/lib/grafana
把Loki、Promtail、Grafana的配置文件挂载到宿主机上,在运行之前,需要先准备好这3个配置文件;
loki.yml Loki的配置文件$PWD/loki/loki.yml
内容如下,使用的是默认配置
loki.yml
这里使用文件存储,映射目录为/tmp/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 33 34 35 36 37 38 39 auth_enabled: false server: http_listen_port: 3100 ingester: lifecycler: address: 127.0 .0 .1 ring: kvstore: store: inmemory replication_factor: 1 final_sleep: 0s chunk_idle_period: 5m chunk_retain_period: 30s wal: dir: /tmp/wal schema_config: configs: - from: 2020-05-15 store: boltdb object_store: filesystem schema: v11 index: prefix: index_ period: 168h storage_config: boltdb: directory: /tmp/loki/index filesystem: directory: /tmp/loki/chunks limits_config: enforce_metric_name: false reject_old_samples: true reject_old_samples_max_age: 168h
官方默认配置文件
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 auth_enabled: false server: http_listen_port: 3100 common: path_prefix: /loki storage: filesystem: chunks_directory: /loki/chunks rules_directory: /loki/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
配置示例 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 61 62 63 64 65 66 67 68 69 auth_enabled: false server: http_listen_port: 3100 ingester: lifecycler: address: 127.0 .0 .1 ring: kvstore: store: inmemory replication_factor: 1 final_sleep: 0s chunk_idle_period: 1h max_chunk_age: 1h chunk_target_size: 1048576 chunk_retain_period: 30s max_transfer_retries: 0 schema_config: configs: - from: 2020-10-24 store: boltdb-shipper object_store: filesystem schema: v11 index: prefix: index_ period: 24h storage_config: boltdb_shipper: active_index_directory: /loki/boltdb-shipper-active cache_location: /loki/boltdb-shipper-cache cache_ttl: 24h shared_store: filesystem filesystem: directory: /loki/chunks boltdb: directory: /tmp/loki/index compactor: working_directory: /loki/boltdb-shipper-compactor shared_store: filesystem limits_config: reject_old_samples: true reject_old_samples_max_age: 168h max_entries_limit_per_query: 9999 chunk_store_config: max_look_back_period: 0s table_manager: retention_deletes_enabled: false retention_period: 0s ruler: storage: type: local local: directory: /loki/rules rule_path: /loki/rules-temp alertmanager_url: http://localhost:9093 ring: kvstore: store: inmemory enable_api: true
官方配置示例:https://grafana.com/docs/loki/latest/configuration/examples/
存储切换:https://juejin.cn/post/6870533848644616206
集群部署:https://zhuanlan.zhihu.com/p/373178364
promtail.yml Promtail的配置文件$PWD/promtail/promtail.yml
内容如下,使用的也是默认配置,这里的clients.url
需要注意下,由于使用的是docker-compose
部署,可以将服务名称loki
作为域名来访问Loki服务
1 vi promtail/promtail.yml
promtail.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 server: http_listen_port: 9080 grpc_listen_port: 0 positions: filename: /tmp/positions.yaml clients: - url: http://loki:3100/loki/api/v1/push scrape_configs: - job_name: system static_configs: - targets: - localhost labels: job: varlogs host: promtail __path__: /var/log/*log
配置示例:https://grafana.com/docs/loki/latest/clients/promtail/configuration/#example-static-config
grafana.ini Grafana的配置文件$PWD/grafana/grafana.ini
内容如下,使用的也是默认配置
1 curl -sfL https://cdn.jonty.top/img/grafana.ini -o ./grafana/grafana.ini
启动 运行docker-compose.yml
脚本安装所有服务,使用如下命令即可;
1 2 docker compose up -d docker compose ps
使用 Grafana 部署完成后登录Grafana
,http://192.168.2.97:3000
初始账号密码:admin/admin (根据配置参数:bb123456)
修改新的密码后进入
添加数据源
选择Loki
,可以看到Grafana
也支持Elasticseach
保存并测试
NuGet集成 添加Serilog 的Loki扩展
Serilog
Serilog.AspNetCore
Serilog.Sinks.Grafana.Loki
1 dotnet add package Serilog.Sinks.Grafana.Loki
仓库:https://github.com/serilog-contrib/serilog-sinks-grafana-loki
案例:https://github.com/serilog-contrib/serilog-sinks-grafana-loki/tree/master/sample
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 using Serilog.Debugging;namespace Serilog.Sinks.Grafana.Loki.Sample ;public static class Program { private const string OutputTemplate = "{Timestamp:dd-MM-yyyy HH:mm:ss} [{Level:u3}] [{ThreadId}] {Message}{NewLine}{Exception}" ; public static void Main (string [] args ) { SelfLog.Enable(Console.Error); Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() .Enrich.WithThreadId() .Enrich.WithProperty("meaning_of_life" , 42 ) .WriteTo.Console(outputTemplate: OutputTemplate) .WriteTo.GrafanaLoki( "http://192.168.2.97:3100" , new List<LokiLabel> { new () { Key = "app" , Value = "console1" } }, credentials: null ) .CreateLogger(); Log.Debug("This is a debug message" ); var person = new Person("Billy" , 42 ); Log.Information("Person of the day: {@Person}" , person); Log.Warning("道路千万条,安全第一条,行车不规范,亲人两行泪" ); try { throw new AccessViolationException("Access denied" ); } catch (Exception ex) { Log.Error(ex, "An error occured" ); } Log.CloseAndFlush(); } }
查询日志
官方文档:https://grafana.com/docs/loki/latest/logql/
扩展 把应用程序日志目录挂载到Promtail > $PWD/promtail/logs/ 目录 上,Promtail可以收集到日志
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 version: "3.9" volumes: server_logs: server_sysfiles: networks: net: services: hostapi: image: host_server:dev networks: - net volumes: - - "server_logs:/app/App_Data/Logs" + - $PWD/promtail/logs/:/app/App_Data/Logs - "server_sysfiles:/app/wwwroot/SysFiles"
参考文档:
https://blog.csdn.net/wayne_primes/article/details/112467639