kubelet日志(kubelet日志报错 用户没权限)

http://www.itjxue.com  2023-01-25 00:03  来源:未知  点击次数: 

kubernetes Pod 异常排错

Pod 运行异常的排错方法。

一般来说,无论 Pod 处于什么异常状态,都可以执行以下命令来查看 Pod 的状态

这些事件和日志通常都会有助于排查 Pod 发生的问题。

Pending 说明 Pod 还没有调度到某个 Node 上面。可以通过 kubectl describe pod pod-name 命令查看到当前 Pod 的事件,进而判断为什么没有调度。如

可能的原因包括

首先还是通过 kubectl describe pod pod-name 命令查看到当前 Pod 的事件

可以发现,该 Pod 的 Sandbox 容器无法正常启动,具体原因需要查看 Kubelet 日志

发现是 cni0 网桥配置了一个不同网段的 IP 地址导致,删除该网桥(网络插件会自动重新创建)即可修复

除了以上错误,其他可能的原因还有

这通常是镜像名称配置错误或者私有镜像的密钥配置错误导致。这种情况可以使用 docker pull image 来验证镜像是否可以正常拉取。

如果是私有镜像,需要首先创建一个 docker-registry 类型的 Secret

然后在容器中引用这个 Secret

CrashLoopBackOff 状态说明容器曾经启动了,但又异常退出了。此时 Pod 的 RestartCounts 通常是大于 0 的,可以先查看一下容器的日志

这里可以发现一些容器退出的原因,比如

如果此时如果还未发现线索,还可以到容器内执行命令来进一步查看退出原因

如果还是没有线索,那就需要 SSH 登录该 Pod 所在的 Node 上,查看 Kubelet 或者 Docker 的日志进一步排查了

通常处于 Error 状态说明 Pod 启动过程中发生了错误。常见的原因包括

从 v1.5 开始,Kubernetes 不会因为 Node 失联而删除其上正在运行的 Pod,而是将其标记为 Terminating 或 Unknown 状态。想要删除这些状态的 Pod 有三种方法:

如果 Kubelet 是以 Docker 容器的形式运行的,此时 kubelet 日志中可能会发现 如下的错误 :

如果是这种情况,则需要给 kubelet 容器设置 --containerized 参数并传入以下的存储卷

处于 Terminating 状态的 Pod 在 Kubelet 恢复正常运行后一般会自动删除。但有时也会出现无法删除的情况,并且通过 kubectl delete pods pod --grace-period=0 --force 也无法强制删除。此时一般是由于 finalizers 导致的,通过 kubectl edit 将 finalizers 删除即可解决。

这里所说的行为异常是指 Pod 没有按预期的行为执行,比如没有运行 podSpec 里面设置的命令行参数。这一般是 podSpec yaml 文件内容有误,可以尝试使用 --validate 参数重建容器,比如

也可以查看创建后的 podSpec 是否是对的,比如

Kubelet 使用 inotify 机制检测 /etc/kubernetes/manifests 目录(可通过 Kubelet 的 --pod-manifest-path 选项指定)中静态 Pod 的变化,并在文件发生变化后重新创建相应的 Pod。但有时也会发生修改静态 Pod 的 Manifest 后未自动创建新 Pod 的情景,此时一个简单的修复方法是重启 Kubelet。

pod容器日志管理

通过本文,你将了解 kubelet 是如何管理 pod 内的容器日志的。

kubelet 定义 pod 日志路径为:

default 命名空间下存在一个名为 stakater-reloader-598f958967-ddkl7 的 pod

获取其 uid ( metadata.uid 字段)

跟据上面信息拼装,我们获取了该 pod 的日志目录

证明上面规则正确

kubernetes/pkg/kubelet/kuberuntime/kuberuntime_sandbox.go

上文我们了解到, kubelet 定义 pod 日志路径为:

我们观察下其路径结构:

kubernetes/pkg/kubelet/kuberuntime/kuberuntime_container.go

