从Brutal算法的实现引申出的随想

前篇 关于 TCP 拥塞的控制算法,入门知识可以从 《TCP拥塞控制算法BBR的原理和改进实践》开始过一遍 Brutal 算法的核心代码剖析 static void brutal_update_rate(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); struct brutal *brutal = inet_csk_ca(sk); u64 sec = tcp_sock_get_sec(tp); u64 min_sec = sec - PKT_INFO_SLOTS; u32 acked = 0, losses = 0; u32 ack_rate; // Scaled by 100 (100=1.00) as kernel doesn't support float u64 rate = brutal->rate; u32 cwnd; u32 mss = tp->mss_cache; u32 rtt_ms = (tp->srtt_us >> 3) / USEC_PER_MSEC; if (!rtt_ms) rtt_ms = 1; for (int i = 0; i < PKT_INFO_SLOTS; i++) { if (brutal->slots[i].sec >= min_sec) { acked += brutal->slots[i].acked; losses += brutal->slots[i].losses; } } if (acked + losses < MIN_PKT_INFO_SAMPLES) ack_rate = 100; else { ack_rate = acked * 100 / (acked + losses); if (ack_rate < MIN_ACK_RATE_PERCENT) ack_rate = MIN_ACK_RATE_PERCENT; } rate *= 100; rate /= ack_rate; // The order here is chosen carefully to avoid overflow as much as possible cwnd = rate / MSEC_PER_SEC; cwnd *= rtt_ms; cwnd /= mss; cwnd *= brutal->cwnd_gain; cwnd /= 10; cwnd = max_t(u32, cwnd, MIN_CWND); brutal_tcp_snd_cwnd_set(tp, min(cwnd, tp->snd_cwnd_clamp)); WRITE_ONCE(sk->sk_pacing_rate, min_t(u64, rate, READ_ONCE(sk->sk_max_pacing_rate))); } 剖析 从 struct tcp_sock 检索当前时间戳,转换成秒 计算出一个最小的时间戳,该时间戳是当前时间戳减去一个固定大小的窗口(PKT_INFO_SLOTS)。这个窗口用来限定考虑的数据包信息的时间范围 遍历存储数据包信息的数组 brutal->slots,累加在时间窗口内的确认(acked)和丢失(losses)的数据包数量 如果在时间窗口内的样本数量小于定义的最小样本数量 MIN_PKT_INFO_SAMPLES,则将确认率 ack_rate 设为 100%,表示没有丢包 如果样本数量足够,则计算确认率 ack_rate,即在考虑的时间窗口内被确认的数据包与总数据包的比例,并乘以 100 转换为百分比格式 如果确认率低于定义的最小确认率 MIN_ACK_RATE_PERCENT,则使用这个最小值 然后根据确认率调整发送速率 rate。这里使用了一些数学操作来避免使用浮点数,因为内核通常不使用浮点运算 接下来,根据新的发送速率、往返时间(RTT),以及最大分段大小(MSS)计算新的拥塞窗口大小 cwnd 拥塞窗口大小还乘以了窗口增益 cwnd_gain(调整因子),然后除以10(因为增益是以十分之一为单位) 拥塞窗口大小被限制在一个最小值 MIN_CWND 和 tp->snd_cwnd_clamp(拥塞窗口的最大允许值)之间 使用 brutal_tcp_snd_cwnd_set 更新 tcp_sock 结构体中的 snd_cwnd 拥塞窗口大小 最后,更新套接字的发送速率 sk_pacing_rate,这也被限制在 sk_max_pacing_rate(最大发送速率)以下 其实我们能明显看出来,TCP 拥塞算法的核心思想,就是根据当前的网络拥塞情况(在这里依赖于判断丢包率)来计算 cwnd,这个显著指标作为核心值会判定直接影响到发送速率和数据包重传策略的决策,这种 AIMD 原则会在网络状况良好时逐渐增加发送窗口,以增大吞吐量,而在检测到网络拥塞(如丢包事件)时则大幅减小窗口大小,以减轻网络负担,理论上来说遵循这种原则的算法都可以算是一种 “君子算法”,但是 Brutal 算法不是的,在他们的文档中就说明了这一点: ...

