想在研发群里装?先学会这几个排查K8s问题的办法
新手学习 K8s 最大的难度感觉是在起步动手实践的时候,Pod 没有正常启动起来,或者运行了一段时间 Pod 自己崩溃了。那么是什么问题导致了它没运行起来,又或者是什么因素导致了它的崩溃,这到底是道德的沦丧还是人性的扭曲。。。不好意思,拿错脚本了。
今天这篇文章我们一起学习总结几个使用 K8s 时常见的错误现象以及排查这些现象背后问题的方法。
学会这些,保证你能在研发组里、运维面前装到
装不成,被打脸,也别来找我,因为我也经常被……诶,发奋图强,争取下次装到。
Pod 的那些状态
使用 K8s 部署我们的服务之后,为了观察 Pod 是否成功,我们都会使用下面这个命令查询 Pod 的状态。
kubectl get podsNAME READY STATUS RESTARTS AGE
my-app-5d7d978fb9-2fj5m 0/1 ContainerCreating 0 10s
my-app-5d7d978fb9-dbt89 0/1 ContainerCreating 0 10s
这里的 STATUS 代表了 Pod 的状态,可能会遇到的状态有下面几个:
ContainerCreating:代表容器正在创建,这是一个中间状态,随着容器创建成功会切换,但是也有可能一直卡在这里,具体问题下面会分析。
ImagePullBackOff:容器镜像拉取失败,具体原因需要结合 describe 命令再去查看。
CrashLoopBackOff:容器崩溃,一般容器崩溃,Deployment 会重新创建一个 Pod,维持副本数量,但是大概率新创建的Pod 还是会崩溃,它不会无限尝试,崩溃超过设置次数就不会再尝试重建Pod,此时Pod的状态就维持在了 CrashLoopBackOff。
Evicted: 因为节点资源不足(CPU/Mem/Storage都有可能),Pod 被驱逐会显示 Evicted 状态,K8s 会按照策略选择认为可驱逐的Pod从节点上 Kill 掉。
Running 这个代表 Pod 正常运行。
下面我们来看一下 Pod 的几个错误状态的原因,以及怎么排查解决它们。
镜像拉取失败
镜像拉取失败后 Pod 的状态字段表示为 ImagePullBackOff,这个发生的情况还是很多的,原因除了我们不小心写错镜像名字之外,还有就是常用软件的一些官方镜像都在国外,比如在docker.io 或者 quay.io 的镜像仓库上,有的时候访问速度会很慢。
下面我们自己故意制造一个镜像名字写错的场景,看怎么使用 kubectl 命令进行排查。比如我在 K8s 教程里一直用的 Deployment 定义:
apiVersion: apps/v1
kind: Deployment
metadata:name: my-go-app
spec:replicas: 2selector:matchLabels:app: go-apptemplate:metadata:labels:app: go-appspec:containers:- name: go-app-containerimage: kevinyan001/kube-go-app:v0.3resources:limits:memory: "200Mi"cpu: "50m"ports:- containerPort: 3000volumeMounts:- name: app-storagemountPath: /tmpvolumes:- name: app-storageemptyDir: {}
我们把镜像的名字故意改错,改成 v0.5,这个镜像是我自己打的,确实还没有 0.5 版本。执行kubectl apply 后,来观察一下 Pod 的状态。
➜ kubectl apply -f deployment.yaml
deployment.apps/my-go-app configured➜ kubectl get pods
NAME READY STATUS RESTARTS AGE
my-go-app-5d7d978fb9-2fj5m 1/1 Running 0 3h58m
my-go-app-5d7d978fb9-dbt89 1/1 Running 0 3h58m
my-go-app-6b77dbbcc5-jpgbw 0/1 ContainerCreating 0 7s➜ kubectl get pods
NAME READY STATUS RESTARTS AGE
my-go-app-5d7d978fb9-2fj5m 1/1 Running 0 3h58m
my-go-app-5d7d978fb9-dbt89 1/1 Running 0 3h58m
my-go-app-6b77dbbcc5-jpgbw 0/1 ErrImagePull 0 14s
.....// 停顿1分钟,再查看Pod 的状态
➜ kubectl get pods
NAME READY STATUS RESTARTS AGE
my-go-app-5d7d978fb9-2fj5m 1/1 Running 0 4h1m
my-go-app-5d7d978fb9-dbt89 1/1 Running 0 4h1m
my-go-app-6b77dbbcc5-jpgbw 0/1 ImagePullBackOff 0 3m11s
上面我们更新了 deployment 之后,观察到 Pod 的状态变化过程是:
ContainerCreating ===> ErrImagePull ===> ImagePullBackOff
首先 deployment 更新 Pod 时是滚动更新,要先把新 Pod 创建出来后能对旧版本 Pod 完成替换。接下来由于镜像拉取错误会反馈一个中间状态 ErrImagePull,此时会再次尝试拉取,如果确定镜像拉取不下来后,最后反馈一个失败的终态 ImagePullBackOff。
怎么排查是什么导致的拉取失败呢?通过 kubectl describe pod {pod-name} 查看它的事件记录
➜ kubectl describe pod my-go-app-6b77dbbcc5-jpgbw
Name: my-go-app-6b77dbbcc5-jpgbw
Namespace: default
Priority: 0
...
Controlled By: ReplicaSet/my-go-app-6b77dbbcc5
Containers:go-app-container:Container ID: Image: kevinyan001/kube-go-app:v0.5Image ID: Port: 3000/TCPHost Port: 0/TCPState: WaitingReason: ErrImagePullReady: False...
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300snode.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:Type Reason Age From Message---- ------ ---- ---- -------Normal Scheduled 2m12s default-scheduler Successfully assigned default/my-go-app-6b77dbbcc5-jpgbw to docker-desktopNormal Pulling 27s (x4 over 2m12s) kubelet Pulling image "kevinyan001/kube-go-app:v0.5"Warning Failed 20s (x4 over 2m4s) kubelet Failed to pull image "kevinyan001/kube-go-app:v0.5": rpc error: code = Unknown desc = Error response from daemon: manifest for kevinyan001/kube-go-app:v0.5 not found: manifest unknown: manifest unknownWarning Failed 20s (x4 over 2m4s) kubelet Error: ErrImagePullNormal BackOff 4s (x5 over 2m4s) kubelet Back-off pulling image "kevinyan001/kube-go-app:v0.5"Warning Failed 4s (x5 over 2m4s) kubelet Error: ImagePullBackOff
Pod 事件记录里,清楚记录了 Pod 从开始到最后经历的状态变化,以及是什么导致状态变化的,其中失败事件里清楚的给出了我们原因,就是镜像找不到。
Events:Type Reason Age From Message---- ------ ---- ---- -------Warning Failed 20s (x4 over 2m4s) kubelet Failed to pull image "kevinyan001/kube-go-app:v0.5": rpc error: code = Unknown desc = Error response from daemon: manifest for kevinyan001/kube-go-app:v0.5 not found: manifest unknown: manifest unknownWarning Failed 20s (x4 over 2m4s) kubelet Error: ErrImagePullNormal BackOff 4s (x5 over 2m4s) kubelet Back-off pulling image "kevinyan001/kube-go-app:v0.5"Warning Failed 4s (x5 over 2m4s) kubelet Error: ImagePullBackOff
还有一种是网络原因,或者镜像仓库没有权限拒绝拉取请求,导致无法拉取成功。因为我这里网络环境、加速器之类的好不容易都配好了,就不给大家演示这两种情况了。
不过排查方式也是一样,使用kubectl describe 命令查看 Pod 的事件,并且使用 docker pull 尝试主动的拉取一下镜像试试,如果确实网络问题拉取不下来的,可以考虑翻墙,或者使用国内的加速节点。
配置加速器,可以考虑使用阿里云的免费加速器,配置文档在下面,需要注册阿里云账号才能使用加速器
https://help.aliyun.com/product/60716.html
启动后容器崩溃
再来看这种错误,这种一般是容器里运行的程序内部出问题导致的容器连续崩溃出现的问题。最后反馈到 Pod 状态上是 CrashLoopBackOff 状态。
演示容器运行中崩溃的情况有点难,不过好在我之前介绍 Go 服务自动采样的时候,做过一个镜像
以下内容引用我之前的文章:Go 服务进行自动采样性能分析的方案设计与实现
我做了个docker 镜像方便进行试验,镜像已经上传到了Docker Hub上,大家感兴趣的可以Down下来自己在电脑上快速试验一下。
通过以下命令即可快速体验。
docker run --name go-profile-demo -v /tmp:/tmp -p 10030:80 --rm -d kevinyan001/go-profiling
容器里Go服务提供的路由如下
所以我们把上面的 deployment Pod 模版里的镜像换成这个 kevinyan001/go-profiling,再通过提供的路由手动制造 OOM,来故意制造容器崩溃的情况。
修改Pod 使用的容器镜像
#执行 kubectl apply -f deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:name: my-go-app
spec:replicas: 2selector:matchLabels:app: go-apptemplate:metadata:labels:app: go-appspec:containers:- name: go-app-containerimage: kevinyan001/go-profiling:latestresources:limits:memory: "200Mi"cpu: "50m"
创建个 SVC 让Pod能接受外部流量
#执行 kubectl apply -f service.yaml
apiVersion: v1
kind: Service
metadata:name: app-service
spec:type: NodePortselector:app: go-appports:- name: httpprotocol: TCPnodePort: 30080port: 80targetPort: 80
程序中提供的路由如下:
访问 http://127.0.0.1:30080/1gb-slice 让容器内存溢出,因为 Deployment 会重启崩溃的 Pod,所以这里非常考验手速:) 估计狂点一分钟,Deployment 就放弃治疗休息会儿再重启 Pod,这时 Pod 的状态成功变成了:
➜ kubectl get pods
NAME READY STATUS RESTARTS AGE
my-go-app-598f697676-f5jfp 0/1 CrashLoopBackOff 2 (18s ago) 5m37s
my-go-app-598f697676-tps7n 0/1 CrashLoopBackOff 2 (23s ago) 5m35s
这个时候我们使用 kubectl describe pod 看崩溃 Pod 的详细信息,会看到容器内程序返回的错误码
➜ kubectl describe pod my-go-app-598f697676-tps7n
Name: my-go-app-598f697676-tps7n
Namespace: defaultPort: 3000/TCPHost Port: 0/TCPState: RunningStarted: Sun, 20 Mar 2022 16:09:29 +0800Last State: TerminatedReason: ErrorExit Code: 137Started: Sun, 20 Mar 2022 16:08:56 +0800Finished: Sun, 20 Mar 2022 16:09:05 +0800
不过要深入排查 Pod 内容器的问题,需要另一个命令 kubectl logs {pod-name} 的协助。
kubectl logs my-go-app-598f697676-tps7n
如果恰巧这个 Pod 被重启了,查不出来任何东西,可以通过增加 — previous 参数选项,查看之前容器的日志。
kubectl logs my-go-app-598f697676-tps7n --previous
容器被驱逐
首先声明,这个问题研发解决不了,但是你发挥一下自己YY的能力:当群里报警、运维@你赶紧看的时候,你来个反杀,告诉他资源不够了赶紧扩容,是不是能装到^_^…
扯远了,现在回正题。集群里资源紧张的时候,K8s 会优先驱逐优先级低的 Pod,被驱逐的 Pod 的状态会是 Evicted,这个情况没办法在本地模拟,贴一个在公司K8s集群遇到这种情况的截图。
kubectl get pod 查看Pod状态
kubectl get pod
上图可以看到有一个Pod 的状态变成了 Evicted。
再来用describe 看下详细信息
kubectl describe pod 查看Pod 的详细信息和事件记录
执行kubectl describe pod
不好意思,历史久远,上面的图太模糊了,图中的Message 一栏里有给出如下信息:
Status: Faild
Reason: Evicted
Message: The node wan low on resource: xxx-storage. Container xxx using xxxKi,
which exceeds its request of ....
总结
一般来说,大多数常见的部署失败都可以使用这些命令进行排查和调试:
kubectl get pods
kubectl describe pod <podname>
kubectl logs <podname>
kubectl logs <podname> --previous
当然,有的时候想看 Pod 的配置信息,还可以使用
kubectl get pod <podname> -o=yaml
,验证一下Pod的配置是不是跟我们提交上去的一样,以及一些其他的额外信息。
get 和 describe 这两个命令除了能看 Pod 的状态和信息记录外,也能看其他资源的状态和信息。
kubectl get pod|svc|deploy|sts|configmap <xxx-name>kubectl describe pod|svc|deploy|sts|configmap <xxx-name>
这些就留给大家后面自己体验吧。为了方便大家在本地试验,公众号回复【k8s】能找到今天用的各种YAML的模版,感兴趣的可以动手实践起来。
- END -
扫码关注公众号「网管叨bi叨」
给网管个星标,第一时间吸我的知识
想在研发群里装?先学会这几个排查K8s问题的办法相关推荐
- 技术群里装偶遇撒狗粮?手起刀落人抬走!
纯属搞笑,重要信息已打码. 我不去当侦探都亏了. 事情是这样的,某个技术群里新来了不少小伙伴,就有人吆喝群主发红包. 其中某男一看,哟!这美女的头像咋跟我是情头啊!这是缘分啊兄弟们,这还了得. 于是就 ...
- 微信群里的这几类人,真让人想打他
昨晚被刚加的微信群疯狂轰炸 被吵醒之后再也没睡着 翻了一下微信列表 我竟然已经有四五十个微信群 于是我开始反思为什么要加这么多群? 难道是因为爱吗? 说出来我自己都不信 一群陌生人能凑在一起 无非是各 ...
- ##引言 写这一篇文章的缘由,是因为群里的一次讨论。一位即将毕业的同学说,“龙哥,毕业以后进入某某类型的公司好不好?” 于是,就这样,LZ给他叽里呱啦的上了...
引言 写这一篇文章的缘由,是因为群里的一次讨论.一位即将毕业的同学说,"龙哥,毕业以后进入某某类型的公司好不好?" 于是,就这样,LZ给他叽里呱啦的上了一课,后来就形成了这篇文章. ...
- 潜伏研发群一个月,我发现了程序员不为人知的秘密!这也太可爱了吧
文章来源于网易号丨InfoQ:Q妹,文章未删改 在公司研发群潜伏了一个月后,Q妹发现了一些不为人知的秘密,这群程序员着实让人上头- (一) 他们没有<吐槽大会>中码农庞博 那般能说会道,高 ...
- 十年前只想混一个Apache邮箱装逼,十年后却成了顶级项目创始人
\u003cp\u003e正式采访 Luke 之前,我们跟他进行了一次电话上的长谈.在那次长谈中,Luke 谈到了他对中国开源三个阶段的看法,谈到了浙大中控的经历对他的影响,谈到了他现在的人生只有工作 ...
- 5年亏炒股指亏1600万,最后才知道Q群里除了他全是骗子
被骗1600万,可能很多人都可以理解,但是连续被骗5年都不发现,确实是一件很奇葩的事情,但再奇葩的事情在现实生活中也有发生. 男子被美女蛊惑,5年超股指亏损1600万,群里竟然只有他一个人客户. 所谓 ...
- Vue 脱坑记 - 查漏补缺(汇总下群里高频询问的xxx及给出不靠谱的解决方案)
前言 文章内容覆盖范围,芝麻绿豆的破问题都有,不止于vue; 给出的是方案,但不是手把手一字一句的给你说十万个为什么! 有三类人不适合此篇文章: "喜欢站在道德制高点的圣母婊" - ...
- Vue 汇总下群里高频询问的xxx及给出不靠谱的解决方案
前言 文章内容覆盖范围,芝麻绿豆的破问题都有,不止于vue; 给出的是方案,但不是手把手一字一句的给你说十万个为什么! 有三类人不适合此篇文章: "喜欢站在道德制高点的圣母婊" - ...
- 潜伏研发群一个月,我发现了程序员不为人知的秘密
一 在公司研发群潜伏了一个月后 Q妹发现了一些不为人知的秘密 这群程序员着实让人上头- 他们没有<吐槽大会>中码农庞博 那般能说会道,高大帅气 相反,有着鲜明个性且具有辨识度的他们 是一群 ...
最新文章
- 1 OC 对象的本质(一个NSObject 对象占用的内存大小)
- web存储中cookie、session区别
- @2021高考生,用 Python 分析专业“钱景”
- 关于Entity Framework自动关联查询与自动关联更新导航属性对应的实体注意事项说明...
- 三种常见嵌入式设备通信协议
- 【Python】字符串和变量拼接的写法
- LeetCode刷题实战(2):Add Two Numbers
- 栈结构功能实现的伪码展示【数据结构F】
- 如果没准备这些面试题,找工作还是缓一缓吧
- Apache Kudu 加速对频繁更新数据的分析
- excel表中怎么插入visio_快速制作组织架构图,还在用Visio就out了,Excel简单三步搞定...
- 基于JAVA+SpringBoot+Vue+Mybatis+MYSQL的小程序医院预约挂号系统
- 单例对象会被jvm的gc时回收吗_【PHP设计模式】单例模式
- 第三方服务-阿里云视频点播服务java服务端SDK相关代码
- hdu1754 I hate it线段树模板 区间最值查询
- struts2的ActionSupport
- 流程引擎之Flowable简介
- 一个拥抱ARVR热点机会
- 边缘计算与深度学习综述
- DSP28335学习笔记-产生PWM波 ——从应用角度出发