研究 k8s 网络工作原理

背景 用 k8s 也有一段时间了,之前遇到过 iptables 等出现问题,导致 k8s 节点间网络出现问题,于是想研究一下 k8s 的网络工作原理。 Docker 网络 首先研究一下 Docker 网络连接是如何实现的。Docker 首先会创建一个 bridge,名为 bridge0: $ ip a show docker0 3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:c4:87:73:bf brd ff:ff:ff:ff:ff:ff inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0 valid_lft forever preferred_lft forever inet6 fe80::42:c4ff:fe87:73bf/64 scope link valid_lft forever preferred_lft forever 默认情况下,每个容器都会有单独的一个 netns,然后创建一对 veth pair,一端留在 global netns,另一端放到容器中。在 global netns 中的 veth 端口会加入到 docker0 中:

Read More

浅谈乱序执行 CPU

背景 最早学习乱序执行 CPU 的时候,是在 Wikipedia 上自学的,后来在计算机系统结构课上又学了一遍,但发现学的和现在实际采用的乱序执行 CPU 又有很大区别,后来又仔细研究了一下,觉得理解更多了,就想总结一下。 经典 Tomasulo 参考 Stanford 教材 经典 Tomasulo,也是 Wikipedia 上描述的 Tomasulo 算法,它的核心是保留站。指令在 Decode 之后,会被分配到一个保留站中。保留站有以下的这些属性: Op:需要执行的操作 Qj,Qk:操作数依赖的指令目前所在的保留站 ID Vj,Qk:操作数的值 Rj,Rk:操作数是否 ready(或者用特殊的 Qj,Qk 值表示是否 ready) Busy:这个保留站被占用 此外还有一个 mapping(Wikipedia 上叫做 RegisterStat),记录了寄存器是否会被某个保留站中的指令写入。 指令分配到保留站的时候,会查询 RegisterStat,得知操作数寄存器是否 ready,如果不 ready,说明有一个先前的指令要写入这个寄存器,那就记录下对应的保留站 ID。当操作数都 ready 了,就可以进入计算单元计算。当一条指令在执行单元中完成的时候,未出现 WAW 时会把结果写入寄存器堆,并且通过 Common Data Bus 进行广播,目前在保留站中的指令如果发现,它所需要的操作数刚好计算出来了,就会把取值保存下来。 这里有一些细节:因为保留站中的指令可能要等待其他指令的完成,为了保证计算单元利用率更高,对于同一个计算类型(比如 ALU),需要有若干个同类的保留站(比如 Add1,Add2,Add3)。在 Wikipedia 的表述中,每个保留站对应了一个计算单元,这样性能比较好,但自然面积也就更大。如果节省面积,也可以减少计算单元的数量,然后每个计算单元从多个保留站中获取计算的指令。 可以思考一下,这种方法的瓶颈在什么地方。首先,每条指令都放在一个保留站中,当保留站满的时候就不能发射新的指令。其次,如果计算单元的吞吐跟不上保留站的填充速度,也会导致阻塞。 这种方法的一个比较麻烦的点在于难以实现精确异常。精确异常的关键在于,异常之前的指令都生效,异常和异常之后的指令不生效,但这种方法无法进行区分。 从寄存器重命名的角度来看,可以认为这种方法属于 Implicit Register Renaming,也就是说,把 Register 重命名为保留站的 ID。 再分析一下寄存器堆需要哪些读写口。有一条规律是,寄存器堆的面积与读写口个数的平方成正比。对于每条发射的指令,都需要从寄存器堆读操作数,所以读口是操作数x指令发射数。当执行单元完成计算的时候,需要写入寄存器堆,所以每个执行单元都对应一个寄存器堆的写口。 硬件实现的时候,为了性能,希望保留站可以做的比较多,这样可以容纳更多的指令。但是,保留站里面至少要保存操作数的值,会比较占用面积,并且时延也比较大。 ROB(ReOrder Buffer) 参考教材 为了实现精确异常,我们需要引入 ROB。在上面的 Tomasulo 算法中,计算单元计算完成的时候,就会把结果写入到寄存器堆中,因此精确异常时难以得到正确的寄存器堆取值。既然我们希望寄存器堆的状态与顺序执行的结果一致,我们需要引入 ROB。

Read More

Locale 影响排序的特殊副作用