December 6, 2023 · 3 min · Sxueck

浅谈Kubernetes Operator模式的开发

前言 这篇博客是属于对《Kubernetes Operator 进阶开发》的初步章节一个笔记总结,作为 2023 第一篇博文,也作为重新深入梳理 K8s 的基础部分,往后会逐步将之前的坑给填上 Operator的基本概念 作为最入门的篇章,我们将从写一个 Demo 开始,在这里将默认已经存在了一个可用的集群环境,当然如果在开始学习之前,你能有一些 Client-Go 基础那就再好不过了 控制器模式 控制器模式在日常生活中的应用非常广泛,也是根据原书的例子进行举例,当然这里缩减了非常多,也加入了些个人的想法 夏天我们需要调整空调制冷,现在过程如下: 空调启动后设置一个温度,例如 25 度 空调检测室温,确定室温是否高于目标值 如果高于目标阈值,例如室温 27 度,则开始制冷操作 如果低于目标阈值,保持静默到下一个探测周期 这其实就是控制器模式典型的工作流程,外部输入一个 “期望值”,期间一个 “控制器” 不断按照 “周期” 观测 “环境状态” 的 “实际值” 和 “期望值” 之间的差异,然后不断调整两者,以便维持平衡,这个过程则被称为 “调谐” Kubernetes中的控制器与CRD Kubernetes 中通过 “声明式API” 定义了一系列的资源对象,然后通过许多的 Controllers 来 “调谐” 这些资源对象的实际状态以向期待状态靠拢,从而实现整个集群 “尽可能” 靠拢配置中声明的期望状态,注意 CRD (Custom Resource Definition)是 Kubernetes 提供的一种机制,允许用户定义新的资源类型,通过定义 CRD,你可以扩展 Kubernetes 的 API,创建和管理自定义资源(Custom Resource,CR),Controller 则是一个自定义的控制器程序,它监视并响应 Kubernetes 集群中的资源变化。对于自定义资源,Controller 用于实现资源的创建、更新和删除逻辑 还是上面空调的例子,我们可以定义一个新的资源类型 AirConditioner,它包含空调的目标温度和当前状态等信息,CR 是基于 CRD 实例化的具体资源。每个空调的具体配置实例就是一个 CR,其包含了目标温度和当前温度等具体值,同样给空调 A (my-airconditioner-A) 和空调 B (my-airconditioner-B) 分别创建了两个温度,每个温度集合的格式都需要符合 CRD 的声明,实例中 Controller 是用于监控 CR 并执行相应操作的程序,它负责监控 AirConditioner 资源,并根据当前温度和目标温度来决定是否启动制冷 ...

September 26, 2023 · 4 min · Sxueck

杂谈 - 关于我买了条跨境专线这件事情

