TT Lab
开始
学习 学习路径 课程

Loki — 不索引日志的日志库

我们把故障开始时间报晚了四十分钟

在 TT Lab 中继续学习

目标

亲手越过 Loki 的查询限额,区分悄悄被截断和被 400 拒绝这两种情况,并做出在限额之内不遗漏地扫描很宽区间的方法。

为什么重要

日志查询的结果数量由三个旋钮决定。请求的 limit 会悄悄截断,服务器的 max_entries_limit_per_query 和 max_query_length 会以 400 拒绝。拒绝马上就能看到,而悄悄被截断的答案却像是“这就是全部”。而且 direction 决定哪些会被截掉,用默认值 backward 找事故的开始,被截掉的恰好就是想要找的东西。需要扫描很宽的区间时,答案不是提高限额,而是把区间切开、问很多次再合并——每个分片都小于限额,合计才可信。

步骤

  1. 在 /root/lk-query-limits 中启动 Loki,把 date +%s 写入 /root/lk-query-limits/anchor.txt,然后用 python3 /opt/lab/d5/gen.py querylimits "$(cat anchor.txt)" 放入数据。接着对 {app="gateway"},以从基准时刻往前两小时的区间做日志查询,把总行数以 total=<정수>(占位符为整数)一行写入 /root/lk-query-limits/01-total.txt。limit 给 5000,并且一定要确认返回的数量比这个值小。
  2. 对同样的两小时区间,用 limit=100、direction=backward 做日志查询,把返回的行数,以及其中最早一行的纳秒时间戳,写成两行存入 /root/lk-query-limits/02-cut.txt——returned=<정수> 和 oldest_ts=<나노초>(占位符依次为整数、纳秒)。亲自确认响应的任何地方都没有“被截断了”的标记。
  3. 用 limit=6000 发出同样的查询。把响应的状态码、正文里写的限额值,以及从服务器 /config 里读出的该设置的值,写成三行存入 /root/lk-query-limits/03-entries.txt——code=<정수>、limit_in_body=<정수>、limit_in_config=<정수>(占位符均为整数)。
  4. 用 60 天的区间发出同样的查询。把状态码和服务器 /config 中的区间长度限额,写成三行存入 /root/lk-query-limits/04-length.txt——code=<정수>、param=<설정 이름>、value=<서버가 찍은 값 그대로>(占位符依次为整数、设置名称、服务器印出的值原样)。
  5. 对同样的查询,分别用两小时区间和 30 分钟区间发出,测量响应统计中的 splits,并读取服务器的分片间隔设置,写成三行存入 /root/lk-query-limits/05-splits.txt——splits_2h=<정수>、splits_30m=<정수>、interval=<서버가 찍은 값 그대로>(占位符依次为整数、整数、服务器印出的值原样)。
  6. 把 limit 分别给成 50、500、5000 三次,发出同样的查询,把结果写入 /root/lk-query-limits/detect.tsv,不要表头,共三行,每行是用制表符分隔的三列 <limit><탭><returned><탭><truncated>(占位符依次为 limit、制表符、returned、制表符、truncated)。truncated 是 yes 或 no,判断标准是第 1 步求出的真正总数。
  7. 创建 /root/lk-query-limits/scan.sh,把两小时区间按每 30 分钟切成四个分片,分别查询并求出合计。每个分片用 limit=5000 发出,如果某个分片的结果数等于 limit,就必须同时输出 INCOMPLETE。把运行脚本的结果,以 sum=<정수> 和 chunks=4(占位符为整数)两行写入 /root/lk-query-limits/07-scan.txt。合计必须与第 1 步的总数相同。
  8. 设计一个查询,在两小时区间内,取出 status=500 的行中最近的 50 条,写成三行存入 /root/lk-query-limits/08-latest.txt——returned=<정수>、newest_ts=<나노초>、oldest_ts=<나노초>(占位符依次为整数、纳秒、纳秒)。如果不足 50 条,有多少就出多少。

参考

放入两小时的数据,求出真正的总数

在 /root/lk-query-limits 中启动 Loki,把 date +%s 写入 /root/lk-query-limits/anchor.txt,然后用 python3 /opt/lab/d5/gen.py querylimits "$(cat anchor.txt)" 放入数据。接着对 {app="gateway"},以从基准时刻往前两小时的区间做日志查询,把总行数以 total=<정수>(占位符为整数)一行写入 /root/lk-query-limits/01-total.txt。limit 给 5000,并且一定要确认返回的数量比这个值小。