背景 最近在答疑的时候,发现同一条命令在不同系统上行为不同,一开始以为是 bash 版本问题,排查后最后发现是 locale 的问题。一个例子如下: $ cat poc.txt | tr '\\n' ' ' 1 + - * / \ a b A B 一 二 测 试 α $ LANG="" sort poc.txt | tr '\\n' ' ' * + - / 1 A B \ a b α 一 二 测 试 $ LANG="zh_CN.UTF-8" sort poc.txt | tr '\\n' ' ' * + - / \ 1 测 二 试 一 a A b B α $ LANG="en_US.

Read More

一次从 RHEL 6 到 CentOS 7 的更新

背景 有一台 RHEL 6 的服务器,各种软件版本太老了,用起来很难受,因此想升级。一开始想升级到 RHEL 7,但是发现必须要从 RedHat 下载 ISO,比较慢,所以我就先切换到 CentOS 6,再升级到 CentOS 7 过程 RHEL 6 Pre upgrade 一开始还是打算升级到 RHEL 7,所以跟随 RedHat 的文档去做 pre upgrade check,发现有一步要跑好久,网上搜了一下,发现这个步骤会扫描已有的各种程序,检查升级以后会不会出现不能运行的问题。但是如果有很多小文件,这一个过程就会进行很久,好在可以设置 exclusion 目录。最后检查出来的结果就是 GNOME 没法升级,建议卸载。 倒腾了一下升级工具,发现需要离线安装,比较麻烦,我就干脆换 CentOS 了。 RHEL 6 -> CentOS 6 首先,把软件源都切换到 CentOS,这一步很简单,因为包都是一样的。只不过,因为 CentOS 6 在 centos-vault 里面,所以用起来比较麻烦。 CentOS 6 -> CentOS 7 由于 CentOS 6 到 CentOS 7 升级涉及的改动比较多,官方提供了一个升级工具。一开始,我想直接升级到 CentOS 7 最新版本,但是报错,看到网上说可以升级到 CentOS 7 的早期版本,试了一下,确实没问题。 一通升级以后,重启,进入更新过程,发现很多包都安装失败了。重启以后,因为找不到 rootfs,挂在了 dracut 的 initramfs 里面。

Read More

轶事一则

