📌持续分享:云原生·Kubernetes·DevOps·AIOps·AI智能体等
欢迎订阅公众号文章👇👇
🔥一起学习云原生、AI等技术体系,持续提升架构与运维实战能力。
点击如下名片,即可关注公众号↓↓↓正文如下👇👇
做 Kubernetes 运维的人,几乎都遇到过这个状态:
CrashLoopBackOff尤其是线上业务突然报警的时候。
监控开始疯狂告警:
服务不可用
接口超时
Pod异常重启
容器连续崩溃你赶紧登录集群:
kubectl get pod -A结果发现:
NAMESPACE NAME READY STATUS RESTARTS AGE
prod user-service-6d7c6c7f8d-x2lmf 0/1 CrashLoopBackOff 43 15m
很多人第一反应:
是不是 K8s 坏了?
是不是节点挂了?
是不是 Deployment 有问题?
是不是得赶紧 delete pod?然后:
疯狂删 Pod;
疯狂 rollout restart;
疯狂重启节点;结果:
问题不仅没解决,
业务还彻底炸了。其实:CrashLoopBackOff 从来都不是根因。
它只是 Kubernetes 告诉你:你的容器一直在崩,我已经重启很多次了。
真正的问题:一定在容器内部。
今天这篇文章,直接按照生产环境真实排障流程,彻底讲透:
CrashLoopBackOff 的定位思路、
经典报错、
日志分析、
排查命令、
线上处理方法。这篇内容, 基本就是 Kubernetes 运维日常排障实战。
很多人会误以为:
Pod 没启动成功。
其实不是。
大多数情况下:
Pod 已经创建成功了。
只是:
容器里的业务程序反复崩溃。
整个流程其实是:
Pod 创建成功
↓
容器启动
↓
业务程序启动失败
↓
主进程退出
↓
kubelet 自动重启容器
↓
再次失败
↓
循环重启
↓
进入 CrashLoopBackOff注意:
这里真正崩的:不是 Kubernetes。
而是:你的业务程序。
真正线上排查,千万不要一上来:
kubectl delete pod因为:
你连问题都没看, Pod 就被删了。
日志也可能直接丢失。
正确顺序应该是:
1、先看 Pod 状态
2、再看 describe 事件
3、再看容器日志
4、最后进入容器排查这个顺序非常关键。
先执行:
kubectl get pod -A例如:
NAMESPACE NAME READY STATUS RESTARTS AGE
prod order-api-7d8f9f6f4-2abcx 0/1 CrashLoopBackOff 15 8m重点看几个字段:
字段 | 含义 |
|---|---|
READY | 容器是否正常 |
STATUS | 当前状态 |
RESTARTS | 重启次数 |
AGE | Pod 存活时间 |
如果:
RESTARTS 很高说明:
容器一直在反复崩溃。
这是最重要的一步。
很多问题:
describe 已经直接告诉你了。
执行:
kubectl describe pod order-api-7d8f9f6f4-2abcx重点看:
Events:这里几乎就是:
Kubernetes 的“事故现场”。
下面看几个生产环境最经典的报错。
线上最常见。
describe 里通常会看到:
Last State: Terminated
Reason: OOMKilled
Exit Code: 137这表示:
容器内存超限, 被 Linux OOM Killer 强制杀死。
例如:
你给 Pod 配置:
resources:
limits:
memory: "512Mi"但:
Java JVM 参数:
-Xmx1024m
程序实际需要:
1G 内存。
结果:
Linux 直接 kill。
执行:
kubectl top pod或者:
kubectl describe pod通常都能看到:
OOMKilled最常见方法:
1、增加内存 limit
limits:
memory: 2Gi
2、降低 JVM 内存
例如:
-Xms256m -Xmx512m
3、检查内存泄漏
很多时候:代码本身存在:
对象泄漏;
缓存未释放;
线程堆积;很多线上事故, 其实不是程序崩。
而是:探针配置错了。
describe 通常会看到:
Liveness probe failed:
Get http://10.244.1.15:8080/healthz:
dial tcp 10.244.1.15:8080:
connect: connection refused随后:
Container xxx failed liveness probe, will be restarted意思是:
Kubernetes 认为:你的程序已经死了。
于是:强制重启容器。
例如:
程序启动需要:
90秒。
但:
探针配置:
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 55秒后:
接口还没起来。
K8s 直接判定:
服务异常。
然后:
kill 掉容器。
于是:
无限循环。
先看:
kubectl logs pod-name确认:
程序到底有没有真正启动成功。
然后检查:
initialDelaySeconds
timeoutSeconds
failureThreshold是否合理。
这是最容易忽略的问题。
例如:
SpringBoot YAML 配置错误。
日志通常会看到:
Failed to load property source或者:
mapping values are not allowed here再比如:
Nginx 配置错误:
nginx: [emerg] unexpected "}"程序会:
启动即退出。
查看 ConfigMap:
kubectl get cm查看具体配置:
kubectl describe cm app-config进入容器确认:
kubectl exec -it pod-name -- sh查看:
cat /app/config/application.yml微服务最经典问题。
日志通常会出现:
Communications link failure或者:
Connection refused还有:
Unknown host mysql-service本质一般是:
数据库没启动;
Service 名称错误;
DNS 解析失败;
账号密码错误;
网络不通。kubectl get svc进入容器:
kubectl exec -it pod-name -- sh执行:
nslookup mysql-servicetelnet mysql-service 3306或者:
nc -zv mysql-service 3306日志通常会看到:
exec: "javaa": executable file not found或者:
permission denied很多时候:
Dockerfile 写错了。
例如:
CMD ["javaa","-jar","app.jar"]少写一个字母。
容器直接起不来。
查看 Deployment:
kubectl get deploy app -o yaml重点检查:
command:
args:
image:是否正确。
这个线上也特别多。
节点磁盘满以后:
日志无法写入; 临时文件创建失败;
程序可能直接退出。
describe 经常出现:
Evicted
The node had condition: [DiskPressure]查看节点磁盘:
df -h查看镜像占用:
docker system df或者 containerd:
crictl images很多人不知道这个命令。
但它极其重要。
因为:
容器如果已经重启。
当前日志:
可能已经是“新容器”。
真正崩溃日志:
在上一次容器里。
所以:
一定要执行:
kubectl logs --previous pod-name这个命令:
能解决至少一半问题。
真正线上排障时, 推荐固定按照下面顺序:
kubectl get pod -A确认异常 Pod。
kubectl describe pod pod-name查看 Events。
kubectl logs pod-name查看当前日志。
kubectl logs --previous pod-name查看崩溃日志。
kubectl exec -it pod-name -- sh进入容器排查。
很多人:
Pod 一挂。
直接:
kubectl delete pod结果:
日志没了; 现场没了; 问题没了线索。
这是非常危险的习惯。
很多崩溃信息:
只存在上一轮容器里。
不看:
--previous你永远定位不到根因。
其实:
绝大多数 CrashLoopBackOff。
根因都是:
程序自身问题。
一定记住:
CrashLoopBackOff 不是故障原因。
它只是:
Kubernetes 告诉你:
“你的容器一直在崩,我正在不断尝试重启。”真正的问题:
一定在:
程序、 配置、 资源、 依赖、 网络、 探针。
最后送大家一句线上排障口诀:
先 describe
再 logs
最后 previous
实在不行再 exec
点击如下名片,即可关注公众号↓↓↓
本文分享自 DevOps和k8s全栈技术 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!