Hi,欢迎来到 卡瓦邦噶!我是 laixintao,现在生活在新加坡。我的工作是 SRE,喜欢在终端完成大部分工作,对各种技术都感兴趣。我从 2013 年开始写这个博客,写的内容很广泛,运维的方法论,编程的思考,工作的感悟,除了技术内容之外,还会分享一些读书感想,旅行游记,电影和音乐等。欢迎留下你的评论。

声明:本博客内容仅代表本人观点,和我的雇主无关。本博客承诺不含有 AI 生成的内容,所有内容未加说明均为博主原创,一经发布自动进入公有领域,本人放弃所有权利。转载无需本人同意。但是依然建议在转载的时候留下本博客的链接,因为这里的很多内容在发布之后会还会不断地继续更新和追加内容。 Not By AI

0.01% 的概率超时问题

前言:这个系列(以及我的博客)好久不更新了,原因有两个,一个是我在学习用双拼打字,手跟不上脑子,写的东西读起来不顺畅,不过现在已经复健了。双拼确实能极大地减少按键次数,在 AI 的时代,每个人需要和 AI 对话,那么怎么赶上时代的潮流,从芸芸众人脱颖而出呢?我的建议是:练习打字,打字打得快,和 AI 沟通效率高,做 AI 时代的佼佼者;第二个原因是我最近在思考人生的意义。上次录制博客 laike9m 提到了存在主义危机,第一次知道这个词,我觉得我就是陷入了存在主义危机。苦苦思索人生的意义,没有思考出什么结果。看了一些书,看的是莫言,刘震云,看了一本漫画,《我以为这辈子完蛋了- [美]艾莉·布罗什》,让我思考了很多。但是依然没有结论。人生没有思考明白,问题先来了。

有一天,我们在上线新的设备,上线之后,用户反馈他们的服务出现了网络超时的错误。超时的概率大概在 0.01%,并且出现的时间和我们上线新的设备的时间完全一致。我们把新上线的设备隔离(不再处理线上流量)用户的服务没有再出现错误了。

我们对新设备的性能非常有信心,不应该比原来的设备转发速度还低。这中间一定是有什么问题。

拓扑图简化如下:

拓扑图

其中,用户的 Client 和 Server 侧之间的网络是无法连通的,我们的网络设备会把用户的 Ethernet 包封装到 UDP 里面发送(overlay,原理就和 VPN 一样),这个设备提供了封装,转发的服务。但是用户的 Client 和 Server 感受不到中间这个 tunnel 的存在,Client 和 Server 之间的 IP 地址是可以直接 ping 通的,TCP 也是可以连通的,全靠我们的设备在中间做了转发。

我们做了一些常规检查没有发现问题,然后重新上线新的设备,要求用户在 Server 端进行抓包。得到文件如下。在一般的问题分析中,我们一般只看 packet 的 header 就够了,不需要看 application 层的 TCP payload,所以在抓包的时候我们会截断 TCP 的 payload,这样,在下载抓包文件和交流的时候,更方便一些,并不影响问题的分析。

请据此分析,造成小概率超时的问题在哪里。

如果没有头绪,请看下面的提示。

在找不到问题的时候,我们会对比正常情况下的表现,通过正常和异常的情况的不同来寻找线索。以下是原有环境的抓包文件,没有超时的请求。

对比两个抓包问题,情分析问题的根因。

==计算机网络实用技术 目录==

这篇文章是计算机网络实用技术系列文章中的一篇,这个系列正在连载中,我计划用这个系列的文章来分享一些网络抓包分析的实用技术。这些文章都是总结了我的工作经历中遇到的问题,经过精心构造和编写,每个文件附带抓包文件,通过实战来学习网路分析。

如果本文对您有帮助,欢迎扫博客右侧二维码打赏支持,正是订阅者的支持,让我公开写这个系列成为可能,感谢!