7.31 号周六的时候,发现 Intel Intrinsics Guide(https://software.intel.com/sites/landingpage/IntrinsicsGuide/) 出现错误,加载数据失败,于是在 Intel 的网站上提交了一个 bug。 8.2 号的时候,Intel 发邮件过来,说已经复现了问题,已经汇报给了后端团队。邮件原文: Thank you for bringing this to our attention. We have verified and encountered the same issue. Please know that we have escalated this issue to our backend technical team. We will get back to you as soon as we have an update. Have a nice day ahead! 8.4 号的时候,Intel 再次发邮件过来,说后端团队正在处理这个问题,会尽快完成修复,请我耐心等待。这个时候我去网站上看,还是有问题。邮件原文: Our backend team is still working on this issue.

Read More

配置 homebridge-broadlink-rm-pro

背景 最近发现空调遥控器电池有点不足,有时候会自动关机,于是拿出以前买的 Broadlink RM mini 3 充当远程的空调遥控器使用。为了方便手机上配置,分别采用了官方的 App 智慧星和 homebridge 进行配置。 步骤 首先用官方的智慧星配置好 Broadlink RM mini 3 的网络,然后配置 homebridge-broadlink-rm-pro。最早的插件作者不怎么更新了,这个版本是目前用的比较多的一个 fork。 安装好以后,在 Home 里面可以看到 Scan Code 的开关。打开以后,用遥控器在 Broadlink RM mini 3 附近按按键,就可以在 Homebridge 日志里看到 hex code 了。然后,就按照插件教程里的方法写配置,例子如下: { "platform": "BroadlinkRM", "name": "Broadlink RM", "accessories": [ { "name": "Air Conditioner", "type": "air-conditioner", "noHumidity": true, "minTemperature": 26, "maxTemperature": 28, "defaultCoolTemperature": 27, "data": { "off": "2600...", "cool28": { "data": "2600..." }, "cool27": { "data": "2600.

Read More

轶事一则

7.17 号周六的时候,一位朋友 @elenacliu 发现深研院网站的一个文档链接,文档中标题写的是 2021,但是网页的标题显示的是 2006 年。于是我发邮件给深研院的招生办,抄送本部的研招办,提交了这个 issue。 7.19 号周一上午的时候,本部的研招办回复了一封邮件,没有理解我想表达的意思,可能以为我是要报考的学生,让我关注明年发布的文档。 7.19 号周一下午的时候,深研院招生办回复了邮件,说“谢谢你的反馈”,不过没有提到是否进行了修复。我晚上再查看页面的时候,发现新的文档链接已经修复了问题。

Read More

Nginx 处理 POST 请求出现 Internal Server Error 排查一则

前言 最近一个服务忽然出现问题,用户反馈,HTTP POST 一个小的 body 不会出错,POST 一个大的 body 就会 500 Internal Server Error。 排查 观察后端日志,发现没有出错的那一个请求。观察 Nginx 日志,发现最后一次日志是几个小时前。最后几条 Nginx 日志写的是 a client request body is buffered to a temporary file。 结论 继续研究后,发现是硬盘满了。Nginx 在处理 POST body 的时候,如果 body 超过阈值,会写入到临时文件中: Syntax: client_body_buffer_size size; Default: client_body_buffer_size 8k|16k; Context: http, server, location Sets buffer size for reading client request body. In case the request body is larger than the buffer, the whole body or only its part is written to a temporary file.

Read More

将 k8s rook ceph 集群迁移到 cephadm

背景 前段时间用 rook 搭建了一个 k8s 内部的 ceph 集群,但是使用过程中遇到了一些稳定性问题,所以想要用 cephadm 重建一个 ceph 集群。 重建过程 重建的时候,我首先用 cephadm 搭建了一个 ceph 集群,再把原来的 MON 数据导入,再恢复各个 OSD。理论上,可能有更优雅的办法,但我还是慢慢通过比较复杂的办法解决了。 cephadm 搭建 ceph 集群 首先,配置 TUNA 源,在各个节点上安装 docker-ce 和 cephadm。接着,在主节点上 bootstrap: cephadm bootstrap --mon-ip HOST1_IP 此时,在主节点上会运行最基础的 ceph 集群,不过此时还没有任何数据。寻找 ceph 分区,会发现因为 FSID 不匹配而无法导入。所以,首先要恢复 MON 数据。 参考文档:cephadm install。 恢复 MON 数据 首先,关掉 rook ceph 集群,找到留存下来的 MON 数据目录,默认路径是 /var/lib/rook 下的 mon-[a-z] 目录,找到最新的一个即可。我把目录下的路径覆盖到 cephadm 生成的 MON 目录下,然后跑起来,发现有几个问题: cephadm 生成的 /etc/ceph/ceph.client.admin.keyring 与 MON 中保存的 auth 信息不匹配,导致无法访问 FSID 不一致,而 cephadm 会将各个设置目录放到 /var/lib/ceph/$FSID 下 第一个问题的解决办法就是临时用 MON 目录下的 keyring 进行认证,再创建一个新的 client.

Read More

C++ 11 的 ABI 问题

背景 有同学遇到这样的一个问题,代码中链接了一个第三方的动态库,在链接的时候出现了不一致的问题,比如有一个函数签名如下: void foobar(std::string s) {} 使用 GCC 11.1.0 编译上面的代码,可以发现它需要的符号是 _Z6foobarNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE,但是第三方库里面却是 _Z6foobarSs,因此找不到对应的符号,链接失败。 问题 经过一番研究,发现 Ss 在 Itanium ABI 中表示的是缩写: In addition, the following catalog of abbreviations of the form "Sx" are used: <substitution> ::= St # ::std:: <substitution> ::= Sa # ::std::allocator <substitution> ::= Sb # ::std::basic_string <substitution> ::= Ss # ::std::basic_string < char, ::std::char_traits<char>, ::std::allocator<char> > <substitution> ::= Si # ::std::basic_istream<char, std::char_traits<char> > <substitution> ::= So # ::std::basic_ostream<char, std::char_traits<char> > <substitution> ::= Sd # ::std::basic_iostream<char, std::char_traits<char> > 这看起来很正常,_Z6foobarSs 表示的是 foobar(std::basic_string<char, std::char_traits<char>, std::allocator<char> >),但是 GCC 11.

Read More