The connection to the server 10.0.0.10:6443 was refused - did you specify the right host or port?这条报错我在过去几年里见过不下二十次每次它的出现都带着同样的节奏机器重启、网络抖了一下、证书到期、或者半夜监控告警。它的表面意思是连不上 6443但真正的含义是——你的kubectl连不上kube-apiserver了而 kube-apiserver 是整个k8s集群唯一的入口它一停集群对外就等于失联。很多人第一次遇到会本能地去查网络、ping IP、翻云厂商控制台结果折腾一小时才发现压根不是网络问题而是控制面压根没起来。这篇内容我会按我自己的排查顺序从这条报错到底是谁抛出来的开始一路讲到静态 Pod、etcd、kubelet、证书、磁盘这些真实根因中间穿插一次完整的现场复盘和一份我自己在用的速查表。适合刚接触 k8s 运维的同学照着抄也适合有一定经验但每次遇到控制面故障还是靠感觉排查的老手对照一下流程。1. 先把这条报错读薄6443、apiserver 与 refused 三个关键词1.1 报错是谁抛出来的它代表哪一层出了问题这条信息是kubectl 客户端打印的不是服务端。它的完整语义是kubectl 拿着 kubeconfig 里配置的server: https://10.0.0.10:6443去发起 TCP 连接内核返回了ECONNREFUSED也就是对端明确拒绝了连接。这个拒绝很关键它说明 TCP 三次握手的第一包发出去之后对方回了一个 RST。由此可以推断出几种可能目标主机在线但 6443 上没有进程监听有进程监听但被防火墙规则 DROP/REJECT中间有个负载均衡或 VIP 没有把流量转发到后端。注意它和no route to host、i/o timeout、connection reset by peer是完全不同的病后面会专门讲。理解这一点后排查方向就很明确了——不要再怀疑 kubectl 装错了、版本不对、Context 选错了虽然也要顺手确认一下问题的重心在服务端那台机器的 6443 端口上。1.2 6443 端口上跑的是什么为什么不是 8080kube-apiserver 默认有两个监听口--secure-port默认 6443提供 HTTPS 与认证鉴权早年的--insecure-port8080 在 1.20 之后已经被彻底移除。所以你看到的 6443就是 kube-apiserver 唯一的正式对外入口。围绕它还有一组端口值得记住排查时非常有用端口组件说明6443kube-apiserver集群唯一入口kubectl 与所有组件都连它2379 / 2380etcd客户端通信 / 节点间 Peer 通信10257kube-controller-manager健康检查与指标10259kube-scheduler健康检查与指标10250kubeletapiserver 反向访问 kubelet 执行 exec/logs10249kube-proxy指标端口这张表的意义在于当你发现 6443 不通时用同样的手法去测 2379 和 10250能迅速判断是整个控制面都没起来还是只有 apiserver 挂了。如果 2379 通、6443 不通说明 etcd 活着问题在 apiserver 这一侧如果两个都不通那多半是 kubelet 或容器运行时出了问题因为这两个进程都是由 kubelet 以静态 Pod 方式拉起来的。1.3 refused和timeout是两种完全不同的病这一点我踩过坑值得单独拎出来说。connection refused意味着目标机器活着、网络链路通、只是端口没人接。i/o timeout意味着包发出去了但没有任何回应通常是防火墙 DROP、安全组没放行、或者主机已经宕机。前者要把注意力放在进程为什么没起来后者要把注意力放在网络为什么不通。两者的排查路径几乎不重叠混淆了就会浪费大量时间。还有一种更迷惑的情况报错里显示的 IP 根本不是你以为的那台机器。这通常出现在使用了 VIP 或多控制面负载均衡的环境里VIP 漂移过去了但后端没有健康的 apiserver此时客户端连 VIP 得到的也是 refused。所以第一条命令永远是确认我连的到底是谁。2. 影响面判断先搞清楚这次故障有多严重2.1 单控制面和多控制面的差别是数量级的我处理过的集群里单 Master 的小集群占了相当大的比例测试环境、边缘节点、还有一些内部工具集群。这种架构下 apiserver 挂掉等于整个集群的管理面归零kubectl全部失效Deployment 无法滚动HPA 不再伸缩Ingress Controller 无法 watch 到新的 Ingress 变更监控里的 kube-state-metrics 会开始大面积报错CI/CD 流水线的部署步骤会卡死在第一步。而三节点堆叠 etcd 的控制面集群容忍度就高得多。挂掉一台剩余两台仍然能选主apiserver 通常通过本地负载均衡比如 haproxy keepalived继续提供服务业务几乎无感。所以判断影响面的第一件事是搞清楚这套集群的控制面拓扑——用kubectl get nodes -l node-role.kubernetes.io/control-plane查前提是还能连上或者直接翻部署文档、看/etc/kubernetes/manifests在几台机器上存在。2.2 控制面挂了已经在跑的业务 Pod 会怎样这是被问得最多的问题也是最容易引起恐慌的地方。答案是已经在运行的 Pod 不会因为 apiserver 挂掉而停止。Pod 的生命周期由节点上的 kubelet 和容器运行时负责apiserver 只是大脑不是心脏。大脑暂时离线心脏还在跳。但业务无感这个说法要打个折扣具体影响取决于你的业务形态已经建立的长连接、正在处理的请求基本不受影响因为流量路径是外部 LB → Service → Pod全程不经过 apiserver需要新建连接的场景比如 Service 的 Endpoints 发生变化Pod 重启、扩缩容kube-proxy 拿不到新数据可能会短暂转发到已下线的后端滚动发布、配置热更新、证书轮换这类依赖 watch 机制的动作会全部停摆探针失败导致的容器重启仍会发生但 kubelet 上报状态会失败你在kubectl get pods里也看不到节点失联的判定会失效也就是说坏掉的节点不会被标记 NotReady调度器也不会做迁移。所以真正的处置顺序应该是先确认业务是否还在正常服务看外部监控和日志再决定是立刻抢修控制面还是先做流量侧兜底。我个人的习惯是只要外部探测显示核心接口成功率没掉就安心修控制面不去动业务。3. 五层排查法从最便宜的检查开始3.1 第零层确认 kubeconfig 指向的是不是你以为的那台机器这一层最便宜三十秒能查完但它的命中率比想象中高。很多时候问题出在本地切了 Context 忘了切回来、KUBECONFIG环境变量指向了另一个文件、或者从别人那里拷了一份 kubeconfig 但里面的 IP 是旧的。# 看当前生效的 context 和 server 地址 kubectl config current-context kubectl config view --minify -o jsonpath{.clusters[0].cluster.server} echo $KUBECONFIG如果输出的 IP 和你以为的不一致问题就找到了。还有一种情况是集群重建过控制面 IP 变了但本地 kubeconfig 还是老的这种情况下报错里显示的 IP 会明显眼熟但不对。我建议把这几条命令做成一个别名遇到报错先跑一遍成本极低。3.2 第一层端口与网络连通性确认 kubeconfig 没问题后登录到控制面节点直接用ss看端口有没有人监听。# 看 6443 是否有 LISTEN ss -lntp | grep 6443 # 从客户端侧测连通性 curl -k -m 5 https://10.0.0.10:6443/healthz三种结果对应三条路第一ss没有任何输出curl直接 refused。说明 apiserver 进程根本没起来往下走第二层查 kubelet 和静态 Pod。第二ss显示有进程在监听但curl仍然 refused。这种情况通常是监听在127.0.0.1而不是0.0.0.0或者中间隔了防火墙。用ss -lntp | grep 6443的输出确认Local Address那一列是0.0.0.0:6443还是127.0.0.1:6443。第三curl超时而不是 refused。那就是防火墙或安全组。检查iptables -L -n | grep 6443、firewall-cmd --list-all、云平台的网络 ACL。要注意 k8s 自己也会写大量 iptables 规则如果规则被误清或者kube-proxy反复重建规则也可能出现短暂不可达。注意在控制面节点上用curl -k测 6443 时如果返回ok说明 apiserver 本身是健康的问题一定在客户端到服务端之间的路径上不要去重启 apiserver那样只会扩大故障面。3.3 第二层容器运行时和 kubelet 活着没有kube-apiserver 在 kubeadm 体系下是以静态 Pod形式运行的而静态 Pod 是由kubelet直接管理的不经过 apiserver 调度。这句话很关键apiserver 依赖 kubelet 才能启动而 kubelet 启动时又要去连 apiserver 注册自己看起来像个循环实际上 kubelet 允许注册失败继续运行静态 Pod 照常拉起等 apiserver 起来后再完成注册。所以排查顺序是先看 kubelet再看容器运行时。systemctl status kubelet --no-pager systemctl status containerd --no-pager # 或 docker journalctl -u kubelet -n 200 --no-pager journalctl -u kubelet -p err -b --no-pagerkubelet 挂掉的原因相当集中我在日志里见过的排名大概是swap 被重新启用、cgroup driver 不一致、/var/lib/kubelet磁盘满、证书文件丢失、配置文件被覆盖。其中 swap 那条几乎是最常见的failed to run Kubelet: running with swap on is not supported, please disable swap或者misconfiguration: kubelet cgroup driver: systemd is different from docker cgroup driver: cgroupfs第一种情况多见于机器重启后/etc/fstab里的 swap 分区被自动挂载回来第二种多见于有人手动装过 docker改了 daemon.json 但没同步 kubelet 的配置。这两条等下在复盘里还会细说。心得journalctl -u kubelet -p err -b这条命令我几乎每次都会先跑-b表示只看本次启动-p err过滤出错误级别能在一屏之内把真正的错误顶到眼前比翻几百行 INFO 日志高效得多。3.4 第三层静态 Pod 里到底有没有 apiserver 容器kubelet 活着不代表静态 Pod 就拉起来了。这时候要绕过 kubectl直接用crictl看容器状态。cat /etc/crictl.yaml # runtime-endpoint: unix:///var/run/containerd/containerd.sock crictl ps -a | grep -E kube-apiserver|kube-controller-manager|kube-scheduler|etcd crictl logs -t 容器ID | tail -n 100如果看到kube-apiserver容器状态是Exitedcrictl logs里的日志就是第一现场。常见的几条Error: --etcd-servers ... connection refusedetcd 没起来或者 etcd 证书不匹配x509: certificate has expired or is not yet valid证书过期kubeadm 默认证书一年有效期到期后 apiserver 会起不来mkdir /var/lib/etcd: no space left on device磁盘满这条最阴险往往表现为 apiserver 反复重启bind: address already in use6443 被别的进程占了通常是有个残留容器或者手动跑起来的 apiserver 进程。如果crictl ps -a里压根没有 apiserver 容器的记录说明 kubelet 没有扫描到静态 Pod 清单。检查/var/lib/kubelet/config.yaml里的staticPodPath字段是不是指向/etc/kubernetes/manifests再确认该目录下的kube-apiserver.yaml还在、权限正确、YAML 语法没被改坏少一个缩进就能让它静默失败。3.5 第四层etcd 与控制面组件的连锁反应apiserver 起来了但反复退出十有八九是 etcd 的问题。因为 apiserver 需要连 etcd 才能对外服务etcd 不可用会导致 apiserver 的就绪探针失败容器被 kubelet 反复重启。# 直接看 etcd 容器日志 crictl ps -a | grep etcd crictl logs -t etcd容器ID | tail -n 100 # 或者通过 apiserver 侧看 kubectl -n kube-system logs etcd-节点名 --tail100etcd 的高频故障我总结下来有三类。第一类是磁盘满或者慢日志里会出现mvcc: database space exceeded这种情况下 etcd 进入只读模式整个集群只能读不能写所有创建/更新操作全部失败。处置方式是先扩容磁盘再执行compact和defrag具体要针对 revisions 做压缩不是简单删文件就行。第二类是时钟不同步etcd 对时间敏感节点间漂移过大会导致选主异常。第三类是数据目录权限被改多见于有人手动动过/var/lib/etcd的属主。注意无论何种情况都不要一上来就rm -rf /var/lib/etcd。在堆叠 etcd 的集群里那样等于把整个集群的元数据清空Pod、Service、Secret、PVC 全部蒸发恢复成本极高。真到了这一步先用etcdutl snapshot save备份再动手。3.6 第五层证书、时间与磁盘这三个隐形杀手前三层都没问题apiserver 也在跑但就是连不上或者连上之后报别的错这时候要看三个隐形因素。证书。kubeadm 部署的集群默认证书有效期一年到期之后 apiserver 的 TLS 握手会失败。检查方式kubeadm certs check-expiration openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -dates续期用kubeadm certs renew all续期之后必须重启控制面静态 Pod 才会生效——把/etc/kubernetes/manifests里的 yaml 临时移出去再移回来或者直接systemctl restart kubelet。时间。节点间时间偏差过大会导致证书校验失败、etcd 选主异常、token 过期等一系列莫名其妙的问题。timedatectl看一眼如果System clock synchronized: no先把时间同步服务拉起来再谈其他。磁盘。这个最容易被忽略也最致命。df -h查一下/var/lib/etcd、/var/lib/kubelet、/var/log、/var/lib/containerd。容器日志是最容易膨胀的东西几百个 Pod 跑几个月/var/log/pods占满根分区是家常便饭。一旦根分区满了kubelet 连自己的状态文件都写不进去表现就是kubelet 活着但什么都不干。4. 一次完整复盘机器重启之后控制面没起来4.1 现场能 SSH 上去但 kubectl 全挂某天早上我收到告警说一套内部测试环境的 API 不可用。SSH 上去一切正常机器活着负载很低但kubectl get nodes直接抛出本文开头那条 refused。这套环境是单 Master 架构控制面和 etcd 都在这台机器上也就是说这台机器一挂整个集群的管理面就全没了。先说影响面判断这套环境上跑的是几个内部工具外部探测显示核心接口仍然正常返回说明业务 Pod 还在跑只是管理面失联。这给了我足够的修复时间不用急着上重启大法。4.2 采集信息的三条命令我没有立刻动手先按顺序跑了三条命令采集现场信息。# 1. 端口是否监听 ss -lntp | grep -E 6443|2379 # 2. kubelet 状态与最近错误 systemctl status kubelet --no-pager | head -n 20 journalctl -u kubelet -p err -b --no-pager | tail -n 50 # 3. 容器运行时状态 systemctl status containerd --no-pager | head -n 10第一条命令的输出是空的6443 和 2379 都没有监听说明 apiserver 和 etcd 容器都没起来。第二条命令里 kubelet 的状态是Active: failed日志最后几行赫然写着failed to run Kubelet: running with swap on is not supported, please disable swap kubelet.service: Main process exited, codeexited, status1/FAILURE到这里根因已经非常清楚了kubelet 因为 swap 被启用而拒绝启动静态 Pod 自然没人管apiserver 和 etcd 也就不会启动。4.3 定位到根因kubelet 没开机自启 swap 被重新挂载继续往下追发现是两个问题叠加。第一这台机器前几天做过一次系统补丁更新并重启kubelet的systemctl is-enabled输出是disabled也就是说开机压根不会自启。第二/etc/fstab里 swap 那一行没有被注释掉重启后 swap 分区被自动挂载回来kubelet 一启动就撞上这个限制直接退出。这两个问题单独出现都不致命组合起来就变成了重启即失联。而之所以之前一直没暴露是因为这台机器装好之后就没重启过。# 确认现状 swapon --show grep -i swap /etc/fstab systemctl is-enabled kubelet输出确认了上面的判断swap 是/dev/mapper/xxx上的一个分区fstab里那一行处于启用状态kubelet 的 enabled 状态是 disabled。4.4 修复与验证一步一步确认控制面真的回来了修复动作本身不复杂但顺序很重要。我按下面的步骤做# 1. 关闭 swap 并注释 fstab防止下次重启复发 swapoff -a sed -i /\sswap\s/s/^/#/ /etc/fstab # 2. 设置开机自启并启动 systemctl enable kubelet systemctl start kubelet # 3. 确认 kubelet 起来了 systemctl is-active kubelet journalctl -u kubelet -n 30 --no-pagerkubelet 起来之后它会去扫描/etc/kubernetes/manifests把静态 Pod 一个个拉起来。这时候不要急着敲kubectl因为 apiserver 启动需要连 etcdetcd 又需要一点时间完成选主和 WAL 回放整个链路有先后依赖。# 等 20~30 秒观察容器状态 watch -n 2 crictl ps | grep -E etcd|apiserver # 端口起来之后再做连通性验证 ss -lntp | grep 6443 curl -k -m 5 https://10.0.0.10:6443/healthzcurl返回ok的那一刻我才去客户端验证kubectl get --raw /healthz kubectl get nodes kubectl -n kube-system get pods这里有一个细节值得说kubectl get nodes之后如果节点显示NotReady不要慌通常是因为 CNI 插件比如 Calico、Flannel的 Pod 也在刚才的故障中重启了需要给它几十秒重新建立网络。等kubectl -n kube-system get pods里所有 Pod 都变成Running且READY列正常节点状态才会回到Ready。这个过程我见过最快的十几秒最慢的一次接近三分钟。最后我把/etc/fstab的改动也在其他几台节点上同步了一遍并把systemctl enable kubelet加进了初始化脚本的检查项。这套环境后来又重启过几次再没出过同样的问题。5. 高频问题速查表与避坑清单5.1 问题与处置速查表下面这张表是我这些年攒下来的基本覆盖了 6443 refused 的绝大多数根因。遇到问题从上往下对按概率排序。现象大概率根因处置方式kubeconfig 里的 IP 不对集群重建或 Context 切换kubectl config view --minify核对6443 无监听、2379 无监听kubelet 未运行systemctl status kubelet查 journalctlkubelet 报 swap 相关错误重启后 fstab 自动挂载 swapswapoff -a并注释 fstabkubelet 报 cgroup driver 不一致运行时配置被改统一 daemon.json 与 kubelet 的 cgroupDriverkubelet enabled 为 disabled开机自启未配置systemctl enable kubelet6443 有监听但连接被拒监听在 127.0.0.1 或被防火墙拦截ss -lntp查绑定地址查 iptables/安全组apiserver 容器 Exitedetcd 不可用或证书过期crictl logs查看第一现场etct 日志出现 space exceeded磁盘满etcd 只读扩容后 compact defrag日志出现 x509 过期证书一年期到kubeadm certs renew all并重启静态 Pod反复重启、无明确日志磁盘满或内存不足 OOMdf -h、dmesg -T | grep -i oom客户端超时而非拒绝防火墙 DROP 或主机宕机查安全组、网络 ACL、主机状态5.2 几条我用血泪换来的经验第一条先判断影响面再动手。我见过有人一看到 refused 就reboot结果单 Master 集群直接进入更长的不可用状态。正确顺序永远是业务是否受影响 → 控制面拓扑是什么 → 有没有其他健康节点 → 再决定修复动作。第二条不要在没有备份的情况下动 etcd。这句话我重复多少次都不嫌多。哪怕只是defrag也建议先etcdutl snapshot save一份到别的目录。一个几百 MB 的快照文件能救你一整个通宵。第三条crictl要提前配好。很多人是在故障发生时才发现自己不会用crictl或者/etc/crictl.yaml没配crictl ps直接报错。这个工具应该在集群部署完的第一天就配好并验证故障现场不是学习的时候。第四条日志要会看最后一次启动。journalctl -u kubelet -b只看本次启动如果不加-b你会翻到几个月前的日志里越看越乱。同理crictl logs --tail 100比全量输出有用得多。第五条改配置前先想下次重启会不会复发。临时swapoff -a只是止血不注释/etc/fstab下次重启照样挂。这类治标不治本的操作在应急时没问题但事后一定要补上持久化配置否则就是给自己埋雷。5.3 预防性配置让下一次重启体面一点故障修完之后我一般会花二十分钟做一轮预防性检查把可能复发的问题提前掐掉。这套检查项现在已经是我的固定动作# 1. 确认 kubelet 与运行时都是开机自启 systemctl is-enabled kubelet containerd # 2. 确认 swap 永久关闭 swapon --show # 应该无输出 grep -i swap /etc/fstab # 应该没有未注释的行 # 3. 确认内核模块开机自动加载 cat /etc/modules-load.d/k8s.conf # overlay # br_netfilter # 4. 确认 sysctl 持久化 cat /etc/sysctl.d/k8s.conf # net.bridge.bridge-nf-call-iptables 1 # net.ipv4.ip_forward 1 # 5. 检查证书剩余有效期 kubeadm certs check-expiration # 6. 检查磁盘水位 df -h /var/lib/etcd /var/lib/kubelet /var/log这几项里第 3 和第 4 项经常被忽略。br_netfilter模块如果没在开机时加载net.bridge.bridge-nf-call-iptables这个 sysctl 就设不上直接后果是 Service 的流量转发异常——Pod 之间能通但 Service IP 访问不通。这个问题在重启后才暴露极难定位因为kubectl get pods一切正常。另外我建议把容器日志的轮转参数显式配上别用默认值。在 kubelet 配置里加上containerLogMaxSize: 100Mi containerLogMaxFiles: 5这样单个容器日志最多占 500Mi几百个 Pod 加起来也可控。默认配置下日志无上限增长撑爆根分区只是时间问题。改完之后systemctl restart kubelet生效注意这个重启不会影响正在运行的业务 Pod。5.4 关于恢复顺序的一点个人习惯最后分享一个我在多次抢修中固定下来的恢复顺序供参考etcd → apiserver → controller-manager 与 scheduler → kubelet 注册 → CNI → 业务侧验证。这个顺序的依据是依赖关系etcd 是地基apiserver 建在它上面其余控制面组件又依赖 apiserver 的 watch。如果顺序反了比如 apiserver 还没起来就去重启 CNI 的 Podkubelet 会因为连不上 apiserver 而无法更新 Pod 状态反而让现场更乱。验证的时候也别只看kubectl get nodes一个绿灯就收工。我一般会再走一遍完整链路创建一个临时的 ConfigMap 看写入是否正常kubectl run一个临时 Pod 看调度是否正常kubectl logs看 apiserver 到 kubelet 的反向通道是否正常最后删掉这些临时资源。这套动作三十秒能跑完但能确认真的是全好了而不是看起来好了。踩过的坑告诉我控制面故障之后最怕的不是没修好而是修了一半以为好了等晚上业务发布的时候才发现调度器还没起来。
阅读完成 · 觉得有帮助?