一、背景¶
- 非K8S集群节点,单独部署某个特殊应用或Job的虚机;
- 各个应用间的日志数据非同一目录;
- 当前K8S环境中已经部署了Loki日志管理系统;
二、非K8S集群虚机日志收集¶
2.1 安装和配置Promtail¶
创建数据目录
mkdir -p /mnt/config
数据采集数据配置 /mnt/config/promtail-config.yaml
[root@master01 ~]# vim /mnt/config/promtail-config.yaml
#用于提供服务并接收来自其他组件或客户端的请求
server:
http_listen_port: 9080
grpc_listen_port: 0
#用于配置追踪和同步日志文件读取位置的设置
positions:
filename: /tmp/positions.yaml #指定位置信息存储的文件路径和文件名
sync_period: 10s
clients: #指定与Loki服务器进行通信的客户端配置
- url: http://loki.zhang-qing.com/loki/api/v1/push #推送的数据接口
scrape_configs: #用于配置抓取日志的目标和标签的设置。
- job_name: system
static_configs: #指定静态目标(如主机)的配置列表
- targets:
- localhost
labels:
job: varlogs
app: varlogs
__path__: /var/log/*.log #需要分析的日志目录
容器化Promtail
# 启动容器
[root@master01 ~]# docker run -d --name promtail -v /mnt/config/:/mnt/config -v /var/log/:/var/log/ grafana/promtail:2.7.4 -config.file=/mnt/config/promtail-config.yaml
# 验证
[root@master01 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3782afe55735 grafana/promtail:2.7.4 "/usr/bin/promtail -…" 5 seconds ago Up 4 seconds promtail
针对Loki开启服务暴露,用来支持数据接收
# 定义ingress
[root@master01 9]# vim loki-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
namespace: logging
name: loki-ingress
spec:
ingressClassName: nginx
rules:
- host: loki.zhang-qing.com
http:
paths:
- pathType: Prefix
backend:
service:
name: loki
port:
number: 3100
path: /
# 应用
[root@master01 9]# kaf loki-ingress.yaml
# 验证
[root@master01 9]# kgi -n logging | grep loki.zhang-qing.com
loki-ingress nginx loki.zhang-qing.com 10.0.0.11 80 81s
2.2 测试验证¶
[root@master01 ~]# curl loki.zhang-qing.com/loki/api/v1/label
{"status":"success","data":["app","component","container","filename","instance","job","namespace","node_name","pod","stream"]}
虚机日志数据:
[root@master01 ~]# ls /var/log/*.log
/var/log/boot.log /var/log/vmware-network.5.log /var/log/vmware-network.log
/var/log/vmware-network.1.log /var/log/vmware-network.6.log /var/log/vmware-vmsvc-root.log
/var/log/vmware-network.2.log /var/log/vmware-network.7.log /var/log/vmware-vmtoolsd-root.log
/var/log/vmware-network.3.log /var/log/vmware-network.8.log /var/log/yum.log
/var/log/vmware-network.4.log /var/log/vmware-network.9.log
Grafana测试验证:
方法一:通过label进行筛选

方法二:直接输入
{job="varlogs",filename="/var/log/mysqld.log"}
{job="varlogs",filename="/var/log/yum.log"}


三、使用Promtail收集Java应用日志发送给Loki¶
场景是一个Spring Boot的Java程序,日志框架使用了Logback。这个程序使用Logback将日志以文件形式写到磁盘上。
Logback的配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false" scan="false" scanPeriod="30 seconds">
<contextName>myapp-name</contextName>
<property name="logsPath" value="logs" />
<timestamp key="currentMonth" datePattern="yyyyMM" />
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>${logsPath}/${currentMonth}/myapp.log</file>
<encoder>
<charset>UTF-8</charset>
<pattern>
<![CDATA[
%d{yyyy-MM-dd HH:mm:ss.SSS,+00:00} [%t] ${SPRING_PROFILES_ACTIVE} %p %logger ${CONTEXT_NAME} - %m%n
]]>
</pattern>
</encoder>
</appender>
<logger name="com.myapp" additivity="false" level="INFO">
<appender-ref ref="FILE" />
</logger>
<root level="WARN">
<appender-ref ref="FILE" />
</root>
</configuration>
其中FileAppender的encoder.pattern决定了日志文件中每行日志的格式。处理java应用的日志是需要关注多行日志模式的,即一条日志可能由多行文本组成。例如:
2023-07-18 20:43:20
[pool-8-thread-1] dev ERROR com.myapp.ProjectQuery myapp-name - query error
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 2
at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:77)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:446)
at com.sun.proxy.$Proxy108.selectOne(Unknown Source)
at org.mybatis.spring.SqlSessionTemplate.selectOne(SqlSessionTemplate.java:166)
at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:83)
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:59)
...
针对这个例子,Java应用使用logback将日志按照固定格式落盘,与应用在同一服务器节点上的Promtail将跟踪日志文件,解析日志并将日志发送到Loki。
promtail.yaml配置如下:
server:
disable: true
clients:
- url: http://loki.zhang-qing.com/loki/api/v1/push
tenant_id: org1
positions:
filename: /app/logs/positions.yaml
target_config:
sync_period: 10s
scrape_configs:
- job_name: java_logs
static_configs:
- targets:
- localhost
labels:
job: java_logs
__path__: /app/logs/*/*.log
pipeline_stages:
- multiline:
firstline: '^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}'
max_lines: 256
max_wait_time: 5s
- regex:
expression: '^(?P<time>\d{4}\-\d{2}\-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}) (?P<message>\[(?P<thread>.*?)\] (?P<profileName>[^\s]+) (?P<level>[^\s]+) (?P<logger>[^\s]+) (?P<contextName>[^\s]+) - [\s\S]*)'
- labels:
contextName:
profileName:
level:
- timestamp:
source: time
format: '2006-01-02 15:04:05.999'
location: "UTC"
- drop:
older_than: 120h
drop_counter_reason: "line_too_old"
- labeldrop:
- filename
- output:
source: message
server.disable=true:禁用Promtail的HTTP和gRPC服务监听
clients:块配置了Promtail如何连接到Loki的实例,配置了loki write的地址,以及使用的租户id
positions.filename:设置了Promtail读取日志文件时记录读取位置的文件
scrape_configs:中配置了一个job java_logs,将跟踪匹配 /app/logs/*/*.log的日志,并为其配置了 multiline, regex, labels, timestamp, drop, labeldrop, output等7个 pipeline stage。
-
multiline:将多行合并成一个多行块,然后将其传递到管道中的下一个阶段。
-
regex:接受一个正则表达式,并提取捕获的命名分组,以便在后续阶段中使用。
-
labels:从前面regex阶段获取到contextName, profileName, level作为日志的label标记。
-
timestamp:从提取的map中解析数据,并覆盖Loki存储的日志的最终时间值。如果没有这个阶段,Promtail将日志条目的时间戳与读取该条目的时间关联起来。
-
drop:是一个筛选阶段,可以根据多个选项丢弃日志。这里配置的是丢弃120小时
之前的日志。
-
labeldrop:从发送到Loki的日志条目的标签集合中删除标签,这里我们将filename这个标签删除了。
-
output:从提取的map中获取数据并更改将发送到Loki的日志行。
四、生产环境中Loki的优化¶
4.1 Loki 中保留日志时长¶
当日志传送到 Loki,由 Loki 来存储日志,不可能将日志永久的存储在 Loki 服务器,也需要按照实际需求做数据保留!
在 Loki 配置文件中,做如下配置:
limits_config:
reject_old_samples: true #是否拒绝旧样本
reject_old_samples_max_age: 72h #72小时之前的样本被拒绝
chunk_store_config:
max_look_back_period: 72h #为避免查询超过保留期的数据,必须小于或等于下方的时间值
table_manager:
retention_deletes_enabled: true #保留删除开启
retention_period: 72h #超过72h的块数据将被删除
4.2 Grafana中Loki日志显示行数¶
Grafana中Loki日志的默认显示行数为 1000,很多博文中都说在下图中更改即可,只不过查询时间较长。

对于 Loki 配置来说,默认最大值是 5000 行,这里无法显示超过 1000 行,还需修改 Grafana 的 Data Sources 中的最大行数的值为 5000 以内。
依次点击【Configuration】-【Data sources】

点击【Loki】

找到Maximum lines,设置为5000

如果查询比 5000 更大的行数,需要修改 Loki 服务的配置文件:
limits_config:
# 没有该配置添加即可,数值改为自己想要的最大查询行数
max_entries_limit_per_query: 9999
最后重启服务,再次修改 Grafana 的 Data Sources 中的行数限制即可。