k8s常见问题
集群状态报错
1、k8s集群中,有pod出现了 Pending ,通过 kubectl describe pod 命令,发现了如下报错
0/4 nodes are available: 1 node(s) had taint {node.kubernetes.io/disk-pressure: }, that the pod didn’t tolerate, 3 node(s) didn’t match node selector.
取消所有节点的 node.kubernetes.io/disk-pressure
kubectl taint nodes --all node.kubernetes.io/disk-pressure-
查看指定窗口日志
error: a container name must be specified for pod *, choose one of: [] or one of the init containers: []
一个pod中启用了多个容器,从此查看日志时需要指定查看该pod的哪个容器,可通过命令 -c <container_name> 指定
kubectl logs $podname -c containerName --tail 100
Node 存储空间不足
sudo docker run --rm -v /var/run/docker.sock:/var/run/docker.sock -v /etc:/etc:ro spotify/docker-gc
执行下面的命令来查看占用空间最多的镜像(按镜像大小由大到小排序):sudo docker images --format '{{.Size}}\t{{.Repository}}:{{.Tag}}\t{{.ID}}' | sort -h -r | column -t
/sys/fs/cgroup 空间不足
很多发行版默认的 fs.inotify.max_user_watches 太小,只有 8192,可以通过增大该配置解决。比如
$ sudo sysctl fs.inotify.max_user_watches=52428
Pod 异常排错
一般来说,无论 Pod 处于什么异常状态,都可以执行以下命令来查看 Pod 的状态
kubectl get pod <pod-name> -o yaml
查看 Pod 的配置是否正确kubectl describe pod <pod-name>
查看 Pod 的事件kubectl logs <pod-name> [-c <container-name>]
查看容器日志
这些事件和日志通常都会有助于排查 Pod 发生的问题。
Pod 一直处于 Pending 状态
Warning FailedScheduling 12s (x6 over 27s) default-scheduler 0/4 nodes are available: 2 Insufficient cpu.
可能的原因包括
- 资源不足,集群内所有的 Node 都不满足该 Pod 请求的 CPU、内存、GPU 或者临时存储空间等资源。解决方法是删除集群内不用的 Pod 或者增加新的 Node。
- HostPort 端口已被占用,通常推荐使用 Service 对外开放服务端口
Pod 一直处于 Waiting 或 ContainerCreating 状态
首先还是通过 kubectl describe pod <pod-name>
命令查看到当前 Pod 的事件
$ kubectl -n kube-system describe pod nginx-pod
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 1m default-scheduler Successfully assigned nginx-pod to node1
Normal SuccessfulMountVolume 1m kubelet, gpu13 MountVolume.SetUp succeeded for volume "config-volume"
Normal SuccessfulMountVolume 1m kubelet, gpu13 MountVolume.SetUp succeeded for volume "coredns-token-sxdmc"
Warning FailedSync 2s (x4 over 46s) kubelet, gpu13 Error syncing pod
Normal SandboxChanged 1s (x4 over 46s) kubelet, gpu13 Pod sandbox changed, it will be killed and re-created.
可以发现,该 Pod 的 Sandbox 容器无法正常启动,具体原因需要查看 Kubelet 日志:
$ journalctl -u kubelet
...
Mar 14 04:22:04 node1 kubelet[29801]: E0314 04:22:04.649912 29801 cni.go:294] Error adding network: failed to set bridge addr: "cni0" already has an IP address different from 10.244.4.1/24
Mar 14 04:22:04 node1 kubelet[29801]: E0314 04:22:04.649941 29801 cni.go:243] Error while adding to cni network: failed to set bridge addr: "cni0" already has an IP address different from 10.244.4.1/24
Mar 14 04:22:04 node1 kubelet[29801]: W0314 04:22:04.891337 29801 cni.go:258] CNI failed to retrieve network namespace path: Cannot find network namespace for the terminated container "c4fd616cde0e7052c240173541b8543f746e75c17744872aa04fe06f52b5141c"
Mar 14 04:22:05 node1 kubelet[29801]: E0314 04:22:05.965801 29801 remote_runtime.go:91] RunPodSandbox from runtime service failed: rpc error: code = 2 desc = NetworkPlugin cni failed to set up pod "nginx-pod" network: failed to set bridge addr: "cni0" already has an IP address different from 10.244.4.1/24
发现是 cni0 网桥配置了一个不同网段的 IP 地址导致,删除该网桥(网络插件会自动重新创建)即可修复
$ ip link set cni0 down
$ brctl delbr cni0
镜像拉取失败,比如
配置了错误的镜像
Kubelet 无法访问镜像(国内环境访问 gcr.io 需要特殊处理)
私有镜像的密钥配置错误
镜像太大,拉取超时(可以适当调整 kubelet 的 --image-pull-progress-deadline 和 --runtime-request-timeout 选项)
CNI 网络错误,一般需要检查 CNI 网络插件的配置,比如
无法配置 Pod 网络
无法分配 IP 地址
容器无法启动,需要检查是否打包了正确的镜像或者是否配置了正确的容器参数
Pod 处于 ImagePullBackOff 状态
这通常是镜像名称配置错误或者私有镜像的密钥配置错误导致。这种情况可以使用 docker pull <image>
来验证镜像是否可以正常拉取。
如果是私有镜像,需要首先创建一个 docker-registry 类型的 Secret
kubectl create secret docker-registry my-secret --docker-server=DOCKER_REGISTRY_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL
然后在容器中引用这个 Secret
spec:
containers:
- name: private-reg-container
image: <your-private-image>
imagePullSecrets:
- name: my-secret
Pod 一直处于 CrashLoopBackOff 状态
CrashLoopBackOff 状态说明容器曾经启动了,但又异常退出了。此时 Pod 的 RestartCounts 通常是大于 0 的,可以先查看一下容器的日志
kubectl describe pod <pod-name>
kubectl logs <pod-name>
kubectl logs --previous <pod-name>
这里可以发现一些容器退出的原因,比如
- 容器进程退出
- 健康检查失败退出
- OOMKilled
Pod 处于 Error 状态
通常处于 Error 状态说明 Pod 启动过程中发生了错误。常见的原因包括
- 依赖的 ConfigMap、Secret 或者 PV 等不存在
- 请求的资源超过了管理员设置的限制,比如超过了 LimitRange 等
- 违反集群的安全策略,比如违反了 PodSecurityPolicy 等
- 容器无权操作集群内的资源,比如开启 RBAC 后,需要为 ServiceAccount 配置角色绑定
Pod 处于 Terminating 或 Unknown 状态
从 v1.5 开始,Kubernetes 不会因为 Node 失联而删除其上正在运行的 Pod,而是将其标记为 Terminating 或 Unknown 状态。想要删除这些状态的 Pod 有三种方法:
- 从集群中删除该 Node。使用公有云时,kube-controller-manager 会在 VM 删除后自动删除对应的 Node。而在物理机部署的集群中,需要管理员手动删除 Node(如 `kubectl delete node <node-name>`。
- Node 恢复正常。Kubelet 会重新跟 kube-apiserver 通信确认这些 Pod 的期待状态,进而再决定删除或者继续运行这些 Pod。
- 用户强制删除。用户可以执行 `kubectl delete pods <pod> --grace-period=0 --force` 强制删除 Pod。除非明确知道 Pod 的确处于停止状态(比如 Node 所在 VM 或物理机已经关机),否则不建议使用该方法。特别是 StatefulSet 管理的 Pod,强制删除容易导致脑裂或者数据丢失等问题。
处于 `Terminating` 状态的 Pod 在 Kubelet 恢复正常运行后一般会自动删除。但有时也会出现无法删除的情况,并且通过 `kubectl delete pods <pod> --grace-period=0 --force` 也无法强制删除。此时一般是由于 `finalizers` 导致的,通过 `kubectl edit` 将 finalizers 删除即可解决。
"finalizers": [
"foregroundDeletion"
]
Namespace 一直处于 terminating 状态
Namespace 一直处于 terminating 状态,一般有两种原因:
- Namespace 中还有资源正在删除中
- Namespace 的 Finalizer 未正常清理
对第一个问题,可以执行下面的命令来查询所有的资源
kubectl api-resources --verbs=list --namespaced -o name | xargs -n 1 kubectl get --show-kind --ignore-not-found -n $NAMESPACE
而第二个问题则需要手动清理 Namespace 的 Finalizer 列表:
kubectl get namespaces $NAMESPACE -o json | jq '.spec.finalizers=[]' > /tmp/ns.json
kubectl proxy &
curl -k -H "Content-Type: application/json" -X PUT --data-binary @/tmp/ns.json http://127.0.0.1:8001/api/v1/namespaces/$NAMESPACE/finalize
排错图解
网络异常排错
说到 Kubernetes 的网络,其实无非就是以下三种情况之一
- Pod 访问容器外部网络
- 从容器外部访问 Pod 网络
- Pod 之间相互访问
Flannel Pods 一直处于 Init:CrashLoopBackOff 状态
Flannel 网络插件非常容易部署,只要一条命令即可
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
kubelet启动失败
是kubelet cgroup驱动程序“ cgroupfs”与docker cgroup驱动程序不同
docker info|grep Driver #查看docker cgroup驱动
修改/etc/docker/daemon.json文件
{
"exec-opts": ["native.cgroupdriver=systemd"]
}
systemctl daemon-reload
systemctl restart docker