1、節點 Crash 與 Vmcore 分析
kdump 介紹?
目前大多 Linux 發新版都會默認開啟 kdump 服務,以方便在內核崩潰的時候, 可以通過 kdump 服務提供的 kexec 機制快速的啟用保留在內存中的第二個內核來收集并轉儲內核崩潰的日志信息(vmcore
?等文件), 這種機制需要服務器硬件特性的支持, 不過現今常用的服務器系列均已支持.
如果沒有特別配置 kdump,當發生 crash 時,通常默認會將 vmcore 保存到?/var/crash
?路徑下,也可以查看?/etc/kdump.conf
?配置來確認:
$ grep ^path /etc/kdump.conf
path /var/crash
參考:kdump詳解-CSDN博客?
快速查看原因?
在需要快速了解崩潰原因的時候, 可以簡單查看崩潰主機(如果重啟成功)的?vmcore-dmesg.txt
?文件, 該文件列出了內核崩潰時的堆棧信息, 有助于我們大致了解崩潰的原因, 方便處理措施的決斷. 如下所示為生成的日志文件通常的路徑:
/var/crash/127.0.0.1-2019-11-11-08:40:08/vmcore-dmesg.txt
2、節點高負載?
1)如何判斷節點高負載??
可以通過?top
?或?uptime
?命令行來確定 load 大小。load 負載判定規則:
- load負載值 <= 0.7 * CPU 核數:低負載
- 0.7 * CPU 核數 < load負載值 <=?CPU 核數:負載存在壓力
- load負載值 >?CPU 核數: 高負載
2)排查思路?
觀察監控:通常不是因為內核 bug 導致的高負載,在系統卡死之前從操作系統監控一般能看出一些問題,可以觀察下系統各項監控指標與load監控指標的變化趨勢,觀察是否存在與load監控指標相同變化趨勢的指標。
排查現場:如果沒有相關監控或監控維度較少不足以查出問題,嘗試登錄節點分析現場。
注:有時負載過高通常使用 ssh 登錄不上,如果可以用 vnc,可以嘗試下使用 vnc 登錄。
3)排查現場思路?
load avg 可以認為是 R狀態線程數和D狀態線程數的總和 (R 代表需要 cpu,是 cpu 負載。 D 通常代表需要 IO,是 IO 負載),因此一般導致load負載高基本是CPU或者IO。
簡單判斷辦法:
ps -eL -o lwp,pid,ppid,state,comm | grep -E " R | D "
然后統計一下各種狀態多少個進程,看看是 D 多還是 R多。
如果是長時間 D多,可以進一步查看進程堆棧看看 D 多的原因是什么,如果是存在大量寫請求,可以考慮業務請求數據量調整批次大小,多批次小數據量寫入,同時優化磁盤落盤的頻率。
cat /proc/<PID>/stack
如果是大量進程/線程在 R 狀態,那就是同時需要 CPU 的進程/線程數過多,CPU 忙不過來了,可以利用 perf 分析程序在忙什么,但此時可以考慮業務擴容或者機器調大CPU核數。
perf -p <PID>
4)線程數量過多?
如果 load 高但 CPU 利用率不高,通常是同時 running 的進程/線程數過多,排隊等 CPU 切換的進程/線程較多。
通常在 load 高時執行任何命令都會非常卡,因為執行這些命令也都意味著要創建和執行新的進程,所以下面排查過程中執行命令時需要耐心等待。
看系統中可創建的進程數實際值:
cat /proc/sys/kernel/pid_max
修改方式: sysctl -w kernel.pid_max=65535
通過以下命令統計當前 PID 數量:
ps -eLf | wc -l
如果數量過多,可以大致掃下有哪些進程,如果有大量重復啟動命令的進程,就可能是這個進程對應程序的 bug 導致。
還可以通過以下命令統計線程數排名:
printf "NUM\tPID\tCOMMAND\n" && ps -eLf | awk '{$1=null;$3=null;$4=null;$5=null;$6=null;$7=null;$8=null;$9=null;print}' | sort |uniq -c |sort -rn | head -10
找出線程數量較多的進程,可能就是某個容器的線程泄漏,導致 PID 耗盡。
隨便取其中一個 PID,用 nsenter 進入進程 netns:
nsenter -n --target <PID>
然后執行?ip a
?看下 IP 地址,如果不是節點 IP,通常就是 Pod IP,可以通過?kubectl get pod -o wide -A | grep <IP>
?來反查進程來自哪個 Pod。
5)陷入內核態過久?
有些時候某些 CPU 可能會執行耗時較長的內核態任務,比如大量創建/銷毀進程,回收內存,需要較長時間 reclaim memory,必須要執行完才能切回用戶態,雖然內核一般會有 migration 內核線程將這種負載較高的核上的任務遷移到其它核上,但也只能適當緩解,如果這種任務較多,整體的 CPU system 占用就會較高,影響到用戶態進程任務的執行,對于業務來說,就是 CPU 不夠用,處理就變慢,發生超時。
CPU 內核態占用的 Prometheus 查詢語句:
sum(irate(node_cpu_seconds_total{instance="10.10.1.14",mode="system"}[2m]
6)IO 高負載
系統如果出現 IO WAIT 高,說明 IO 設備的速度跟不上 CPU 的處理速度,CPU 需要在那里干等,這里的等待實際也占用了 CPU 時間,導致系統負載升高,可能就會影響業務進程的處理速度,導致業務超時。
如何判斷IO處于高負載?
使用?top
?命令看下當前負載:
top - 19:42:06 up 23:59, 2 users, load average: 34.64, 35.80, 35.76
Tasks: 679 total, 1 running, 678 sleeping, 0 stopped, 0 zombie
Cpu(s): 15.6%us, 1.7%sy, 0.0%ni, 74.7%id, 7.9%wa, 0.0%hi, 0.1%si, 0.0%st
Mem: 32865032k total, 30989168k used, 1875864k free, 370748k buffers
Swap: 8388604k total, 5440k used, 8383164k free, 7982424k cachedPID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND9783 mysql 20 0 17.3g 16g 8104 S 186.9 52.3 3752:33 mysqld5700 nginx 20 0 1330m 66m 9496 S 8.9 0.2 0:20.82 php-fpm6424 nginx 20 0 1330m 65m 8372 S 8.3 0.2 0:04.97 php-fpm
%wa
?(wait) 表示 IO WAIT 的 cpu 占用,默認看到的是所有核的平均值,要看每個核的?%wa
?值需要按下 "1":
top - 19:42:08 up 23:59, 2 users, load average: 34.64, 35.80, 35.76
Tasks: 679 total, 1 running, 678 sleeping, 0 stopped, 0 zombie
Cpu0 : 29.5%us, 3.7%sy, 0.0%ni, 48.7%id, 17.9%wa, 0.0%hi, 0.1%si, 0.0%st
Cpu1 : 29.3%us, 3.7%sy, 0.0%ni, 48.9%id, 17.9%wa, 0.0%hi, 0.1%si, 0.0%st
Cpu2 : 26.1%us, 3.1%sy, 0.0%ni, 64.4%id, 6.0%wa, 0.0%hi, 0.3%si, 0.0%st
Cpu3 : 25.9%us, 3.1%sy, 0.0%ni, 65.5%id, 5.4%wa, 0.0%hi, 0.1%si, 0.0%st
Cpu4 : 24.9%us, 3.0%sy, 0.0%ni, 66.8%id, 5.0%wa, 0.0%hi, 0.3%si, 0.0%st
Cpu5 : 24.9%us, 2.9%sy, 0.0%ni, 67.0%id, 4.8%wa, 0.0%hi, 0.3%si, 0.0%st
Cpu6 : 24.2%us, 2.7%sy, 0.0%ni, 68.3%id, 4.5%wa, 0.0%hi, 0.3%si, 0.0%st
Cpu7 : 24.3%us, 2.6%sy, 0.0%ni, 68.5%id, 4.2%wa, 0.0%hi, 0.3%si, 0.0%st
Cpu8 : 23.8%us, 2.6%sy, 0.0%ni, 69.2%id, 4.1%wa, 0.0%hi, 0.3%si, 0.0%st
Cpu9 : 23.9%us, 2.5%sy, 0.0%ni, 69.3%id, 4.0%wa, 0.0%hi, 0.3%si, 0.0%st
Cpu10 : 23.3%us, 2.4%sy, 0.0%ni, 68.7%id, 5.6%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu11 : 23.3%us, 2.4%sy, 0.0%ni, 69.2%id, 5.1%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu12 : 21.8%us, 2.4%sy, 0.0%ni, 60.2%id, 15.5%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu13 : 21.9%us, 2.4%sy, 0.0%ni, 60.6%id, 15.2%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu14 : 21.4%us, 2.3%sy, 0.0%ni, 72.6%id, 3.7%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu15 : 21.5%us, 2.2%sy, 0.0%ni, 73.2%id, 3.1%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu16 : 21.2%us, 2.2%sy, 0.0%ni, 73.6%id, 3.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu17 : 21.2%us, 2.1%sy, 0.0%ni, 73.8%id, 2.8%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu18 : 20.9%us, 2.1%sy, 0.0%ni, 74.1%id, 2.9%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu19 : 21.0%us, 2.1%sy, 0.0%ni, 74.4%id, 2.5%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu20 : 20.7%us, 2.0%sy, 0.0%ni, 73.8%id, 3.4%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu21 : 20.8%us, 2.0%sy, 0.0%ni, 73.9%id, 3.2%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu22 : 20.8%us, 2.0%sy, 0.0%ni, 74.4%id, 2.8%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu23 : 20.8%us, 1.9%sy, 0.0%ni, 74.4%id, 2.8%wa, 0.0%hi, 0.0%si, 0.0%st
Mem: 32865032k total, 30209248k used, 2655784k free, 370748k buffers
Swap: 8388604k total, 5440k used, 8383164k free, 7986552k cached
wa
?通常是 0%,如果經常在 1% 之上,說明存儲設備的速度已經太慢,無法跟上 cpu 的處理速度。
IO高負載如何排查??
使用 iostat 檢查設備是否 hang 住??
iostat -xhd 2
如果有 100% 的?%util
?的設備,說明該設備基本 hang 住了
觀察高 IO 的磁盤讀寫情況?
# 捕獲 %util 超過 90 時 vdb 盤的讀寫指標,每秒檢查一次
while true; do iostat -xhd | grep -A1 vdb | grep -v vdb | awk '{if ($NF > 90){print $0}}'; sleep 1s; done
如果讀寫流量或 IOPS 不高,但?%util
?不高,通常是磁盤本身有問題了,需要檢查下磁盤。 在云上托管的 k8s 集群通常就使用的云廠商的云盤(比如騰訊云CBS),可以拿到磁盤 ID 反饋下。
如果讀寫流量或 IOPS 高,繼續下面的步驟排查出哪些進程導致的 IO 高負載。
查看哪些進程占住磁盤?
fuser -v -m /dev/vdb
查找 D 狀態的進程?
D 狀態 (Disk Sleep) 表示進程正在等待 IO,不可中斷,正常情況下不會保持太久,如果進程長時間處于 D 狀態,通常是設備故障
ps -eo pid,ppid,stat,command## 捕獲 D 狀態的進程
while true; do ps -eo pid,ppid,stat,command | awk '{if ($3 ~ /D/) {print $0}}'; sleep 0.5s; done
觀察高 IO 進程?
iotop -oP
# 展示 I/O 統計,每秒更新一次
pidstat -d 1
# 只看某個進程
pidstat -d 1 -p 3394470
使用 pidstat 統計?
timeout 10 pidstat -dl 3 > io.txt
cat io.txt | awk '{if ($6>2000||$5>2000)print $0}'
使用 ebpf 抓高 IOPS 進程?
安裝 bcc-tools:
yum install -y bcc-tools
分析:
$ cd /usr/share/bcc/tools
$ ./biosnoop 5 > io.txt
$ cat io.txt | awk '{print $3,$2,$4,$5}' | sort | uniq -c | sort -rn | head -106850 3356537 containerd vdb R1294 3926934 containerd vdb R864 1670 xfsaild/vdb vdb W578 3953662 kworker/u180:1 vda W496 3540267 logsys_cfg_cli vdb R459 1670 xfsaild/vdb vdb R354 3285936 php-fpm vdb R340 3285934 php-fpm vdb R292 2952592 sap1001 vdb R273 324710 python vdb R
$ pstree -apnhs 3356537
systemd,1 --switched-root --system --deserialize 22└─containerd,3895└─{containerd},3356537
$ timeout 10 strace -fp 3895 > strace.txt 2>&1
# vdb 的 IOPS 高,vdb 掛載到了 /data 目錄,這里過濾下 "/data"
$ grep "/data" strace.txt | tail -10
[pid 19562] newfstatat(AT_FDCWD, "/data/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/6974/fs/data/log/monitor/snaps/20211010/ps-2338.log", {st_mode=S_IFREG|0644, st_size=6509, ...}, AT_SYMLINK_NOFOLLOW) = 0
[pid 19562] newfstatat(AT_FDCWD, "/data/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/6974/fs/data/log/monitor/snaps/20211010/ps-2339.log", {st_mode=S_IFREG|0644, st_size=6402, ...}, AT_SYMLINK_NOFOLLOW) = 0
[pid 19562] newfstatat(AT_FDCWD, "/data/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/6974/fs/data/log/monitor/snaps/20211010/ps-2340.log", {st_mode=S_IFREG|0644, st_size=6509, ...}, AT_SYMLINK_NOFOLLOW) = 0
[pid 19562] newfstatat(AT_FDCWD, "/data/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/6974/fs/data/log/monitor/snaps/20211010/ps-2341.log", {st_mode=S_IFREG|0644, st_size=6509, ...}, AT_SYMLINK_NOFOLLOW) = 0
[pid 19562] newfstatat(AT_FDCWD, "/data/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/6974/fs/data/log/monitor/snaps/20211010/ps-2342.log", {st_mode=S_IFREG|0644, st_size=6970, ...}, AT_SYMLINK_NOFOLLOW) = 0
[pid 19562] newfstatat(AT_FDCWD, "/data/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/6974/fs/data/log/monitor/snaps/20211010/ps-2343.log", {st_mode=S_IFREG|0644, st_size=6509, ...}, AT_SYMLINK_NOFOLLOW) = 0
[pid 19562] newfstatat(AT_FDCWD, "/data/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/6974/fs/data/log/monitor/snaps/20211010/ps-2344.log", {st_mode=S_IFREG|0644, st_size=6402, ...}, AT_SYMLINK_NOFOLLOW) = 0
[pid 19562] newfstatat(AT_FDCWD, "/data/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/6974/fs/data/log/monitor/snaps/20211010/ps-2345.log", <unfinished ...>
[pid 19562] newfstatat(AT_FDCWD, "/data/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/6974/fs/data/log/monitor/snaps/20211010/ps-2346.log", {st_mode=S_IFREG|0644, st_size=7756, ...}, AT_SYMLINK_NOFOLLOW) = 0
[pid 19562] newfstatat(AT_FDCWD, "/data/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/6974/fs/data/log/monitor/snaps/20211010/ps-2347.log", Process 3895 detached
$ grep "/data" strace.txt > data.txt
# 合并且排序,自行用腳本分析下哪些文件操作多
$ cat data.txt | awk -F '"' '{print $2}' | sort | uniq -c | sort -n > data-sorted.txt
7)如果負載太高導致機器完全無法操作怎么辦??
此情況下建議直接重啟。
3、磁盤爆滿
1)什么情況下磁盤可能會爆滿 ??
kubelet 有 gc 和驅逐機制,通過以下參數:
--image-gc-high-threshold
--image-gc-low-threshold
--eviction-hard
,?--eviction-soft
--eviction-minimum-reclaim
控制 kubelet 的 gc 和驅逐策略來釋放磁盤空間,如果配置正確的情況下,磁盤一般不會爆滿。
通常導致爆滿的原因可能是配置不正確或者節點上有其它非 K8S 管理的進程在不斷寫數據到磁盤占用大量空間導致磁盤爆滿。
2)磁盤爆滿會有什么影響 ??
影響 K8S 運行我們主要關注 kubelet 和容器運行時這兩個最關鍵的組件,它們所使用的目錄通常不一樣:
- kubelet 一般不會單獨掛盤,直接使用系統磁盤,因為通常占用空間不會很大;
- 容器運行時單獨掛盤的場景比較多,一般情況下會單獨掛盤。
當磁盤爆滿的時候我們也要看 kubelet 和 容器運行時使用的目錄是否在這個磁盤,通過?df
?命令可以查看磁盤掛載點。
3)容器運行時使用的目錄所在磁盤爆滿?
如果容器運行時使用的目錄所在磁盤空間爆滿,可能會造成容器運行時無響應,比如 docker,執行 docker 相關的命令一直 hang 住, kubelet 日志也可以看到 PLEG unhealthy,因為 CRI 調用 timeout,當然也就無法創建或銷毀容器,通常表現是 Pod 一直 ContainerCreating 或 一直 Terminating。
docker 默認使用的目錄主要有:
/var/run/docker
: 用于存儲容器運行狀態,通過 dockerd 的?--exec-root
?參數指定。/var/lib/docker
: 用于持久化容器相關的數據,比如容器鏡像、容器可寫層數據、容器標準日志輸出、通過 docker 創建的 volume 等
Pod 啟動可能報類似下面的事件:
Warning FailedCreatePodSandBox 53m kubelet, 172.22.0.44 Failed create pod sandbox: rpc error: code = DeadlineExceeded desc = context deadline exceeded
Warning FailedCreatePodSandBox 2m (x4307 over 16h) kubelet, 10.179.80.31 (combined from similar events): Failed create pod sandbox: rpc error: code = Unknown desc = failed to create a sandbox for pod "apigateway-6dc48bf8b6-l8xrw": Error response from daemon: mkdir /var/lib/docker/aufs/mnt/1f09d6c1c9f24e8daaea5bf33a4230de7dbc758e3b22785e8ee21e3e3d921214-init: no space left on device
Warning Failed 5m1s (x3397 over 17h) kubelet, ip-10-0-151-35.us-west-2.compute.internal (combined from similar events): Error: container create failed: container_linux.go:336: starting container process caused "process_linux.go:399: container init caused \"rootfs_linux.go:58: mounting \\\"/sys\\\" to rootfs \\\"/var/lib/dockerd/storage/overlay/051e985771cc69f3f699895a1dada9ef6483e912b46a99e004af7bb4852183eb/merged\\\" at \\\"/var/lib/dockerd/storage/overlay/051e985771cc69f3f699895a1dada9ef6483e912b46a99e004af7bb4852183eb/merged/sys\\\" caused \\\"no space left on device\\\"\""
Pod 刪除可能報類似下面的事件:
Normal Killing 39s (x735 over 15h) kubelet, 10.179.80.31 Killing container with id docker://apigateway:Need to kill Pod
4)kubelet 使用的目錄所在磁盤爆滿?
如果 kubelet 使用的目錄所在磁盤空間爆滿(通常是系統盤),新建 Pod 時連 Sandbox 都無法創建成功,因為 mkdir 將會失敗,通常會有類似這樣的 Pod 事件:
Warning UnexpectedAdmissionError 44m kubelet, 172.22.0.44 Update plugin resources failed due to failed to write checkpoint file "kubelet_internal_checkpoint": write /var/lib/kubelet/device-plugins/.728425055: no space left on device, which is unexpected.
kubelet 默認使用的目錄是?/var/lib/kubelet
, 用于存儲插件信息、Pod 相關的狀態以及掛載的 volume (比如?emptyDir
,?ConfigMap
,?Secret
),通過 kubelet 的?--root-dir
?參數指定。
5)如何分析磁盤占用 ??
如果運行時使用的是 Docker,請參考:分析 Docker 磁盤占用-CSDN博客
6)如何恢復 ??
如果容器運行時使用的 Docker,我們無法直接重啟 dockerd 來釋放一些空間,因為磁盤爆滿后 dockerd 無法正常響應,停止的時候也會卡住。我們需要先手動清理一點文件騰出空間好讓 dockerd 能夠停止并重啟。
可以手動刪除一些 docker 的 log 文件或可寫層文件,通常刪除 log:
$ cd /var/lib/docker/containers
$ du -sh * # 找到比較大的目錄
$ cd dda02c9a7491fa797ab730c1568ba06cba74cecd4e4a82e9d90d00fa11de743c
$ cat /dev/null > dda02c9a7491fa797ab730c1568ba06cba74cecd4e4a82e9d90d00fa11de743c-json.log.9 # 刪除log文件
注意:?使用?
cat /dev/null >
?方式刪除而不用?rm
,因為用 rm 刪除的文件,docker 進程可能不會釋放文件,空間也就不會釋放;log 的后綴數字越大表示越久遠,先刪除舊日志。
然后將該 node 標記不可調度,并將其已有的 pod 驅逐到其它節點,這樣重啟 dockerd 就會讓該節點的 pod 對應的容器刪掉,容器相關的日志(標準輸出)與容器內產生的數據文件(沒有掛載 volume, 可寫層)也會被清理:
kubectl drain <node-name>
重啟 dockerd:
systemctl restart dockerd
# or systemctl restart docker
等重啟恢復,pod 調度到其它節點,排查磁盤爆滿原因并清理和規避,然后取消節點不可調度標記:
kubectl uncordon <node-name>
7)如何規避 ??
正確配置 kubelet gc 和 驅逐相關的參數,即便到達爆滿地步,此時節點上的 pod 也都早就自動驅逐到其它節點了,不會存在 Pod 一直 ContainerCreating 或 Terminating 的問題。
4、PID 爆滿
1)如何判斷 PID 耗盡?
首先要確認當前的 PID 限制,檢查全局 PID 最大限制:
cat /proc/sys/kernel/pid_max
也檢查下線程數限制:
cat /proc/sys/kernel/threads-max
再檢查下當前用戶是否還有?ulimit
?限制最大進程數。
確認當前實際 PID 數量,檢查當前用戶的 PID 數量:
ps -eLf | wc -l
如果發現實際 PID 數量接近最大限制說明 PID 就可能會爆滿導致經常有進程無法啟動,低版本內核可能報錯:?Cannot allocate memory
,這個報錯信息不準確,在內核 4.1 以后改進了:?fork: report pid reservation failure properly · torvalds/linux@35f71bc · GitHub
2)如何解決?
臨時調大 PID 和線程數限制:
echo 65535 > /proc/sys/kernel/pid_max
echo 65535 > /proc/sys/kernel/threads-max
永久調大 PID 和線程數限制:
echo "kernel.pid_max=65535 " >> /etc/sysctl.conf && sysctl -p
echo "kernel.threads-max=65535 " >> /etc/sysctl.conf && sysctl -p
k8s 1.14 支持了限制 Pod 的進程數量:?Process ID Limiting for Stability Improvements in Kubernetes 1.14 | Kubernetes
參考:如何限制pod 進程/線程數量?-CSDN博客
5、判斷 arp_cache 是否溢出?
node 內核日志會有有下面的報錯:
arp_cache: neighbor table overflow!
查看當前 arp 記錄數:
$ arp -an | wc -l
1335
查看 arp gc 閥值:
$ sysctl -a | grep gc_thresh
net.ipv4.neigh.default.gc_thresh1 = 128
net.ipv4.neigh.default.gc_thresh2 = 512
net.ipv4.neigh.default.gc_thresh3 = 1024
net.ipv6.neigh.default.gc_thresh1 = 128
net.ipv6.neigh.default.gc_thresh2 = 512
net.ipv6.neigh.default.gc_thresh3 = 1024
當前 arp 記錄數接近?gc_thresh3
?比較容易 overflow,因為當 arp 記錄達到?gc_thresh3
?時會強制觸發 gc 清理,當這時又有數據包要發送,并且根據目的 IP 在 arp cache 中沒找到 mac 地址,這時會判斷當前 arp cache 記錄數加 1 是否大于?gc_thresh3
,如果沒有大于就會 時就會報錯:?arp_cache: neighbor table overflow!
解決方案?
調整節點內核參數,將 arp cache 的 gc 閥值調高 (/etc/sysctl.conf
):
net.ipv4.neigh.default.gc_thresh1 = 80000
net.ipv4.neigh.default.gc_thresh2 = 90000
net.ipv4.neigh.default.gc_thresh3 = 100000
分析是否只是部分業務的 Pod 的使用場景需要節點有比較大的 arp 緩存空間。
如果不是,就需要調整所有節點內核參數。
如果是,可以將部分 Node 打上標簽,比如:
kubectl label node host1 arp_cache=large
然后用 nodeSelector 或 nodeAffnity 讓這部分需要內核有大 arp_cache 容量的 Pod 只調度到這部分節點,推薦使用 nodeAffnity,yaml 示例:
template:spec:affinity:nodeAffinity:requiredDuringSchedulingIgnoredDuringExecution:nodeSelectorTerms:- matchExpressions:- key: arp_cacheoperator: Invalues:- large
參考:K8S node ARP 表爆滿 如何優化-CSDN博客
6、inotify 資源耗盡
inotify詳解參考:linux inotify 資源詳解-CSDN博客
1)inotify 耗盡的危害?
如果 inotify 資源耗盡,kubelet 創建容器將會失敗:
Failed to watch directory "/sys/fs/cgroup/blkio/system.slice": inotify_add_watch /sys/fs/cgroup/blkio/system.slice/var-lib-kubelet-pods-d111600d\x2dcdf2\x2d11e7\x2d8e6b\x2dfa163ebb68b9-volumes-kubernetes.io\x7esecret-etcd\x2dcerts.mount: no space left on device
2)查看 inotify watch 的限制?
每個 linux 進程可以持有多個 fd,每個 inotify 類型的 fd 可以 watch 多個目錄,每個用戶下所有進程 inotify 類型的 fd 可以 watch 的總目錄數有個最大限制,這個限制可以通過內核參數配置:?fs.inotify.max_user_watches
。
查看最大 inotify watch 數:
$ cat /proc/sys/fs/inotify/max_user_watches
8192
3)查看進程的 inotify watch 情況?
使用下面的腳本查看當前有 inotify watch 類型 fd 的進程以及每個 fd watch 的目錄數量,降序輸出,帶總數統計:
#!/usr/bin/env bash
#
# Copyright 2019 (c) roc
#
# This script shows processes holding the inotify fd, alone with HOW MANY directories each inotify fd watches(0 will be ignored).
total=0
result="EXE PID FD-INFO INOTIFY-WATCHES\n"
while read pid fd; do \exe="$(readlink -f /proc/$pid/exe || echo n/a)"; \fdinfo="/proc/$pid/fdinfo/$fd" ; \count="$(grep -c inotify "$fdinfo" || true)"; \if [ $((count)) != 0 ]; thentotal=$((total+count)); \result+="$exe $pid $fdinfo $count\n"; \fi
done <<< "$(lsof +c 0 -n -P -u root|awk '/inotify$/ { gsub(/[urw]$/,"",$4); print $2" "$4 }')" && echo "total $total inotify watches" && result="$(echo -e $result|column -t)\n" && echo -e "$result" | head -1 && echo -e "$result" | sed "1d" | sort -k 4rn;
示例輸出:
total 7882 inotify watches
EXE PID FD-INFO INOTIFY-WATCHES
/usr/local/qcloud/YunJing/YDEyes/YDService 25813 /proc/25813/fdinfo/8 7077
/usr/bin/kubelet 1173 /proc/1173/fdinfo/22 665
/usr/bin/ruby2.3 13381 /proc/13381/fdinfo/14 54
/usr/lib/policykit-1/polkitd 1458 /proc/1458/fdinfo/9 14
/lib/systemd/systemd-udevd 450 /proc/450/fdinfo/9 13
/usr/sbin/nscd 7935 /proc/7935/fdinfo/3 6
/usr/bin/kubelet 1173 /proc/1173/fdinfo/28 5
/lib/systemd/systemd 1 /proc/1/fdinfo/17 4
/lib/systemd/systemd 1 /proc/1/fdinfo/18 4
/lib/systemd/systemd 1 /proc/1/fdinfo/26 4
/lib/systemd/systemd 1 /proc/1/fdinfo/28 4
/usr/lib/policykit-1/polkitd 1458 /proc/1458/fdinfo/8 4
/usr/local/bin/sidecar-injector 4751 /proc/4751/fdinfo/3 3
/usr/lib/accountsservice/accounts-daemon 1178 /proc/1178/fdinfo/7 2
/usr/local/bin/galley 8228 /proc/8228/fdinfo/10 2
/usr/local/bin/galley 8228 /proc/8228/fdinfo/9 2
/lib/systemd/systemd 1 /proc/1/fdinfo/11 1
/sbin/agetty 1437 /proc/1437/fdinfo/4 1
/sbin/agetty 1440 /proc/1440/fdinfo/4 1
/usr/bin/kubelet 1173 /proc/1173/fdinfo/10 1
/usr/local/bin/envoy 4859 /proc/4859/fdinfo/5 1
/usr/local/bin/envoy 5427 /proc/5427/fdinfo/5 1
/usr/local/bin/envoy 6058 /proc/6058/fdinfo/3 1
/usr/local/bin/envoy 6893 /proc/6893/fdinfo/3 1
/usr/local/bin/envoy 6950 /proc/6950/fdinfo/3 1
/usr/local/bin/galley 8228 /proc/8228/fdinfo/3 1
/usr/local/bin/pilot-agent 3819 /proc/3819/fdinfo/5 1
/usr/local/bin/pilot-agent 4244 /proc/4244/fdinfo/5 1
/usr/local/bin/pilot-agent 5901 /proc/5901/fdinfo/3 1
/usr/local/bin/pilot-agent 6789 /proc/6789/fdinfo/3 1
/usr/local/bin/pilot-agent 6808 /proc/6808/fdinfo/3 1
/usr/local/bin/pilot-discovery 6231 /proc/6231/fdinfo/3 1
/usr/local/bin/sidecar-injector 4751 /proc/4751/fdinfo/5 1
/usr/sbin/acpid 1166 /proc/1166/fdinfo/6 1
/usr/sbin/dnsmasq 7572 /proc/7572/fdinfo/8 1
4)調整 inotify watch 限制?
如果看到總 watch 數比較大,接近最大限制,可以修改內核參數調高下這個限制。
臨時調整:
sudo sysctl fs.inotify.max_user_watches=524288
永久生效:
echo "fs.inotify.max_user_watches=524288" >> /etc/sysctl.conf && sysctl -p
打開 inotify_add_watch 跟蹤,進一步 debug inotify watch 耗盡的原因:
echo 1 >> /sys/kernel/debug/tracing/events/syscalls/sys_exit_inotify_add_watch/enable
7、soft lockup (內核軟死鎖)
1)內核報錯?
Oct 14 15:13:05 VM_1_6_centos kernel: NMI watchdog: BUG: soft lockup - CPU#5 stuck for 22s! [runc:[1:CHILD]:2274]
2)原因?
發生這個報錯通常是內核繁忙 (掃描、釋放或分配大量對象),分不出時間片給用戶態進程導致的,也伴隨著高負載,如果負載降低報錯則會消失。
3)什么情況下會導致內核繁忙?
短時間內創建大量進程 (可能是業務需要,也可能是業務bug或用法不正確導致創建大量進程)
4)如何優化?
?