kubelet日志(kubelet日志报错 用户没权限)
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
完了以后重新加入集群.这次可以正常工作了.
-----------------------------------------------------------------