以前有关注到 Loki 这个开源项目,还是 1.x 的,借鉴了prometheus 的 chunks 存储,labels, promql 查询等特性,做了一套日志系统。

和 docker / k8s 集成比较好,最近看到版本到 2.2,感觉可以小试一下

轻量级的日志系统 Loki 体验

以前有关注到 Loki 这个开源项目,还是 1.x 的,借鉴了prometheus 的 chunks 存储,labels, promql 查询等特性,做了一套日志系统。

和 docker / k8s 集成比较好,最近看到版本到 2.2,感觉可以小试一下

整体架构

上面的架构图,描述了 Loki 的几个基础组件

  • promtail: 日志采集组件,通过配置scrape_configs(嗯,看着和prometheus的采集配置好像啊),采集对应的本地文件,journal日志,使用positions(一个yaml配置文件),管理采集偏移量等,在通过http的方式,推送到远方的存储中
  • loki: 核心日志存储,查询组件,可以拆分为 querier,ingester, distributor 等多个组件,也可以部署为一个进程,数据存储可以落地为S3的文件中(大杀器啊,对象存储实在是太好用了)
  • grafana: 数据查询模块,可以使用LogQL(一种类似promql的语法)查询

当然还有一些其他组件,如loki-canary(日志链路可用性监控), fluentd(和fluentd对接的)等

梳理下需求

自己一直有多个 VPS,先看下自己有哪些节点

  • 3台VPS:广州,香港,美国各1, 配置1核心/2G ~ 2核心4G,都有公网IP
  • 1台软路由:没有公网IP,可上网
  • 1台NAS:11T硬盘,16G内存,家里,可上网

3 个都是 linux 系统,而且上面的组件都容器化的,使用 docker-compose 管理,以前使用过 ELK,对于 VPS 来讲,太重了,一直找不到一个合适的日志系统,现在可以在 loki 上小试牛刀

因为都已经容器化,所以 docker 的标准输出是需要收集的,采集的需求总结如下

  • docker 标准输出
  • 本地的日志文件
  • systemd 日志文件

对应不通过的地区,还需要打通相关的网络环境,能够统一收集到内网的 NAS 中

如何收集 docker 标准输出日志

对于 docker 标出输出,默认实现上,是会落到本地文档的,可以通过docker inspect id, 返回的LogPath 看到具体文件路径地址

一般的采集方案,就是自动获取 LogPath,然后采集对应的文件即可

Loki 采用的是实现了一个标准的 Docker Log Driver, 安装好插件,就可以直接推送了,使用流程如下

先部署 loki-docker-driver

docker plugin install grafana/loki-docker-driver:latest --alias loki --grant-all-permissions

在 docker 配置默认的 log-driver,重启 dockerd 进程

{
    "live-restore": true,
    "log-driver": "loki",
    "log-opts": {
        "loki-url": "http://127.0.0.1:16310/loki/api/v1/push",
        "loki-external-labels": "instance=vpn"
    }
}

使用docker info命令,如果看到 Logging Driver: loki,那么说明服务已经安装/配置成功

也可以对部分容器使用 loki 的日志,设置 docker run --log-driver=loki 等配置即可,具体可以参考下官方文档 Docker Driver Client

注意:如果是已经存在的 docker 实例,需要销毁,重启后才会生效

和以前传统的方案有哪些区别呢?

  • 和服务发现配置文件相比,使用 driver 的方式,和 docker 集成度很高,对用户无感知,使用体验最好
  • 日志文件貌似不会再落地,直接推送到远端,理论上性能最高(当然也需要保证远端服务的高可用)
  • 有一个点,日志的可用性上,没有具体测试,比如 loki 万一挂了,是否会记录当前日志点,再重试推送等

如何收集本地的日志文件

在所有的日志场景中,肯定有落地本地的业务日志,这个就需要 promtail 这个组件

promtail 也推荐容器化部署,把日志目录挂载到容器内即可,使用 docker-compose 部署示例如下

promtail:
    container_name: promtail
    image: grafana/promtail:2.2.0
    restart: always
    command: -config.file=/etc/loki/promtail.yaml -config.expand-env=true
    environment:
      - INSTANCE=www
    extra_hosts:
      - loki.ifooth.com:host-gateway
    volumes:
      - /data/logs/:/data/logs/
      - /data/promtail:/data/promtail
      - ../../etc/loki/:/etc/loki/