没有链接的目录还没有写完,敬请期待……

  1. 序章
  2. 抓包技术以及技巧
  3. 理解网络的分层模型
  4. 数据是如何路由的
  5. 网络问题排查的思路和技巧
  6. 不可以用路由器?
  7. 网工闯了什么祸?
  8. 网络中的环路和防环技术
  9. 延迟增加了多少?
  10. TCP 延迟分析
  11. 压测的时候 QPS 为什么上不去?
  12. 压测的时候 QPS 为什么上不去?答案和解析
  13. 重新认识 TCP 的握手和挥手
  14. 重新认识 TCP 的握手和挥手:答案和解析
  15. TCP 下载速度为什么这么慢?
  16. TCP 长肥管道性能分析
  17. 请求为什么超时了?
  18. 请求为什么超时了?答案和解析
  19. 后记:学习网络的一点经验分享
与本博客的其他页面不同,本页面使用 署名-非商业性使用-禁止演绎 4.0 国际 协议。
 

PDF 电子书重排和裁剪

很久之前画重金买的阅读器1是 A4 纸大小,无论是阅读电子书还是 paper 都很好。但是后来莫名其妙地屏幕部分区域失灵了(情况和这里2介绍的差不多),维修找不到售后,京东推给 SONY,SONY 客服根本不知道有这么个产品,所以索性换了另一款阅读器:remarkable2.

这件事也加深了我 SONY 品控差的印象,之前的买过的 SONY 产品还包括 PSV,遥感漂移了(好像 switch 也有这类问题,所以可以饶恕吧);PS4 手柄莫名其妙也坏了,PS4 主机后来也坏了。以后不想再买 SONY 的产品了……

回到 Remarkable2,这款屏幕是 10.3 英寸,没有比之前的尺寸小很多,有一些 PDF 阅读起来就不太方便。有一些阅读器支持重排版和裁剪,有这个功能就解决问题了。但是仔细一想——重排版和裁剪不应该是一个软件功能吗?那么直接使用软件对 PDF 进行处理,然后阅读处理之后的文档不是也可以解决问题吗?

然后就发现了这个软件 K2pdfopt3,可以重新版本 PDF 为阅读器的尺寸。并且可以自动删除 PDF 的白边。

比如下面这个文档,对于印刷比较友好,左侧页面有右侧的留白,右侧页面有左侧的留白,但是使用阅读器,就浪费空间了。

K2pdfopt 可以自动裁切这种空白,命令是:

效果如下。

裁剪之后就没有浪费的空白页面了

双栏的论文 PDF 页可以改成单栏的:

对双栏 PDF 重排

最后在阅读器上的效果如下:

  1. https://www.kawabangga.com/posts/3161 ↩︎
  2. https://www.bilibili.com/video/BV1dt411R75L/ ↩︎
  3. https://www.willus.com/k2pdfopt/ ↩︎

 

IP 网段的几种常见表示方式

IP Network

也叫做 CIDR (Classless Inter-Domain Routing),表示一个网络段,比如 192.168.0.0/24。

路由设备通过网络掩码去匹配地址,所以子网的划分一般用这种形式。/24 有的地方也用掩码 255.255.255.0,表示的内容是一样的。

ipcalc1 这个工具可以帮助计算 IP 网络。

ipcalc

IP Range

表示一个 IP 范围,从起始 IP 到结束 IP。比如 192.168.1.1 到 192.168.1.100,一共 100 个 IP。

它可以表示如:192.168.1.100 – 192.168.2.10 这种连续的段,但是 Network 是无法表示出来的。

IP Glob

使用 * 通配符来匹配 IP 的某部分,语法类似于 shell 中对文件名的 glob 匹配。

比如 192.168.1.* 就等同与 192.168.1.0/24。但是 192.168.1.2* 就没有与之等同的 Network 表示。

反过来,192.168.1.0/26 的范围是 192.168.1.0 – 192.168.1.63, 也不能用 IP Glob 表示。

SSH 的 ~/.ssh/config 就是用 IP Glob 来定义不同的 IP (Host)登陆的配置的。

IPSet

IPSet 是一个 Set,一般来说是 IP 地址和 CIDR 的集合,所以可以表示任意 IP 的集合。ACL 一般用 IPSet 的方式来配置。

  1. https://formulae.brew.sh/formula/ipcalc ↩︎
 

Burn out 逃生指南