前前言 这是一篇杂谈,意味着不会像其他技术博文一样要求思路很严谨,也或者里面会有些错误或者不可靠的地方,欢迎探讨。 前言 再又一次看到谷歌搜索的人机认证后,我叹了口气。 自从高二有了自己第一台电脑后,怀着对 Linux 极大的兴趣,通过折腾 VPS 逐渐初次接触到了 “翻墙” 这个概念,才恍然意识,原来英文互联网能获取那么多一针见血的教程/知识,原来 Youtube (那时候对哔哩哔哩还没感兴趣)的视频那么好看,加上 Google 针对百度压倒性的搜索质量等等,让我从 “简中局域网居民”(不含贬义) 逐渐变成了一位 “国际互联网公民”。 那时候 Shadowsocks 横空出世,机场的概念还没兴起,自建梯子是当时的主流,支持 AliPay 的 Vultr 凭借着最低 35 元/月的最低套餐,变成了不知道多少 “年轻人的第一台 VPS”,我犹记得刚刚开始的时候,Vultr 东京 NTT 线路节点到深圳也才 45ms,学着教程部署一套 SS 后畅游外网似乎是一套那么理所当然的操作。 然而 Vultr 被人挤满后,当时夸张到需要连开五台机器,才能找出可以正常没有被墙的 IP,只要识别到了该 IDC 东京网段,网站会屏蔽你的服务,这就是因为低门槛导致不可控的因素极具增多,不是每个人都是一名合格的邻居,爬虫/垃圾邮件/DDOS/反动内容充斥着这里时候,我们意识到是时候找条新路子了。 一年亦或者是两年后,一方面是对持续维护翻墙服务器感到了厌烦,SS/SSR 在当时属于不断升级对抗阶段,如果更新不及时被 GFW 发现协议特征(最为经典就是 TCP 重放探测了),你会发现服务器 IP 被墙并不是件很意外的事情。另一方面,机场的概念被提出,只需要一台最低价格套餐就能享受到数以十计的节点,速度拉满,这简直不要太舒服。 机场时期 机场模式将会是目前以及未来的主流。 机场模式伴随度过大学时光,Stackoverflow / Github / Google 学术等等也是借着机场的便利才得以顺通,对于大部分人来说,机场完完全全可以当成最优解,只需要刷刷 Ins 或者谷歌搜索这种用途,又何必用其他的手段呢。 然而事情的便利都是有代价的,机场低廉的价格意味着需要进行超售才能维持成本,一条号称全 BGP 线路机场套餐 300G 才 10 元,一个出口 IP 被几百人使用,注意一个合格的机场是不会直连国外落地节点的,客户线路太不可控,万一效果不理想炸起来口碑就没了。一般都是在国内放置一个 BGP 入口,可以是阿里云/腾讯云等等,三网客户汇聚后转发到对于 BGP 节点优秀的出口线路。例如 PCCW / CN2 这些。 ...

June 28, 2023 · 3 min · Sxueck

在Ubuntu Server使用KVM与调优

KVM 是一种让 Linux 变成虚拟机监控程序的技术,它可以让多个虚拟机在同一台物理机上运行。为了让虚拟机正常运行,KVM 需要一些操作系统的组件,如内存管理器、进程调度程序、输入/输出堆栈、设备驱动程序、安全管理器和网络堆栈等,这些组件都包含在 KVM 中。每个虚拟机都像一个 Linux 进程一样运行,使用虚拟的硬件设备,如网卡、图形适配器、CPU、内存和磁盘等。 初始化和安装软件依赖 由于 KVM 和 Linux 内核紧密相关,所以为了保持文档的一致性,我们这里使用 5.15 版本的 Ubuntu 内核 $ uname -a Linux sxueck-server 5.15.0-1034-realtime #37-Ubuntu SMP PREEMPT_RT Wed Mar 1 20:50:08 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux $ sudo apt update $ sudo apt upgrade $ sudo apt -y install bridge-utils cpu-checker libvirt-clients libvirt-daemon qemu qemu-kvm genisoimage libguestfs-tools linux-tools-common $ echo 1024 | sudo tee /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages # 启用hugepages 在进行任务之前,我们需要对前置条件进行检查 $ kvm-ok INFO: /dev/kvm exists KVM acceleration can be used 上面代表了当前的系统能良好支持 KVM 环境 ...

May 10, 2023 · 7 min · Sxueck

Linux内核技术eBPF浅析