如果返回的数量等于 limit,就说明被截断了,不能用作总数。比它小,就说明这个区间已经全部收到了。这个数字在后面的步骤里,始终是判断“有没有被截断”的标准。用指标查询(sum(count_over_time(...[2h])))也可以数,但范围聚合不包含区间的左端,可能会差出一行左右。

limit 会一声不响地截断

对同样的两小时区间,用 limit=100、direction=backward 做日志查询,把返回的行数,以及其中最早一行的纳秒时间戳,写成两行存入 /root/lk-query-limits/02-cut.txt——returned=<정수> 和 oldest_ts=<나노초>(占位符依次为整数、纳秒)。亲自确认响应的任何地方都没有“被截断了”的标记。

backward 是从最近的开始填。所以返回的 100 行中最早的时刻,不是区间的开始,而是晚得多的时刻。想一想,如果把这个值报告为事故开始时刻,会怎么样。

服务器限额会直截了当地拒绝

用 limit=6000 发出同样的查询。把响应的状态码、正文里写的限额值,以及从服务器 /config 里读出的该设置的值,写成三行存入 /root/lk-query-limits/03-entries.txt——code=<정수>、limit_in_body=<정수>、limit_in_config=<정수>(占位符均为整数)。

400 的正文很友好——连超过了哪个限额、超过了多少,都用数字写着。在 /config 的 limits_config 里找到同名的设置,确认两个值是否相同。如果自动化丢掉这个正文,只留下状态码,以后就找不到原因。

区间太长也会拒绝

用 60 天的区间发出同样的查询。把状态码和服务器 /config 中的区间长度限额,写成三行存入 /root/lk-query-limits/04-length.txt——code=<정수>、param=<설정 이름>、value=<서버가 찍은 값 그대로>(占位符依次为整数、设置名称、服务器印出的值原样)。

把 start 设为 60 天前就行。正文里会把查询长度和限额并排写出来。在 /config 的 limits_config 里找同名的设置——值的写法可能与正文不同。

查询会被切成时间分片来运行

对同样的查询,分别用两小时区间和 30 分钟区间发出,测量响应统计中的 splits,并读取服务器的分片间隔设置,写成三行存入 /root/lk-query-limits/05-splits.txt——splits_2h=<정수>、splits_30m=<정수>、interval=<서버가 찍은 값 그대로>(占位符依次为整数、整数、服务器印出的值原样)。

splits 在 data.stats.summary 里。算一算分片数是怎样由区间长度和设置值得出的——短区间里出现 0,也是有含义的。

如何察觉被截断了

把 limit 分别给成 50、500、5000 三次,发出同样的查询,把结果写入 /root/lk-query-limits/detect.tsv,不要表头,共三行,每行是用制表符分隔的三列 <limit><탭><returned><탭><truncated>(占位符依次为 limit、制表符、returned、制表符、truncated)。truncated 是 yes 或 no,判断标准是第 1 步求出的真正总数。

返回的行数等于 limit,几乎可以肯定是被截断了;小于总数,则肯定是被截断了。也想一想,这两个标准有没有不一致的情况——如果收到的数量与总数相同,即使与 limit 相等,也是没被截断。

应用 ① ——把区间切开,不遗漏地数

创建 /root/lk-query-limits/scan.sh,把两小时区间按每 30 分钟切成四个分片,分别查询并求出合计。每个分片用 limit=5000 发出,如果某个分片的结果数等于 limit,就必须同时输出 INCOMPLETE。把运行脚本的结果,以 sum=<정수> 和 chunks=4(占位符为整数)两行写入 /root/lk-query-limits/07-scan.txt。合计必须与第 1 步的总数相同。

关键是让每个分片的起点和终点互不重叠——如果两个分片都数了边界上的那一秒,合计就会变大,漏掉了则会变小。区间的左端包含、右端不包含,就很干净。

应用 ② ——准确取回最近的 50 条错误

设计一个查询,在两小时区间内,取出 status=500 的行中最近的 50 条,写成三行存入 /root/lk-query-limits/08-latest.txt——returned=<정수>、newest_ts=<나노초>、oldest_ts=<나노초>(占位符依次为整数、纳秒、纳秒)。如果不足 50 条,有多少就出多少。

方向和 limit 必须一起确定。想要“最近的”时的方向,与第 2 步找事故开始时需要的方向恰好相反,这就是本实验的要点。也要一并确认返回的数量是否等于 limit。