Kubernetes排错(九)-节点排错
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)如何优化?
参考:k8s node soft lockup (内核软死锁) 优化方案-CSDN博客
8、no space left on device
- 有时候节点 NotReady, kubelet 日志报
no space left on device
。 - 有时候创建 Pod 失败,
describe pod
看 event 报no space left on device
。
出现这种错误有很多中可能原因,下面根据现象找对应原因。
1)inotify watch 耗尽
节点 NotReady,kubelet 启动失败,看 kubelet 日志:
Jul 18 15:20:58 VM_16_16_centos kubelet[11519]: E0718 15:20:58.280275 11519 raw.go:140] Failed to watch directory "/sys/fs/cgroup/memory/kubepods": inotify_add_watch /sys/fs/cgroup/memory/kubepods/burstable/pod926b7ff4-7bff-11e8-945b-52540048533c/6e85761a30707b43ed874e0140f58839618285fc90717153b3cbe7f91629ef5a: no space left on device
系统调用 inotify_add_watch
失败,提示 no space left on device
, 这是因为系统上进程 watch 文件目录的总数超出了最大限制,可以修改内核参数调高限制,详细请参考上文【inotify 资源耗尽】处理。
2)cgroup 泄露
查看当前 cgroup 数量:
$ cat /proc/cgroups | column -t
#subsys_name hierarchy num_cgroups enabled
cpuset 5 29 1
cpu 7 126 1
cpuacct 7 126 1
memory 9 127 1
devices 4 126 1
freezer 2 29 1
net_cls 6 29 1
blkio 10 126 1
perf_event 3 29 1
hugetlb 11 29 1
pids 8 126 1
net_prio 6 29 1
cgroup 子系统目录下面所有每个目录及其子目录都认为是一个独立的 cgroup,所以也可以在文件系统中统计目录数来获取实际 cgroup 数量,通常跟 /proc/cgroups
里面看到的应该一致:
$ find -L /sys/fs/cgroup/memory -type d | wc -l
127
当 cgroup 泄露发生时,这里的数量就不是真实的了,低版本内核限制最大 65535 个 cgroup,并且开启 kmem 删除 cgroup 时会泄露,大量创建删除容器后泄露了许多 cgroup,最终总数达到 65535,新建容器创建 cgroup 将会失败,报 no space left on device
详细请参考:k8s node cgroup 泄露如何优化?-CSDN博客
3)磁盘被写满
Pod 启动失败,状态 CreateContainerError
:
csi-cephfsplugin-27znb 0/2 CreateContainerError 167 17h
Pod 事件报错:
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/containers/storage/overlay/051e985771cc69f3f699895a1dada9ef6483e912b46a99e004af7bb4852183eb/merged\\\" at \\\"/var/lib/containers/storage/overlay/051e985771cc69f3f699895a1dada9ef6483e912b46a99e004af7bb4852183eb/merged/sys\\\" caused \\\"no space left on device\\\"\""
4)limits 单位错误
参考:
k8s pod request/limit 值不带单位会发生什么?-CSDN博客
Kubernetes Pod CPU资源设置最佳实践-CSDN博客
Pod 事件报错:
Mount Volume.SetUp failed for volume "kube-api-access-j562g" :write /var/lib/kubelet/pods /7c251070
-cf3c-4180-97a2-647e858f3f2/volumes/kubernetes.io~projected/kube-api-access-j562g/..2023_07_25_07_25_22.573608539/ca.crt: no space left on device
可能是因为定义 requests 和 limits 时忘了写单位,或单位有误:
limits:memory: 512mi # 应该大写开头,改成 512Mi
limits:memory: 512 # 没有单位默认为字节,太小,应带上单位
根因:可能是因为内存相关的 volume 都受 memory limit 限制 (projected volume, emptydir 等)。
9、IPVS no destination available
参考:k8s node 报IPVS no destination available-CSDN博客
1)现象
内核日志不停报 no destination available
这样的 warning 日志,查看 dmesg:
[23709.680898] IPVS: rr: TCP 192.168.0.52:80 - no destination available
[23710.709824] IPVS: rr: TCP 192.168.0.52:80 - no destination available
[23832.428700] IPVS: rr: TCP 127.0.0.1:30209 - no destination available
[23833.461818] IPVS: rr: TCP 127.0.0.1:30209 - no destination available
2)原因
一般是因为有 Service 用了 externalTrafficPolicy:Local
,当 Node 上没有该 Service 对应 Pod 时,Node 上的该 Service 对应 NodePort 的 IPVS 规则里,RS 列表为空。
当有流量打到这个 Node 的对应 NodePort 上时,由于 RS 列表为空,内核就会报这个 warning 日志。
在云厂商托管的 K8S 服务里,通常是 LB 会去主动探测 NodePort,发到没有这个 Service 对应 Pod 实例的 Node 时,报文被正常丢弃,从而内核报 warning 日志。
这个日志不会对服务造成影响,可以忽略不管。如果是在腾讯云 TKE 环境里,并且用的 TencentOS,可以设置一个内核参数来抑制这个 warning 日志输出:
sysctl -w net.ipv4.vs.ignore_no_rs_error=1
10、内存碎片化
1)判断是否内存碎片化严重
内存页分配失败,内核日志报类似下面的错:
mysqld: page allocation failure. order:4, mode:0x10c0d0
mysqld
是被分配的内存的程序order
表示需要分配连续页的数量(2^order),这里 4 表示 2^4=16 个连续的页mode
是内存分配模式的标识,定义在内核源码文件include/linux/gfp.h
中,通常是多个标识相与运算的结果,不同版本内核可能不一样,比如在新版内核中GFP_KERNEL
是__GFP_RECLAIM | __GFP_IO | __GFP_FS
的运算结果,而__GFP_RECLAIM
又是___GFP_DIRECT_RECLAIM|___GFP_KSWAPD_RECLAIM
的运算结果
当 order 为 0 时,说明系统以及完全没有可用内存了,order 值比较大时,才说明内存碎片化了,无法分配连续的大页内存。
2)内存碎片化造成的问题
容器启动失败
K8S 会为每个 pod 创建 netns 来隔离 network namespace,内核初始化 netns 时会为其创建 nf_conntrack 表的 cache,需要申请大页内存,如果此时系统内存已经碎片化,无法分配到足够的大页内存内核就会报错(v2.6.33 - v4.6
):
runc:[1:CHILD]: page allocation failure: order:6, mode:0x10c0d0
Pod 状态将会一直在 ContainerCreating,dockerd 启动容器失败,日志报错:
Jan 23 14:15:31 dc05 dockerd: time="2019-01-23T14:15:31.288446233+08:00" level=error msg="containerd: start container" error="oci runtime error: container_linux.go:247: starting container process caused \"process_linux.go:245: running exec setns process for init caused \\\"exit status 6\\\"\"\n" id=5b9be8c5bb121264899fac8d9d36b02150269d41ce96ba6ad36d70b8640cb01c
Jan 23 14:15:31 dc05 dockerd: time="2019-01-23T14:15:31.317965799+08:00" level=error msg="Create container failed with error: invalid header field value \"oci runtime error: container_linux.go:247: starting container process caused \\\"process_linux.go:245: running exec setns process for init caused \\\\\\\"exit status 6\\\\\\\"\\\"\\n\""
kubelet 日志报错:
Jan 23 14:15:31 dc05 kubelet: E0123 14:15:31.352386 26037 remote_runtime.go:91] RunPodSandbox from runtime service failed: rpc error: code = 2 desc = failed to start sandbox container for pod "matchdataserver-1255064836-t4b2w": Error response from daemon: {"message":"invalid header field value \"oci runtime error: container_linux.go:247: starting container process caused \\\"process_linux.go:245: running exec setns process for init caused \\\\\\\"exit status 6\\\\\\\"\\\"\\n\""}
Jan 23 14:15:31 dc05 kubelet: E0123 14:15:31.352496 26037 kuberuntime_sandbox.go:54] CreatePodSandbox for pod "matchdataserver-1255064836-t4b2w_basic(485fd485-1ed6-11e9-8661-0a587f8021ea)" failed: rpc error: code = 2 desc = failed to start sandbox container for pod "matchdataserver-1255064836-t4b2w": Error response from daemon: {"message":"invalid header field value \"oci runtime error: container_linux.go:247: starting container process caused \\\"process_linux.go:245: running exec setns process for init caused \\\\\\\"exit status 6\\\\\\\"\\\"\\n\""}
Jan 23 14:15:31 dc05 kubelet: E0123 14:15:31.352518 26037 kuberuntime_manager.go:618] createPodSandbox for pod "matchdataserver-1255064836-t4b2w_basic(485fd485-1ed6-11e9-8661-0a587f8021ea)" failed: rpc error: code = 2 desc = failed to start sandbox container for pod "matchdataserver-1255064836-t4b2w": Error response from daemon: {"message":"invalid header field value \"oci runtime error: container_linux.go:247: starting container process caused \\\"process_linux.go:245: running exec setns process for init caused \\\\\\\"exit status 6\\\\\\\"\\\"\\n\""}
Jan 23 14:15:31 dc05 kubelet: E0123 14:15:31.352580 26037 pod_workers.go:182] Error syncing pod 485fd485-1ed6-11e9-8661-0a587f8021ea ("matchdataserver-1255064836-t4b2w_basic(485fd485-1ed6-11e9-8661-0a587f8021ea)"), skipping: failed to "CreatePodSandbox" for "matchdataserver-1255064836-t4b2w_basic(485fd485-1ed6-11e9-8661-0a587f8021ea)" with CreatePodSandboxError: "CreatePodSandbox for pod \"matchdataserver-1255064836-t4b2w_basic(485fd485-1ed6-11e9-8661-0a587f8021ea)\" failed: rpc error: code = 2 desc = failed to start sandbox container for pod \"matchdataserver-1255064836-t4b2w\": Error response from daemon: {\"message\":\"invalid header field value \\\"oci runtime error: container_linux.go:247: starting container process caused \\\\\\\"process_linux.go:245: running exec setns process for init caused \\\\\\\\\\\\\\\"exit status 6\\\\\\\\\\\\\\\"\\\\\\\"\\\\n\\\"\"}"
Jan 23 14:15:31 dc05 kubelet: I0123 14:15:31.372181 26037 kubelet.go:1916] SyncLoop (PLEG): "matchdataserver-1255064836-t4b2w_basic(485fd485-1ed6-11e9-8661-0a587f8021ea)", event: &pleg.PodLifecycleEvent{ID:"485fd485-1ed6-11e9-8661-0a587f8021ea", Type:"ContainerDied", Data:"5b9be8c5bb121264899fac8d9d36b02150269d41ce96ba6ad36d70b8640cb01c"}
Jan 23 14:15:31 dc05 kubelet: W0123 14:15:31.372225 26037 pod_container_deletor.go:77] Container "5b9be8c5bb121264899fac8d9d36b02150269d41ce96ba6ad36d70b8640cb01c" not found in pod's containers
Jan 23 14:15:31 dc05 kubelet: I0123 14:15:31.678211 26037 kuberuntime_manager.go:383] No ready sandbox for pod "matchdataserver-1255064836-t4b2w_basic(485fd485-1ed6-11e9-8661-0a587f8021ea)" can be found. Need to start a new one
查看slab (后面的0多表示伙伴系统没有大块内存了):
$ cat /proc/buddyinfo
Node 0, zone DMA 1 0 1 0 2 1 1 0 1 1 3
Node 0, zone DMA32 2725 624 489 178 0 0 0 0 0 0 0
Node 0, zone Normal 1163 1101 932 222 0 0 0 0 0 0 0
系统 OOM
内存碎片化会导致即使当前系统总内存比较多,但由于无法分配足够的大页内存导致给进程分配内存失败,就认为系统内存不够用,需要杀掉一些进程来释放内存,从而导致系统 OOM
3)解决方法
周期性地或者在发现大块内存不足时,先进行drop_cache操作:
echo 3 > /proc/sys/vm/drop_caches
必要时候进行内存整理,开销会比较大,会造成业务卡住一段时间(慎用):
echo 1 > /proc/sys/vm/compact_memory
4)如何防止内存碎片化
参考:linux 如何防止内存碎片化?-CSDN博客
11、节点 NotReady
1)诊断流程
查看节点状态
kubectl get nodes
kubectl describe node <node-name> # 查看详细状态和事件
检查节点kubelet服务状态
# 登录节点执行
systemctl status kubelet # 检查kubelet服务状态
systemctl status docker # 检查容器运行时状态
df -h # 检查磁盘空间
free -h # 检查内存使用
top # 检查CPU占用
检查 kubelet 日志
journalctl -u kubelet -f # 实时查看kubelet日志
2)常见原因及解决方案
kubelet 服务异常
现象:kubectl describe node
显示Kubelet stopped posting node status
排查:
systemctl status kubelet
journalctl -u kubelet | grep -i error
解决:
systemctl restart kubelet
# 若频繁崩溃,检查配置文件:/etc/kubernetes/kubelet.conf
网络通信问题
现象:节点无法与 API Server 通信,或 Pod 网络不通
排查:
# 检查节点到API Server的连通性
curl https://<apiserver-ip>:6443 -k# 检查Pod网络(在节点上执行)
ping <pod-ip>
解决:
- 检查防火墙规则,开放必要端口(如 6443、10250)
- 重启网络服务:
systemctl restart network
- 检查 CNI 插件配置:
ls /etc/cni/net.d/
磁盘压力
现象:kubectl describe node
显示DiskPressure
排查:
df -h # 检查根分区和/var/lib/docker分区
du -sh /var/lib/docker/* | sort -hr | head -n 10 # 查找大文件
解决:
# 清理无用容器和镜像
docker system prune -af# 扩展磁盘空间或迁移数据
内存压力
现象:kubectl describe node
显示MemoryPressure
排查:
free -h
ps -eo pid,user,%mem,args --sort=-%mem | head # 查看内存占用top进程
解决:
# 终止内存占用过高的进程
kill -9 <pid># 增加节点内存或调整Pod资源限制
节点资源耗尽
现象:CPU、内存或 PID 达到上限
排查:
top # 检查CPU占用
ps -A --no-headers | wc -l # 检查进程数量(接近pid_max时会有问题)
解决:
- 终止不必要的进程
- 调整
pid_max
:echo 4194303 > /proc/sys/kernel/pid_max
- 增加节点资源或调整调度策略
节点内核或系统问题
现象:kubelet 频繁崩溃,日志显示内核错误
排查:
dmesg | grep -i error # 检查内核日志
uname -r # 检查内核版本
解决:
# 升级内核
yum update kernel # CentOS/RHEL
apt update && apt upgrade -y # Ubuntu/Debian# 重启节点
reboot
kubelet 配置错误
现象:kubelet 无法启动或频繁重启
排查:
# 检查配置文件
cat /etc/kubernetes/kubelet.conf
cat /var/lib/kubelet/config.yaml# 检查启动参数
systemctl cat kubelet | grep ExecStart
解决:
# 重置kubelet配置
kubeadm reset
kubeadm join ... # 重新加入集群
3)处理流程
紧急恢复
# 标记节点不可调度,避免新Pod调度到该节点
kubectl cordon <node-name># 驱逐节点上的Pod(需确保PDB允许)
kubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data
修复问题
根据上述排查结果,修复对应的问题(如重启服务、清理磁盘、调整配置等)。
恢复节点
# 检查节点状态
kubectl get nodes# 标记节点为可调度
kubectl uncordon <node-name>
4)预防措施
pod 设置合理的资源请求和限制
resources:requests:cpu: "100m"memory: "256Mi"limits:cpu: "200m"memory: "512Mi"
配置节点问题检测器
# 部署NodeProblemDetector
kubectl apply -f https://raw.githubusercontent.com/kubernetes/node-problem-detector/master/deployment/node-problem-detector.yaml
定期监控节点健康
- 使用 Prometheus 监控节点指标(CPU、内存、磁盘等)
- 设置告警规则:当节点状态异常时及时通知
自动化节点修复
- 使用 Cluster Autoscaler 自动替换不健康的节点
- 配置 PodDisruptionBudget 保障关键应用可用性
5)验证修复效果
# 检查节点状态
kubectl get nodes -o wide# 检查节点事件
kubectl describe node <node-name> | grep -A 20 "Events:"# 检查Pod调度情况
kubectl get pods -o wide --all-namespaces
通过以上步骤,可系统性地排查和解决 Kubernetes 节点 NotReady 问题,确保集群的高可用性。
12、kubelet日志出现"Error reading pod manifest"
当 kubelet 日志中出现 "Error reading pod manifest" 错误时,通常表示 kubelet 无法正确读取或解析 Pod 配置文件。以下是详细的排查和解决步骤:
1)错误原因分析
- 配置文件路径错误:kubelet 无法找到指定路径下的 Pod 配置文件
- 文件权限问题:kubelet 进程没有读取配置文件的权限
- 文件格式错误:YAML/JSON 格式不正确,包含语法错误
- 磁盘故障:存储配置文件的磁盘出现问题
- kubelet 配置错误:
--pod-manifest-path
参数设置不正确 - 配置文件被篡改:文件内容被意外修改或损坏
2)排查步骤
查看完整错误日志
journalctl -u kubelet | grep -i "Error reading pod manifest" -A 5 -B 5
示例错误:
Error reading pod manifest file "/etc/kubernetes/manifests/nginx.yaml": open /etc/kubernetes/manifests/nginx.yaml: no such file or directory
检查配置文件路径
# 查看kubelet启动参数中的配置文件路径
ps -ef | grep kubelet | grep -E 'pod-manifest-path|config'# 示例输出:
# --pod-manifest-path=/etc/kubernetes/manifests
# --config=/var/lib/kubelet/config.yaml# 检查配置文件是否存在
ls -l /etc/kubernetes/manifests/
验证文件权限
# 检查文件权限
ls -l /etc/kubernetes/manifests/nginx.yaml# 确保kubelet用户(通常是root)有读取权限
chmod 644 /etc/kubernetes/manifests/*
检查文件内容格式
# 使用yaml校验工具检查格式
yum install yamllint -y # CentOS/RHEL
apt install yamllint -y # Ubuntu/Debian# 校验文件
yamllint /etc/kubernetes/manifests/nginx.yaml
常见格式错误:
- 缩进不正确(YAML 对缩进敏感)
- 缺少必要字段(如
apiVersion
、kind
) - 非法字符或未闭合的引号
检查磁盘状态
# 检查磁盘空间
df -h# 检查inode使用情况
df -i# 检查磁盘健康状态
smartctl -a /dev/sda # 根据实际磁盘设备修改
验证 kubelet 配置
# 查看kubelet配置文件
cat /var/lib/kubelet/config.yaml | grep staticPodPath# 示例输出:
# staticPodPath: /etc/kubernetes/manifests
3)解决方法
修复配置文件路径
# 若路径错误,修改kubelet配置文件
vi /var/lib/kubelet/config.yaml# 添加或修改staticPodPath
staticPodPath: /etc/kubernetes/manifests# 重启kubelet
systemctl restart kubelet
修复文件权限
# 确保kubelet有读取权限
chmod 644 /etc/kubernetes/manifests/*
chown root:root /etc/kubernetes/manifests/*
修正文件格式
# 示例:修复缩进错误
apiVersion: v1
kind: Pod
metadata:name: nginx
spec:containers:- name: nginximage: nginx:1.14.2ports:- containerPort: 80
恢复损坏的配置文件
# 从备份恢复
cp /path/to/backup/nginx.yaml /etc/kubernetes/manifests/# 或重新创建配置文件
kubectl run nginx --image=nginx --restart=Never --dry-run=client -o yaml > /etc/kubernetes/manifests/nginx.yaml
检查磁盘问题
# 清理磁盘空间
yum clean all # CentOS/RHEL
apt clean # Ubuntu/Debian# 修复文件系统错误
fsck /dev/sda1 # 根据实际分区修改(需要卸载分区)
重新配置 kubelet
# 重置kubelet配置
kubeadm reset# 重新加入集群
kubeadm join <control-plane-host>:<control-plane-port> --token <token> --discovery-token-ca-cert-hash <hash>
4)预防措施
使用静态 Pod 配置最佳实践
# 使用kubelet配置文件而非命令行参数
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
staticPodPath: /etc/kubernetes/manifests
添加配置文件校验机制
# 使用pre-commit钩子校验YAML文件
cat << EOF > .pre-commit-config.yaml
repos:
- repo: https://github.com/adrienverge/yamllintrev: v1.26.3hooks:- id: yamllint
EOF
监控配置文件变更
# 使用inotify监控文件变化
yum install inotify-tools -y
inotifywait -m -r -e modify,create,delete /etc/kubernetes/manifests/
定期备份配置文件
# 每天备份配置文件
crontab -e
0 0 * * * tar -czf /backup/kubelet-manifests-$(date +%Y%m%d).tar.gz /etc/kubernetes/manifests/
5)验证修复效果
# 检查kubelet日志
journalctl -u kubelet | grep -i "Error reading pod manifest"# 检查Pod状态
kubectl get pods --field-selector spec.nodeName=<node-name># 示例输出:
# NAME READY STATUS RESTARTS AGE
# nginx 1/1 Running 0 5m
通过以上步骤,可有效解决 kubelet 无法读取 Pod 配置文件的问题,确保静态 Pod 正常运行。