promtail 采集配置

server:
  http_listen_port: 9080
  grpc_listen_port: 0

# 采集基准点, 保证不会重复采集日志
positions:
  filename: /data/promtail/positions.yaml

# 远程地址
clients:
  - url: http://192.168.31.24:3100/loki/api/v1/push

# 采集配置
scrape_configs:
  - job_name: app
    static_configs:
      - targets:
          - localhost
        labels:
          instance: ${INSTANCE}
          job: app
          __path__: /data/logs/**/*.log

${INSTANCE} 等变量,可以使用 docker 的环境变量替换,又解决了一个使用的痛点

上面是最简单的示例,会采集 /data/logs/ 下任意目录下的任意 .log 后缀文件,其实 promtail 还有很强大的 pipeline 功能,通过这个,可以把日志转换为 labels,查询效率很高

如何收集 systemd / journal 日志

在 linux 系统中,肯定还有些基础服务,比如 dockerd 进程,containerd 进程,kubelet 进程等等,都是必须以 systemd 的方式托管,这类怎么收集呢?

  • 一种方案是使用日志文件的方式采集,可以参考使用上面的步骤
  • 还有一种是使用 promtail 提供的journal功能
scrape_configs:
  - job_name: journal
    journal:
      max_age: 24h
      json: true
      labels:
        job: systemd-journal

    relabel_configs:
      - source_labels: ["__journal__systemd_unit"]
        target_label: "unit"

上面这个给了 scrape_configs 就可以格式化的采集systemd文件,注意里面有个Path未配置,默认路径/var/log/journal, /run/log/journal

promtail 也支持 syslog 等其他方式,可以按需使用,因为这里没有需求,就没有再测试了

题外话:如何跨网络采集日志

在上面的场景中,都是采集日志,但是我们的VPS,家里的软路由,都是不同区域的,如果把日志收集到一个地方那,比如我的 NAS 存储中

这个就需要用到我的反向代理了

frontend nas-loki
    bind *:16310
    mode http
    default_backend nas-loki-backends

backend nas-loki-backends
    mode http
    option httpchk
    // 配置好http的check, loki提供了 /ready 检测接口
    http-check send meth GET uri /ready ver HTTP/1.1 hdr Host loki.ifooth.com

    server frp 127.0.0.1:36071 check inter 2s
    server v2ray 127.0.0.1:19310 check inter 2s

通过 frp, v2ray 2个组件,分别建立2个反向代理的链路, 再通过 haproxy 做高可用,保证反向代理的稳定性

PS: 这里没有使用 nginx 做高可用的原因,是应该 haproxy 有其他的服务在使用,而且有可视化,强大的健康检查,如果已经有了 nginx 组件,也可以使用 nginx 实现

最后是 loki 的部署

loki 我们的存储组件,如果是生产环境,必须要多少实例部署

loki 如果生产环境不是,会比较复杂,因为这里只有一个节点,所有就是使用了一个all in one方式,也可以使用

docker-compose 配置文件如下:

loki:
    container_name: loki
    image: grafana/loki:2.2.0
    restart: always
    command: -config.file=/etc/loki/loki.yaml
    user: "0:0"
    volumes:
      - ../../etc/loki:/etc/loki
      - /share/CACHEDEV2_DATA/data/loki:/data/loki
    ports:
      - "3100:3100"

配置文件传送门 -> loki.yaml

上面是单节点,存储使用本地的方式,如果没有大的存储,推荐是对接COS,S3等对象存储服务

使用 grafana 查看日志

上面的配置好了以后,就可以使用grafana查看日志了

首先需要配置数据源 -w650

在Explore参考中,可以查看我们采集到的日志 这里只通过 label 过滤数据

在给一个模糊查询的 case

LogQL 分部分

  • 前面类似 promql 的部分,称为log stream selector,查询效率很高,但是必须是labels类型的数据,使用 prometheus 的4中过滤语法。注意, LogQL中,必须包含至少一个 selector
  • 后面的部分,称为log pipeline,是模糊查询,格式化,pipe 过滤等使用

通过 LogQL,还可以完成日志的基本统计,告警等

总结下

  • 非常轻量,4个节点,只使用大概1GB的内存,家用 NAS 也可以运行
  • 历史数据存储可以放 COS/S3 等对象存储,大量节省资源
  • 基于 labels 的查询,速度非常块,也支持模糊查询等