在同一家公司工作两年以上,有很大概率会 burn out(意思就是精疲力尽,俺不中了)。如果岗位又是 SRE,那么 burn out 时刻几乎是必然。

为什么会这样?一个是因为工作的时间越长,做的东西就越多,维护的东西也越多,维护的工作就越多,然而新的项目还是要做,就会忙不过来了。加上在大公司分工明确,没有人关心甲方的死活,所以你依赖的库时常会有不兼容更新,依赖的组件经常因为组织结构调整而下线,依赖的 IDC 也会下线,安全团队时不时也会找过来让做一些安全方面的加固。总有一天,会发现自己的 todo list 里面放满了待迁移的事项,自己的用户天天来问一些相同的问题,老板有新的想法需要马上实现。每天下了班都在想着工作,每天都不想上班。这个时候,你就知道这是 burn out 了。

作为一个资深的 SRE,我这里有两条靠谱的路可以逃生。

第一条:每两年换一次工作。

很显然,这样的话,上面这些工作就不会积累下来压死骆驼了。但是如果不想工作三十年打 15 份工的话,就需要一些技巧了。

建议一:提高工作效率,而不是工作时间

之所以放在第一条,是因为这是最重要的一条建议,也是常常被我们忽略的一条。

工作总量 = 工作效率 x 时间

在工作量大的时候,自然而然想到的是延长工作时间,这是非常不可取的,工作时间应该固定在每天 8小时,一周5天,不能再增加了。尤其是 SRE,工作时间越长,出错的概率越大,出了事故就得去救火,review incidents,提出改进措施,实施改进措施,带来更多的工作。此外,如果延长工作时间,那后面要讨论的心理管理等话题就都没有意义了。

所以工作量增加的时候,重点要放在提高工作效率,而不是增加工作时间,end of story.

建议二:安排工作的优先级和时间

在焦头烂额的时候,如果有人天天来跟你说「这个需求很重要,什么时候能完成?」可能就会先做这个需求。有段时间每天至少 5 个人来问我 xx 什么时候可以做完,我的回答每天都一样,「和最初承诺的时间一样,如果最近有空了可以加快一些」。因为最初的时间就是按照优先级排列的,不会因为有人天天来问就变得快一些。

优先级如何排列,也不是只看需求方说的。如果对方提了一个不合理的时间,要了解下为什么这样着急。很多 deadline 都是随意拍脑袋定的,可能是为了某人在某个时间点可以向大老板汇报,可能是依赖你的工作的人先承诺了一个 deadline,也可能就是随意定的一个日期。在焦头烂额的一堆工作中,有几个有着让人焦虑的 deadline,让人很难忽略这些工作。但是优先级不应该按照 deadline 来排列,而应该按照真正的重要程度来排列。

  • 项目的发布日期已经对外宣布,用户期待在这一天使用新功能;
  • 线上的系统摇摇欲坠,必须更新一个 fix;
  • 新的集群需要部署,但是如果晚几天部署,也不会 block 任何人的工作;
  • ……

遇到不合理的预期的时候,可以问这几个问题:

  • 这个需求是服务谁的?为什么要做?如果不按照这个时间上线会有什么后果?
  • 其他人的工作是否可以并行做,如果我的这个工作不做,会 block 谁?

有时候把项目在 deadline 之前做完了,却发现后续的一段时间并没有用起来,或者项目继续被其他人 block 着,原来给出的时间线本身就是不切实际的。在最开始就讨论好项目整体的计划,了解真正的紧急程度,避免这些问题。

按照优先级给出需求方截止时间,然后按时间交付工作。但是这之间难免会遇到其他事情,比如临时插入了更紧急的需求,线上发生了事故需要立即处理等等。这一般也不是问题,在时间线有变化,无法按时交付的时候,应该立即通知需求方遇到的困难,新的预估时间。忌讳的是没有和需求方同步,直到交付日期的时候才说,因为某某原因项目无法按时交付了。

建议三:大项目如何推进?

对于大型的项目,尤其是需要多方参与的那种,如果你不幸当了项目的 owner,那么这个建议很实用:用笔记软件记录每天的进展,记录每天遇到的问题,以及这些问题的进展。

