Cilium 踩坑总结
测试环境
KVM - hayden@HaydenArchDesktop ~> virsh version
Compiled against library: libvirt 8.6.0 Using library: libvirt 8.6.0 Using API: QEMU 8.6.0 Running hypervisor: QEMU 7.0.0
VM OS: root@fedora ~# cat /etc/os-release
NAME=”Fedora Linux” VERSION=”36 (Thirty Six)” ID=fedora VERSION_ID=36
KVM 虚拟机两台
Master: hostname: fedora
Node: hostname: knode1
kubernetes 版本: v1.24.3
kubernetes 安装方式: kubeadm
docker版本: docker://20.10.17
NOTE: (尝试使用Containerd, 但是翻车了, 控制平面的Pod启动不了, 所以放弃了,遂使用CRI-Dockerd,配置了Docker runtime, Containerd使用默认的参数无法正常的启动, 看起来即使真的升级到了1.24 迁移还是一个问题)
Kernel Version: 5.17.5-300.fc36.x86_64
Helm参数如果是在KVM启动的虚拟机,可以通过这个安装参数来开启更多功能,但是受限于我的KVM虚拟网卡驱动不能attach xdp 程序, 所以。。。。xdp 加速无法启用,但是其他的高级特性均可开启, 集群状态正常。
helm upgrade -i cilium cilium/cilium \
--namespace kube-system \
--set tunnel=disabled \
--set autoDirectNodeRoutes=true \
--set loadBalancer.mode=dsr \
--set kubeProxyReplacement=strict \
--set enableIPv4Masquerade=false \
--set loadBalancer.algorithm=maglev \
--set devices=enp1s0 \
--set k8sServiceHost=192.168.31.100 \
--set k8sServicePort=6443 \
--set hubble.relay.enabled=true \
--set hubble.ui.enabled=true
Cilium Clitouch ./install_cilium_cli.sh
CILIUM_CLI_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/cilium-cli/main/stable.txt)
CLI_ARCH=amd64
if [ "$(uname -m)" = "aarch64" ]; then CLI_ARCH=arm64; fi
curl -L --fail --remote-name-all https://github.com/cilium/cilium-cli/releases/download/${CILIUM_CLI_VERSION}/cilium-linux-${CLI_ARCH}.tar.gz{,.sha256sum}
sha256sum --check cilium-linux-${CLI_ARCH}.tar.gz.sha256sum
sudo tar xzvfC cilium-linux-${CLI_ARCH}.tar.gz /usr/local/bin
rm cilium-linux-${CLI_ARCH}.tar.gz{,.sha256sum}
Hubble Clitouch ./install_hubble_client.sh
HUBBLE_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/hubble/master/stable.txt)
HUBBLE_ARCH=amd64
if [ "$(uname -m)" = "aarch64" ]; then HUBBLE_ARCH=arm64; fi
curl -L --fail --remote-name-all https://github.com/cilium/hubble/releases/download/$HUBBLE_VERSION/hubble-linux-${HUBBLE_ARCH}.tar.gz{,.sha256sum}
sha256sum --check hubble-linux-${HUBBLE_ARCH}.tar.gz.sha256sum
sudo tar xzvfC hubble-linux-${HUBBLE_ARCH}.tar.gz /usr/local/bin
rm hubble-linux-${HUBBLE_ARCH}.tar.gz{,.sha256sum}
Cilium Cli 安装插件命令cilium upgrade # 原地升级
# 安装并直接替换kubeproxy
cilium install --version 1.14.1 --set kubeProxyReplacement=true --set=ipam.operator.clusterPoolIPv4PodCIDRList="10.42.0.0/16" --set k8sServiceHost=kube3s.liarlee.site --set k8sServicePort=6443
# 升级
hayden@arch ~> cilium upgrade
🔮 Auto-detected Kubernetes kind: K3s
ℹ️ Using Cilium version 1.14.2
🔮 Auto-detected cluster name: default
# 看状态
cilium status
# 使用hubble
cilium hubble port-forward &
cilium hubble enable --ui
hubble observe --since=1m -t l7 -
特性以及状态检查默认的安装完成之后开启特性如下:
Kubeproxy Bypass
Iptables Bypass
LoadBalancer 算法: Meglav
LoadBalancer 特性: DSR
报文Masquerade 封装: Disabled
Hubble : Enable
隧道封包: Disabled
下面是 CIlium Status的命令返回结果:
root@fedora ~# cilium status
/¯¯\
/¯¯\__/¯¯\ Cilium: OK
\__/¯¯\__/ Operator: OK
/¯¯\__/¯¯\ Hubble: OK
\__/¯¯\__/ ClusterMesh: disabled
\__/
Deployment cilium-operator Desired: 2, Ready: 2/2, Available: 2/2
Deployment hubble-relay Desired: 1, Ready: 1/1, Available: 1/1
DaemonSet cilium Desired: 2, Ready: 2/2, Available: 2/2
Deployment hubble-ui Desired: 1, Ready: 1/1, Available: 1/1
Containers: hubble-relay Running: 1
hubble-ui Running: 1
cilium Running: 2
cilium-operator Running: 2
Cluster Pods: 14/14 managed by Cilium
Image versions hubble-ui quay.io/cilium/hubble-ui:v0.9.0@sha256:0ef04e9a29212925da6bdfd0ba5b581765e41a01f1cc30563cef9b30b457fea0: 1
hubble-ui quay.io/cilium/hubble-ui-backend:v0.9.0@sha256:000df6b76719f607a9edefb9af94dfd1811a6f1b6a8a9c537cba90bf12df474b: 1
cilium quay.io/cilium/cilium:v1.12.0@sha256:079baa4fa1b9fe638f96084f4e0297c84dd4fb215d29d2321dcbe54273f63ade: 2
cilium-operator quay.io/cilium/operator-generic:v1.12.0@sha256:bb2a42eda766e5d4a87ee8a5433f089db81b72dd04acf6b59fcbb445a95f9410: 2
hubble-relay quay.io/cilium/hubble-relay:v1.12.0@sha256:ca8033ea8a3112d838f958862fa76c8d895e3c8d0f5590de849b91745af5ac4d: 1
ds/cilium 中的命令返回结果:
root@fedora:/home/cilium# cilium status
KVStore: Ok Disabled
Kubernetes: Ok 1.24 (v1.24.3) [linux/amd64]
Kubernetes APIs: ["cilium/v2::CiliumClusterwideNetworkPolicy", "cilium/v2::CiliumEndpoint", "cilium/v2::CiliumNetworkPolicy", "cilium/v2::CiliumNode", "core/v1::Namespace", "core/v1::Node", "core/v1::Pods", "core/ ...
如何配置kubelet的节点自动回收资源
配置Node节点按照磁盘阈值回收空间
https://aws.amazon.com/cn/premiumsupport/knowledge-center/eks-worker-nodes-image-cache/
修改Kubelet参数
Kubelet默认提供了GC的参数 --image-gc-high-threshold 参数用于定义触发映像垃圾收集的磁盘使用百分比。默认值为 85%。
--image-gc-low-threshold 参数用于定义映像垃圾收集尝试释放的磁盘使用百分比。默认值为 80%。
如果是自己管理的Node,最好的方式是直接配置kubelet命令行的参数,将上面的参数指定需要的阈值,然后重启kubelet即可。配置文件一般在 : /etc/kubernetes/kubelet.config"imageGCHighThresholdPercent": 70,
"imageGCLowThresholdPercent": 50,
由 CPU Steal Time 指标解释
观察vmstat命令输出的内容, 或者 top 命令 最上面输出的内容。可以看到Stealtime增加,大部分时候都是因为虚拟机的超卖, 总结来说st指示了vCPU的繁忙程度。
进程相关参数的说明https://www.kernel.org/doc/html/latest/scheduler/sched-stats.html
schedstats also adds a new /proc//schedstat file to include some of the same information on a per-process level. There are three fields in this file correlating for that process to:1 time spent on the cpu2 time spent waiting on a runqueue3 # of timeslices run on this cpu
执行命令的输出结果hayden@VM-16-6-ubuntu /p/3720475> cat schedstat
2236062 223986 22
命令中的三个数值说明:
2236062 进程在CPU的时间
223986 进程在CPU调度上面等待的时间
22 在这个CPU运行的时间片数量NOTE: 有一个博客写这个是 上下文交换的次数 , 和 sched 文件中的 nr_switches 数量相同, 不能确定是否正确。
Epoll vs select vs poll vs io_uring
select方式:使用fd_set结构体告诉内核同时监控那些文件句柄,使用逐个排查方式去检查是否有文件句柄就绪或者超时。该方式有以下缺点:文件句柄数量是有上限的,逐个检查吞吐量低,每次调用都要重复初始化fd_set。
poll方式:该方式主要解决了select方式的2个缺点,文件句柄上限问题(链表方式存储)以及重复初始化问题(不同字段标注关注事件和发生事件),但是逐个去检查文件句柄是否就绪的问题仍然没有解决。
epoll方式:该方式可以说是C10K问题的killer,他不去轮询监听所有文件句柄是否已经就绪。epoll只对发生变化的文件句柄感兴趣。其工作机制是,使用”事件”的就绪通知方式,通过epoll_ctl注册文件描述符fd,一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd, epoll_wait便可以收到通知, 并通知应用程序。而且epoll使用一个文件描述符管理多个描述符,将用户进程的文件描述符的事件存放到内核的一个事件表中, 这样数据只需要从内核缓存空间拷贝一次到用户进程地址空间。而且epoll是通过内核与用户空间共享内存方式来实现事件就绪消息传递的,其效率非常高。但是epoll是依赖系统的(Linux)。
io_uring 其实是内核5.10之后引进的一种方式,这个方式大大的减少了应用程序的系统调用次数。 性能有增长。
关于内核Config中的参数 CONFIG_NO_HZ
关于Tick, Tickless的研究。
https://www.kernel.org/doc/html/latest/timers/no_hz.html
这几个参数的最终意义都和 Jitter 相关, 设置的参数含义是 : CPU时钟中断的周期, 如果是 100HZ , 那么1s的时间内CPU会中断100次。目前最新的内核支持 NOHZ , 也就是在没有任务的时候处于节能的考虑不进行中断。当有需要运行的业务时还是会正常的触发CPU中断。NOHZ主要的功能时省电, 调整这个参数的意义就是让CPU处在合理的中断次数。过多的中断会导致相关的任务被打断。
git://git.kernel.org/pub/scm/linux/kernel/git/frederic/dynticks-testing.git
Nginx Performance Test
写在最前面, 这个问题还在研究中, 我目前还没有合适的模型用来研究这个问题, Pending….
Nginx 性能测试与压力计算使用al2023 + 默认的yum仓库软件版本, 具体信息记录如下:
按照下面的配置, 在Nginx上面发布一个目录, 其中放了一个Fedora镜像, 大小大约2.3GB, 固定Nginx处理请求的大小, 控制大部分的因素来尝试获取精确的结果。
OS Version
[root@ip-172-31-53-146 ~]# cat /etc/os-release
NAME="Amazon Linux"
VERSION="2023"
ID="amzn"
ID_LIKE="fedora"
VERSION_ID="2023"
PLATFORM_ID="platform:al2023"
PRETTY_NAME="Amazon Linux 2023"
ANSI_COLOR="0;33"
CPE_NAME="cpe:2.3:o:amazon:amazon_linux:2023"
HOME_URL="https://aws.amazon.com/linux/"
BUG_REPORT_URL="https://github.com/amazonlinux/amazon-linux-2023"
SUPPORT_END="2028-03-01"
在一个机器上面部署MySQL 然后初始化配置一个密码和用户。
[root@ip-172-31-53-200 ~]# mysql --version
mysql Ver 8.0.32 for Linux on x86_64 (MySQL Community Server - GPL)
直接使用bitnami的wordpress, 仅部署wordpress在另一个机器上面,然后配置连接到上面的那台数据库。
额外配置一下Docker-compose 的CPU affinity, 绑到cpu0上面, 让这个Container只能用cpu0, 模拟只有一个核心, 后面应该还会给他放开。
---
version: '2'
services:
wordpress:
cpuset: "0"
network_mode: host
5. 尝试给点压力, 做个基准测试。
### 测试1 单线程计算QPS
> 公式: 1000ms/RT = QPS
>
> 这个是单线程的QPS 与 RT 的关系, 试图走一把流程验证这个。
运行命令 wrk 配置 1connection 1thread 进行测试, 测试时间5分钟。
获取 RT,HTTP请求从发出到响应的时间 :
![2023-04-27_10-38.png](https://s2.loli.net/2023/04/27/pf2Z5erTzcayPFJ.png)
基于当前获取到的RT就可以计算出单线程的QPS :
```bash
RT:45ms # 这个时候获取的RT是客户端从发出数据包 到 收到完整的请求的页面返回的时间, 基于上面的抓包结果计算。
1000ms/45ms = 22 QPS
实际命令返回的结果:
~ ❯❯❯ wrk -t1 -c1 -d5m --latency http://nginx.liarlee.site:8080
Running 5m test @ http://nginx.liarlee.site:8080
1 threads and 1 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 45.00ms 3.60ms 169.02ms 88.75%
Req/Sec 22.24 4.27 30.00 76.72%
Latency Distribution
50% 44.28ms
75% 46.23ms
90% 48.51ms
99% 54.40ms
6668 requests in 5.00m, 337.69MB read
Requests/sec: 22.23 # 确实是 22左右, 基本上和计算得出的结果一致。
Transfer/sec: 1.13MB
测试2 计算服务当前的最佳线程数量默认的情况下, 当一个进程处理的时候, 线程接收到请求, OnCPU处理这个请求, 发出数据库查询,切换到Sleep状态,数据库处理完成,线程回到CPU继续运算,发送结果给客户端,这样完成一个Request。
计算服务的最佳线程数量其实是 需要多少个线程占用CPU,最终可以填满 CPU 1s 的时间, 让CPU尽可能都用来处理业务请求。
所以最大的线程数量是变化的, 与CPU time 相关 或者说 与RT相关, 对于一些特定的请求,控制了大部分变量的场景下, 可以计算一个最佳的线程数量。 计算公式:
最佳线程数: CPU TIme + Wait Time / CPU Time = 2 + 42 / 2 = 22
打开文件数限制可能的并发数量测试 conntrack 的问题, 发现连接数不太高, 试图通过wrk 提高并发的连接数量。使用命令:
wrk -t2 -c30000 -d30s http://reg.liarlee.site:80
如果我的理解没有问题, 那么我应该可以创建30000个连接 在 客户端 和 服务端 之间。但是结果:
# 客户端尝试建立 8000c
ec2-user@arch ~> wrk -t1 -c8000 -d30s http://reg.liarlee.site:80
Running 30s test @ http://reg.liarlee.site:80
1 threads and 8000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 49.94ms 42.36ms 428.68ms 92.65%
Req/Sec 21.15k 8.19k 41.30k 66.08%
608596 requests in 30.04s, 218.23MB read
Socket errors: connect 6980, read 0, write 0, timeout 0
Requests/sec: 20260.60
Transfer/sec: 7.27MB
# 服务端看到的是:
[root@reg tools]# ss -s
Total: 282
TCP: 1103 (estab 28, closed 1063, orphaned 0, timewait 2)
Transport Total IP IPv6
RAW 0 0 0
UDP 8 4 4
TCP 40 36 4
INET 48 40 8
FRAG 0 0 0
这和预期的差距挺大的, 1103 让我非常容易的想到了 1024 的文件描述符限制。 于是
ec2-user@arch ~> ulimit -n
1024
客户端 wrk 的文件描述符可用调大到 100000 。
# 客户端
ec2-user@arch ~ [127]> ulimit -n
100000
ec2-user@arch ~> wrk -t2 -c30000 -d30s http://reg.liarlee.site:80
Running 30s test @ http://reg.liarlee.site:80
2 threads and 30000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 515.29ms 411.20ms 2.00s 78.35%
Req/Sec 1.76k 2.70k 11.96k 85.71%
39021 requests in 30.28s, 13.99MB read
Socket errors: connect 0, read 298852, write 16, timeout 3115
Requests/sec: 1288.65
Transfer/sec: 473.17KB
# 服务端
[root@reg tools]# ss -s
Total: 282
TCP: 7544 (estab 28, closed 7504, orphaned 1641, timewait 2)
Transport Total IP IPv6
RAW 0 0 0
UDP 8 4 4
TCP 40 36 4
INET 48 40 8
FRAG 0 0 0
如果从上面的角度来看, 那么客户端的打开文件数是限制, 现在打开文件数调大了, 服务端还是无法建立更多的连接, 这个还得继续看看 , 我感觉是cpu的问题, 毕竟我访问的页面的是有内容的, 不是一个空请求。
# 尝试看了以下服务端的ss 命令
ec2-user@arch ~> ss -s
Total: 31669
TCP: 31268 (estab 7959, closed 1182, orphaned 1250, timewait 1)
Transport Total IP IPv6
RAW 1 0 1
UDP 4 2 2
TCP 30086 30080 6
INET 30091 30082 9
FRAG 0 0 0
确实发起了30000 但是 进入estab状态的连接只有 8000 左右。这基本上可以确定是服务端的限制了,具体是什么地方限制了待查。客户端 : c5 实例类型上面的 wrk服务端 : t3.micro - docker compose 部署的 harbor
连接异常导致的orphans一个遗憾的事情, 最近遇到了一个可能是 cilium 的一个问题, 在小规模集群的场景下, Cilium 会错误的处理FIN , FIN ACK, 导致安全组的 Connection Track 数量被打满。 之前没有注意过这个行为,在Docker的环境中测试下, 尝试使用 iptables 屏蔽容器发送出来的FIN, 记录步骤和命令:
dnf install -y docker
systemctl enable --now docker
docker pull reg.liarlee.site/docker.io/library/nginx/nginx:latest
docker run -dt --rm --name nginx -p 81:80 reg.liarlee.site/docker.io/library/nginx:l ...
OOM行为
关于OOM行为的思考 以及Kswapd的动作和行为。http://evertrain.blogspot.com/2018/04/oom.html更详细的打分算法见源码 https://github.com/torvalds/linux/blob/master/mm/oom_kill.c
发生之后OOM killer会将kill的信息记录到系统日志/var/log/messages,检索相关信息就能匹配到是否触发。
grep 'Out of memory' /var/log/messages
也可以通过dmesg
dmesg -Tx | egrep -i 'killed process'
查看分数最高的进程#!/bin/bash
for proc in $(find /proc -maxdepth 1 -regex '/proc/[0-9]+'); do
printf "%2d %5d %s\n" \
"$(cat $proc/oom_score)" \
"$(basename $proc)" \
"$(cat $proc/cmdline | tr '\0' ' ' | head -c 50)"
done 2>/dev/null | sort -nr | head -n 10
保护措施设置OverCommit只有在OverCommit的时候才会触发OOM, 默认是许可一定程度的OverCommit的。
https://docs.kernel.org/vm/overcommit-accounting.html
vm.overcommit_memory 作用是控制OverCommit是否被许可。
0
Heuristic overcommit handling. Obvious overcommits of address space are refused. Used for a typical system. It ensures a seriously wild allocation fails while allowing overcommit to reduce swap usage. root is allowed to allocate slightly more memory in this mode. This is the default.
1
Always overcommit. Appropriate for some scientific applications. Classic example is code using sparse arrays and just relying on the virtual memory consisting almost entirely of zero pages.
2
Don’t overcommit. The total address space commit for the system is not permitted to exceed swap + a configurable amount (default is 50%) of physical RAM. Depending on the amount you use, in most situations this means a process will not be killed while accessing pages but will receive errors on memory allocation as appropriate.
Useful for applications that want to guarantee their memory allocations will be available in the future without having to initialize every page.
[root@ip-172-31-9-192 log]# cat /proc/meminfo | grep Comm
CommitLimit: 4794236 kB
Committed_AS: 2344744 kB
---
CommitLimit: 可提交内存的上限, 超过这个上限系统认为目前内存已经是OverCommit。
Committed_AS: 已经提交内存的上限,当前所有进程已经提交的内存使用,这个不是已经分配出去的, 是进程申请的。
设置Killer的行为root@ip-172-31-11-235:/home/ec2-user|⇒ cat /proc/sys/vm/oom_kill_allocating_task
# 值为0:会 kill 掉得分最高的进程
# 值为非0:会kill 掉当前申请内存而触发OOM的进程
设置进程
对于需要保护的进程可以使用OOM_ADJ=-17 将这个进程从OOM Killer的列表中移除(已经在内核的2.6之后废弃,处于兼容性保留了这个文件接口
调整OOM_SCORE_ADJ, 范围是 -1000 < oom_score_adj < 1000
直接调整到-1000,会出现在计算分数列表的最后 echo -1000 > /proc/31595/oom_score_adj
手动触发一次OOM规则, Kill符合要求的进程 echo f > /proc/sysrq-trigger
调整服务的OOM Score对于服务本身的保护方式, 可以采用使用Systemd Unit file里面进行 OOMADJSCORE=*** 的方式来指定,例如保护MySQL的进程不会在OOM Killer的列表中。
cat /usr/lib/systemd/system/mariadb.service
[Service]
Type=simple
User=mysql
Group=mysql
ExecStartPre=/usr/libexec/mariadb-prepare-db-dir %n
ExecStart=/usr/bin/mysqld_safe --basedir=/usr
ExecStartPost=/usr/libexec/mariadb-wait-ready $MAINPID
# Setting Here. and setting in the /proc/$PID/oom_score_adj.
OOMScoreAdjust=-1000
sudo systemctl daemon-reload && sudo systemctl restart mariadb
避免OOM的方式
关闭OverCommit: 关闭OverCommit会导致进程如果无法拿到内存就fast fail , 不会出现OOMKiller干掉无辜进程的情况。
开启OverCommit, 开启一定程度的Swap: 开启Swap会导致内存在接近99%的时候,会出现系统响应变慢的问题,但是会由于申请的内存没有超过TotalMEM + TotalSWAP,因此不会触发OOM ,但是会导致明显的系统响应问题。 如果超过了 TotalMEM + TotalSWAP 会立刻触发一次OOMkiller结束进程。
开启OverCommit, 调整进程的优先级: 对于特定的进程进行保护。OOM会按照设置的积分计算需要Kill的进程。
开启OverCommit, 调整OOMkiller的行为方式: 计算积分后Kill 或者 直接Kill当前新申请内存的进程。这个方式感觉和默认的关闭OverCommit的行为类型,都是拒绝新的进程以保证旧的进程可以存活。
更可靠的方式Faceboook的oomdhttps://github.com/facebookincubator/oomdoomd使用的是 PSI接口来评估内存的压力,可以通过自定义规则的方式来对来进行内存压力的分析,而不是简单的内存用量。
Fedora的Early OOMhttps://github.com/rfjakob/earlyoomEarlyOOM的作用是提前OOM,这样可以保障用户空间的图形桌面不会到交换空间去, 主要解决的问题是内存压力过大的交换动作会将桌面环境换出导致响应变慢。
输出结果记录Cgroup - Pod
首先是哪个程序超过了cgroup的limit, 触发了cgroup 的oom, 这里是 xray 这个进程自己。
kern :warn : [Thu Aug 17 17:29:27 2023] xray invoked oom-killer: gfp_mask=0x100cca(GFP_HIGHUSER_MOVABLE), order=0, oom_score_adj=-997
这个部分是触发kprint的信息, 解释如下:在哪个CPU(1)上, 运行的进程PID(1586622), commandline的名称(xray), 哪个版本的内核, 是否被标记为 taint.
kern :warn : [Thu Aug 17 17:29:27 2023] CPU: 1 PID: 1586622 Comm: xray Kdump: loaded Not tainted 5.14.0-333.el9.x86_64 #1
硬件信息, BIOS信息
kern :warn : [Thu Aug 17 17:29:27 2023] Hardware name: Red Hat KVM, BIOS 1.15.0-2.module_el8.6.0+2880+7d9e3703 04/01/2014
打印发生oom时刻的堆栈
kern :warn : [Thu Aug 17 17:29:27 2023] Call Trace:
kern :warn : [Thu Aug 17 17:29:27 2023] <TASK>
kern :warn : [Thu Aug 17 17:29:27 2023] dump_stack_lvl+0x34/0x48
kern :warn : [Thu Aug 17 17:29:27 2023] dump_header+0x4a/0x201
kern :warn : [Thu Aug 17 17:29:27 2023] oom_kill_process.cold+0xb/0x10
kern :warn : [Thu Aug 17 17:29:27 2023] out_of_memory+0xed/0x2e0
kern :warn : [Thu Aug 17 17:29:27 2023] mem_cgroup_out_of_memory+0x13a/0x150
kern :warn : [Thu Aug 17 17:29:27 2023] try_charge_memcg+0x79d/0x860
kern :warn : [Thu Aug 17 17:29:27 2023] ? __mem_cgroup_charge+0x55/0x80
kern :warn : [Thu Aug 17 17:29:27 2023] charge_memcg+0x7a/0xf0
kern :warn : [Thu Aug 17 17:29:27 2023] __mem_cgroup_ch ...
Perf 命令的Performance分析
https://blog.gmem.cc/perf 一个非常详细的博客, 太强啦。
使用perf进行性能的简单输出root@ip-172-31-11-235:~|⇒ perf stat htop -d 1
Performance counter stats for 'htop -d 1':
181.764747 task-clock (msec) # 0.055 CPUs utilized
52 context-switches # 0.286 K/sec
0 cpu-migrations # 0.000 K/sec
320 page-faults # 0.002 M/sec
<not supported> cycles
<not supported> instructions
<not supported> branches
<not supported> branch-misses
3.283236218 seconds time elapsed
使用perf记录性能指标到文件[root@ip-172-31-41-141 tmp]# perf record -F 99 -a -g -p 44551
[root@ip-172-31-41-141 tmp]# sudo perf record -F 99 -a -g -- sleep 60
Warning:
PID/TID switch overriding SYSTEM
^C[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.021 MB perf.data (2 samples) ]
root@ip-172-31-11-235:~|⇒ sudo perf script > out.perf
生成火焰图通常的做法是将 out.perf 拷贝到本地机器,在本地生成火焰图:
$ git clone --depth 1 https://github.com/brendangregg/FlameGraph.git
# 折叠调用栈
$ perf script > out.perf
$ FlameGraph/stackcollapse-perf.pl out.perf > out.folded
# 生成火焰图
$ FlameGraph/flamegraph.pl out.folded > out.svg
生成火焰图可以指定参数,–width 可以指定图片宽度,–height 指定每一个调用栈的高度,生成的火焰图,宽度越大就表示CPU耗时越多。FlameGraph/flamegraph.pl < out.profile > out.svg
[root@ip-172-31-18-198 timechart]# perf timechart record -g – curl http://localhost:19999[root@ip-172-31-18-198 timechart]# perf timechartWritten 0.0 seconds of trace to output.svg.
制造一个D进程Most proper way is to use freezer cgroup. It puts process to uninterruptible sleep in case of FROZEN cgroup state.
mkdir /sys/fs/cgroup/freezermount -t cgroup -ofreezer freezer /sys/fs/cgroup/freezermkdir /sys/fs/cgroup/freezer/frozenecho FROZEN > /sys/fs/cgroup/freezer/frozen/freezer.stateecho pidof you_process > /sys/fs/cgroup/freezer/frozen/tasksecho pgrep cp > /sys/fs/cgroup/freezer/frozen/tasksecho THAWED > /sys/fs/cgroup/freezer/frozen/freezer.state
To put again to interruptible sleep, just change cgroup state to THAWED.
动态追踪
添加一个动态追踪的Tracepoint Eventperf probe --add="probe:io_schedule_timeout"
perf probe --add="probe:io_schedule_timeout%return"
# 使用
perf record -e probe:tcp_sendmsg -a -g -- sleep 5
# 分析
perf report --stdio
移除一个动态追踪的Tracepoint Eventperf probe --del="probe:io_schedule_timeout"
perf probe -d "probe:io_schedule_timeout"
列出所有存在的probe perf probe -l
查看追踪的结果perf script
perf probe -V tcp_sendmsg # 列出可用的变量列表
perf probe --add 'tcp_sendmsg size' # 追踪这个变量
# Add a tracepoint for tcp_sendmsg() return, and capture the return value:
perf probe 'tcp_sendmsg%return $retval'
关于Off-cpu进程的分析
按步骤生成]$ /usr/share/bcc/tools/offcputime -df -p `pgrep -nx mysqld` 30 > out.stacks
[...copy out.stacks to your local system if desired...]
]$ git clone https://github.com/brendangregg/FlameGraph
]$ cd FlameGraph
]$ ./flamegraph.pl --color=io --title="Off-CPU Time Flame Graph" --countname=us < out.stacks > out.svg
一条命令出图 ]$ grep do_command < out.stacks | ./flamegraph.pl --color=io --title="Off-CPU Time Flame Graph" --countname=us > out.svg
Perf命令的常见参数
内核设置要启用内核动态追踪,需要使用内核编译参数CONFIG_KPROBES=y、CONFIG_KPROBE_EVENTS=y。要追踪基于帧指针的内核栈,需要内核编译参数CONFIG_FRAME_POINTER=y。要启用用户动态追踪,需要使用内核编译参数CONFIG_UPROBES=y、CONFIG_UPROBE_EVENTS=y
子命令列表
perf支持一系列的子命令:子命令 说明annotate 读取perf.data并显示被注解的代码bench 基准测试的框架config 在配置文件中读写配置项diff 读取perf.data并显示剖析差异evlist 列出perf.data中的事件名称inject 用于增强事件流的过滤器kmem 跟踪/度量内核内存属性kvm 跟踪/度量KVM客户机系统list 显示符号化的事件列表lock 分析锁事件mem 分析内存访问record 执行剖析report 显示剖析结果sched 分析调度器stat 获取性能计数top 显示成本最高的操作并动态刷新trace 类似于strace的工具probe 定义新的动态追踪点
perrf record 命令参数 --exclude-perf 不记录perf自己发起的事件
-p 收集指定进程的事件,逗号分割的PID列表
-a 使用Per-CPU模式,如果不指定-C,则相当于全局模式。如果指定-C,则可以选定若干CPU
-g 记录调用栈
-F 以指定的频率剖析
-T 记录样本时间戳
-s 记录每个线程的事件计数器,配合 perf report -T使用
微基准测试From youtube video:
root@HaydenArchDesktop /tmp# perf bench sched pipe
# Running 'sched/pipe' benchmark:
# Executed 1000000 pipe operations between two processes
Total time: 2.407 [sec]
2.407455 usecs/op
415376 ops/sec
root@HaydenArchDesktop /tmp# taskset -c 0 perf bench sched pipe
# Running 'sched/pipe' benchmark:
# Executed 1000000 pipe operations between two processes
Total time: 2.381 [sec]
2.381081 usecs/op
419977 ops/sec
# 这里的时间提升不明显的原因是, 我的Archlinux是ZenKernel, 感觉可能在调度上已经做了不少的事情 ,如果随便启动一个redhat , 这个指标的差距会比较大。
sysctl 参数笔记
一些关于sysctl参数设置的收集和解释。
/etc/sysctl.d/00-defaults.confkernel.printk输出内核日志信息的级别。
# 映射到的proc文件系统位置 - /proc/sys/kernel/printk
# Maximize console logging level for kernel printk messages
kernel.printk = 8 4 1 7
# (1) 控制台日志级别:优先级高于该值的消息将被打印至控制台。
# (2) 缺省的消息日志级别:将用该值来打印没有优先级的消息。
# (3) 最低的控制台日志级别:控制台日志级别可能被设置的最小值。
# (4) 缺省的控制台:控制台日志级别的缺省值。
内核中共提供了八种不同的日志级别,在 linux/kernel.h 中有相应的宏对应。
#define KERN_EMERG "<0>" /* systemis unusable */
#define KERN_ALERT "<1>" /* actionmust be taken immediately */
#define KERN_CRIT "<2>" /*critical conditions */
#define KERN_ERR "<3>" /* errorconditions */
#define KERN_WARNING "<4>" /* warning conditions */
#define KERN_NOTICE "<5>" /* normalbut significant */
#define KERN_INFO "<6>" /*informational */
#define KERN_DEBUG "<7>" /*debug-level messages */
kernel.panic设置内核的Panic之后自动重启
# Wait 30 seconds and then reboot
kernel.panic = 30
neigh.default.gc设置arp缓存相关的参数https://zhuanlan.zhihu.com/p/94413312
# Allow neighbor cache entries to expire even when the cache is not full
net.ipv4.neigh.default.gc_thresh1 = 0
net.ipv6.neigh.default.gc_thresh1 = 0
# Avoid neighbor table contention in large subnets
net.ipv4.neigh.default.gc_thresh2 = 15360
net.ipv6.neigh.default.gc_thresh2 = 15360
net.ipv4.neigh.default.gc_thresh3 = 16384
net.ipv6.neigh.default.gc_thresh3 = 16384
# gc_thresh1
存在于ARP高速缓存中的最少层数,如果少于这个数,
垃圾收集器将不会运行。
缺省值是128。
# gc_thresh2
保存在 ARP 高速缓存中的最多的记录软限制。
垃圾收集器在开始收集前,允许记录数超过这个数字 5 秒。
缺省值是 512。
# gc_thresh3
保存在 ARP 高速缓存中的最多记录的硬限制,
一旦高速缓存中的数目高于此,
垃圾收集器将马上运行。
缺省值是1024。
/etc/sysctl.d/99-amazon.confsched_autogroup_enabled通过CFS分组提高了桌面环境的性能表现。这个小小的补丁仅为 Linux Kernel 增加了 233 行代码,却将高负荷下桌面响应最大延迟降低到原先的十分之一,平均延迟降低到六十分之一!该补丁的作用是为每个 TTY 动态地创建任务分组。(https://linuxtoy.org/archives/small-patch-but-huge-improvement.html)
# https://cateee.net/lkddb/web-lkddb/SCHED_AUTOGROUP.html
# https://www.postgresql.org/message-id/50E4AAB1.9040902@optionshouse.com
# This setting enables better interactivity for desktop workloads and not
# suitable for many server workloads.
# 启用后,内核会创建任务组来优化桌面程序的调度。它将把占用大量资源的应用程序放在它们自己的任务组,根据PostgreSQL的测试, 关闭这个选项会将数据库的性能提高30%(上面的Link)。 在后台的服务进程中是提高性能的选项。
# 0:禁止
# 1:开启
kernel.sched_autogroup_enabled=0
/usr/lib/sysctl.d/00-system.confBridge-nf-call-iptables网桥设备关闭netfilter模块,开关需要按需求来指定。关闭这个模块会在网桥2层可以转发的时候直接转发, 不会走三层进行数据传输,也就是说不会过Iptables。Kubernetes需要开启这个参数的原因是: https://imroc.cc/post/202105/why-enable-bridge-nf-call-iptables/, 修复了Coredns不定期解析失败的问题。
# Disable netfilter on bridges.
net.bridge.bridge-nf-call-ip6tables = 0
net.bridge.bridge-nf-call-iptables = 0
net.bridge.bridge-nf-call-arptables = 0
/usr/lib/sysctl.d/10-default-yama-scope.confYamaYama is a Linux Security Module that collects system-wide DAC security protections that are not handled by the core kernel itself. This is selectable at build-time with CONFIG_SECURITY_YAMA, and can be controlled at run-time through sysctls in /proc/sys/kernel/yama
https://www.kernel.org/doc/html/latest/admin-guide/LSM/Yama.html
[root@ip-172-31-11-235 sysctl.d]$ cat 10-default-yama-scope.conf
# When yama is enabled in the kernel it might be used to filter any user
# space access which requires PTRACE_MODE_ATTACH like ptrace attach, access
# to /proc/PID/{mem,personality,stack,syscall}, and the syscalls
# process_vm_readv and process_vm_writev which are used for interprocess
# services, communication and introspection (like synchronisation, signaling,
# debugging, tracing and profiling) of processes.
#
# Usage of ptrace attach is restricted by normal user permissions. Normal
# unprivileged processes cannot interact through ptrace with processes
# that they cannot send signals to or processes that are running set-uid
# or set-gid.
#
# yama ptrace scope can be used to reduce these permissions even more.
# This should normally not be done because it will break various programs
# relying on the default ptrace security restrictions. But can be used
# if you don't have any other way to separate processes in their own
# domains. A different way to restrict ptrace is to set the selinux
# deny_ptrace boolean. Both mechanisms will break some programs relying
# on the ptrace system call and might force users to elevate their
# priviliges to root to do their work.
#
# For more information see Documentation/security/Yama.txt in the kernel
# sources. Which also describes the defaults when CONFIG_SECURITY_YAMA
# is enabled in a kernel build (currently 1 for ptrace_scope).
#
# This runtime kernel parameter can be set to the following options:
# (Note that setting this to anything except zero will break programs!)
#
# 0 - Default attach security permissions.
# 1 - Restricted attach. Only child processes plus normal permissions ...
btrfs 笔记
学习btrfs文件系统笔记.
btrfs 管理模式和标准的文件系统不同。 btrfs 的顶级卷可以理解为 存储池, 跨越多个设备添加所有的空间到顶级卷。在顶级卷中可以直接创建目录结构进行使用, 但是并不推荐。推荐的方式是在顶级卷下面创建子卷, 然后挂载子卷使用, 这样可以最大程度的发挥 btrfs 文件系统的高级特性。子卷可以和标准目录使用 ls 命令输出没有任何差异, 为了在更好的区分 子卷 还是 普通目录, 在创建子卷的时候使用 @VOLUME_NAME 的格式进行创建。 ^3e41e6
例如:
/mnt/btrfs/ <-- 这一层还是 xfs 文件系统的范围,下面的三个目录都是手动创建的挂载点
|-- docker_data <-- 在这里挂载 @docker_data
|-- harbor_data <-- 在这里挂载 @harbor_data
`-- root <-- 在这里挂载 btrfs top volume, subvolume=/
|-- @docker_data <-- 在这里创建 btrfs subvolume @docker_data. subvolume=@docker_data
`-- @harbor_data <-- 在这里创建 btrfs subvolume @harbor_data, subvolume=@harbor_data
在 top volume 里面创建 subvolume 挂载到其他位置使用。实际使用中不管理子卷的时候 top volume 可以不挂载。
配置完成之后, 看到的应该是这样的。
/mnt/btrfs/ <-- 这一层还是 xfs 文件系统的范围
|-- docker_data <-- 在这里挂载 btrfs subvolume @docker_data
`-- harbor_data <-- 在这里挂载 btrfs subvolume @harbor_data
查看当前os支持的特性➤ mkfs.btrfs -O list-all
Filesystem features available:
mixed-bg - mixed data and metadata block groups (compat=2.6.37, safe=2.6.37)
quota - hierarchical quota group support (qgroups) (compat=3.4)
extref - increased hardlink limit per file to 65536 (compat=3.7, safe=3.12, default=3.12)
raid56 - raid56 extended format (compat=3.9)
skinny-metadata - reduced-size metadata extent refs (compat=3.10, safe=3.18, default=3.18)
no-holes - no explicit hole extents for files (compat=3.14, safe=4.0, default=5.15)
fst - free-space-tree alias
free-space-tree - free space tree, improved space tracking (space_cache=v2) (compat=4.5, safe=4.9, default=5.15)
raid1c34 - RAID1 with 3 or 4 copies (compat=5.5)
zoned - support zoned (SMR/ZBC/ZNS) devices (compat=5.12)
bgt - block-group-tree alias
block-group-tree - block group tree, more efficient block group tracking to reduce mount time (compat=6.1)
rst - raid-stripe-tree alias
raid-stripe-tree - raid stripe tree, enhanced file extent tracking (compat=6.7)
squota - squota support (simple accounting qgroups) (compat=6.7)
创建 Btrfs 卷单一设备文件系统:
mkfs.btrfs -n 64k -m single -d single -L liarlee_test /dev/nvme1n1
多设备文件系统:
mkfs.btrfs -d single -m raid1 /dev/nvme1n1 /dev/nvme2n1 /dev/nvme3n1
更改 Btrfs 的 Raid 存储方式可以将文件系统的冗余方式进行转换, 例如 raid0, raid1, single.最好在初始的时候就设定好, 后面的转换会导致一段时间的IO不可用。 btrfs balance start -dconvert=raid1 -mconvert=raid1 /mnt
^9c4bbd
创建轻量副本文件默认情况下 cp 命令的行为是不启用 CoW 特性的, 需要这个参数.
cp --reflink source dest
创建 Subvolume 和 删除 Subvolume创建 Subvolume
btrfs su create @test
Create subvolume './@test'
删除 Subvolume
btrfs su del @test/
Delete subvolume (no-commit): '/mnt/btrfs/root/@test'
挂载 Btrfs 顶级卷mkdir /mnt/btrfs/root/
mount -t btrfs /dev/nvme1n1 /mnt/btrfs/root/
挂载 Btrfs 子卷命令行挂载
mkdir /mnt/btrfs/test/
mount -t btrfs -o subvol=@test/ /dev/nvme1n1 /mnt/btrfs/test/
写入 fstab
UUID=519abb44-a6a3-4ed1-b99d-506e9443e73f /mnt/btrfs/root btrfs defaults,compress=zstd,autodefrag,ssd,space_cache=v2,vol=@
UUID=519abb44-a6a3-4ed1-b99d-506e9443e73f /mnt/btrfs/docker_data btrfs defaults,compress=zstd,autodefrag,ssd,space_cache=v2,subvol=@docker_data
UUID=519abb44-a6a3-4ed1-b99d-506e9443e73f /mnt/btrfs/harbor_data btrfs defaults,compress=zstd,autodefrag,ssd,space_cache=v2,subvol=@harbor_data
Btrfs 文件系统碎片整理btrfs filesystem defragment -r /mnt/btrfs/root
Btrfs 文件系统在线检查btrfs scrub start /mnt/btrfs/root
Btrfs 对特定的文件进行压缩一般来说, 压缩选项是挂载的时候指定的 compress=zstd 执行自动压缩, compress-force=zstd 执行强制压缩. 这会对挂载之后写入的新文件生效, 旧文件是需要手动处理的.
btrfs property set <PATH> compression <VALUE>
Btrfs 测试Read Throughput
[global]
directory=/mnt
ioengine=libaio
direct=1
rw=randread
bs=16M
size=64M
time_based
runtime=20
group_reporting
norandommap
numjobs=1
thread
[job1]
iodepth=2
Read 测试结果
[root@ip-172-31-10-64 fio]# fio ./job1
job1: (g=0): rw=randread, bs=16M-16M/16M-16M/16M-16M, ioengine=libaio, iodepth=2
fio-2.14
Starting 1 thread
job1: Laying out IO file(s) (1 file(s) / 64MB)
Jobs: 1 (f=1): [r(1)] [100.0% done] [256.0MB/0KB/0KB /s] [16/0/0 iops] [eta 00m:00s]
job1: (groupid=0, jobs=1): err= 0: pid=26359: Thu Nov 18 07:59:17 2021
read : io=5232.0MB, bw=266745KB/s, iops=16, runt= 20085msec
slat (msec): min=1, max=70, avg=21.39, stdev=23.43
clat (msec): min=2, max=130, avg=101.29, stdev=31.01
lat (msec): min=8, max=139, avg=122.68, stdev=24.96
clat percentiles (msec):
| 1.00th=[ 7], 5.00th=[ 60], 10.00th=[ 63], 20.00th=[ 66],
| 30.00th=[ 93], 40.00th=[ 110], 50.00th=[ 118], 60.00th=[ 120],
| 70.00th=[ 122], 80.00th=[ 126], 90.00th=[ 127], 95.00th=[ 128],
| 99.00th=[ 130], 99.50th=[ 131], 99.90th=[ 131], 99.95th=[ 131],
| 99.99th=[ 131]
lat (msec) : 4=0.31%, 10=3.36%, 20=0.92%, 50=0.31%, 100=28.75%
lat (msec) : 250=66.36%
cpu : usr=0.04%, sys=3.80%, ctx=994, majf=0, minf=8193
IO depths : 1 ...