首页 运维干货别踩坑!K8s 集群运维 10 个高频问题一站式解决

别踩坑!K8s 集群运维 10 个高频问题一站式解决

运维派隶属马哥教育旗下专业运维社区,是国内成立最早的IT运维技术社区,欢迎关注公众号:yunweipai
领取学习更多免费Linux云计算、Python、Docker、K8s教程关注公众号:马哥linux运维

引言

生产环境的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 Pendingkubectl describe pod 看Events资源不足、污点、存储等待
CrashLoopBackOffkubectl logs --previous启动命令错误、依赖不可达、权限问题
Service无法访问kubectl get endpointsSelector不匹配、kube-proxy异常、网络策略
镜像拉取失败kubectl describe pod 看错误认证失败、镜像不存在、网络不通
Node NotReadysystemctl status kubeletkubelet崩溃、资源耗尽、证书过期
存储卷异常kubectl describe pvcStorageClass缺失、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

预防性措施

  1. 资源规划:为每个命名空间设置合理的ResourceQuota,为每个Pod设置准确的resources requests和limits
  2. 高可用部署:关键应用使用反亲和性分布到不同节点,控制面节点至少3个
  3. 监控告警:对节点CPU、内存、磁盘、Pod重启、调度延迟等关键指标建立告警
  4. 定期演练:每个季度模拟一次故障,验证备份可用性和恢复流程
  5. 版本管理:保持kubelet、containerd、etcd等核心组件版本一致,升级前先在测试环境验证
  6. 日志收集:确保kubelet、containerd、etcd、CoreDNS日志汇聚到统一平台,便于事后复盘

遇到问题先看事件、再查日志、最后看配置,按这个顺序能覆盖80%以上的常见故障。复杂问题往往需要综合分析多个组件的状态,善用kubectl get和kubectl describe两个命令能快速定位问题边界。

别踩坑!K8s 集群运维 10 个高频问题一站式解决插图

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

网友评论comments

发表回复

您的电子邮箱地址不会被公开。

暂无评论

Copyright © 2012-2022 YUNWEIPAI.COM - 运维派 京ICP备16064699号-6
扫二维码
扫二维码
返回顶部