引言 最近在阅读 《Linux内核观测技术BPF》这本书,非常薄的一本书却能带领我走入 Bpf 技术的世界,但是它只能起到一个入门的作用,用于工作的时候还是有点捉襟见肘,这篇文章只能当成一个较为深入的理解,不能取代系统性的学习。 当我们使用网络应用时,性能和可靠性是至关重要的。然而,由于内核网络栈的设计和实现方式,传统的用户空间网络应用在处理大量网络流量时会面临性能瓶颈。为了解决这一问题,Linux 内核引入了一种名为 eBPF(Extended Berkeley Packet Filter)的技术,它允许开发人员在内核中运行自定义的代码,从而提高网络应用程序的性能和灵活性。而下面要重点提到的 XDP(eXpress Data Path)是 eBPF 的一个子集,提供了一种快速的数据处理方法,它通过在数据包接收阶段运行代码来提高网络应用的性能。 这里有个点需要注意一下,eBPF 和 BPF 都是一种可以在Linux内核中使用的虚拟机技术,但是它们有一些不同之处,BPF 最初是一种用于数据包过滤的技术。随着时间的推移,它的功能逐渐扩展,被用于在内核中运行的各种任务,例如系统跟踪和安全审计等。BPF 程序在内核中直接执行,它可以访问内核数据结构,但是它的功能和灵活性受到了一些限制。 而 eBPF 是对 BPF 的扩展,即 “extended BPF”。它通过引入一个新的指令集,允许在内核中运行的 BPF 程序更加灵活和强大。eBPF 可以在安全的环境下执行用户定义的代码,而不会影响内核的稳定性和安全性。eBPF 还支持在内核和用户空间之间进行通信,并提供了一些新的功能,例如支持用户定义的映射和钩子程序。 总的来说,eBPF 是 BPF 的一个更加灵活和功能强大的版本,它为内核和用户空间提供了更多的通信和交互能力,同时保持了与内核的兼容性和安全性。我们入门的话会从 BPF 开始进行解析。 微服务时代Linux内核的问题 如果论起 eBpF 最出名的案例,莫过于 Cilium 了,我参考了 Cilium 项目发起人的演讲《How to Make Linux Microservice-Aware with Cilium and eBPF》,在云原生时代,为什么 eBpf 至关重要。而他其中列出了四个为什么当前 Linux 内核不适合当前的时代(背景为内核尚未对云原生做支持的时代)。 Abstractions 抽象 上图是一张和网络相关的抽象,它清晰地展示了 Linux 内核中各个部分的抽象,例如如果我们想用 Netfilter 做包过滤,那就必须要经过 socket 和 TCP 协议栈。而如果从网卡往上看的话,包需要经过 Netdevice -> traffic shaping -> Ethernet 等等才能到达上部分应用。 ...

March 19, 2023 · 1 min · Sxueck

祝贺站点在一周年之际获得各项技术评分优秀

年底事情太多了,加上最近 COVID-2019 的事件,我也不幸中招,一直没有时间更新博客 分数就当乐呵就行,纯当给生活增点趣味 SSL 安全评分 SSL 技术评分 A+ 级 速度 谷歌 Insights 评分满分 Gtmetrix 速度测试优秀 至此也算给 2022 画上一个极其潦草的句号(明年保证将以前的坑填完!!!)

December 24, 2022 · 1 min · Sxueck

K3s更换底层DataStore实现简易HA

前言 K3s 自带的 SQLite 应付普通的嵌入式小服务来说绰绰有余,但是对于公司这种动辄吃掉几个核的高频调度来说,虽然勉强能支撑起日常的响应,却总有时候出现奇奇怪怪的 BUG,我已经不止一次碰到了因为默认 ServiceAccount 和 Namespace 中的资源无法做实时绑定,导致 SpringCloud 一直无法正常启动的问题了,虽然可以通过万能重启做解决,但这远算不上高可用边缘部署方案 下面是官方给 K3s 画的一副简图,我们之前已经将默认的 LB 换成了 MetaLB 并表示效果非常优秀,这次我们也来缝缝补补,按照文档来说,K3s 支持以下的外接数据库 K3s supports the following datastore options Embedded SQLite PostgreSQL (certified against versions 10.7, 11.5, and 14.2) MySQL (certified against versions 5.7 and 8.0) MariaDB (certified against version 10.6.8) Etcd (certified against version 3.5.4) Embedded etcd for High Availability 因为想要为以后的 Cluster Metrics 做接口准备,所以就暂时不考虑内嵌 Etcd 的方式,而是选择自己外接,这样对于整个集群的运行状况有一个直观的了解,也方便做监控 —— K3s 砍去了太多东西换取轻量化 我们都知道 K8s 的默认 Datastore 是 Etcd,而 K3s 则是使用了一种称为 Kine 的组件将 Etcd 的 K/V 操作翻译为了关系数据库的语法,Kine 将自己的接口暴露给 K3s ApiServer,也就是说,在集群组件看起来,自己还是针对 Etcd 进行读写,然而如果我们设置真正的 Etcd Backend,Kine 会略过并直接暴露真实的 Etcd-servers 给 K3s ApiServer ...

November 22, 2022 · 9 min · Sxueck