容器应用启动失败故障排查实战指南

一、问题概述

容器应用启动失败是 Kubernetes 和 Docker 环境中最常见的运维问题之一。本文系统梳理容器启动失败的各类场景,提供完整的排查思路和解决方案。

二、常见故障现象

2.1 容器状态异常

1
2
3
4
5
6
7
8
9
10
# 查看容器状态
kubectl get pods -n <namespace>
docker ps -a

# 典型异常状态:
# - CrashLoopBackOff: 容器反复重启
# - Error: 容器启动失败
# - ImagePullBackOff: 镜像拉取失败
# - CreateContainerConfigError: 容器配置错误
# - ContainerCreating: 长时间卡在创建中

2.2 关键诊断命令

1
2
3
4
5
6
7
8
9
10
11
# 查看容器详细信息
kubectl describe pod <pod-name> -n <namespace>
docker inspect <container-id>

# 查看容器日志
kubectl logs <pod-name> -n <namespace> [--previous]
docker logs <container-id>

# 进入容器调试
kubectl exec -it <pod-name> -n <namespace> -- /bin/sh
docker exec -it <container-id> /bin/sh

三、故障分类与排查

3.1 镜像拉取失败 (ImagePullBackOff)

症状:Pod 状态显示 ImagePullBackOff 或 ErrImagePull

排查步骤

1
2
3
4
5
6
7
8
9
10
11
# 1. 查看具体错误信息
kubectl describe pod <pod-name>

# 2. 检查镜像名称和标签
# 常见问题:镜像名拼写错误、标签不存在

# 3. 检查镜像仓库认证
kubectl get secret <registry-secret> -n <namespace> -o yaml

# 4. 手动拉取测试
docker pull <image-name>:<tag>

解决方案

1
2
3
4
5
6
7
8
9
10
11
12
13
# 配置镜像拉取密钥
apiVersion: v1
kind: Secret
metadata:
name: regcred
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: <base64-encoded-config>

# 在 Pod 中引用
spec:
imagePullSecrets:
- name: regcred

3.2 容器反复重启 (CrashLoopBackOff)

症状:容器启动后立即退出,RESTARTS 计数持续增加

排查步骤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 1. 查看容器退出码
kubectl get pod <pod-name> -o jsonpath='{.status.containerStatuses[*].lastState}'

# 常见退出码含义:
# 0: 正常退出
# 1: 应用程序错误
# 127: 命令未找到
# 137: 被 OOMKilled (128+9)
# 143: 被 SIGTERM 终止 (128+15)

# 2. 查看上次崩溃的日志
kubectl logs <pod-name> --previous

# 3. 检查资源限制
kubectl describe pod <pod-name> | grep -A 5 "Limits"

常见原因与解决

退出码 可能原因 解决方案
1 应用配置错误、依赖缺失 检查配置文件、环境变量
127 ENTRYPOINT 命令不存在 检查 Dockerfile 或 command 配置
137 内存不足被 OOMKilled 增加 memory limit
1 端口已被占用 检查端口冲突

3.3 健康检查失败 (Liveness/Readiness Probe)

症状:容器运行但被 Kubernetes 不断重启

排查步骤

1
2
3
4
5
6
# 查看探针配置
kubectl get pod <pod-name> -o jsonpath='{.spec.containers[*].livenessProbe}'
kubectl get pod <pod-name> -o jsonpath='{.spec.containers[*].readinessProbe}'

# 查看事件日志
kubectl get events --field-selector involvedObject.name=<pod-name>

优化建议

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 30 # 给应用足够启动时间
periodSeconds: 10 # 检查间隔
timeoutSeconds: 5 # 超时时间
failureThreshold: 3 # 失败次数阈值

readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
successThreshold: 1
failureThreshold: 3

3.4 配置挂载失败

症状:CreateContainerConfigError 或 MountVolume.SetUp failed

排查步骤

1
2
3
4
5
6
7
8
9
10
# 查看挂载配置
kubectl describe pod <pod-name> | grep -A 10 "Mounts"

# 检查 ConfigMap/Secret 是否存在
kubectl get configmap <name> -n <namespace>
kubectl get secret <name> -n <namespace>