kubernetes/pkg/kubelet/kuberuntime/kuberuntime_container.go

其中 restartCount 重启次数这个值我们可以通过以下方式获取:

通过观察我们可以发现,这个目录下的日志文件是软链接类型文件:

其实是 kubelet 在启动容器后,生成的链接。

通过样例,我们不难发现链接匹配的规则如下:(适用于docker运行时,且为json日志插件)

其中容器 id 我们可以通过以下方式获取

日志文件的软链接设置逻辑实际发生于容器启动后:

kubelet 启动容器核心源码: kubernetes/pkg/kubelet/dockershim/docker_container.go

其中 ds.createContainerLogSymlink(r.ContainerId) 便是创建日志文件软链接的逻辑,我们接下来对其进行深入分析

创建容器日志软链接,主要分为三个步骤:

其中 path, realPath, err := ds.getContainerLogPath(containerID) 为获取容器信息逻辑,关于返回值解析:

kubernetes/pkg/kubelet/dockershim/docker_container.go 源码

ds.os.Symlink(realPath, path) 为调用系统接口,创建日志文件软链接

通过上述分析,我们可以得出以下结论(docker运行时下):

/var/lib/docker/containers/容器id/容器id-json.log 的链接。

k8s-日志落地

#容器方便的同时带来的挑战

1. 如果日志还放在容器内部,会随着容器删除而删除

2. 容器多按照传统的仓库日志方式 显然不现实

#本身特性

1. 容器日志输出到控制台 本身docker提供了一种日志采集能力 如果落地到了本地文件 目前还没有一种比较好的动态采集方式

2. 新扩容的pod属性信息(日志文件路径 日志源 可能发生的变化)

#需要收集那些日志

1. k8s 系统组件日志 部署在k8s应用的日志

#当我们执行docker logs查看日志的时候是调用了docker守护进程去查看他接管的这个日志 在本地的文件系统中去读这个日志

#cd /var/lib/docker/找到容器ID进入里面 有一个已json文件已容器id命名的里面就是日志

#/var/lib/kubelet/pods/08ec113c8abdf4544

方案一:Node上部署一个日志收集程序

? DaemonSet方式部署日志收集程序

? 对本节点/var/log/kubelet/pods和

/var/lib/docker/containers/两个目录下的日志进

行采集

? Pod中容器日志目录挂载到宿主机统一目录上

方案二:Pod中附加专用日志收集的容器

? 每个运行应用程序的Pod中增加一个日志

收集容器,使用emtyDir共享日志目录让

日志收集程序读取到。

方案一:Node上部署一个日志收集程序 每个Node仅需部署一个日志收集程序,

资源消耗少,对应用无侵入 应用程序日志如果写到标准输出和标准错误输出,

那就不支持多行日志。

方案二:Pod中附加专用日志收集的容器 低耦合

每个Pod启动一个日志收集代理,增加资源消耗,

并增加运维维护成本

#匹配目录收集规则

方案(1):DaemonSet方式部署日志收集程序

/var/lib/docker/containers/*/*-json.log

/var/lib/kubelet/pods/*/volumes/kubermetes.io~emtpdir/

