解决k8s某节点无法查看pod日志问题
问题描述
生产环境:
- k8s版本:v1.17.9
- 集群:三台master,一台worker,使用kubeadm部署
问题现象:
master执行 kubectl logs
出现下面错误:
error: You must be logged in to the server (the server has asked for the client to provide credentials (pods/log xxx))
通过k8sClient请求apiserver /api/v1/namespaces/{namespace}/pods/{name}/log
时,同样不行。
但是执行 kubectl get pods
或者 请求 GET /api/v1/namespaces/{namespace}/pods
能正常返回结果。
换个节点,在master2上执行 kubectl logs
,能正常返回。
排查过程
首先盲猜大概率就是该节点证书的问题。
但不久前集群证书已经通过 kubeadm alpha certs renew all
更新过了,再次通过 kubeadm alpha certs check-expiration
查看证书日志,是没问题的。
难道是该节点的kubectl config没更新,于是在出错节点更新config cp -f /etc/kubernetes/admin.conf ${HOME}/.kube/config
,网上还有些说使用 export KUBECONFIG=/etc/kubernetes/admin.conf
来更新。
发现还是没用。
仔细想想,这个跟kubectl确实是没关系的,因为绕过kubectl直接请求api时也出现了问题。
所以问题应该就是在 kubelet与api-server 交互上。
-
检查kubelet日志
systemctl status kubelet
,虽然有一些错误日志,但没看到一些有用的关键信息。 -
那可能就是 kube-apiserver 服务证书没更新,于是找到对应节点的 apiserver 的pod,执行:
kubectl -n kube-system delete pod kube-apiserver-xxxx
很奇怪,还是不行?而且重启后这个pod看似重启了,但数据好像没更新(我看到该pod的重启次数没刷新)。
那就直接使用docker查看 docker ps |grep kube-apiserver
,果然,该容器的UP时间都没刷新。
最终杀手锏,暴力重启 docker restart containerID
问题就解决了!
原因分析
排查了我老半天,而且是带着半信半疑排查解决的,总结一下这个问题:
为什么获取pod列表就可以,获取pod日志就不行?
-
首先无论是kubectl还是使用k8sClient,实质都是请求apiserver进行交互的,认证信息都在~/.kube/config里。
-
为了缓解各模块对API Server的访问压力,kubelet会定期从从 API Server 获取指定的资源对象信息(LIST/WATCH方法)保存到etcd。
-
获取pod列表实际是通过apiserver访问etcd里的数据;而查看pod日志需要通过 apiserver -> kubelet -> CRI(containerd)。中间apiserver与kubelet的交互需要证书验证。
为什么直接删掉kube-apiserver pod达不到重启的效果,需要
docker restart
?
-
默认情况下kubelet是会定时扫描/etc/kubernetes/manifests(kubeadm默认目录,或通过
--pod-manifest-path
来指定目录),然后根据这个文件夹下的 YAML/JSON 文件来创建/删除静态 Pod。 -
也就是说,执行
kubectl delete pod kube-apiserver
时,不是通过apiserver,而是通过Kubelet来删除。 -
kubelet监听到删除事件,只是将mirror pod(annotations里有
kubernetes.io/config.mirror
的key)标记删除,仅仅删除etcd里的信息,随后又会自动上传static pod信息并重新生成pod。
所以直接删掉kube-apiserver pod是不会重启真正的容器的。
为什么证书刚更新过,还需要删除kube-apiserver container证书才能正常?
- 证书过期了,k8s调度没有问题,只是apiserver与kubelet之间通信的证书没有生效。
- controller-manager和schduler都是利用的client-go去调用apiserver,是利用kubernetes 的配置文件
/etc/kubernetes/
路径下的controller-manager.conf和scheduler.conf来进行安全通信。 - 而apiserver调用kubelet,是通过读取apiserver初始化的配置缓存信息,获取kubelet 的client证书,再进行https的安全通信。
- 实际上这些k8s组件都没有自动加载证书的功能,但恰巧controller-manager和schduler都重启过。
- 此时apiserver容器里的证书刚好过期,与 kubelet交互认证失败,所以获取 pod log 或者 exec 会失败。
所以出现创建、查看和删除资源是没问题;获取pod日志时,apiserver调用kubelet接口时才有问题。
总结
更新证书后,除了重启控制节点的kubelet,还需要手动重启 kube-apiserver、kube-controller、kube-scheduler、etcd 这4个容器。
docker ps | grep -v pause | grep -E "etcd|scheduler|controller|apiserver" | awk '{print $1}' | xargs docker restart