RDS QPS 下降引发的网络流控分析记录
找到了另一个大佬对于TCP在linux内核的分析和测试: Link
Topic 1 现象看到一个朋友的问题, 由 RDS QPS 下降引发的网络问题分析:
引用原文的问题描述:
这个问题一开始是在进行RDS实验的时候发现的。最初的情景是,多台机器同时对数据库进行select和insert操作时,会发现insert操作会造成select操作qps大幅下降,且insert操作结束之后select操作的qps仍不能回升。起初以为是RDS的问题,但是在复现问题、监控RDS之后发现RDS的压力其实很小。于是开始怀疑是网络的问题,在简化了场景和操作之后,发现能在过去做tcp实验的机器上复现,于是用这个更简单的场景进行问题复现和分析。
正确答案以及复盘: https://yishenggong.com/2023/05/06/why-does-my-network-speed-drop-cn/
感谢大佬@plantegg 提供的这个案例和知识星球, tcp协议和 os 网络系统的分析我之前真是一句都说不出来, 这次确实完整的走了一遍网络的部分。
下面是我的一些思路和资料整理:
看完了问题的描述, 我基本上可以给出一个经验的判断,这个问题是aws t2实例的流控, 因为表现和之前的测试非常一致, 为了确认这个问题我又进行了相关的测试,基准信息如下:
实例类型:t2.micro
实例的基准带宽: 64Mbps
实例的突增带宽: 1Gbps
实例的EBS规格: 3000 IOPS, 100GB根卷存储, 125MiB/s
实例的OS : AmazonLinux2023.
Congestion: Cubic
具体的测试方法:
启动4个实例 t2.micro, hostname 分别是 nginx \ client1 \ client2 , 方便测试和区分实例; 启动一个额外的实例Client3 来尝试验证是不是流控。
nginx上面启动一个Nginx server, 发布一个 2GB 大小的文件; Client 1、 Client2 两个实例上面使用curl命令下载。
尝试保持这个流量一段时间之后, 带宽会被限制到64 Mbps。
由于是流控的问题, 所以现象会稳定出现。
Summary:
我理解这个应该是一个对tcp流的限制, 所以验证这个问题的方法是启动一个iperf的服务并重新生成流量, 如果是流控的话, 那么新的流量应该不受限制, 简单的证明方式就是在Nginx Server上面启动一个Iperf3 的 Server , 然后在发生降低速度的时刻, 使用 Client3 iperf 测试 Nginx Server, 这个时候观察到的现象是两个原有网络链接的速度还是很低, 但是iperf3 的测试网络吞吐量是比较高的。
t系列的实例都是突增类型, 可以允许一定的时间使用超过实例基准性能的资源, 在网络流量超过基准性能一段时间之后, 实例的带宽会被限制。 之前真的一直都没有思考过实例的带宽限制都是怎么做的, 让我在描述更多的细节我也确实无法提供, 借着这个机会,深入研究一下。
重新开始分析这个问题:
分析网络抓包看看,被限速的是Server的实例, 所以直接看Server的抓包.
贴一个我自己抓包截图,两个不同的流:
Server to Client 1:
Server to Client 2:
对与上面的两个截图来看, 斜率表越大,下载速度越快, 在斜率较大的前半段, 并未发生任何的TCP异常; 后半段斜率变小的期间,一直有规律的红色标记, 那个标记对应到抓包的结果中,是快速重传的数据包, 并且发生快速重传的时间比较规律, 这类规律的快速重传影响了传输的速度。
查看RTT的变化, 截图如下:
Server to Client 1:
Server to Client 2:
上面的这个部分可以看出整体增大了RTT ,基于之前的tc流控测试,rtt增大的链路上, 发送和接收的窗口需要相应的增大, 才能保证带宽的利用率较高。 按照实验更增加 wmem 和 rmem, 三个实例的设置都增加, 增加之后的速度并没有改变, 依旧还是被限制的水平。RTT从抓包的结果上面来看就是有明显的变化。
查看Server 实例的CWND, 这个CWND的记录直接使用命令行看:
左侧是Server上面的命令行执行结果 命令是 ss -4tni , 命令输出的结果中, 有当前的TCP链接的 RTT, CWND的值, 可以观察到, 到两个Server的链接中RTT的值 ~60ms 左右, 我使用了watch 0.1s来执行ss命令和观察, 重点关注上面的几个参数, 可以发现这几个数值的范围如下:
RTT ~ 50ms ~ 70ms 之间变化的频率基本上与cwnd的变化频率一致。
CWND 40 - 64 之间变化, 到达64之后回落到40左右, 然后快速提高到64再回落。
引用一个描述比较清晰的中文文章,其中也介绍了常见的几种拥塞控制算法的模式, 帮我大概理解了下网络的部分:
TCP 连接建立后先经过 Slow Start 阶段,每收到一个 ACK,CWND 翻倍,数据发送率以指数形式增长,等出现丢包,或达到 ssthresh,或到达接收方 RWND 限制后进入 Congestion Avoidance 阶段。1
从另一个文章中找到了如下的一个公式:
因此想要充分利用带宽,必须让窗口大小接近 BDP 的大小,才能确保最大吞吐量。
我们通过如下的例子来讨论一下,究竟 rwnd 和 cwnd 的值与理论最大使用带宽的关系是什么?
min(cwnd, rwnd) = 16 KB
RTT = 100 ms
16 KB = 16 X 1024 X 8 = 131072 bits
131072 / 0.1 = 1310720 bits/s
1310720 bits/s = 1310720 / 1000000 = 1.31 Mbps
因此,无论发送端和接收端的实际带宽为多大,当窗口大小为 16 KB 时,传输速率最大只能为 1.31 Mbps 。2
可以看到上面的公式中的第一个, 在计算的时候会在cwnd, rwnd 两个指标中选取最小的生效, 那么基于上面的变动, 我们已经把wmem , rmem 都改到了较大的值, 那么这个时候较小 的就是 cwnd 了, 这个值增加不上去就无法充分的利用带宽。
那么现在的问题变成了为什么 cwnd 无法继续增长?
Cubic 在 BIC 的基础上,一个是通过三次函数去平滑 CWND 增长曲线,让它在接近上一个 CWND 最大值时保持的时间更长一些,再有就是让 CWND 的增长和 RTT 长短无关,即不是每次 ACK 后就去增大 CWND,而是让 CWND 增长的三次函数跟时间相关,不管 RTT 多大,一定时间后 CWND 一定增长到某个值,从而让网络更公平,RTT 小的连接不能挤占 RTT 大的连接的资源。1
如果cwnd的大小在拥塞避免之后是基于时间来进行增长的,那么就可以结合上面我观察到的现象, 基本上过一段时间就会出现一定量的快速重传(抓包结果), 周期性的RTT 与 cwnd的变化(命令输出结果)比较吻合了。
基于上面的命令输出结果可以认为确实是通过RTT的增加 + 丢包控制 CWND, 压着CWND的值上不去的原因就是快速重传, 让cwnd随时间增大之后快速重传, 然后触发拥塞避免, 回退到较低的值, 然后开始循环。
感谢Shuo Chen 和 Hao Chen 大佬的测试分析思路以及工具,反复的理解这个Thread里面的说法, 现在基本上可以抓住这个问题的逻辑了。
走着一圈之后, 写完了上面自己的总结, 现在复制大佬的答案:
@bnu_chenshuo 从发生 retransmit 的间隔看,太规律了。我现在怀疑是 aws 有意限速,通过非常巧妙的少量丢包或乱序(图一中粗线下挂的小黑点),并控制 RTT,让 Linux TCP sender 的 cwnd 钳制在几十 KB,RTT 在 10ms,进而限制吞吐量在几千 KB/s。
我在 t3.micro 上用 FreeBSD 13 复现。观察到 AWS 先用对付 Linux 的办法:故意丢包乱序 + RTT=10ms,发现不灵之后,恼羞成怒,包也不丢了,直接卡脖子。总之就是不让你白嫖网速。对 Linux 是智斗,每秒钟丢你一两个包,RTT=10ms,让你自己cwnd小、速度上不去,你也不好说啥。对 FreeBSD 就上武力了。
???对于测试的结果, 我这边确实不同,我读取到的RTT 60ms 明显要比大佬测试的时候的RTT 10ms 更高. 我把这个理解为 中国区AWS 与 Global的差异, 这个问题也许还能继续分析?
调整 rmem , wmem 的大小到 MTU * CWND = 9001 * 60 = 540600 ,这个数值x2 设置到发送和接收的窗口, 重新抓包, 理论上应该看不到快速重传了。并且拥塞窗口应该是稳定在 60 左右。
测试的过程中观察, 设置 rmem wmem 最终的值为 1000000, ss命令中的 CWND稳定在 57 , 抓包的结果中没有快速重传, RTT也比较稳定了, 不再有跳跃和突然增加到 100+ 的RTT, 速度还是被限制了, 这时候较小的是 RWND, 所以也是为什么 CWND 可以比较稳定不再变化的原因。
Topic 2 如何使用wireshark查看丢包率?如何查看实例在限速状态的 丢包率 和 重传率?
在客户端的抓包结果里面, 使用字段 tcp.stream eq 0 and tcp.analysis.fast_retransmission 调整到对应的 tcp stream, 然后在Conversion
s窗口中统计的 Percent Filter 里面的百分比就是重传率, 丢包应该是 : tcp.analysis.lost_segment
或者就直接使用IO Graphs, 然后可以看到下面的图像,可以看到这个丢包比较小, 所以我在Y轴设置 100 的 Factor, 这样看起来清楚些。
如图:
基于这个抓包结果里面的基本上是乱序和快速重传,Server是发送数据的源头, 客户端没有收到认为这个是丢包。
那么感觉丢包率看Client的抓包结果应该就可以。 查看重传率 从Server这测的抓包结果来看。
Topic 3 RTT的变长是不是流控可以设计的?这个rtt确实无法区分到底是底层的流控带来的, 或者是设计故意拖长的,更新额外测试的结果:
将三个实例完全换成 c5.large, 后续没有RTT非常高的的问题了, 如图:
尝试处理掉CPU 的 IOwait, 也就是将这个文件缩小到 500MB ,之后nginx的数据都从内存取出, 发现RTT增加到了120ms, 维持了较长的时间之后还会回到60ms, 不知道这个原因是什么, 但是看起来RTT不稳定可能确实和os本身没什么关系。
试试其他规格的实例: 尝试在c5的实例上面直接使用tc卡住网桥设备的带宽, 来模拟链路中的网络设备? 观察rtt看看是否有任何的变化。
测试的方法使用docker-compose启动两个pod, 文件如下:
---
version: "3.8"
services:
pod1:
image: 111.dkr.ecr.cn-north-1.amazonaws.com.cn/haydenarchlinux:latest
restart: always
pod2:
image: 111.dkr.ecr.cn-north-1.amazonaws.com.cn/haydenarchlinux:latest
restart: always
...
将ArchLinux作为节点加入EKS UnmanagedNode
添加一个自管理的节点添加这个自管理的节点是直接添加进入集群的, 并未使用EKS节点组的概念, 所以这个节点是可以被重启, 或者健康检查失败的, 并不具有任何的扩展或者弹性管理的能力。
创建集群,启动一个新的 EC2, 登录到已经启动的 EKS 优化 OS 内,准备复制一些脚本过来。
添加EC2的标签: kubernetes.io/cluster/clusterName owned
配置EC2的Instance Profile
控制台获取 Kubernetes APIServer的Endpoint URL
获取 apiserver b64 CA : cat ~/.kube/config 这个文件里面可以找到 ,或者是通过EKS的控制台上面, 找到 Base64 的 CA。
编辑 userdata, 或者 ssh 登录到ec2上面创建一个bash脚本用来调用 bootstrap.sh
mkdir ~/eks; touch ~/eks/start.sh
---
#!/bin/bash
set -ex
B64_CLUSTER_CA=
API_SERVER_URL=
K8S_CLUSTER_DNS_IP=10.100.0.10
/etc/eks/bootstrap.sh ${ClusterName} --b64-cluster-ca ${B64_CLUSTER_CA} --apiserver-endpoint ${API_SERVER_URL}
集群里面没有节点组 , 也不会创建aws-auth configmap 所以节点无法正常的加入集群, 需要手动创建。
apiVersion: v1
data:
mapRoles: |
- groups:
- system:bootstrappers
- system:nodes
rolearn: [CLUSTER_ROLE]
username: system:node:{{EC2PrivateDNSName}}
mapUsers: |
[]
kind: ConfigMap
metadata:
name: aws-auth
namespace: kube-system
需要复制的文件sudo pacman -S containerd # 安装Containerd
scp /etc/eks/bootstrap.sh root@54.222.253.235:/etc/eks/bootstrap.sh # 复制bootstrap
scp /usr/bin/imds root@54.222.253.235:/usr/bin/imds # shell 脚本, 用来帮忙调用ec2 metadata 获取实例和VPC子网的信息
scp -pr /etc/eks/ root@54.222.253.235:/etc/eks/ # 直接复制了eks的相关脚本和配置模板
scp -pr /var/lib/kubelet/kubeconfig root@54.222.253.235:/var/lib/kubelet/kubeconfig # 复制kubeletconfig配置文件模板
scp -pr /etc/kubernetes/ root@54.222.253.235:/etc/kubernetes/ # 复制 kubernetes 的配置文件
scp -pr /etc/kubernetes/kubelet/ root@54.222.253.235:/etc/kubernetes/kubelet/ # 上面的命令没有递归复制, 所以需要指定
# 设置对应的内核参数, 如果不做kubelet 会报错提示 这些参数不符合要求。
kernel.panic = 10
kernel.panic_on_oops = 1
vm.overcommit_memory = 1
Bootstrap 脚本内容分析记录一下脚本自动配置的内容, 大概就是 获取变量, aws的服务地址, ec2 元数据地址, 替换模板中的变量生成Kubelet配置文件 和 Containerd 的配置文件(这个替换是一次性的, 也就是说, bootstrap只能变更模板中的变量一次, 第二次执行只会生成刷新一次集群的信息, 以及重启服务)。
读取bootstrap后面给出的参数,设置变量, 例如: ClusterName etc.
查看Kubelet的版本, 决定Runtime, containerd | dockerd, 判断条件是 kubelet 版本 大于 1.24++ kubelet --version
++ grep -Eo '[0-9]\.[0-9]+\.[0-9]+'
+ KUBELET_VERSION=1.24.9
---
+ IS_124_OR_GREATER=true
+ DEFAULT_CONTAINER_RUNTIME=containerd
设置ECR以及Pause容器地址# 获取region以及aws service domain
+ AWS_DEFAULT_REGION=cn-north-1
+ AWS_SERVICES_DOMAIN=amazonaws.com.cn
# 调用脚本 /etc/eks/get-ecr-uri.sh cn-north-1 amazonaws.com.cn ''
+ ECR_URI=918309763551.dkr.ecr.cn-north-1.amazonaws.com.cn
+ PAUSE_CONTAINER_IMAGE=918309763551.dkr.ecr.cn-north-1.amazonaws.com.cn/eks/pause
+ PAUSE_CONTAINER=918309763551.dkr.ecr.cn-north-1.amazonaws.com.cn/eks/pause:3.5
+ CA_CERTIFICATE_DIRECTORY=/etc/kubernetes/pki
+ CA_CERTIFICATE_FILE_PATH=/etc/kubernetes/pki/ca.crt
创建证书目录:+ mkdir -p /etc/kubernetes/pki
+ sed -i s,MASTER_ENDPOINT,https://CE0253A94B6B14215AE3282580CFA5E3.yl4.cn-north-1.eks.amazonaws.com.cn,g /var/lib/kubelet/kubeconfig
+ sed -i s,AWS_REGION,cn-north-1,g /var/lib/kubelet/kubeconfig
+ sed -i s,CLUSTER_NAME,NewClusterForManualJoin,g /var/lib/kubelet/kubeconfig
获取 VPC CIDR# imds shell script help to get metadata.
imds: /usr/bin/imds
++ imds latest/meta-data/local-ipv4
++ imds latest/meta-data/network/interfaces/macs/02:66:06:2e:48:08/vpc-ipv4-cidr-blocks
创建kubelet 配置, 计算预留的资源 和 MaxPod 等等参数的数值。/etc/kubernetes/kubelet/kubelet-config.json
+ mkdir -p /etc/systemd/system/kubelet.service.d
+ sudo mkdir -p /etc/containerd
+ sudo mkdir -p /etc/cni/net.d
+ sudo mkdir -p /etc/systemd/system/containerd.service.d
创建containerd 配置文件+ printf '[Service]\nSlice=runtime.slice\n'
+ sudo tee /etc/systemd/system/containerd.service.d/00-runtime-slice.conf
+ sudo sed -i s,SANDBOX_IMAGE,918309763551.dkr.ecr.cn-north-1.amazonaws.com.cn/eks/pause:3.5,g /etc/eks/containerd/containerd-config.toml
kubelet配置和启动。+ sudo cp -v /etc/eks/containerd/kubelet-containerd.service /etc/systemd/system/kubelet.service
+ sudo chown root:root /etc/systemd/system/kubelet.service
+ sudo containerd config dump
+ systemctl enable kubelet
+ systemctl start kubelet
制作 NodeTemplate AMI
如果使用已经加入过集群的实例直接制作AMI, 节点会无法加入, kubelet报错是APIserver拒绝这个节点的加入。
2023-04-19T15:10:13Z kubelet-eks.daemon[11577]: E0419 15:10:13.325154 11577 event.go:267] Server rejected event '&v1.Event{TypeMeta:v1.TypeMeta{Kind:"", APIVersion:""}, ObjectMeta:v1.ObjectMeta{Name:"ip-10-0-1-249.cn-northwest-1.compute.internal.17575e9c08aefd8d", GenerateName:"", Namespace:"default", SelfLink:"", UID:"", ResourceVersion:"", Generation:0, CreationTimestamp:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), DeletionTimestamp:<nil>, DeletionGracePeriodSeconds:(*int64)(nil), Labels:map[string]string(nil), Annotations:map[string]string(nil), OwnerReferences:[]v1.OwnerReference(nil), Finalizers:[]string(nil), ZZZ_DeprecatedClusterName:"", ManagedFields:[]v1.ManagedFieldsEntry(nil)}, InvolvedObject:v1.ObjectReference{Kind:"Node", Namespace:"", Name:"ip-10-0-1-249.cn-northwes ...
Linux OS 网络流量控制测试
内核参数的说明对于 TCP 来说,会遇到如下的几个参数。
如果我们需要查看一下当前 OS 内与 tcp 相关的 Kernel 参数, 命令如下:
]$ sysctl -a | egrep "rmem|wmem|tcp_mem|adv_win|moderate"
其中主要需要关注的是:
net.ipv4.tcp_rmem = 4096 131072 6291456
net.ipv4.tcp_wmem = 4096 16384 4194304
这两个参数表示的是当前内核预留的 Socket Buffer, 单位是Bytes, 也是具体指 内存 的大小。具体的说明我找到 Kernel文档的说明如下:
tcp_rmem - vector of 3 INTEGERs: min, default, max
min: Minimal size of receive buffer used by TCP sockets.
It is guaranteed to each TCP socket, even under moderate memory
pressure.
Default: 4K
default: initial size of receive buffer used by TCP sockets.
This value overrides net.core.rmem_default used by other protocols.
Default: 87380 bytes. This value results in window of 65535 with
default setting of tcp_adv_win_scale and tcp_app_win:0 and a bit
less for default tcp_app_win. See below about these variables.
max: maximal size of receive buffer allowed for automatically
selected receiver buffers for TCP socket. This value does not override
net.core.rmem_max. Calling setsockopt() with SO_RCVBUF disables
automatic tuning of that socket's receive buffer size, in which
case this value is ignored.
Default: between 87380B and 6MB, depending on RAM size.
tcp_wmem - vector of 3 INTEGERs: min, default, max
min: Amount of memory reserved for send buffers for TCP sockets.
Each TCP socket has rights to use it due to fact of its birth.
Default: 4K
default: initial size of send buffer used by TCP sockets. This
value overrides net.core.wmem_default used by other protocols.
It is usually lower than net.core.wmem_default.
Default: 16K
max: Maximal amount of memory allowed for automatically tuned
send buffers for TCP sockets. This value does not override
net.core.wmem_max. Calling setsockopt() with SO_SNDBUF disables
automatic tuning of that socket's send buffer size, in which case
this value is ignored.
Default: between 64K and 4MB, depending on RAM size.
这文档中的说明,默认的三个值分别是: 最小, 默认, 最大。测试了一下, 如果只是需要实际调整的话, 调整那个最大值即可, 在高延迟的链路中, 调整默认值或者最大值就可以生效。 在测试的过程中, 将三个值都写成一致,控制变量。
对于TCP协议的接收与发送两方, 各自有自己的 RecvBuffer 和 SendBuffer, 发送方会考虑链路上面可以承载的数据量(带宽), 以及 对方可以承载的数据量(rmem)参数使用的内存用量。
实际上, 只是更新 max 的值, 并不会更新 Recvbuffer.如果想增大 receive buffer 的大小, 可以增加 tcp_rmem 的 default 的值大小.
测试环境两个 EC2 c5.2xlarge, 其中一个部署 Nginx , 并使用如下配置文件部分设置 , 发布一个 Fedora ISO, 大小大约 2G。另一个上面只是客户端, 使用的访问客户端是Curl。
http {
server {
autoindex on;
autoindex_localtime on;
}
}
}
这个配置文件中的其他部分就省略吧。
测试准备测试的过程其实涉及了四个部分的 延迟 问题:
发送方的应用程序性能。
发送方的发送缓冲区大小, SendBuffer
接收方的接收缓冲区大小, RecvBuffer
接收方的应用程序性能。
调整延迟和控制带宽 , 这两个部分控制的都是网络传输部分的性能,测试的过程中默认认为 发送 以及接收方的性能都完全没有问题。
基准两个机器的内核参数使用默认值, 先通过 tc 流量控制注入一些延迟, 查看并分析RTT对于传输速度的影响。
两个server之间默认的内核参数:
服务端参数
net.ipv4.tcp_rmem = 4096 131072 6291456
net.ipv4.tcp_wmem = 4096 16384 4194304
客户端参数
net.ipv4.tcp_wmem = 4096 131072 6291456
net.ipv4.tcp_rmem = 4096 16384 4194304
curl 的 测试命令 如下:
~]$ curl -o /dev/null -s -w "time_namelookup:%{time_namelookup}\ntime_connect: %{time_connect}\ntime_appconnect: %{time_appconnect}\ntime_redirect: %{time_redirect}\ntime_pretransfer: %{time_pretransfer}\ntime_starttransfer: %{time_starttransfer}\ntime_total: %{time_total}\n" http://nginx.liarlee.site/Fedora-Workstation-Live-x86_64-38_Beta-1.3.iso
如果使用默认的参数, 那么2G的 ISO 文件可以快速的传完, 这是两个实例的基准表现, 这也是创建了一个TCP Connection 的较好的性能表现, EC2 之间的带宽 10Gbps。
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0*
Trying 172.31.48.133:80...
100 1967M 100 1967M 0 0 1116M 0 0:00:01 0:00:01 --:--:-- 1116M
* Connection #0 to host nginx.liarlee.site left intact
time_connect: 0.005461
time_starttransfer: 0.005797
time_total: 1.762040
总体看起来使用了 2s 不到的时间, 就传输完成了2G的文件。
仅控制带宽在添加了带宽 控制, 带宽控制在 1000Mbps, 带宽监控结果:
Curl命令的结果:
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1967M 100 1967M 0 0 118M 0 0:00:16 0:00:16 --:--:-- 118M
time_connect: 0.002070
time_starttransfer: 0.002304
time_total: 16.633562
在仅仅控制带宽的前提下, 由于延迟忽略不计, 所以传输的数据量可以使用全部的带宽,这个场景下面传输的速度取决于网络带宽的大小, 网络带宽越大, 传输的数据量和速度都会相应的增加。
控制带宽+延迟给服务端添加一个 50ms 的 延迟, 使用 tc 工具, 参考文档, 命令如下:
tc qdisc del dev eth0 root
tc qdisc add dev eth0 root handle 1:0 htb default 1
tc class add dev eth0 parent 1:0 classid 1:1 htb rate 1000mbit
tc qdisc add dev eth0 parent 1:1 handle 2:0 netem delay 50ms
测试的时候使用curl命令, 将结果直接输出到 /dev/null, 这个场景下, 客户端收写数据的速度非常快,这样的测试排除了大文件落硬盘速度慢的问题, 但是也让客户端收到这批数据之后立刻可以发送ack给服务端(Client - ACK——> Server), 告知服务端我这边的数据已经处理完了, 回收 RecvBuffer 空间.
在添加带宽控制和 50ms 延迟之后, 带宽使用:
ping 命令 确认 rtt :
root@arch ~# ping nginx.liarlee.site
PING nginx.liarlee.site (172.31.48.133) 56(84) bytes of data.
64 bytes from i ...
删除所有非Running状态的Pod
背景 如果所有的节点上面都有Taint, 然后这个没有Taint的节点磁盘满了, 会导致当前的节点上面留下许多状态不正常的Pod, 这些Pod大概率是停留在了Evicted状态, 或者是Completed, 甚至是 Unknown 。
这种状态的Pod Deployment默认的情况下不会自动回收, 所以需要人工操作一下。
Knowledge Source
处理方法记录一个命令来处理这个类型的Pod。
kubectl delete pod --field-selector="status.phase==Failed"
测试方法
集群内两个节点, 其中一个节点Taint kubectl taint nodes ip-172-31-60-181.cn-north-1.compute.internal app=grafana:NoSchedule
启动30个Grafana
登录到节点上面, 创建一个巨大的文件, 触发DiskPressure。 fallocate -l 72G ./large.file
等待节点的DiskPressure被识别, 然后触发驱逐。
删除文件, 取消DiskPressure状态, 等待 30 个新的Pod Ready。rm -rf ./large.file
使用命令清除所有不是Running状态的Pod。~$ kubectl delete pod --field-selector="status.phase==Failed"
Over.
bpftrace 使用tracepoint 追踪 tcp 状态变化
入门教程大佬的博客, 入门教程: http://arthurchiao.art/blog/bpf-advanced-notes-1-zh/
记录基础的bpftrace使用方法单行程序的使用方法
[root@localhost-live ~]# bpftrace -e 'tracepoint:syscalls:sys_enter_execve { printf("%s %s\n", comm, str(args->filename));}'
Tracepoint如何获取可用参数的解释[root@localhost-live sys_enter_execve]# pwd
/sys/kernel/tracing/events/syscalls/sys_enter_execve
[root@localhost-live sys_enter_execve]# grep -ri .
format:name: sys_enter_execve
format:ID: 742
format:format:
format: field:unsigned short common_type; offset:0; size:2; signed:0;
format: field:unsigned char common_flags; offset:2; size:1; signed:0;
format: field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
format: field:int common_pid; offset:4; size:4; signed:1;
format: field:int __syscall_nr; offset:8; size:4; signed:1;
format: field:const char * filename; offset:16; size:8; signed:0;
format: field:const char *const * argv; offset:24; size:8; signed:0;
format: field:const char *const * envp; offset:32; size:8; signed:0;
format:print fmt: "filename: 0x%08lx, argv: 0x%08lx, envp: 0x%08lx", ((unsigned long)(REC->filename)), ((unsigned long)(REC->argv)), ((unsigned long)(REC->envp))
trigger:# Available triggers:
trigger:# traceon traceoff snapshot stacktrace enable_event disable_event enable_hist disable_hist hist
filter:none
id:742
enable:0
记录尝试追踪tcp状态变化的方法关于bpftrace追踪的总结, 追踪tcp状态的方法, 通过使用特定的tracepoint的来获取tcp状态的变化:
~$ bpftrace -e 'tracepoint:sock:inet_sock_set_state { printf("%s %d %d\n", comm, pid, args->newstate); }'
~$ bpftrace -e 'tracepoint:sock:inet_sock_set_state { printf("%s - %d -> %d - %d - %s\n",strftime("%H:%M:%S.%L", nsecs), args->oldstate, args->newstate, pid, comm); }'
关于返回值的说明:
https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9/-/blob/main/include/net/tcp_states.h
tcp_set_state 是一个内核函数,用于设置 TCP 套接字的状态。它有 12 个可能的返回值,分别对应 TCP 协议中定义的 12 种状态1。这些状态是:
1 TCP_ESTABLISHED:连接已建立2 TCP_SYN_SENT:主动打开连接,已发送 SYN 包3 TCP_SYN_RECV:被动打开连接,已收到 SYN 包4 TCP_FIN_WAIT1:主动关闭连接,已发送 FIN 包5 TCP_FIN_WAIT2:主动关闭连接,已收到对方的 ACK 包6 TCP_TIME_WAIT:主动关闭连接,等待一段时间以确保对方收到最后一个 ACK 包7 TCP_CLOSE:连接已关闭8 TCP_CLOSE_WAIT:被动关闭连接,已收到 FIN 包9 TCP_LAST_ACK:被动关闭连接,已发送最后一个 ACK 包10 TCP_LISTEN:监听状态,等待被动打开连接11 TCP_CLOSING:双方同时关闭连接,交换 FIN 和 ACK 包的过程中12 TCP_NEW_SYN_RECV:临时状态,用于处理 SYN 队列溢出的情况
追踪点理解起来还是比较简单的, 查看追踪点可用的参数,在这个部分可以看。
~$ cat /sys/kernel/debug/tracing/events/syscalls/sys_enter_sendmsg/format
name: sys_enter_sendmsg
ID: 1252
format:
field:unsigned short common_type; offset:0; size:2; signed:0;
field:unsigned char common_flags; offset:2; size:1; signed:0;
field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
field:int common_pid; offset:4; size:4; signed:1;
field:unsigned char common_preempt_lazy_count; offset:8; size:1; signed:0;
field:int __syscall_nr; offset:12; size:4; signed:1;
field:int fd; offset:16; size:8; signed:0;
field:struct user_msghdr * msg; offset:24; size:8; signed:0;
field:unsigned int flags; offset:32; size:8; signed:0;
print fmt: "fd: 0x%08lx, msg: 0x%08lx, flags: 0x%08lx", ((unsigned long)(REC->fd)), ((unsigned long)(REC->msg)), ((unsigned long)(REC->flags))
如果是使用 kprobe 的话, 不能使用 args, 需要使用确定的arg0 - argN, 这个部分还在摸索, 目前尝试用这个参数还是失败。 取到对应的得值, 但是print不出来。
诗词收集
收集一些自己平时见到的诗句:
Update 2018-12-24
邻人焉有许多鸡,乞丐何曾有二妻。当时尚有周天子,何事纷纷说魏齐。
算不尽芸芸众生微贱命,回头看五味杂陈奈何天。
何时杖策相随去,任性逍遥不学禅。
百无一用是情深,万般奈何为情困。
桃李春风一杯酒,江湖夜雨十年灯。
杀人放火金腰带,修桥补路无尸骸。
残雪凝辉冷画屏,落梅横笛已三更,更无人处月胧明。我是人间惆怅客,知君何事泪纵横,断肠声里忆平生。 纳兰性德《浣溪沙·残雪凝辉冷画屏》
曾经沧海难为水,除却巫山不是云。取次花丛懒回顾,半缘修道半缘君。
人生到处应何似,应似飞鸿踏雪泥。
山有木兮木有枝,心悦君兮君不知。
嫦娥应悔偷灵药,碧海青天夜夜心。
满堂花醉三千客,一剑霜寒十四州。
早岁读书无甚解,晚年省事有奇功。
车遥遥,马憧憧。君游东山东复东,安得奋飞逐西风。愿我如星君如月,夜夜流光相皎洁。月暂晦,星常明。留明待月复,三五共盈盈。 范成大《车遥遥篇》
一身诗意千寻瀑,万古流芳四月天。
未曾相逢先一笑,初会便已许平生。
三千年读史不过功名利禄,九万里悟道终归诗酒田园。
君埋泉下泥销骨,我寄人间雪满头。
伤心桥下春波绿,曾是惊鸿照影来。
醉后不知天在水,满船清梦压星河。
秋风吹尽旧庭柯,黄叶丹枫客里过。一点禅灯半轮月,今宵寒较昨宵多。
细雨生寒未有霜,庭前木叶半青黄。小春此去无多日,何处梅花一绽香。
岁久人无千日好,春深花有几时红。是非入耳君须忍,半作痴呆半作聋。
清风不识字,何故乱翻书。
三过平山堂下,半生弹指声中。十年不见老仙翁,壁上龙蛇飞动。欲吊文章太守,仍歌杨柳春风。休言万事转头空,未转头时皆梦。
山月不知心里事,水风空落眼前花。
未若锦囊收艳骨,一抔冷土掩风流。
莫言下岭便无难,赚得行人空喜欢。正入万山圈子里,一山放过一山拦。
有美一人兮婉如清扬,识曲别音兮令姿煌煌。绣袂捧琴兮登君子堂,如彼萱草兮使我忧忘。欲赠之以紫玉尺,白银珰,久不见之兮湘水茫茫。
生如逆旅,一苇以航。
Update 2023-01-09
回头万里,故人长绝。易水萧萧西风冷,满座衣冠似雪。《贺新郎·别茂嘉十二弟》辛弃疾
洛阳城里春光好,洛阳才子他乡老。《菩萨蛮·洛阳城里春光好》韦庄
伤心桥下春波绿,曾是惊鸿照影来。《沈园二首·其一》陆游
谁见幽人独往来,缥缈孤鸿影。《卜算子·黄州定慧院寓居作》苏轼
琵琶弦上说相思。当时明月在,曾照彩云归。《临江仙·梦后楼台高锁》晏几道
此后锦书休寄,画楼云雨无凭。《清平乐·留人不住》晏几道
美人自刎乌江岸,战火曾烧赤壁山,将军空老玉门关。
食肉何曾尽虎头,卅年书剑海天秋。文章幸未逢黄祖,襆被今犹窘马周。自是汝才难用世,岂真吾相不当侯。须知少日拏云志,曾许人间第一流。 清代吴庆坻《题三十计小象》
时人不识凌云木,直待凌云始道高。
总有千古,横有八荒,前途似海,来日方长。
鹏北海,风朝阳,又携书剑路茫茫。明年此日青云去,却笑人间举子忙。
Update 2023-03-01 派克直播间飞花令
不是逢人苦誉君,亦狂亦侠亦温文。照人胆似秦时月,送我情如岭上云。
一生一代一双人,争教两处销魂。相思相望不相亲,天为谁春?
也信美人终作土,不堪幽梦太匆匆。
银字笙调。心字香烧。料芳悰、乍整还凋。待将春恨,都付春潮。过窈娘堤,秋娘渡,泰娘桥。
中心藏之,何日忘之!
Update 2023-03-05 派克直播间飞花令
钓鱼台,十年不上野鸥猜。白云来往青山在,对酒开怀。欠伊周济世才,犯刘阮贪杯戒,还李杜吟诗债。酸斋笑我,我笑酸斋。晚归来,西湖山上野猿哀。二十年多少风流怪,花落花开。望云霄拜将台。袖星斗安邦策,破烟月迷魂寨。酸斋笑我,我笑酸斋。
知君用心如日月,事夫誓拟同生死。还君明珠双泪垂,恨不相逢未嫁时。
千古风流八咏楼,江山留与后人愁。水通南国三千里,气压江城十四州。
问余何意栖碧山,笑而不答心自闲。桃花流水窅然去,别有天地非人间。
德也狂生耳。偶然间,缁尘京国,乌衣门第。有酒惟浇赵州土,谁会成生此意。不信道、竟逢知己。青眼高歌俱未老,向尊前、拭尽英雄泪。君不见,月如水。共君此夜须沉醉。且由他,蛾眉谣诼,古今同忌。身世悠悠何足问,冷笑置之而已。寻思起、从头翻悔。一日心期千劫在,后身缘、恐结他生里。然诺重,君须记。 纳兰性德《金缕曲·赠梁汾》
恨君不似江楼月,南北东西,南北东西,只有相随无别离。恨君却似江楼月,暂满还亏,暂满还亏,待得团圆是几时? 吕本中《采桑子·恨君不似江楼月》
自君之出矣,不复理残机。思君如满月,夜夜减清辉。 张九龄《赋得自君之出矣》
Update 2023-03-08 派克直播间飞花令, “江” 字飞花令
江东子弟今虽在,肯与君王卷土来? 王安石《叠题乌江亭》
江雨霏霏江草齐,六朝如梦鸟空啼。 韦庄《台城》
残灯无焰影幢幢,此夕闻君谪九江。垂死病中惊坐起,暗风吹雨入寒窗。元稹《闻乐天授江州司马》
少年听雨歌楼上,红烛昏罗帐。壮年听雨客舟中,江阔云低、断雁叫西风。而今听雨僧庐下,鬓已星星也。悲欢离合总无情,一任阶前、点滴到天明。 蒋捷《虞美人·听雨》
杀尽江南百万兵,腰间宝剑血犹腥!老僧不识英雄汉,只管哓哓问姓名。 朱元璋 《不惹庵示僧》
Update 2023-08-11 历史记录
缺月挂疏桐,漏断人初静。谁见幽人独往来,缥缈孤鸿影。惊起却回头,有恨无人省。拣尽寒枝不肯栖,寂寞沙洲冷。 ——《卜算子·黄州定会院寓居作》
制作一个可用的malloc image
写一个melloc的C程序#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#define SIGTERM_MSG "SIGTERM received.\n"
void sig_term_handler(int signum, siginfo_t *info, void *ptr)
{
write(STDERR_FILENO, SIGTERM_MSG, sizeof(SIGTERM_MSG));
}
void catch_sigterm()
{
static struct sigaction _sigact;
memset(&_sigact, 0, sizeof(_sigact));
_sigact.sa_sigaction = sig_term_handler;
_sigact.sa_flags = SA_SIGINFO;
sigaction(SIGTERM, &_sigact, NULL);
}
int main(int argc, char *argv[])
{
if ( argc != 2 )
{
printf("ERROR ELEMENT COUNTS.");
return 1;
}
// printf ("%ld\n", atol(argv[1]));
int *p;
long n = atol(argv[1]) * 1024 * 1024;
// printf ("%ld\n", n);
printf("Allocate Memory Size: %ld MB.\n", atol(argv[1]));
// Allocate Memory.
p = (int *)malloc(n);
if (p != NULL)
printf("SUCCESS.");
else
printf("FAILED.");
memset(p, 0, n);
catch_sigterm();
free(p);
sleep(3000);
return 0;
}
写一个DockerfileFROM alpine
RUN apk add build-base
COPY mem.c .
RUN gcc -o mem mem.c
# or RUN gcc -static -o mem mem.c
FROM alpine
COPY --from=0 ./mem .
ENTRYPOINT [ "/mem" ]
进行一个很新的测试Build Image.
dive build -t liarlee-malloc:latest .
OR
docker build -t liarlee-malloc:latest .
Docker RUN.docker run --name malloc --rm -dt liarlee-malloc:latest 200
# docker run --name malloc --rm -dt liarlee-malloc:latest [MemorySize(MB)]
Kubernetes Deployment RUN.---
apiVersion: apps/v1
kind: Deployment
metadata:
name: liarlee-malloc
spec:
selector:
matchLabels:
app: malloc
replicas: 1
template:
metadata:
labels:
app: malloc
spec:
containers:
- name: malloc
image: liarlee-malloc:latest
args: [ "200" ]
Nginx性能调整(不一定对
Nginx 性能优化看了不少的文档和说明, 尝试调整一下Nginx,看看与默认的设置性能表现能有多大的差距,顺便记录一下步骤,不记录的话自己会忘记的。
Sysctl 参数~]$ cat /etc/sysctl.d/99-hayden.cof
net.ipv4.tcp_wmem = 8192 4194304 8388608
net.ipv4.tcp_rmem = 8192 4194304 8388608
net.core.somaxconn = 262144
net.core.default_qdisc=fq
net.ipv4.tcp_congestion_control=bbr
Nginx Config~]$ cat /etc/nginx/nginx.conf
user nginx;
worker_processes 1;
worker_cpu_affinity 10;
# error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;
events {
worker_connections 10240;
}
...# 后面的都是默认值。
Systemd Nginx Service~]$ systemctl cat nginx
# /usr/lib/systemd/system/nginx.service
[Unit]
Description=The nginx HTTP and reverse proxy server
After=network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target
[Service]
Type=forking
PIDFile=/run/nginx.pid
# Nginx will fail to start if /run/nginx.pid already exists but has the wrong
# SELinux context. This might happen when running `nginx -t` from the cmdline.
# https://bugzilla.redhat.com/show_bug.cgi?id=1268621
ExecStartPre=/usr/bin/rm -f /run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/usr/sbin/nginx -s reload
KillSignal=SIGQUIT
TimeoutStopSec=5
KillMode=process
PrivateTmp=true
LimitAS=infinity
LimitRSS=infinity
LimitCORE=infinity
LimitNOFILE=65536
LimitNPROC=65535
Nice=-20
[Install]
WantedBy=multi-user.target
CPU Isolate~]$ cat /etc/default/grub
GRUB_CMDLINE_LINUX_DEFAULT="console=tty0 console=ttyS0,115200n8 net.ifnames=0 biosdevname=0 nvme_core.io_timeout=4294967295 rd.emergency=poweroff rd.shell=0 isolcpus=1 nohz_full=1 rcu_nocbs=1"
GRUB_TIMEOUT=0
GRUB_DISABLE_RECOVERY="true"
GRUB_TERMINAL="ec2-console"
GRUB_X86_USE_32BIT="true"
调整中断~]$ cat /proc/interrupts
CPU0 CPU1
27: 0 845 PCI-MSI 81920-edge ena-mgmnt@pci:0000:00:05.0
28: 1455005 0 PCI-MSI 81921-edge eth0-Tx-Rx-0
29: 0 1870959 PCI-MSI 81922-edge eth0-Tx-Rx-1
LOC: 119785 15755 Local timer interrupts
~]$ echo 0 > /proc/irq/29/smp_affinity_list
~]$ echo 0 > /proc/irq/28/smp_affinity_list
~]$ echo 0 > /proc/irq/27/smp_affinity_list
关闭 Irqbalance~]$ sudo systemctl stop irqbalance.service
~]$ sudo systemctl disable irqbalance.service
Removed symlink /etc/systemd/system/multi-user.target.wants/irqbalance.service.
测试这些都做完之后,启动一个新的实例,安装nginx之后不做任何的动作, 使用ab命令测试默认页 index.html。 对比
调整后的实例结果如下使用命令: ab -c 3000 -n 50000 http://172.31.37.166:80/index.html
第一次Document Path: /index.html
Document Length: 732 bytes
Concurrency Level: 3000
Time taken for tests: 2.225 seconds
Complete requests: 50000
Failed requests: 0
Total transferred: 48250000 bytes
HTML transferred: 36600000 bytes
Requests per second: 22474.64 [#/sec] (mean)
Time per request: 133.484 [ms] (mean)
Time per request: 0.044 [ms] (mean, across all concurrent requests)
Transfer rate: 21179.71 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 67 139.7 47 1124
Processing: 25 63 16.1 61 274
Waiting: 0 47 13.7 46 254
Total: 57 130 143.2 106 1191
Percentage of the requests served within a certain time (ms)
50% 106
66% 125
75% 131
80% 135
90% 142
95% 149
98% 165
99% 1173
100% 1191 (longest request)
第二次Document Path: /index.html
Document Length: 732 bytes
Concurrency Level: 3000
Time taken for tests: 2.232 seconds
Complete requests: 50000
Failed requests: 0
Total transferred: 48250000 bytes
HTML transferred: 36600000 bytes
Requests per second: 22397.25 [#/sec] (mean)
Time per request: 133.945 [ms] (mean)
Time per request: 0.045 [ms] (mean, across all concurrent requests)
Transfer rate: 21106.78 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 67 141.3 47 1092
Processing: 22 63 15.8 60 106
Waiting: 0 47 13.5 46 84
Total: 60 130 143.5 108 1175
Percentage of the requests served within a certain time (ms)
50% 108
66% 122
75% 131
80% 134
90% 141
95% 149
98% 165
99% 1150
100% 1175 (longest request)
第三次Document Path: /index.html
Document Length: 732 bytes
Concurrency Level: 3000
Time taken for tests: 2.220 seconds
Complete requests: 50000
Failed requests: 0
Total transferred: 48250000 bytes
HTML transferred: 36600000 bytes
Requests per second: 22526.08 [#/sec] (mean)
Time per request: 133.179 [ms] (mean)
Time per request: 0.044 [ms] (mean, across all concurrent requests)
Transfer rate: 21228.19 [Kbytes/sec] received
Connection ...
Docker/Containerd/Harbor 配置代理
记录一下 Docker Daemon / Containerd 配置代理的步骤,尽管能用的时候不太多。
Update - 2023-07-10 Add Harbor.
Docker创建文件]$ mkdir -pv /etc/systemd/system/docker.service.d
]$ touch /etc/systemd/system/docker.service.d/proxy.conf
写入内容[Service]
Environment="HTTP_PROXY=socks5://<-->:<-->/"
Environment="HTTPS_PROXY=socks5://<-->:<-->/"
Environment="NO_PROXY=localhost,127.0.0.1"
重启DockerDaemon]$ sudo systemctl daemon-reload && sudo systemctl restart docker
Containerd创建环境变量文件]$ mkdir -pv /etc/systemd/system/containerd.service.d
]$ touch /etc/systemd/system/containerd.service.d/proxy.conf
写入内容[Service]
Environment="HTTP_PROXY=socks5://<-->:<-->/"
Environment="HTTPS_PROXY=socks5://<-->:<-->/"
Environment="NO_PROXY=localhost,127.0.0.1"
重启 Containerd]$ sudo systemctl daemon-reload && sudo systemctl restart docker
Harbor 仓库加代理另一个方法是直接给harbor仓库添加代理, 让Harbor来进行代理访问 ,帮忙pull镜像, 集群直接指向这个仓库即可。
harbor我是直接使用docker-compose 的方式部署的, 这样简单一些。
我的软件安装目录是在: /opt/harbor
找到harbor 的配置文件: /opt/harbor/harbor.yml
配置文件部分如下:
189 # Global proxy
190 # Config http proxy for components, e.g. http://my.proxy.com:3128
191 # Components doesn't need to connect to each others via http proxy.
192 # Remove component from `components` array if want disable proxy
193 # for it. If you want use proxy for replication, MUST enable proxy
194 # for core and jobservice, and set `http_proxy` and `https_proxy`.
195 # Add domain to the `no_proxy` field, when you want disable proxy
196 # for some special registry.
197 proxy:
198 http_proxy: socks5://ip:port
199 https_proxy: socks5://ip:port
200 no_proxy:
201 components:
202 - core
203 - jobservice
204 - trivy
启动服务之后进入容器进行确认:
core]$ dc exec -it core bash
harbor [ /harbor ]$ env | grep -i proxy
PERMITTED_REGISTRY_TYPES_FOR_PROXY_CACHE=docker-hub,harbor,azure-acr,aws-ecr,google-gcr,quay,docker-registry,github-ghcr
NO_PROXY=redis,chartmuseum,portal,db,.internal,.local,127.0.0.1,localhost,core,trivy-adapter,nginx,postgresql,registryctl,log,jobservice,registry,notary-server,notary-signer,exporter
HTTPS_PROXY=socks5://ip:port
HTTP_PROXY=socks5://ip:port
环境变量已经生效了, 然后直接在控制台创建项目, 创建新的仓库缓存代理。
我创建的仓库如下,除了列出的其他留空:
提供者
目标名
目标URL
Quay
registry.k8s.io
https://registry.k8s.io
GtihubGHCR
ghcr.io
https://ghcr.io
Quay
k8s.gcr.io (Archived)
https://k8s.gcr.io
Quay
gcr.io
https://gcr.io
Quay
quay.io
https://quay.io
Docker Hub
docker.io
https://hub.docker.com
DockerRegistry
public.ecr.aws
https://public.ecr.aws
所有仓库的健康检查是通过的, 然后去创建项目, 就行了。
创建完成项目之后, 就可以通过当前这个仓库的项目地址来pull 镜像了。
docker pull reg.liarlee.site/docker.io/library/nginx:latest
docker pull reg.liarlee.site/registry.k8s.io/metrics-server/metrics-server@sha256:1ab8d2722ce57979eb05ec0594cb9173e07ace16a253c747bb94c31b138a07dc
docker pull reg.liarlee.site/public.ecr.aws/amazonlinux/amazonlinux:minimal
docker pull reg.liarlee.site/quay.io/prometheus/prometheus
docker pull reg.liarlee.site/quay.io/argoproj/argocd
docker pull reg.liarlee.site/ghcr.io/dexidp/dex
完全隔离CPU的方法
完全隔离CPU, 并排空CPU所有的进程,分配指定的任务到CPU上。
当前的情况下, 我有CPU 0 - 3, 我希望隔离出CPU3 来进行指定的任务运行(方法是传递内核参数并重启)
vim /etc/default/grub
GRUB_CMDLINE_LINUX='................isolcpu=3 nohz_full=3'
grub2-mkconfig -o /boot/grub2/grub.cfg
sync && systemctl reboot
重启之后,CPU3 就已经从CFS的调度列表上面拿掉了, 可以通过如下的参数证明。
# 查看设置是否已经正确的生效, 分离的CPU会表示为序号, 一般是: 0-2 , 3 。 这两种表示方式。
cat /sys/devices/system/cpu/isolated
3
cat /sys/devices/system/cpu/nohz_full
3
关于nohz_full这个参数, 需要编译内核的时候就启用这个功能。
# 需要启用的参数如下:
CONFIG_NO_HZ_COMMON=y
CONFIG_NO_HZ_FULL=y
CONFIG_NO_HZ=y
这部分的内容还包括的Kernel的Tickless等等知识, 这个部分的内容我在Redhat的文档中有看到。但是文档比较旧了, 这个设置是基于Redhat 7 版本的说明, 可能现在有更好的方法,我不太确定。
重启之后, CPU3上面已经完全不会有用户空间的进程被调度上去了,同时, 由于已经配置了NOHZ的参数,CPU3 上面也不会有Kernel Timer Interrept触发,因此也少了一部分中断。
Redhat的文档中定义了如下的方式进行验证, 我直接抄下面的内容了。
关于如何验证Cpu隔离的文档: https://access.redhat.com/solutions/3875421配置隔离CPU的方法已经确认功能是否激活的方法:https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/monitoring_and_managing_system_status_and_performance/index
perf stat -C 1 -e irq_vectors:local_timer_entry taskset -c 3 sleep 3
先记录这么多吧, 这个功能用到的地方实在是有限,基本上不怎么需要。并且需要知道是现在已经不怎么需要使用这个工具进行调优了, Redhat有 Tuned 守护进程, 可以通过默认的profile对OS进行调优。