以前有一次我们要新建一个数据中心,infra 把机器准备好,然后中间件团队部署好各种服务,缓存,队列,网关等等,然后业务团队部署好业务程序,最后上线。但是我们已经好几年没有完整地上线过一个数据中心了,很多代码中都已经编码了 IDC 的名字,所以这项工作异常困难,要么这个组件启动不了,要么那个组件存在硬编码问题。

负责这个项目的同事是一个很靠谱的人,每遇到一个问题,他都在文档中记录下来。问题原因,负责人,解决方法,解决进度。项目结束之后,这个文档列出来长长的一串问题。看到这个文档我的感受有二:项目真难,这位负责的同事真靠谱。

同时我也学会了这项工作方法, 那时候起我就开始写工作笔记(用的软件是 Roam Research,笔记经过整理记录在公司的文档系统中),每一个项目都有详细的记录,记录的问题也成千上万了。

工作笔记的好处多多,显而易见的是,没有人能记住如此多的问题和细节,所以必须追踪记录。另外也让工作进度和内容透明,如果项目不能如期完成,也能知道问题在哪里。如果没有项目文档,无法解释项目进展和问题,就只能项目负责人的问题了。

经过实践我发现一个额外的好处是,可以带来工作心态的变化。

如果没有记录——想起来这个项目满是头疼,已经经历过 x 问题,y 问题,天知道还要经历什么问题,感觉每走一步都困难重重,想起来就头疼。

有了记录——我们已经解决了 x 问题和 y 问题,我倒要看看还可能出现什么问题!

建议四:使用异步的沟通方式

前面提到过我们要提高工作效率。一个重要的方法就是不要破坏自己的整块时间,不要让自己总是处于被打断的情况。如果养成了过几分钟就要切换到聊天软件查看消息的状态,那工作效率就完蛋了。

要像使用邮件一样使用消息软件,异步轮询沟通。(证明:基于忙轮询的 DPDK 比基于中断的 Linux 网络栈,性能就高多了)。

怎样做呢?前面我们已经学会写工作笔记了,在被 block 需要与人沟通的地方,就在这里记录下需要沟通和确认的地方。然后在每天定时(比如早上刚来和午饭之后)遍历所有在 block 的点,对每一个点都问一遍相关的同事需要确认的问题。但是一定要把所有的细节说全,比如咨询一个网络问题,要提供自己的 IP,对方的 IP 端口,现象是什么,预期结果是什么,traceroute 是什么。防止对方缺少信息需要跟你再次确认。这就回到问问题的艺术的话题1了。这样就不需要等待回复,所有相关的消息发出去之后就可以继续做没有被 block 的工作,然后等下下次轮询的时间查看消息。

对于收到的信息也一样,几乎所有的消息都不必立即回复。也可以用轮询的方式处理。很多人问问题的时候都不懂如何一次性把信息都提供出来,比如,报告网络问题,连从哪里到哪里有问题都说不明白。不必在等待回复上浪费时间。

建议五:安排工作计划

这条建议可以让你带着一个好心情上班:每周安排好这周要做的事情,每天安排好明天要做的事情,可以已经确定的优先级来安排。

如果没有工作计划,那每天上班看到的就是一个长长的 todo list,怎么能让人不焦虑。

如果有工作计划的话,至少确定今天只要完成这些工作就好了。心理上的负担也会轻松很多。明天的工作就让明天的自己去担心好了。

建议六:每天至少完成一件事情

这条建议可以让你带着一个好心情下班:每天至少完成一件事情,比如解决集群搭建中一个 block 的点,比如完整实现一个需求。

如果一天的时间都在开会,和不同的人讨论细节,到下班的时候一事无成,是很挫败的。每天至少动手完成了点什么,这点满足感会带来很大的不同。

建议七:不要完全放弃有长期收益的事情

不要花所有的时间去做紧急的事情,要花时间去做不紧急但是重要的事情。

比如:

  • 提高监控的覆盖度;
  • 自动化一个操作;
  • 从根本解决一个性能问题;

每天忙于救火,就永远无法从这种工作状态中脱身。去从根本解决问题,工作也会越来越少,形成良性循环。