# 检查 PV/PVC 状态
kubectl get pvc -n <namespace>
kubectl get pv

常见问题

  1. ConfigMap/Secret 不存在:创建后再部署
  2. 挂载路径冲突:检查多个卷是否挂载到同一路径
  3. 权限问题:检查 fsGroup 和 securityContext
1
2
3
4
securityContext:
fsGroup: 1000
runAsUser: 1000
runAsGroup: 1000

3.5 资源不足导致启动失败

症状:Pending 状态或 OOMKilled

排查步骤

1
2
3
4
5
6
7
8
9
10
# 查看节点资源
kubectl top nodes
kubectl describe node <node-name>

# 查看资源配额
kubectl describe quota -n <namespace>
kubectl describe limitrange -n <namespace>

# 查看 Pod 资源请求
kubectl get pod <pod-name> -o jsonpath='{.spec.containers[*].resources}'

解决方案

1
2
3
4
5
6
7
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"

3.6 网络相关问题

症状:容器启动但无法连接外部服务

排查步骤

1
2
3
4
5
6
7
8
9
10
11
12
# 检查 DNS 解析
kubectl exec <pod-name> -- nslookup <service-name>

# 检查网络策略
kubectl get networkpolicy -n <namespace>

# 检查 Service 配置
kubectl get svc -n <namespace>
kubectl describe svc <service-name>

# 测试网络连通性
kubectl exec <pod-name> -- curl -v <target-service>

3.7 依赖服务未就绪

症状:应用启动时连接数据库/缓存失败

解决方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 使用 initContainer 等待依赖
initContainers:
- name: wait-for-db
image: busybox
command: ['sh', '-c', 'until nc -z database 5432; do echo waiting; sleep 2; done']

# 或使用启动脚本
command: ["/bin/sh", "-c"]
args:
- |
echo "Waiting for database..."
until pg_isready -h $DB_HOST -p $DB_PORT; do
sleep 2
done
echo "Database ready, starting app..."
exec /app/start.sh

四、排查流程图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
容器启动失败

├─→ 状态:ImagePullBackOff
│ └─→ 检查镜像名、标签、认证

├─→ 状态:CrashLoopBackOff
│ ├─→ 查看退出码
│ ├─→ 检查日志 (含 previous)
│ └─→ 检查资源限制

├─→ 状态:CreateContainerConfigError
│ └─→ 检查 ConfigMap/Secret/Volume

├─→ 状态:Pending
│ ├─→ 检查节点资源
│ ├─→ 检查调度约束
│ └─→ 检查配额限制

└─→ 状态:Running 但服务异常
├─→ 检查健康探针
├─→ 检查网络连通性
└─→ 检查依赖服务

五、实战案例

案例 1:Java 应用 OOMKilled

问题:Spring Boot 应用频繁重启,退出码 137

排查

1
2
kubectl describe pod app-xyz | grep -i "oom"
# 输出:Reason: OOMKilled

解决

1
2
3
4
5
6
7
# 调整 JVM 参数和容器内存限制
resources:
limits:
memory: "1Gi"
env:
- name: JAVA_OPTS
value: "-Xmx512m -Xms256m"

案例 2:配置热更新导致重启

问题:ConfigMap 更新后应用异常

解决:使用子路径挂载避免自动更新,或实现配置热加载

1
2
3
4
5
6
7
8
# 方式 1:禁用自动更新
volumeMounts:
- name: config
mountPath: /app/config.json
subPath: config.json # 子路径挂载不会自动更新

# 方式 2:应用支持热加载
# 监听配置文件变化,无需重启

六、预防建议

  1. 设置合理的资源限制:基于实际压测结果配置
  2. 完善健康检查:区分 liveness 和 readiness
  3. 优雅关闭处理:捕获 SIGTERM,完成正在处理的请求
  4. 日志标准化:输出到 stdout/stderr,便于收集
  5. 配置版本管理:ConfigMap/Secret 变更先灰度
  6. 依赖检查:启动前验证关键依赖可用

七、总结

容器启动失败排查需要系统性思维,按照”状态识别 → 日志分析 → 配置检查 → 资源验证 → 网络诊断”的流程逐步排查。建立标准化的排查清单和监控告警,可以大幅缩短故障恢复时间。