速成课 · No. 19
当你的代码跑在自己的笔记本上时,你可以暂停它、四处翻看。当它跑在生产环境、服务真实用户时,你做不到——你只能从系统告诉你的东西里知道发生了什么。Observability 就是让系统自己解释自己的手艺,通过 logs、metrics 和 traces,这样当凌晨三点出问题时,你可以发问并得到答案,而不是靠猜。
只讲精髓 · 每个想法一个画面 · 掌握术语
整个领域之所以存在,是因为一个硬邦邦的事实:你没法像调试自己机器上的代码那样去调试一个活着的生产系统。所以你必须让系统告诉你它在做什么。
你没法给生产环境挂上 debugger
飞行员没法在飞行途中爬出去检查引擎——他们完全靠仪表盘上的仪表来飞,信任那些表盘来告诉自己正在发生什么。
在你的笔记本上,你可以暂停代码、检查变量、单步执行。在 production 里——真实的服务器、真实的用户、就在此刻——你没法把它停下来看。你只能从系统运行时发出的信号里去了解正在发生什么。这就是核心的转变:在生产环境里你是靠仪表飞行的,而 observability 关乎的是在你需要它之前,就把对的仪表装在面板上。
Monitoring 回答已知的问题;observability 回答新的问题
汽车仪表盘显示你早就知道要盯的那几样东西——速度、油量、温度。但当一种奇怪的新噪声响起,你需要能去调查一个谁都没为它装表的东西。
Monitoring 是盯着你预料到的问题——为已知故障模式设置的 dashboard 和 alert。Observability 是更深层的属性:你能不能仅凭系统产生的数据,回答那些你没有预见到的问题?真实的故障通常是新鲜的——「为什么从下午两点起巴西的用户结账变慢了?」——而 observability 衡量的是你的系统是否发出了足够多的信息,让你能去追一个你从未计划过的问题。
black box 是一个你只能靠猜的系统
一台自动售货机收了某人的钱却什么都没给,既没有显示屏也没有小票——你能做的只有耸耸肩然后猜,因为它什么都不告诉你。
一个几乎不发出任何信息的系统就是个 black box:当它出毛病时,你只能靠猜和重启。代价会在最糟的时刻显现——一次生产事故,每一分钟都很要紧,而你完全不知道该往哪儿看。Observability 的费用是预先支付的,在系统平静时给它埋点,这样当它着火时你才有光可循。添加它的时机,是在你需要它之前。
你没法暂停生产环境去检查它。Observability 就是把系统建成会自己解释自己——这样你能回答那些你从未预见的问题,而不只是盯着那些你预见到的。
最古老也最详细的信号是 log:一份关于系统做了什么的流水日记。它是你最先伸手去拿的东西,也是最容易被滥用的东西。
log 是系统的日记
一本船舶航海日志:带时间戳的条目记下了什么时候发生了什么——「10:42,驶入港口」——这样事后任何人都能重构整段航程。
一条 log 是一个事件的带时间戳记录:「用户 42 登录了」、「支付失败:卡被拒」、「开始处理订单 91」。每一行都是关于某件已发生之事的一条小而详细的笔记,在代码运行时写下。Logs 是最丰富的信号,因为它们带着具体细节——具体是什么、具体什么时候、把细节都附在一起。当你需要知道某一个案例里到底发生了什么时,logs 就是你要看的地方。
structured logs 是可搜索的
一鞋盒手写便条和一张电子表格之间的区别——两者都装着事实,但只有一个能让你瞬间筛出关于客户 42 的一切。
一行自由文本在数百万条条目里很难搜索。Structured logs 把每个事件记成数据——像 user_id: 42、status: failed、duration_ms: 230 这样的字段——这样你就能查询它们:「显示今天所有耗时超过 500ms 的失败支付。」这把 logs 从一堵你要滚动翻看的文字墙,变成了一个你可以质询的数据库。结构正是让 logs 在生产规模下可用的东西。
level 把信号和噪声分开
一本日记,日常条目用铅笔写,紧急情况用红墨水写——这样在危机中你可以直接翻到红色部分,无视其余。
每条 log 都有一个标记其重要性的 level:DEBUG 和 INFO 用于日常细节,WARN 用于有点不对劲的事,ERROR 用于真正的失败。Level 让你在调查时把音量调高、在正常运行时调低,并在条目的洪流里直接跳到错误。用得好,level 就是你让重要的行保持可查的方式;被忽视时,每条 log 看起来都同样紧急,没有一条能脱颖而出。
log 太多和太少一样没用
一本记下每一次呼吸的日记,和一本空白的日记一样无法阅读——你需要的信号被埋在没人能筛出来的噪声里。
Logs 让人忍不住过度使用,而过度记录是有实实在在的代价的:存储贵、搜索慢,还会把要紧的行埋在噪声之下。这门手艺在于记录那些你之后真正会想要的东西——决策、失败、关键事件——细节足够有用,又不至于多到把信号淹没。有目的地记录,而不是条件反射式地记录。
Logs 是一份关于发生了什么的详细日记。让它们结构化,这样你能查询;分好 level,这样你能筛选;保持聚焦,这样信号不会丢失在噪声里。
Logs 告诉你单个事件的事。Metrics 告诉你整体随时间的事——那些一眼就能看出系统是健康还是在下滑的数字。
metric 是一个随时间变化的数字
一张监测整夜心率的病床图表——不是每一次心跳,只是稳定采样的那个数字,这样一个上升的趋势就会跳出来。
一个 metric 是随时间被追踪的一项测量值:每秒请求数、错误率、响应时间、已用内存。和 log 不同,它不是关于某一个事件——它是聚合值,被持续采样,这样你就能看到趋势和尖峰。Metrics 采集和存储都很便宜,因为它们只是数字,这意味着你可以一直、为所有东西保留它们,并看着你系统的形状如何移动。
counter 只往上走;gauge 上下浮动
里程表只会一路往上爬,累计有史以来开过的总里程;速度表则随你此刻开得多快而起起落落。
两种基本形状覆盖了大多数 metric。一个 counter 只会增加——服务过的总请求数、总错误数——你盯的是它变化的速率。一个 gauge 上下移动以显示一个当前值——使用中的内存、活跃连接数、队列长度。知道你看的是哪一种,就告诉了你该怎么读它:counter 的斜率才是故事;gauge 的当前高度才是。
dashboard 让趋势变得可见
控制室里的一墙仪表:操作员一眼就能看到一切都是绿的、稳的,或者某个表盘正在朝红色爬升。
Metrics 通常在 dashboard 上查看——把关键数字随时间的图表并排放在一起。一个好的 dashboard 能让你在几秒内看到系统的健康状况,并捕捉到某条线拐错方向的那一刻。这正是 metrics 体现价值的地方:不在任何单个数值里,而在那条可见的趋势里——它在用户抱怨完之前就说出「下午两点开始有东西不对了」。
four golden signals
医生快速查一遍生命体征——脉搏、体温、血压、呼吸——一组很小的指标,不用测量一切就能抓住大多数毛病。
你不需要一千个 metric 才能起步。Four golden signals 覆盖了一个系统大部分的健康状况:latency(请求耗时多久)、traffic(需求有多大)、errors(多少请求失败)和 saturation(你的资源有多满)。盯住这四个,你就能尽早抓住绝大多数问题。它们是一个服务的生命体征——是首先要装上面板的那几个表盘。
Metrics 是随时间变化的数字——往上爬的 counter、来回摆的 gauge——显示在 dashboard 上。从 four golden signals 起步:latency、traffic、errors、saturation。
在一个由许多服务组成的系统里,一个用户请求会触及它们中的许多个。一个 trace 一路跟随那单个请求穿过全程——回答 logs 和 metrics 回答不了的问题:在这一切之中,时间或失败到底发生在哪里?
trace 跨服务跟随一个请求
一个包裹的追踪历史:在这里被揽收、在那里被分拣、飞越运输、最终送达——一个包裹穿过每一双经手的手的整段旅程。
在现代系统里,单个请求会在许多服务间跳转——网关调用订单服务,订单服务调用支付,支付调用一个数据库。一个 trace 记录某一个请求的整段旅程,被拼接在一起,这样你就能看到它经过的每一跳。它回答一个 logs 和 metrics 都回答不了的问题:不是「发生了什么」或「有多少」,而是「这一个特定请求穿过整个系统的路径是什么?」
span 显示时间花在了哪里
一份被拆成各段航程的行程单,每一段都有自己的时长——你立刻就能看出,是那三小时的中转、而不是飞行本身,吃掉了一整天。
一个 trace 由 span 构成——每一步一个,每个都计时这一段花了多久。摆成瀑布图,这些 span 就精确地显示出一个慢请求把时间花在了哪里:数据库的 span 用了 900ms,而其余一切只用了 50。没有这个,「请求很慢」在许多服务之间就是个谜;有了它,你就直接指向罪魁祸首。Span 把一个含糊的「慢」变成一个精确的位置。
tracing 是你调试分布式系统的方式
顺着一根掉落的接力棒一路追回接力赛中去,找出究竟是哪一次交接失败了——光看最终结果根本看不出来。
当一个请求失败或慢如蜗牛、而工作又分散在多个服务之间时,trace 往往是找到「在哪里」的唯一办法。Metrics 告诉你系统慢了;logs 告诉你每个服务说了什么;trace 把一个请求的路径串在一起,指向那个崩掉的确切服务和步骤。对任何分布式的东西来说,tracing 就是那个把「它在某个地方出错了」变成「就在这儿」的工具。
一个 trace 跨越每一个服务跟随一个请求,拆成一个个计时的 span——这样你就能看出,在一个分布式系统里,时间或失败究竟实际发生在哪里。
Logs、metrics 和 traces 被称为 observability 的三大支柱,因为每一个回答一个不同的问题。力量在于把它们一起用,而不在于挑一个。
每根支柱回答一个不同的问题
用三件工具调查一个问题:一张显示事情何时变化的图表、一张显示在哪里的地图,和一份显示究竟是什么的详细报告——各自单独都没用,合在一起就一锤定音。
三大支柱分担了工作。Metrics 回答「是不是有东西出错了,趋势如何?」——那个便宜、永远在线的总览。Traces 回答「在所有服务之中,问题在哪里?」——把它收窄到某一步。Logs 回答「在那里究竟发生了什么?」——现场的全部细节。没有任何单独一个是足够的;每一个都在其他的止步处接力。
自然的流程:察觉、定位、解释
医生在图表上看到发烧,再检查找出是哪个器官,然后做那个能点出病名的特定化验——先是宽泛的信号,再收窄,再精确。
实际操作中你是按顺序穿过它们的。一个 metric 或 alert 告诉你有东西不对(错误率跳升)。一个 trace 显示给你看在哪里(支付服务那一步在失败)。那一点上的一条 log 告诉你究竟是什么(支付提供方返回了一个超时)。从宽泛到收窄到精确:metric 察觉,trace 定位,log 解释。知道这个流程,就掌握了调试生产环境的大半。
correlation 把它们绑在一起
每一份文件、照片和报告上都写着同一个案件编号——这样调查员就能把关于一起事件的一切都拉到一起,而不必分别翻找每一堆。
把这些支柱链接起来时,它们要强得多。给一个请求的 metrics、trace 和每一行 log 都附上一个共享的 request ID(或 trace ID),就能让你从「这个请求失败了」直接跳到它的 trace 和它确切的 logs。没有 correlation,你就有三个分开的草垛;有了它,一拉就把一起事件的整个故事拉到一起。支柱之间的链接,正是让它们成为一个系统、而非三件工具的东西。
Metrics 察觉,traces 定位,logs 解释——从宽泛到收窄到精确。用共享的 request ID 把它们关联起来,三件工具就变成了一个关于哪里出了错的故事。
Observability 让你能去调查;alerting 告诉你何时开始。难的部分不是检测问题——而是决定哪些问题值得叫醒一个人。
对用户真正感受到的东西发 alert
烟雾报警器该为一场真正的火灾而响,而不是每次有人烤吐司时都响——否则人们会把电池抠出来,从而错过那场真的。
一个 alert 是当某样东西越线时的自动通知。这门艺术在于对用户真正感受到的症状发 alert——结账失败、网站变慢——而不是对每一次内部的抽动。一台服务器到了 80% 内存并不是问题,只要用户没事;而失败订单的一个尖峰是问题,哪怕每台服务器看起来都健康。对面向用户的结果发 alert,你就会为那些真正要紧的事去呼叫人。
SLI、SLO、SLA:定义何谓「够好」
一家快递公司承诺「99% 的包裹在两天内送达」——一个清晰、可测量的、关于何谓可接受服务的标准,事先约定好的。
三个缩写把「多好才算够好」正式化了。一个 SLI 是那项测量(在 300ms 内被服务的请求所占的百分比)。一个 SLO 是你为它设定的目标(应该达到 99.9%)。一个 SLA 是对客户的合约承诺,未达成时有后果。设定一个明确的 SLO,把可靠性从一种模糊的感觉变成一个你可以拿来要求系统的数字——以及一条何时该行动的清晰界线。
alert fatigue 是无声的杀手
一辆为鸡毛蒜皮不停哔哔叫的车,会训练司机无视所有哔哔声——于是那一个本来要紧的警告,和其余的一起被屏蔽掉了。
Alert 太多本身就是一种失败:当人们为那些不要紧的事被呼叫时,他们就开始完全无视 alert,而那个真的就在噪声里被错过了——这就是 alert fatigue。每一个 alert 都应该是可行动的、值得一个人此刻就去关注的;如果不是,它就该待在 dashboard 上,而不是在 pager 里。更少、更锐利的 alert 胜过一场洪流,因为一个没人信任的 alert,比没有 alert 还糟。
对用户感受到的症状发 alert,用一个 SLO 来定义何谓「够好」,并防范 alert fatigue——因为一个没人信任的 alert,比没有还糟。
Observability 是内建进去的,不是事后栓上去的。这门手艺在于边做边埋点、保持信号干净,并把你的注意力花在那少数几样揭示最多的东西上。
边建边埋点,而不是火烧起来之后
在盖房子的时候就把烟雾探测器接上线——而不是站在烟雾里希望自己当初装了。
添加 observability 最糟的时机,就是在一次故障当中,那时你才发现系统什么都不告诉你。所以你要边做边把它建进去:为新功能发出一个 metric,记录它的关键决策,确保一个请求能被 trace 着穿过它。在代码还新鲜时加上的一点点埋点,能省下之后几个小时的盲目瞎猜。把「我以后在生产环境里怎么看到这个?」当作构建它的一部分,而不是事后的补充。
从 golden signals 起步,再生长
一个新店主先盯着那几个要紧的数字——销售额、客流量、投诉——只有当具体的问题冒出来时,才加上更细的追踪。
你不需要在第一天就做到全覆盖。从 four golden signals 和关键决策点上的几条 log 起步,然后在真实事故向你暴露出某个盲点的地方再加上细节。每一次故障都是一堂课,教你当初希望自己记录过什么——所以让你的 observability 沿着系统真正给你惊喜的那些轮廓去生长,而不是一开始就把一切都埋点。
- 它能告诉我什么 在生产环境里——还是说它出问题时是个 black box? - golden signals——我有没有在追踪 latency、traffic、errors 和 saturation? - Logs——结构化、分了 level、聚焦于那些要紧的决策和失败了吗? - Tracing——我能不能跟随一个请求穿过它触及的那些服务?
- Correlation——metrics、logs 和 traces 共享一个 request ID 吗? - Alerts 和一个 SLO——我是不是对用户感受到的东西、针对一个定义好的目标发出呼叫?
- observability / monitoring——回答未预见的问题,对比盯着已知的问题。 - log / structured / level——那份详细的日记,被做得可查询、可筛选。 - metric / counter / gauge / dashboard——随时间变化的数字、那两种形状,以及你怎么查看它们。 - trace / span——一个请求跨服务的路径,被拆成一个个计时的步骤。 - four golden signals——latency、traffic、errors、saturation。 - alert / SLI / SLO / SLA——那个 通知、那项测量、那个目标、那个承诺。 - alert fatigue / correlation (request ID) ——那个噪声陷阱,以及把一切串在一起的那根线。
- 当有东西出问题时,系统 告诉你该往哪儿看,而不是把你扔下去瞎猜。 - 你走 metric → trace → log,从察觉走到定位再走到解释。 - 一个共享的 request ID 把一起事件的信号绑在一起。 - 你的 alerts 对面向用户的 症状触发,而人们仍然信任它们。 - 你有明确的 SLO,并且你 边建边 埋点,而不是在故障当中才埋。
好的 observability 是内建进去的:一开始就有 golden signals、structured logs、可 trace 的请求、用 ID 关联起来,以及针对一个真实 SLO 的几个值得信任的 alert。