举个例子:在给产品值班的时候,会有很多用户来问问题。我一般会提供用户文档链接,文档中有答案。如果对于一个问题没有现有的文档可以回答,要么是产品设计出了问题——为什么用户会有此疑问?要么是文档不够全面,我会去写一个关于这个问题的文档,然后再给用户文档链接。虽然表面上可以直接回答的问题花了更长的时间去解决,但是长远来看,将来的用户可能因为这个文档就不来问这个问题了,即使有人问相同的问题,我也可以给文档链接。

有关操作的自动化,也不是所有的操作都应该自动化,也要看投入产出比。如果一个操作一个月才有机会操作一次,那么用文档记录下来如何手工操作,也可以。相较之下,手工操作反而可能成本更低。此外,如果使用频率不高,那么下次用到的时候,自动化的流程很可能是坏掉的,需要临时去 debug 哪里出了问题。

  1. 程序员如何高效和同行交流 ↩︎
 

c100m 问题

互联网诞生以来,如何让一台服务器服务更多的用户就成为了软件工程师一直试图解决的难题。c10k1 指的是如何让一台服务器同时服务 10k 个用户的连接。工程师们发明了一种又一种技术方案来挑战性能的极限,Event driven IO,Async IO,Erlang,等等。Whatsapp 用 Erlang 在 24 核心的机器上支持了 2百万 个连接,MigratoryData 用 12 核心的机器,Java 语言,支持了 1千万的连接。虽然技术进步,硬件也在进步,这项性能挑战来到了 c100m,一亿个连接……

一亿个连接是什么概念呢?就算微信这种超大体量的用户,只需要几台机器就可以提供接入服务了。

本文小试牛刀,用 Linux 网络栈来尝试建立和保持尽可能多的连接。但是止步于 TCP 连接的建立,不做数据传输,所以除了好玩,没有实际意义。

实验针对的是主动发起连接的一侧,这样比较好控制速率。

实验的环境是,client 端用一个程序发起 TCP 连接建立,server 端用上一篇博文介绍的 XDP bounce 程序来回复 SYN-ACK 包2,假装建立好连接。然后我们观察 client 端最多可以建立多少连接。仅仅是建立连接而已,不会做数据发送。

Client 端的代码如下,代码来自 c1000k3,稍微做了修改,支持了并发,这样建立连接速度快一些。

代码的逻辑是:fork 1000 个进程,每一个进程对一个 dest port 建立 10000 个连接,总共是1千万个连接数。

实验需要两台机器对着发,因为 server 端使用的是 XDP,性能很高,所以 1核心的机器就足够了。

Client 端我用的 8 核心的机器。

需要进行配置的内容

  1. 修改 ulimit,这样程序才能用更多的 fd:ulimit -n 1048576;
  2. 修改 TCP keepalive,keepalive 会消耗额外的资源:sudo sysctl -w net.ipv4.tcp_keepalive_time=720000
  3. 如果内存不够,通过 swap 来扩展;
  4. 使用 cgroups 限制程序能用的 CPU,防止 ssh 都连不上:

几分钟就可以跑到 1千万连接,轻轻松松。

下面可以修改参数来尝试 1亿连接数:

还是用上面的环境,跑到3千万连接的时候虚拟机崩溃了,只提示 fatal error。

我又去 DigitalOcean 开了台虚拟机,看能不能跑到更高。

这次跑到 5千万连接机器又崩溃了。

约5千万连接数

到之类就没办法继续研究是什么原因了,可能是虚拟化的问题,如果折腾一下物理机环境,说不定可以跑到1亿。

另外我发现一个有趣的地方,如果 abort client 程序,大量连接的 fd 需要 kernel 去回收,会造成所有的 CPU 100% kernel state,机器几乎卡死了,直到连接回收完毕。这部分好像是用 cgroups 限制不住的。所以,如果在不可信的共享的执行环境,通过建立大量的连接再退出进程,说不定有可能恶意挤兑其他租户的资源?

  1. https://en.wikipedia.org/wiki/C10k_problem ↩︎
  2. XDP 实现所有的 TCP 端口都接受 TCP 建立连接 ↩︎
  3. https://github.com/ideawu/c1000k ↩︎