引言
生产环境的Kubernetes集群运行久了,各种奇奇怪怪的问题都会冒出来。本文挑选10个日常运维中最高频的故障场景,从现象到根因到修复,手把手过一遍。每个案例都给出完整的排查路径、关键命令和验证方法,照着做能解决大部分常见问题。
集群环境说明:本文以Kubernetes 1.28版本为例,节点系统为CentOS 7.9或Ubuntu 22.04,容器运行时为containerd 1.7.x。多节点集群场景,包含3个控制面节点和若干工作节点。
问题一:Pod一直处于Pending状态
问题背景
Pod创建后一直卡在Pending状态,既不调度到节点,也不报错。这种情况在生产环境中很常见,新手往往不知道该从哪里入手。
适用场景
- 新建应用部署后发现Pod起不来
- 节点扩容后Pod仍然无法调度
- 资源紧张时应用无法重新调度
核心知识点
Pending状态的Pod意味着调度器无法将它分配到任何节点。常见原因有三类:资源不足、亲和性/污点限制、存储卷挂载等待。
实战步骤
第一步:查看Pod状态详情
kubectl get pod -n <namespace> <pod-name> -o wide
kubectl describe pod -n <namespace> <pod-name>
describe输出中的Events部分最关键,看最后几行事件记录。
典型现象示例:
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 3m default-scheduler 0/5 nodes are available: 1 Insufficient cpu, 3 node(s) had taints that the pod didn't tolerate, 1 resource allocation disabled.
第二步:检查集群资源状态
kubectl top nodes
kubectl describe node <node-name>
查看每个节点的CPU、内存、存储使用情况。特别注意Allocatable字段,它才是实际可调度的资源量。
kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.allocatable.cpu}{" CPU\t"}{.status.allocatable.memory}{"\t"}{.status.conditions[?(@.type=="Ready")].status}{"\n"}{end}'
第三步:检查污点和容忍
kubectl get node -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints
如果节点有污点,Pod没有对应的容忍配置就不会调度。
第四步:检查存储类和PVC状态
kubectl get pvc -n <namespace>
kubectl describe pvc <pvc-name>
挂载迟迟不能完成的PVC会阻塞Pod调度。
根因定位与修复
原因一:资源不足
CPU或内存不够时,调度器找不到满足条件的节点。
修复方案:要么扩容节点,要么降低Pod资源请求值,要么清理现有低优先级Pod。
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
原因二:节点污点限制
kubectl taint node <node-name> key=value:NoSchedule-
临时去掉污点让Pod调度进去,但生产环境建议通过添加容忍来解决问题。
tolerations:
- key: "key"
operator: "Exists"
effect: "NoSchedule"
原因三:PVC挂载超时
检查StorageClass是否存在、PV是否正确绑定、存储后端是否正常。
验证方式
kubectl get pod -n <namespace> <pod-name>
# 状态变为 Running 表示调度成功
# 继续观察是否进入 Running 状态
kubectl logs -n <namespace> <pod-name> --previous
风险提醒
修改污点会影响其他Pod的调度行为。移除污点前确认该节点是否专门用于特定 workloads。
回滚方案
如果是通过修改Pod YAML解决的问题,直接重新apply原配置即可。如果涉及节点污点修改,用taint命令恢复或打标签替代。
问题二:Pod一直处于CrashLoopBackOff状态
问题背景
Pod启动后立刻崩溃,反复重启,始终无法正常运行。这是线上最常见的Pod故障之一,往往意味着应用本身有问题。
适用场景
- 应用刚部署就报错
- 更新版本后突然无法启动
- 节点异常后应用无法恢复
核心知识点
CrashLoopBackOff是Kubernetes报告容器反复崩溃的状态。容器退出码非0,kubelet会按指数退避策略重启,每次等待时间翻倍:10秒、20秒、40秒…最长5分钟。
实战步骤
第一步:获取容器退出码和重启原因
kubectl get pod -n <namespace> <pod-name> -o jsonpath='{.status.containerStatuses[*]}{"\n"}'
重点关注exitCode、lastState、restartCount字段。
kubectl describe pod -n <namespace> <pod-name> | grep -A 20 "Last State"
第二步:查看应用日志
kubectl logs -n <namespace> <pod-name> --previous
–previous参数拿的是上次崩溃前的容器日志,非常关键。
第三步:进入容器内部排查
kubectl exec -it -n <namespace> <pod-name> -- /bin/sh
如果Pod内只有一个容器,可以省略-c参数。
第四步:检查容器启动命令和环境变量
kubectl get pod -n <namespace> <pod-name> -o jsonpath='{.spec.containers[*].command}'
kubectl get pod -n <namespace> <pod-name> -o jsonpath='{.spec.containers[*].env}'
第五步:检查健康检查配置
kubectl describe pod -n <namespace> <pod-name> | grep -A 5 "Liveness"
kubectl describe pod -n <namespace> <pod-name> | grep -A 5 "Readiness"
探针失败次数过多也会导致容器被认为不健康。
根因定位与修复
原因一:应用启动命令错误
检查command和args是否正确,镜像ENTRYPOINT和CMD是否有冲突。
command: ["/bin/sh", "-c"]
args: ["node app.js && sleep infinity"]
原因二:依赖服务不可达
应用启动时需要连接数据库、Redis等,如果连接失败会直接退出。
# 在Pod内测试连接
kubectl exec -it -n <namespace> <pod-name> -- nc -zv <service-name> 3306
kubectl exec -it -n <namespace> <pod-name> -- nslookup <service-name>
原因三:配置文件错误或缺失
挂载的ConfigMap或Secret没有正确创建。
kubectl get configmap -n <namespace> <configmap-name>
kubectl get secret -n <namespace> <secret-name>
原因四:权限问题
容器内进程没有足够权限读写挂载的目录。
kubectl exec -it -n <namespace> <pod-name> -- ls -la /data
kubectl exec -it -n <namespace> <pod-name> -- id
原因五:健康检查过早杀死进程
startupProbe配置不当会导致容器还在启动中就被杀掉。
startupProbe:
httpGet:
path: /healthz
port: 8080
failureThreshold: 30
periodSeconds: 10
验证方式
# 观察重启次数是否归零
kubectl get pod -n <namespace> <pod-name> -w
# 确认容器运行稳定
kubectl exec -it -n <namespace> <pod-name> -- ps aux
风险提醒
容器内的数据丢失无法恢复(无持久化存储时)。修复前先确认应用是否支持多次重启不丢数据。
回滚方案
如果是版本更新导致的问题,回滚镜像版本:
kubectl rollout undo deployment <deployment-name> -n <namespace>
kubectl rollout status deployment <deployment-name> -n <namespace>
问题三:Pod无法通过Service访问
问题背景
Pod之间无法通过Service名称通信,curl或wget都超时。应用明明运行正常,但就是连不上。
适用场景
- 应用间调用失败
- Ingress进来流量无法到达后端Pod
- 集群内部DNS解析异常
核心知识点
Service访问涉及三个环节:DNS解析、kube-proxy转发、Pod监听。任何一个环节出问题都会导致访问失败。
实战步骤
第一步:确认Pod和Service都存在
kubectl get pod -n <namespace> -o wide | grep <pod-name>
kubectl get svc -n <namespace> | grep <svc-name>
第二步:从同命名空间其他Pod测试访问
kubectl exec -it -n <namespace> <source-pod> -- curl -v http://<svc-name>:<port>/
第三步:检查Endpoints是否正常
kubectl get endpoints -n <namespace> <svc-name>
Endpoints为空说明selector没有匹配到任何Pod。
kubectl describe svc -n <namespace> <svc-name>
第四步:检查kube-proxy是否正常运行
kubectl get pod -n kube-system -o wide | grep kube-proxy
kubectl logs -n kube-system <kube-proxy-pod> --tail=50
第五步:测试ClusterIP连通性
kubectl exec -it -n <namespace> <source-pod> -- curl -v http://<cluster-ip>:<port>/
第六步:检查iptables规则
kubectl exec -it <node-with-kube-proxy> -- iptables -L -t nat | grep <svc-name>
根因定位与修复
原因一:Endpoints为空
selector配置错误或匹配到的Pod还没ready。
spec:
selector:
app: my-app # 检查标签是否匹配
修复:确认Pod标签和Service selector一致。
kubectl get pod -n <namespace> --show-labels | grep <label>
原因二:kube-proxy异常
kube-proxy崩溃或watch资源出错。
systemctl status kube-proxy
journalctl -u kube-proxy -n 50
重启kube-proxy:
kubectl delete pod -n kube-system <kube-proxy-pod>
原因三:DNS解析失败
kubectl exec -it -n <namespace> <pod-name> -- nslookup <svc-name>
kubectl exec -it -n <namespace> <pod-name> -- cat /etc/resolv.conf
CoreDNS相关问题:
kubectl get pod -n kube-system -o wide | grep dns
kubectl logs -n kube-system <coredns-pod> --tail=50
原因四:Pod网络策略阻止
kubectl get networkpolicy -n <namespace>
kubectl describe networkpolicy <policy-name>
验证方式
# 多次测试确认稳定
for i in {1..10}; do kubectl exec -it -n <namespace> <source-pod> -- curl -s http://<svc-name>:<port>/healthz && echo " OK" || echo " FAIL"; done
# 检查sessionAffinity是否生效
kubectl exec -it -n <namespace> <source-pod> -- ss -s
风险提醒
修改kube-proxy重启会影响所有Service流量,生产环境滚动操作。NetworkPolicy配置错误可能阻断所有流量。
回滚方案
kube-proxy是DaemonSet,不支持rollout undo。如果需要回滚配置,手动恢复之前的ConfigMap或重启kube-proxy Pod:
# 重启kube-proxy Pod触发配置重新加载
kubectl delete pod -n kube-system -l k8s-app=kube-proxy
# 或者恢复之前的网络策略yaml
kubectl apply -f <old-networkpolicy.yaml>
问题四:镜像拉取失败
问题背景
Pod调度到节点后,镜像下载失败,容器始终无法启动。这种问题在配置了私有镜像仓库但没做镜像拉取授权时特别常见。
适用场景
- 部署新应用时报错ImagePullBackOff
- 节点重启后原地重建Pod失败
- 更换镜像仓库地址后无法拉取
核心知识点
ImagePullBackOff表示kubelet反复尝试拉取镜像但持续失败。常见原因包括:镜像不存在、认证失败、网络不通、registry白名单限制。
实战步骤
第一步:查看Pod事件
kubectl describe pod -n <namespace> <pod-name> | grep -A 10 "Events"
典型错误信息:
Failed to pull image "registry.example.com/app:latest": rpc error: code = Unknown desc = Error: Not Found
Failed to pull image "registry.example.com/app:latest": rpc error: code = Unknown desc = unauthorized: authentication required
Failed to pull image "registry.example.com/app:latest": rpc error: code = Unknown desc = http: server gave HTTP response to HTTPS client
第二步:确认镜像确实存在
# 在有网络权限的节点上直接测试
ctr -n k8s.io images ls | grep <image-name>
crictl images | grep <image-name>
第三步:检查镜像仓库认证信息
kubectl get secret -n <namespace> | grep docker
kubectl describe secret <registry-secret-name>
Secret类型必须是kubernetes.io/dockerconfigjson。
第四步:检查节点hosts和证书
cat /etc/hosts | grep <registry-domain>
ls -la /etc/docker/certs.d/<registry-domain>/
第五步:在节点上手动拉取测试
crictl pull <registry>/<image>:<tag>
ctr -n k8s.io images pull <registry>/<image>:<tag>
根因定位与修复
原因一:镜像不存在或tag错误
# 确认正确镜像地址
docker pull <registry>/<image>:<tag>
修复:更正镜像地址或tag。
原因二:认证凭据错误
创建或更新Secret:
kubectl create secret docker-registry <secret-name> \
--docker-server=<registry> \
--docker-username=<user> \
--docker-password=<password> \
--docker-email=<email> \
-n <namespace>
PodSpec引用Secret:
spec:
imagePullSecrets:
- name: <secret-name>
原因三:私有仓库证书问题
配置私有仓库CA证书。对于containerd 1.7.x版本,需要在/etc/containerd/config.toml中配置:
# 编辑containerd配置
vi /etc/containerd/config.toml
# 在 [plugins."io.containerd.grpc.v1.cri".registry] 下添加:
[plugins."io.containerd.grpc.v1.cri".registry.configs."<registry>".tls]
ca_file = "/etc/ssl/certs/<ca>.crt"
或者在节点上直接安装CA证书(适用于所有容器运行时):
# Debian/Ubuntu
cp ca.crt /usr/local/share/ca-certificates/<registry>.crt
update-ca-certificates
# RHEL/CentOS
cp ca.crt /etc/pki/ca-trust/source/anchors/<registry>.crt
update-ca-trust extract
配置修改后重启containerd:
systemctl restart containerd
原因四:网络访问限制
检查节点能否访问镜像仓库:
curl -I https://<registry>/v2/
验证方式
# 删除Pod触发重新调度
kubectl delete pod -n <namespace> <pod-name>
# 观察镜像拉取是否成功
kubectl get events -n <namespace> --sort-by='.lastTimestamp' | tail -20
风险提醒
删除Pod会影响业务连续性,确认有足够副本数再操作。私密凭据更新需要同步到所有使用它的命名空间。
回滚方案
恢复到之前可用的镜像版本:
spec:
containers:
- name: app
image: <registry>/<image>:<previous-tag>
问题五:Node NotReady
问题背景
集群中某个节点状态变成NotReady,kubelet无法正常汇报节点状态。该节点上的Pod不会调度新实例,但现有Pod是否受影响取决于情况。
适用场景
- 节点异常后无法管理
- 节点上应用无响应
- 集群容量异常减少
核心知识点
节点进入NotReady状态是因为kubelet在40秒内没有向API Server发送心跳。常见原因包括:kubelet进程挂了、系统资源耗尽、网络不通、证书过期。
实战步骤
第一步:确认节点状态
kubectl get nodes
kubectl describe node <node-name>
kubectl get node <node-name> -o jsonpath='{.status.conditions[*]}'
第二步:登录节点检查kubelet状态
systemctl status kubelet
journalctl -u kubelet -n 50 --no-pager
第三步:检查节点资源
free -h
df -h
top -bn1 | head -20
第四步:检查容器运行时
systemctl status containerd # 或 docker
systemctl status kubelet
第五步:检查证书是否过期
openssl x509 -in /var/lib/kubelet/pki/kubelet-client-current.pem -noout -dates
openssl x509 -in /var/lib/kubelet/pki/kubelet-server-current.pem -noout -dates
根因定位与修复
原因一:kubelet进程崩溃或被杀死
systemctl restart kubelet
systemctl enable kubelet
原因二:系统内存或磁盘耗尽
清理磁盘:
# 清理旧日志
journalctl --vacuum-size=500M
# 清理containerd未使用镜像
crictl rmi $(crictl images -q)
# 清理docker未使用镜像(如果使用docker运行时)
docker system prune -af
# 清理containerd旧镜像
# 先查看有哪些未使用的镜像
ctr -n k8s.io images ls -q | head -20
# 手动删除不需要的镜像(不要删除正在使用的)
ctr -n k8s.io images rm <image1> <image2> <image3>
# 清理/var/lib/kubelet目录下的临时文件
find /var/lib/kubelet -name "*.tmp" -delete
增加节点资源或调度更多Pod出去。
原因三:kubelet证书过期
自动轮转失败时需要手动更新。在故障节点上执行:
# 删除旧的kubelet证书和kubeconfig
rm -rf /var/lib/kubelet/pki/
rm -f /var/lib/kubelet/kubelet.conf
# 重启kubelet,kubelet会自动向API Server申请新证书
systemctl restart kubelet
# 确认证书已重新申请
ls -la /var/lib/kubelet/pki/
openssl x509 -in /var/lib/kubelet/pki/kubelet-client-current.pem -noout -dates
原因四:网络不通
ping <api-server-ip>
telnet <api-server-ip> 6443
curl -k https://<api-server-ip>:6443/healthz
检查节点防火墙:
systemctl status firewalld
iptables -L -n | grep 6443
原因五:containerd或docker异常
systemctl restart containerd
# 或
systemctl restart docker
验证方式
kubectl get node <node-name>
# 状态变为 Ready
# 确认节点可调度
kubectl describe node <node-name> | grep -A 5 "Allocatable"
# 确认Pod可以调度到该节点
kubectl describe node <node-name> | grep -A 5 "Taints"
风险提醒
重启kubelet会导致节点上Pod短暂网络中断。驱逐Pod会影响运行中的业务。操作前确认集群有足够冗余。
回滚方案
如果问题无法快速恢复,先隔离节点防止Pod继续调度:
kubectl cordon <node-name>
kubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data
等节点恢复后un cordon:
kubectl uncordon <node-name>
问题六:存储卷挂载异常
问题背景
Pod挂载的PVC一直处于Pending状态,或者Pod启动后无法正常读写数据目录。存储问题是生产环境中后果最严重的故障之一。
适用场景
- 数据库Pod无法启动
- 有状态应用部署失败
- 日志写入报错
核心知识点
Kubernetes存储涉及StorageClass、PersistentVolumeClaim、PersistentVolume、CSI驱动四个组件。任何一个环节断裂都会导致挂载失败。
实战步骤
第一步:检查PVC状态
kubectl get pvc -n <namespace>
kubectl describe pvc -n <namespace> <pvc-name>
Pending状态的PVC说明还没有找到合适的PV。
第二步:检查PV状态
kubectl get pv
kubectl describe pv <pv-name>
第三步:检查StorageClass
kubectl get storageclass
kubectl describe storageclass <sc-name>
第四步:检查CSI驱动状态
kubectl get pod -n kube-system | grep csi
kubectl logs -n kube-system <csi-driver-pod> --tail=50
第五步:检查节点挂载情况
# 在Pod所在节点执行
mount | grep <pv-name>
df -h | grep <mount-path>
根因定位与修复
原因一:StorageClass不存在
# 确认集群有哪些StorageClass
kubectl get storageclass
# 创建必要的StorageClass
kubectl apply -f - <<EOF
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: standard
provisioner: kubernetes.io/gce-pd
parameters:
type: pd-standard
replication-type: regional-pd
EOF
原因二:PVC绑定到错误的PV
# 检查绑定状态
kubectl get pvc -n <namespace> -o wide
# 删除重建PVC(数据会丢失)
kubectl delete pvc <pvc-name> -n <namespace>
原因三:CSI驱动问题
# 重启CSI驱动
kubectl delete pod -n kube-system <csi-driver-pod>
# 检查驱动版本兼容性
kubectl get csidrivers
原因四:节点无法访问存储后端
NFS、Ceph、GlusterFS等网络存储需要节点能连通。
showmount -e <nfs-server>
ceph -s
原因五:权限问题
# 检查FSGroup配置
kubectl get pod -n <namespace> <pod-name> -o jsonpath='{.spec.securityContext.fsGroup}'
kubectl get pod -n <namespace> <pod-name> -o jsonpath='{.spec.initContainers[*].securityContext}'
修改Pod安全上下文:
spec:
securityContext:
fsGroup: 2000
runAsUser: 1000
验证方式
# 进入Pod测试读写
kubectl exec -it -n <namespace> <pod-name> -- sh -c "echo test > /data/test.txt && cat /data/test.txt"
# 确认挂载点正常
kubectl exec -it -n <namespace> <pod-name> -- df -h | grep /data
# 检查文件权限
kubectl exec -it -n <namespace> <pod-name> -- ls -la /data
风险提醒
删除PVC会永久丢失数据。修改存储类不会影响已绑定的PV/VC。跨节点存储迁移需要先备份数据。
回滚方案
# 如果使用了错误的StorageClass,重新创建PVC指定正确的storageClassName
# 如果是权限问题,恢复安全上下文配置
# 如果是CSI问题,回滚CSI驱动版本
问题七:资源配额耗尽导致Pod被驱逐
问题背景
集群或命名空间的ResourceQuota/LimitRange限制被触发,Pod被强制终止。应用无预警崩溃,影响业务可用性。
适用场景
- 多个团队共用集群时资源竞争
- 资源限制配置过于严格
- 应用内存泄漏逐渐逼近限制
核心知识点
Kubernetes通过ResourceQuota限制命名空间总资源、LimitRange限制单个Pod/容器的资源请求。超出限制会触发Evicted状态。
实战步骤
第一步:确认Pod被驱逐
kubectl get pod -n <namespace> | grep Evicted
kubectl describe pod -n <namespace> <pod-name>
第二步:检查命名空间资源配额
kubectl get resourcequota -n <namespace>
kubectl describe resourcequota -n <namespace>
kubectl get limitrange -n <namespace>
kubectl describe limitrange -n <namespace>
第三步:检查实际资源使用
kubectl top pod -n <namespace>
kubectl top nodes
第四步:分析配额消耗
# 计算当前命名空间总资源
kubectl api-resources --verbs=list --namespaced -o wide | grep -i quota
根因定位与修复
原因一:内存Limit不足
Pod实际使用超过limits设置,被OOMKilled。
resources:
requests:
memory: "512Mi"
limits:
memory: "1Gi"
增加limits或优化应用内存使用。
原因二:命名空间总CPU配额耗尽
检查当前ResourceQuota:
kubectl get resourcequota -n <namespace> -o yaml
增加ResourceQuota:
apiVersion: v1
kind: ResourceQuota
metadata:
name: increase-quota
spec:
hard:
requests.cpu: "20"
requests.memory: 40Gi
limits.cpu: "40"
limits.memory: 80Gi
原因三:PVC数量超过限制
kubectl get pvc -n <namespace> | wc -l
删除不需要的PVC:
kubectl delete pvc <unused-pvc-name> -n <namespace>
原因四:Pod数量超过限制
apiVersion: v1
kind: ResourceQuota
metadata:
name: pod-quota
spec:
hard:
pods: "100"
验证方式
# 持续监控资源使用
watch -n 5 'kubectl top pod -n <namespace>'
# 确认Pod不再被驱逐
kubectl get events -n <namespace> --sort-by='.lastTimestamp' | grep -i evict
风险提醒
增加ResourceQuota需要集群层面有足够资源。删除PVC会丢失数据。修改LimitRange只影响新建的Pod。
回滚方案
# 恢复ResourceQuota到之前的值
kubectl apply -f <old-resourcequota.yaml>
# 如果是单个Pod问题,恢复limits配置
kubectl patch deployment <deployment-name> -n <namespace> --type merge -p '{"spec":{"template":{"spec":{"containers":[{"name":"app","resources":{"limits":{"memory":"512Mi"}}}]}}}}'
问题八:etcd集群故障
问题背景
etcd是Kubernetes的核心数据存储,集群状态全部保存在其中。etcd故障会导致API Server不可用,整个集群基本瘫痪。
适用场景
- 控制面节点异常
- 集群无法响应任何kubectl命令
- 证书验证失败
核心知识点
生产环境etcd应该至少3节点集群,容忍1节点故障。数据损坏、磁盘满、证书过期、网络分区是最常见的故障原因。
实战步骤
第一步:检查etcd服务状态
systemctl status etcd
journalctl -u etcd -n 50 --no-pager
第二步:检查etcd健康状态
ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
endpoint health
第三步:检查etcd成员状态
ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
member list -w table
第四步:检查磁盘空间
df -h /var/lib/etcd
du -sh /var/lib/etcd/*
第五步:检查证书
openssl x509 -in /etc/kubernetes/pki/etcd/server.crt -noout -dates
openssl x509 -in /etc/kubernetes/pki/etcd/peer.crt -noout -dates
根因定位与修复
原因一:磁盘空间不足
etcd数据目录所在磁盘占满会导致写入失败,集群不可写。
清理方案:
# 先检查wal目录大小
du -sh /var/lib/etcd/member/wal
# 执行defrag(不会丢数据)
ETCDCTL_API=3 etcdctl defrag --endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key
# 清理旧快照
find /var/lib/etcd -name "*.snap" -mtime +7 -delete
如果磁盘确实满了,需要扩容或清理其他文件。
原因二:etcd启动失败
# 查看详细错误
journalctl -u etcd -n 100 | grep -i error
# 检查配置文件
cat /etc/kubernetes/etcd.conf
原因三:节点证书过期
检查证书过期时间:
# 检查kubelet客户端证书
openssl x509 -in /var/lib/kubelet/pki/kubelet-client-current.pem -noout -dates
# 检查kubelet服务器证书
openssl x509 -in /var/lib/kubelet/pki/kubelet-server-current.pem -noout -dates
# 检查API Server访问证书(控制面节点)
openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -dates
如果证书已过期,在控制面节点上使用kubeadm重新生成。首先备份旧证书:
# 备份旧证书
mkdir -p /backup/pki-$(date +%Y%m%d)
cp -r /etc/kubernetes/pki/* /backup/pki-$(date +%Y%m%d)/
创建kubeadm配置文件指定证书有效期:
cat > /tmp/kubeadm-config.yaml <<EOF
apiVersion: kubeadm.k8s.io/v1beta3
kind: InitConfiguration
timeouts:
controlPlaneComponentManifestTimeout: 10m
---
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
etcd:
local:
serverCertSANs:
- <控制面节点IP>
peerCertSANs:
- <控制面节点IP>
certificates:
validityPeriod: 87600h # 10年
EOF
重新生成控制面证书:
# 重新生成apiserver证书
kubeadm init phase certs apiserver --config=/tmp/kubeadm-config.yaml
# 重新生成etcd证书
kubeadm init phase certs etcd-server --config=/tmp/kubeadm-config.yaml
kubeadm init phase certs etcd-peer --config=/tmp/kubeadm-config.yaml
kubeadm init phase certs etcd-client --config=/tmp/kubeadm-config.yaml
# 重启API Server
systemctl restart kubelet
确认证书时间已更新:
openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -dates
openssl x509 -in /etc/kubernetes/pki/etcd/server.crt -noout -dates
原因四:单个节点故障恢复
先确认故障节点的member ID:
# 查看所有成员
ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
member list
从集群移除故障节点(用实际member ID替换<member-id>):
ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
member remove <member-id>
# 重新添回节点(如果节点恢复)
ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
member add <节点名称> --peer-urls=https://<节点IP>:2380
# 重启etcd服务
systemctl restart etcd
验证方式
# 确认所有节点健康
ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
endpoint health --cluster
# 测试写入
ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
put test-key "test-value"
风险提醒
etcd数据损坏可能是不可逆的。操作前务必先备份:etcdctl snapshot save。修改etcd配置后必须重启服务,会有短暂不可用。
回滚方案
# 从快照恢复
ETCDCTL_API=3 etcdctl snapshot restore /backup/etcd-snapshot.db \
--data-dir=/var/lib/etcd \
--initial-cluster=<cluster-name> \
--initial-advertise-peer-urls=https://<ip>:2380
systemctl restart etcd
问题九:DNS解析异常
问题背景
集群内Pod之间无法通过Service名称通信,DNS查询超时或返回错误结果。CoreDNS是集群DNS的核心组件,它的健康直接决定服务发现是否正常。
适用场景
- 应用启动后连不上数据库
- Pod间调用莫名超时
- 集群内网络时断时续
核心知识点
Kubernetes DNS基于CoreDNS实现,通过kube-dns服务暴露。Pod的/etc/resolv.conf指向kube-dns的ClusterIP进行查询。Service域名格式为<svc-name>.<namespace>.svc.cluster.local。
实战步骤
第一步:确认CoreDNS Pod状态
kubectl get pod -n kube-system -o wide | grep coredns
kubectl logs -n kube-system <coredns-pod> --tail=50
第二步:进入测试Pod验证DNS
kubectl run -it --rm debug --image=busybox -n default --restart=Never -- nslookup kubernetes.default
kubectl run -it --rm debug --image=busybox -n default --restart=Never -- nslookup <svc-name>.<namespace>
如果不指定命名空间,默认使用default命名空间。测试完后Pod会自动删除。
第三步:检查Pod的resolv.conf
kubectl exec -it <pod-name> -n <namespace> -- cat /etc/resolv.conf
确认nameserver指向正确的ClusterIP。
第四步:测试直接IP连通性
kubectl exec -it <pod-name> -n <namespace> -- ping <cluster-ip>
kubectl exec -it <pod-name> -n <namespace> -- curl -v https://<cluster-ip>:<port>
第五步:检查CoreDNS配置
kubectl get configmap -n kube-system coredns -o yaml
根因定位与修复
原因一:CoreDNS Pod异常
# 重启CoreDNS
kubectl delete pod -n kube-system <coredns-pod>
# 确认副本数
kubectl get deployment -n kube-system coredns
kubectl scale deployment coredns -n kube-system --replicas=3
原因二:upstream DNS配置错误
CoreDNS的forward配置决定了集群外域名查询走哪里。
# ConfigMap中检查
forward . /etc/resolv.conf
# 或指定上游服务器
forward . 8.8.8.8
原因三:headless Service问题
kubectl get svc <svc-name> -n <namespace> -o yaml | grep clusterIP
headless Service的clusterIP为None,这时DNS会返回Pod IP列表而不是单个IP。
原因四:busybox镜像版本问题
旧版本busybox的nslookup实现不完整。换用其他镜像测试:
kubectl run -it --rm debug --image=alpine --restart=Never -- sh
# apk add bind-tools
# nslookup <svc-name>
原因五:网络策略阻止DNS流量
kubectl get networkpolicy -n kube-system
确认没有规则阻止CoreDNS的53端口。
验证方式
# 持续测试DNS解析
kubectl exec -it <pod-name> -n <namespace> -- sh -c 'for i in {1..10}; do nslookup kubernetes.default && break || sleep 1; done'
# 检查解析延迟
kubectl exec -it <pod-name> -n <namespace> -- time nslookup <svc-name>
风险提醒
CoreDNS是集群核心组件,修改配置和重启都会短暂影响DNS服务。滚动重启CoreDNS一般不会影响业务,但有短暂解析失败。
回滚方案
# 恢复CoreDNS ConfigMap
kubectl apply -f coredns-configmap-backup.yaml
# 如果是缩容问题,恢复副本数
kubectl scale deployment coredns -n kube-system --replicas=<原数量>
问题十:Pod安全策略导致权限不足
问题背景
应用运行在非root用户、强制安全上下文或限制性PodSecurityPolicy下,某些目录无法写入、某些端口无法监听、某些系统调用被拒绝。
适用场景
- 应用报Permission denied错误
- 容器内无法创建文件
- 特权操作被拒绝
核心知识点
Pod安全通过PSP(PodSecurityPolicy,已废弃)、Pod Security Standards、SecurityContext三层面控制。容器级别的runAsUser、capabilities、allowPrivilegeEscalation等设置会限制进程权限。
实战步骤
第一步:查看Pod安全上下文
kubectl get pod -n <namespace> <pod-name> -o yaml | grep -A 20 securityContext
kubectl describe pod -n <namespace> <pod-name> | grep -A 10 "Security"
第二步:查看容器安全上下文
kubectl get pod -n <namespace> <pod-name> -o jsonpath='{.spec.containers[*].securityContext}'
第三步:检查Pod所在节点的安全模块状态
# 检查是否启用SELinux
getenforce
# 检查AppArmor状态
apparmor_status
# 检查seccomp配置文件
cat /proc/sys/kernel/seccomp/computer_mode
第四步:查看相关事件和日志
kubectl describe pod -n <namespace> <pod-name> | grep -A 5 "Warning"
# 检查audit日志
auditctl -l | grep <pod-name>
根因定位与修复
原因一:非root用户无法写入目录
修改安全上下文使用root运行(不推荐用于有漏洞的镜像):
spec:
securityContext:
runAsUser: 0
更好的方案是修改目录权限:
spec:
securityContext:
runAsUser: 1000
volumes:
- name: data
persistentVolumeClaim:
claimName: my-pvc
initContainers:
- name: fix-permission
image: busybox
command: ["sh", "-c", "chown -R 1000:1000 /data"]
volumeMounts:
- name: data
mountPath: /data
原因二:缺少必要capability
应用需要绑定低位端口(如80、443)但没有SYS_ADMIN capability。
spec:
containers:
- name: app
securityContext:
capabilities:
add:
- NET_BIND_SERVICE
drop:
- ALL
原因三:特权模式被禁用
运行特权容器被PSP或PSA拒绝:
spec:
containers:
- name: app
securityContext:
privileged: false
allowPrivilegeEscalation: false
原因四:hostPath挂载被拒绝
spec:
volumes:
- name: host-sys
hostPath:
path: /sys
type: Directory
containers:
- name: app
volumeMounts:
- name: host-sys
mountPath: /host/sys
readOnly: true
确保PSP允许hostPath类型。
验证方式
# 确认应用正常运行
kubectl exec -it -n <namespace> <pod-name> -- id
# 应该显示配置的runAsUser
# 测试写入权限
kubectl exec -it -n <namespace> <pod-name> -- touch /data/test-file
kubectl exec -it -n <namespace> <pod-name> -- rm /data/test-file
# 确认非特权运行
kubectl exec -it -n <namespace> <pod-name> -- cat /proc/self/status | grep Cap
风险提醒
给予容器过高权限(privileged、SYS_ADMIN)会增大安全风险。生产环境应遵循最小权限原则。hostPath访问节点文件系统可能导致容器逃逸。
回滚方案
# 恢复到之前的安全上下文配置
kubectl patch deployment <deployment-name> -n <namespace> --type merge -p '{"spec":{"template":{"spec":{"securityContext":{"runAsUser":1000}}}}}'
总结
高频问题排查矩阵
| 问题现象 | 首选排查命令 | 常见根因 |
|---|---|---|
| Pod Pending | kubectl describe pod 看Events | 资源不足、污点、存储等待 |
| CrashLoopBackOff | kubectl logs --previous | 启动命令错误、依赖不可达、权限问题 |
| Service无法访问 | kubectl get endpoints | Selector不匹配、kube-proxy异常、网络策略 |
| 镜像拉取失败 | kubectl describe pod 看错误 | 认证失败、镜像不存在、网络不通 |
| Node NotReady | systemctl status kubelet | kubelet崩溃、资源耗尽、证书过期 |
| 存储卷异常 | kubectl describe pvc | StorageClass缺失、CSI驱动问题、权限 |
| Pod被驱逐 | kubectl describe pod 看配额 | 资源Limit不足、命名空间配额耗尽 |
| etcd故障 | etcdctl endpoint health | 磁盘满、证书过期、成员不一致 |
| DNS异常 | nslookup 测试 | CoreDNS异常、upstream配置错误 |
| 权限不足 | kubectl describe pod 看安全上下文 | runAsUser限制、capability缺失 |
日常巡检清单
# 节点健康
kubectl get nodes -o wide
kubectl top nodes
# Pod健康
kubectl get pods -A | grep -v Running
kubectl get pods -A | grep -i evicted
# 资源配额
kubectl get resourcequota -A
kubectl describe limitrange -n <namespace>
# 存储状态
kubectl get pvc -A
kubectl get pv
# 核心组件
kubectl get pods -n kube-system
# 事件
kubectl get events -A --sort-by='.lastTimestamp' | tail -50
预防性措施
- 资源规划:为每个命名空间设置合理的ResourceQuota,为每个Pod设置准确的resources requests和limits
- 高可用部署:关键应用使用反亲和性分布到不同节点,控制面节点至少3个
- 监控告警:对节点CPU、内存、磁盘、Pod重启、调度延迟等关键指标建立告警
- 定期演练:每个季度模拟一次故障,验证备份可用性和恢复流程
- 版本管理:保持kubelet、containerd、etcd等核心组件版本一致,升级前先在测试环境验证
- 日志收集:确保kubelet、containerd、etcd、CoreDNS日志汇聚到统一平台,便于事后复盘
遇到问题先看事件、再查日志、最后看配置,按这个顺序能覆盖80%以上的常见故障。复杂问题往往需要综合分析多个组件的状态,善用kubectl get和kubectl describe两个命令能快速定位问题边界。

本文链接:https://www.yunweipai.com/49290.html





网友评论comments