/var/lib/kubelet/pods/*/

方式2: 挂载到统一的目录 解决日志覆盖的方法? 推荐方法让开发根据容器名称命名日志文件

保持唯一性就可以了 这种方法维护起来比较好 也比较简单 但是缺点可能消耗资源多一点

data:

? kubernetes.yml: |-

? ? - type: docker

? ? ? containers.ids:

? ? ? - "*"

#传统日志配置采集工具重要设置什么

1. 日志路径

2. 写正则 格式化日志

3. 日志源(命名空间 容器 service 项目)

阿里云日志采集工具:log-pilot

kubelet 原理解析二:pleg

如果你的 node 突然 notready,或者 pod状态异常时,你会 describe node 或describe pod 来查看原因,你可能会看到这一行报错:

pleg 是什么,如何排查并解决上面的问题呢?

PLEG:全称是Pod Lifecycle Event Generator,主要负责将Pod状态变化记录event以及触发Pod同步

可以看下官方PLEG示意图中红色的部分:

以node notready 这个场景为例:

Kubelet中的NodeStatus机制会定期检查集群节点状况,并把节点状况同步到API Server。而NodeStatus判断节点就绪状况的一个主要依据,就是PLEG。

PLEG定期检查节点上Pod运行情况,并且会把pod 的变化包装成Event发送给Kubelet的主同步机制syncLoop去处理。但是,在PLEG的Pod检查机制不能定期执行的时候,NodeStatus机制就会认为这个节点的状况是不对的,从而把这种状况同步到API Server,我们就会看到 not ready

PLEG有两个关键的时间参数,一个是检查的执行间隔,另外一个是检查的超时时间。以默认情况为准,PLEG检查会间隔一秒,换句话说,每一次检查过程执行之后,PLEG会等待一秒钟,然后进行下一次检查;而每一次检查的超时时间是三分钟,如果一次PLEG检查操作不能在三分钟内完成,那么这个状况,会被NodeStatus机制当做集群节点NotReady的凭据,同步给API Server。

如下图,上边一行表示正常情况下PLEG的执行流程,下边一行则表示有问题的情况。relist是检查的主函数。

PLEG Start就是启动一个协程,每个relistPeriod(1s)就调用一次relist,根据最新的PodStatus生成PodLiftCycleEvent。

relist是PLEG的核心,它从container runtime中查询属于kubelet管理的containers/sandboxes的信息,并与自身维护的 pods cache 信息进行对比,生成对应的 PodLifecycleEvent,然后输出到 eventChannel 中,通过 eventChannel 发送到 kubelet syncLoop 进行消费,然后由 kubelet syncPod 来触发 pod 同步处理过程,最终达到用户的期望状态。

not healthy 是如何发生的:

Healthy() 函数会以 “PLEG” 的形式添加到 runtimeState 中,Kubelet 在一个同步循环(SyncLoop() 函数)中会定期(默认是 10s)调用 Healthy() 函数。Healthy() 函数会检查 relist 进程(PLEG 的关键任务)是否在 3 分钟内完成。如果 relist 进程的完成时间超过了 3 分钟,就会报告 PLEG is not healthy。

主要逻辑:

即使将relist设置为每1秒调用一次,如果容器运行时响应缓慢,或者一个周期中发生许多容器更改时,也可能需要花费1秒以上的时间才能完成。因此,下一个relist将在上一个完成后调用。

例如,如果relist需要5秒才能完成,则下一次relist时间为6秒(1秒+ 5秒)

因此如果容器数量很大或者进程有问题,会出现延时,3 分钟内没有响应,就会出现not healthy ,因此监控relist的延迟是有必要的,kubelet 暴露了和 pleg 相关的指标,可以通过这些指标查看当前的状态

relist 指标监控:

最后,updateCache()将检查每个Pod,并在一个循环中一个接一个地更新它,因此,如果在同一relist期间更改了许多Pod ,则此过程可能会成为瓶颈。然后新Pod生命周期事件将发送到eventChannel。

出现 pleg not healthy,一般有以下几种可能:

docker进程hang 死导致 pleg遇到过很多次,一般是由于 docker 版本过旧,或者机器负载过高导致,可以把 kubelet和 docker 的日志等级调到最高,对比日志定位后进行处理

kubernetes常见故障

kubernetes常见故障

1. 节点CNI不可用,其它节点无法连接到故障节点的Pod

2. Subpath方式挂载的Configmap,特定条件下出现Pod无限重启的问题

3. 集群DNS服务器无法通过上游DNS解析外部名称

4. 节点假死,但是持有的Ceph RBD的Watcher不释放,导致有状态服务的Pod调度走后仍然无法启动

5. 误删Etcd数据、持久卷

有四个有用的命令可以对Pod进行故障排除:

kubectl logs 有助于检索Pod容器的日志

kubectl describe pod 检索与Pod相关的事件列表很有用

kubectl get pod 用于提取存储在Kubernetes中的Pod的YAML定义

kubectl exec -ti bash 在Pod的一个容器中运行交互式命令很有用

常见Pod错误

Pod可能会出现启动和运行时错误。

启动错误包括:

ImagePullBackoff

ImageInspectError

ErrImagePull

ErrImageNeverPull

RegistryUnavailable

InvalidImageName

运行时错误包括:

CrashLoopBackOff

RunContainerError

KillContainerError

VerifyNonRootError

RunInitContainerError

CreatePodSandboxError

ConfigPodSandboxError

KillPodSandboxError

SetupNetworkError

TeardownNetworkError

有些错误比其他错误更常见。

以下是最常见的错误列表以及如何修复它们的方法。

ImagePullBackOff

当Kubernetes无法获取到Pod中某个容器的镜像时,将出现此错误。

共有三个可能的原因:

镜像名称无效-例如,你拼错了名称,或者image不存在

你为image指定了不存在的标签

你尝试检索的image属于一个私有registry,而Kubernetes没有凭据可以访问它

前两种情况可以通过更正image名称和标记来解决。

针对第三种情况,你应该将私有registry的访问凭证通过Secret添加到k8s中并在Pod中引用它。

官方文档中有一个有关如何实现此目标的示例。

CrashLoopBackOff

如果容器无法启动,则Kubernetes将显示错误状态为:CrashLoopBackOff。

通常,在以下情况下容器无法启动:

应用程序中存在错误,导致无法启动

你未正确配置容器

Liveness探针失败太多次

你应该尝试从该容器中检索日志以调查其失败的原因。

如果由于容器重新启动太快而看不到日志,则可以使用以下命令:

$ kubectl logs pod-name --previous?

这个命令打印前一个容器的错误消息。

RunContainerError

当容器无法启动时,出现此错误。

甚至在容器内的应用程序启动之前。

该问题通常是由于配置错误,例如:

挂载不存在的卷,例如ConfigMap或Secrets

将只读卷安装为可读写

你应该使用kubectl describe pod 命令收集和分析错误。

处于Pending状态的Pod

当创建Pod时,该Pod保持Pending状态。

为什么?

假设你的调度程序组件运行良好,可能的原因如下:

集群没有足够的资源(例如CPU和内存)来运行Pod

当前的命名空间具有ResourceQuota对象,创建Pod将使命名空间超过配额

该Pod绑定到一个处于pending状态的 PersistentVolumeClaim

最好的选择是检查kubectl describe命令输出的“事件”部分内容:

$ kubectl describe pod pod name?

对于因ResourceQuotas而导致的错误,可以使用以下方法检查集群的日志:

$ kubectl get events --sort-by=.metadata.creationTimestamp?

处于未就绪状态的Pod

如果Pod正在运行但未就绪(not ready),则表示readiness就绪探针失败。

当“就绪”探针失败时,Pod未连接到服务,并且没有流量转发到该实例。

就绪探针失败是应用程序的特定错误,因此你应检查kubectl describe中的“ 事件”部分以识别错误。

2. 服务的故障排除

如果你的Pod正在运行并处于就绪状态,但仍无法收到应用程序的响应,则应检查服务的配置是否正确。

service旨在根据流量的标签将流量路由到Pod。

因此,你应该检查的第一件事是服务关联了多少个Pod。

你可以通过检查服务中的端点(endpoint)来做到这一点:

$ kubectl describe service service-name | grep Endpoints?

端点是一对,并且在服务(至少)以Pod为目标时,应该至少有一个端点。

如果“端点”部分为空,则有两种解释:

你没有运行带有正确标签的Pod(提示:你应检查自己是否在正确的命名空间中)

service的selector标签上有错字

如果你看到端点列表,但仍然无法访问你的应用程序,则targetPort可能是你服务中的罪魁祸首。

你如何测试服务?

无论服务类型如何,你都可以使用kubectl port-forward来连接它:

$kubectl port-forward service/service-name 3000:80?

这里:

是服务的名称

3000 是你希望在计算机上打开的端口

80 是服务公开的端口

3.Ingress的故障排除

如果你已到达本节,则:

Pod正在运行并准备就绪

服务会将流量分配到Pod

但是你仍然看不到应用程序的响应。

这意味着最有可能是Ingress配置错误。

由于正在使用的Ingress控制器是集群中的第三方组件,因此有不同的调试技术,具体取决于Ingress控制器的类型。

但是在深入研究Ingress专用工具之前,你可以用一些简单的方法进行检查。

Ingress使用serviceName和servicePort连接到服务。

你应该检查这些配置是否正确。

你可以通过下面命令检查Ingress配置是否正确:

$kubectl describe ingress ingress-name?

如果backend一列为空,则配置中必然有一个错误。

如果你可以在“backend”列中看到端点,但是仍然无法访问该应用程序,则可能是以下问题:

你如何将Ingress暴露于公共互联网

你如何将集群暴露于公共互联网

你可以通过直接连接到Ingress Pod来将基础结构问题与Ingress隔离开。

首先,获取你的Ingress控制器Pod(可以位于其他名称空间中):

$ kubectl get pods --all-namespaces?

NAMESPACE NAME READY STATUS?

kube-system coredns-5644d7b6d9-jn7cq 1/1 Running?

kube-system etcd-minikube 1/1 Running?

kube-system kube-apiserver-minikube 1/1 Running?

kube-system kube-controller-manager-minikube 1/1 Running?

kube-system kube-proxy-zvf2h 1/1 Running?

kube-system kube-scheduler-minikube 1/1 Running?

kube-system nginx-ingress-controller-6fc5bcc 1/1 Running?

描述它以检索端口:

# kubectl describe pod nginx-ingress-controller-6fc5bcc?

--namespace kube-system \?

| grep Ports?

最后,连接到Pod:

$ kubectl port-forward nginx-ingress-controller-6fc5bcc 3000:80 --namespace kube-system?

此时,每次你访问计算机上的端口3000时,请求都会转发到Pod上的端口80。

现在可以用吗?

如果可行,则问题出在基础架构中。你应该调查流量如何路由到你的集群。

如果不起作用,则问题出在Ingress控制器中。你应该调试Ingress。

如果仍然无法使Ingress控制器正常工作,则应开始对其进行调试。

目前有许多不同版本的Ingress控制器。

热门选项包括Nginx,HAProxy,Traefik等。

你应该查阅Ingress控制器的文档以查找故障排除指南。

由于Ingress Nginx是最受欢迎的Ingress控制器,因此在下一部分中我们将介绍一些有关调试ingress-nginx的技巧。

调试Ingress Nginx

Ingress-nginx项目有一个Kubectl的官方插件。

你可以用kubectl ingress-nginx来:

检查日志,后端,证书等。

连接到ingress

检查当前配置

你应该尝试的三个命令是:

kubectl ingress-nginx lint,它会检查 nginx.conf

kubectl ingress-nginx backend,以检查后端(类似于kubectl describe ingress )

kubectl ingress-nginx logs,查看日志

请注意,你可能需要为Ingress控制器指定正确的名称空间–namespace 。

------------------------------------------------------------------------------------------------------

kubernetes之故障排查和节点维护(二)

系列目录

案例现场:

测试环境集群本来正常,突然间歇性地出现服务不能正常访问,过一会儿刷新页面又可以正常访问了.进入到服务所在的pod查看输出日志并没有发现异常.使用kubectl get node命令正好发现一个节点是NotReady状态

为了方便观察,使用kubectl get node --watch来观测一段时间,发现k8s-node1节点不断的在Ready和NotReady状态之间切换(使用kubectl get node -o wide可以查看节点的ip信息).

进入到出现问题的节点,使用命令journalctl -f -u kubelet来查看kubelet的日志信息,把错误日志截出来一段搜索一下,发现问题和这个问题基本上是一样的,发现这个问题的时间和github上issue提出的时间是在同一天,也没有看到解决办法.但是基本能确定是因为集群中k8s-node1上的kubernetes版本不一致造成的(从上面截图上可以看到,这个节点的版本是1.14.1其它的都是1.13.1,是怎么升上来的不清楚,可能是其它同事误操作升级导致的)

搜索kubernetes NotReady查看了一些解决经验,很多都是重启docker,重启kubectl等,然后都解决不了问题.于是尝试重置这个节点.

从集群中删除Node

由于这个节点上运行着服务,直接删除掉节点会导致服务不可用.我们首先使用kubectl drain命令来驱逐这个节点上的所有pod

kubectl drain k8s-node1 --delete-local-data --force --ignore-daemonsets

以上命令中--ignore-daemonsets往往需要指定的,这是因为deamonset会忽略unschedulable标签(使用kubectl drain时会自动给节点打上不可调度标签),因此deamonset控制器控制的pod被删除后可能马上又在此节点上启动起来,这样就会成为死循环.因此这里忽略daemonset.

实际在使用kubectl drain时候,命令行一直被阻塞,等了很久还在被阻塞.使用kubectl get pod命令查看pod状态时.其中一个叫作busybox的pod一直处于Terminating状态. 使用kubectl delete pod busybox同样无法删除它.这时候可以使用命令kubectl delete pods busybox --grace-period=0 --force来强制马上删除pod.

这时候控制台阻塞状态结束.下面执行命令kubectl delete node k8s-node1来删除这个节点.然后我们重新安装kubelet,kubeadm和kubectl

卸载旧版本

如果是通过yum方式安装的,可以通过yum list installed|grep xxx形式来找到已安装的组件,然后删除它们.删除以后重新安装.

这里之所以要重新安装是因为版本升级成了较为新的版本,如果版本是一样的,其它的不确定因素导致节点不稳定,又找不到具体原因,则可以通过kubeadm reset来重置安装.

重置命令并不会重置设置的iptables规则和IPVS如果想要重置iptables,则需要执行以下命令:

iptables -F iptables -t nat -F iptables -t mangle -F iptables -X

如果想要重置IPVS,则需要执行以下命令:

ipvsadm -C

这里我能够基本确定是由于版本不一致导致的,因此我并不重置iptables和IPVS,仅仅是重装组件.

重新加入集群

重置完成以后,我们把删除掉的k8s-node1节点使用kubeadm join重新加入到集群中

如果忘记了主节点初始化时候生成的加入token,可以在主节点上执行kubeadm token create --print-join-command重新生成加入token,然后把生成的命令复制到要加入集群的节点上执行.

重新加入集群后,观察了一段时间,一直是Ready状态,感觉终于稳定了,但是同事又反馈部署服务时出现以下错误

Failed create pod sandbox: rpc error: code = Unknown desc = failed to set up sandbox container "5159f7918d520aee74c5a08c8707f34b61bcf1c340bfc444125331034e1f57f6" network for pod "test-58f4789cb7-7nlk8": NetworkPlugin cni failed to set up pod "test-58f4789cb7-7nlk8_default" network: failed to set bridge addr: "cni0" already has an IP address different from 10.244.4.1/24

幸好有伟大的互联网,通过搜索,找到以下解决方案

由于这次启动以后初次部署pod就失败了,因此此节点上还没有运行的服务,我们不需要执行kubectl drain,可以直接把这个节点删除.然后执行以下命令

kubeadm reset

systemctl stop kubelet

systemctl stop docker

rm -rf /var/lib/cni/

rm -rf /var/lib/kubelet/*

rm -rf /etc/cni/

ifconfig cni0 down

ifconfig flannel.1 down

ifconfig docker0 down

ip link delete cni0

ip link delete flannel.1

systemctl start docker

完了以后重新加入集群.这次可以正常工作了.

-----------------------------------------------------------------

(责任编辑:IT教学网)

更多

相关Flash actionscript文章

推荐Flash actionscript文章