留下什么,丢弃什么
目标
把跟踪全部存下来,成本承受不起;随机地减少,又会让真正想看的请求消失。本实验用数字来确定这二者之间的取舍。
材料
/opt/lab/trace/spans.jsonl 요청 1만 건 (지속 시간·상태·서비스)
mkdir -p /root/trace && cp /opt/lab/trace/* /root/trace/ && cd /root/trace
head -2 spans.jsonl
需要留下的文件
01-shape.txt 분포와 오류 건수
02-head.txt 무작위 1% 가 남기는 것
03-tail.txt 오류·느린 것을 전부 남길 때
04-cost.txt 저장 비용
05-memory.txt 꼬리 표본의 숨은 비용
06-decide.md 우리가 쓸 규칙
07-notes.md 왜 그런지
先看分布
求出 spans.jsonl 的总条数、p50、p95、p99 和错误条数,保存到 01-shape.txt,并用一句话写出这个分布的形状。
python3 - <<'PY'
import json
d = sorted(json.loads(l)['duration_ms'] for l in open('spans.jsonl'))
n = len(d)
print(n, d[n//2], d[int(n*0.95)], d[int(n*0.99)])
PY
对于这种分布,平均值什么也说明不了。它的形状是大多数请求很快、少数请求极慢,所以平均值会落在两者之间一个谁也没有经历过的值上。
随机 1% 会漏掉什么
从全部请求中随机选取 1%,把这份样本中保留下来的超过 p99 的请求和错误各有多少个保存到 02-head.txt。写出随机样本漏掉了什么。
import random; random.seed(7)
sample = random.sample(rows, len(rows)//100)
会留下 100 条左右,其中超过 p99 的大概只有一两个。
故障调查需要的恰恰就是这一两个。随机样本能很好地留下常见的,却会丢掉罕见的——而我们想看的永远是罕见的那一类。
结束之后再挑选
按“错误全部保留、超过 p99 的也全部保留、其余保留 1%”来挑选,把保留的条数保存到 03-tail.txt,并写出它是随机 1% 的多少倍。
因为是在请求结束之后才作出判断,所以称为尾部采样(tail sampling)。
把 129 条错误、超过 p99 的(非错误)请求,以及其余请求的 1% 加起来,会得到 300 条左右——大约是随机 1% 的三倍,但想看的一个都没有丢。
采样比例就是成本
假设一条跟踪为 4KB,以每秒 1,000 个请求、保留 30 天为基准,计算 (1) 全部存储、(2) 随机 1%、(3) 尾部采样的存储量,并保存到 04-cost.txt。
公式是:每秒请求数 × 86,400 × 保留天数 × 单条大小 × 采样比例。
全部存储的话,一天就是个很大的数字。如果不先做这个计算,一上来就说“先全部打开”,下个月看到账单时才会明白。
尾部采样的隐性成本
估算要做尾部采样,Collector 必须把什么保存多久,以及此时需要多少内存,并保存到 05-memory.txt。
请求结束之后才能知道它是慢还是出错。所以 Collector 必须把该请求的所有跨度保存在内存中,直到结束。
所需内存 ≈ 每秒请求数 × 平均耗时(秒)× 单条大小。
这个数字大的时候,需要把 Collector 拆成多台,而那时同一条跟踪的跨度必须发往同一个 Collector,才能作出判断。这正是尾部采样运维变得困难的地方。
把它写成规则
在 06-decide.md 中写出我们的服务要使用的采样规则。用数字确定如何处理错误、慢请求的阈值、其余请求的比例,并写明各自的理由。
“适当地采样”不是规则。下一个人能原样把它搬进配置里,才算规则。
还要确定阈值是取 p99,还是取固定值(例如 1 秒)。p99 会随着服务变慢而一同升高,所以在变慢的过程中,可能什么也捕获不到。
写给下一个阅读的人
从这里看到的内容中挑选至少四点,整理到 07-notes.md 中。不要写做了什么,而要写为什么会这样。
设想一下,是将来重新调整采样配置时的你自己在读这份笔记。“选择了尾部采样”没有帮助,而“随机采样能很好地留下常见的却会丢掉罕见的,我们想看的永远是罕见的那一类”则有帮助。