Ubuntu 修复 kubelet 10250 端口错误
了解 kubelet API 端口 10250 的用途,修复 kubeadm init 中的 address already in use,并排查防火墙丢包导致 kubectl logs 和 exec 无法使用的问题。
端口 10250 的用途
端口 10250 是 kubelet API。所有提到该端口的错误都属于两类相反的问题之一。第一种情况是端口已被其他进程占用,因此 kubeadm init 无法运行。第二种情况是没有任何请求能够到达该端口,因此 kubectl logs 和 kubectl exec 无法访问一个其他方面看起来完全正常的节点。
kubelet 是 Kubernetes 在每个节点上运行的代理。它启动容器,并将容器状态报告给控制平面。它还监听 TCP 10250,并提供控制平面调用的 HTTPS API。运行 kubectl logs、kubectl exec、kubectl attach 或 kubectl port-forward 时,API server 会连接该端口。metrics-server 也会在同一端口抓取 /metrics/resource,这正是 kubectl top node 能够运行的原因。
该 API 需要身份验证。kubeadm 会关闭匿名访问,并让 kubelet 使用集群 CA(证书颁发机构)。因此,没有凭据的请求会收到 Unauthorized,而不是获得某个容器内的 shell。请记住这一点,因为这也是验证端口可达性的最快方法。如果您还不熟悉端口,Linux 上端口的实际含义介绍了本指南所采用的模型。
这两种故障模式都源于同一个要求。kubelet 启动前,端口 10250 必须处于空闲状态;kubelet 运行后,控制平面必须能够访问该端口。
您遇到的是哪一个问题
在相关节点上运行以下命令。下面的每条命令都需要您在自己的服务器上手动执行。
sudo ss -lntp | grep 10250
sudo systemctl status kubelet --no-pagerss -lntp会列出正在监听的 TCP 套接字,以及每个套接字对应的进程。-l表示仅显示监听中的套接字,-n保留端口的数字格式,-t将结果限制为 TCP,-p显示所属进程。最后一个标志需要 root 权限,否则进程列会为空,无法获取有用信息。
如果某行以 users:(("kubelet",pid=1043,fd=23)) 结尾,表示 kubelet 正在运行并占用该端口。如果您原本希望该端口空闲,这就是原因。如果 ss 完全没有输出,而控制平面仍无法访问此节点,那么暂时还不是防火墙问题,因为当前没有任何服务监听该端口。先查明 kubelet 未运行的原因,再修改防火墙规则。
systemctl status kubelet可提供另一部分信息。active (running)显示几分钟前的启动时间属于正常情况。您运行 kubeadm init 或 kubeadm join 之前,如果 kubelet 每隔几秒重启一次,也属于正常情况:软件包安装的单元会在安装时启动,但找不到配置后退出。上游文档将这种崩溃循环描述为预期行为,因为 kubelet 会等待 kubeadm 告知它应执行的操作。如果您不熟悉 systemd 的重启行为,请参阅systemd 服务类型和重启策略的工作方式,了解本节所需的背景知识。
运行 kubeadm init 时,为什么端口 10250 已被占用
kubeadm init 会在写入任何数据前执行预检。预检会尝试绑定控制平面所需的每个端口。绑定端口失败时,预检会报告端口 10250 并停止。这不是缺陷。kubeadm 会拒绝在已有集群残留内容的基础上创建第二个集群。
实际中通常有以下 4 种原因:
- 之前的
kubeadm init或kubeadm join执行到一半失败。kubelet 已经获得配置,因此正在运行并占用该端口。 - 已启动但未完成的
kubeadm reset。reset 会停止 kubelet,但不会禁用该单元。因此下次重启时,监听进程会再次启动。 - 同一台服务器上安装了 k3s 或其他 Kubernetes 发行版。k3s 内置 kubelet,该 kubelet 也会绑定 10250。
- apt 引入并由其 systemd 单元启动的
kubelet,而你尚未在该服务器上运行 kubeadm。
在修改任何内容前,先确定具体原因:
sudo ss -lntp 'sport = :10250'
systemctl list-units --type=service --state=running | grep -Ei 'kubelet|k3s|k0s'如果监听进程属于 k3s,请停止操作并确定实际需要使用哪个集群。k3s 和 kubeadm 不能共用一台服务器,因为它们会占用相同的端口和同一个 CNI(容器网络接口)目录。k3s 安装程序会在服务器节点的 /usr/local/bin/k3s-uninstall.sh 和代理节点的 k3s-agent-uninstall.sh 留下卸载脚本。
为什么终止 kubelet 不能释放端口
sudo pkill kubelet 会释放端口 10250 约 10 秒。软件包提供的 unit 设置了重启策略,因此 systemd 会启动新的 kubelet,新的进程又会绑定同一个端口。您可以自行查看该策略:
systemctl show kubelet -p Restart -p RestartSec
sudo systemctl stop kubelet
sudo ss -lntp | grep 10250Restart=always 配合 RestartSec=10 正是该 unit 的默认设置。这就是为什么 kill 看起来先是生效,随后又失效。要释放端口,应使用 systemctl stop,因为对于您明确要求停止的 unit,systemd 不会继续重启它。
对于承载半个集群的节点,仅释放端口仍然不够。/var/lib/kubelet/config.yaml、/etc/kubernetes/pki 下的证书以及 /etc/kubernetes/manifests 中的静态 pod 清单都仍然存在。后续的 preflight 检查会因这些文件而失败;强行跳过检查会导致集群证书与其配置不匹配。请正确重置该节点。
干净地重置节点
sudo kubeadm reset -f
sudo rm -rf /etc/cni/net.d
rm -rf $HOME/.kube
sudo systemctl stop kubelet
sudo ss -lntp | grep -E '10250|6443|2379'-f会跳过确认提示。Reset 会尽力撤销 init 或 join 执行的更改。它会删除本地文件和配置;在控制平面节点上删除本地 etcd 成员;清理 /etc/kubernetes/pki 中的证书;并删除 kubelet 配置和清单。
文档明确说明了 reset 会留下哪些内容,而其中每一项都可能导致问题。它不会清理 /etc/cni/net.d,因此旧的 CNI 插件配置仍会保留,新集群会读取该配置。它不会清理 kube-proxy 应用到主机的任何 iptables、nftables 或 IPVS 规则。它不会处理 $HOME/.kube,因此 kubectl 仍会连接已不存在的集群,并返回看似新出现的证书错误。
残留的数据包规则是最棘手的部分。手动刷新表也会删除 ufw 安装的规则,因为 Ubuntu 中的 ufw 通过同一后端写入规则。这样服务器会暂时失去过滤保护,直到运行 sudo ufw reload。如果节点本来就要重建,请在 reset 后重启。重启会清除 kube-proxy 添加的运行时规则,比手动处理一个部分刷新过的规则集更省时间。为什么 iptables 规则和 nftables 规则会出现在彼此的输出中解释了底层的工作原理。
最后一个 ss 命令应不输出任何内容。10250、6443 或 2379 上没有监听程序,表示节点已准备好进行全新的 kubeadm init。
为什么 kubectl logs 和 kubectl exec 在端口 10250 上超时
这是相反的故障表现,而且不会直接显示为端口问题。集群可以正常启动。节点状态为 Ready。Pod 也能运行。随后某个命令失败:
Error from server: Get "https://10.0.0.12:10250/containerLogs/default/web-0/web": dial tcp 10.0.0.12:10250: i/o timeout从消息末尾开始阅读。API server 尝试通过 TCP 连接到节点的 10250 端口,但没有收到响应。i/o timeout 表示数据包被静默丢弃,因此有某个组件正在过滤这些数据包:节点上的主机防火墙,或云服务商控制面板中的独立网络防火墙。相同位置出现 connect: connection refused 则表示相反的情况。数据包已经到达,但没有进程监听该端口,因此 kubelet 已停止。这与连接被拒绝与连接超时中描述的是同一对原因,只是这里使用了不同的端口。
在整个过程中,节点仍保持 Ready 状态,因为节点状态通过另一个方向传递。kubelet 通过 6443 端口向外连接 API server,并发送自己的心跳;这不需要允许进入 10250 端口的连接。因此,阻断 10250 端口后,集群仍可正常调度 Pod,但 logs、exec、port-forward 和 metrics 操作会失败。
kubectl top node 响应 error: Metrics API not available 是通过 metrics-server 观察到的同一故障,其日志会显示节点和端口:
unable to fully scrape metrics from node worker-1: unable to fetch metrics from node worker-1: Get "https://10.0.0.12:10250/metrics/resource": dial tcp 10.0.0.12:10250: i/o timeout在修改任何防火墙规则前测试路径
从控制平面节点运行以下命令,并将目标设为工作节点的地址:
nc -zv 10.0.0.12 10250
curl -sk -o /dev/null -w '%{http_code}\n' https://10.0.0.12:10250/healthznc -z 建立连接后将其关闭;如果端口接受连接,则输出 succeeded!。curl 命令是更好的测试方式,因为它可以证明 kubelet 正在提供服务,而不只是证明端口处于开放状态。它会输出 401。这是正常结果:TLS(传输层安全)握手已完成,随后 kubelet 拒绝了未经身份验证的请求,这正是预期行为。-k 会跳过证书检查;由于这里测试的是路径而不是信任链,因此可以这样做。
长时间暂停后以超时结束,表示数据包被丢弃。curl: (7) Failed to connect 立即返回,表示可访问的主机上该端口处于关闭状态。请从控制平面节点测试,而不要从笔记本电脑测试,因为在这里,只有控制平面节点的访问结果 relevant。
控制平面和工作节点分别需要哪些端口
以下是上游列出的入站端口。控制平面节点需要 TCP 6443,用于 API server,并向所有运行 kubectl 的对象开放。TCP 2379 到 2380 用于 etcd 客户端和对等端 API,由 API server 和 etcd 自身使用。TCP 10250 用于 kubelet API,由节点自身和控制平面使用。TCP 10259 用于 kube-scheduler,TCP 10257 用于 kube-controller-manager,这两个端口仅由节点自身使用。
工作节点需要 TCP 10250,用于 kubelet API,由节点自身和控制平面使用。TCP 10256 用于 kube-proxy,由节点自身以及执行运行状况检查的负载均衡器使用。TCP 和 UDP 30000 到 32767 用于 NodePort 服务,这是默认范围,可由需要这些服务的对象访问。
您的 CNI 插件还会在此列表之外增加自己的端口。Flannel 和 VXLAN 模式的 Calico 需要节点之间使用 UDP 4789。使用 BGP 的 Calico 需要 TCP 179。请查阅插件文档,并在节点之间开放这些端口。否则,即使本节中的所有端口都已开放,不同节点上的 pod 也无法相互通信。
在不暴露到互联网的情况下开放 10250
kubelet API 可以在该节点上的任意容器内启动进程。应将开放的 10250 视为获得该节点的 root 访问权限,并按源地址限制访问。绝不能允许来自任意位置的访问。
sudo ufw allow from 10.0.0.0/24 to any port 10250 proto tcp comment 'kubelet API'
sudo ufw allow from 10.0.0.0/24 to any port 10256 proto tcp comment 'kube-proxy'
sudo ufw status numbered将 10.0.0.0/24 替换为各节点共用的网络。ufw status numbered 会按索引列出活动规则,因此可以使用 sudo ufw delete <number> 删除错误规则。VPS 的 ufw 基础知识介绍了规则的排列方式,该顺序决定实际生效的是哪条规则。
某个 ufw 设置本身就会导致 Kubernetes 失效。跨节点的 Pod 流量需要转发,而不是在本地投递;ufw 默认会丢弃转发数据包。在 /etc/default/ufw 中设置 DEFAULT_FORWARD_POLICY="ACCEPT",然后运行 sudo ufw reload。如果不这样设置,即使 10250 端口完全开放,节点之间的 Pod 到 Pod 流量仍会失败。
还应检查提供商的防火墙。大多数 VPS 面板都有位于服务器前端的网络层防火墙,ufw status 无法看到该防火墙。如果数据包根本没有到达服务器,那么在节点上添加规则不会产生任何作用。
端口可访问但请求仍然失败
有些 10250 端口请求会立即失败,而不是一直挂起。这说明连接已成功建立,但请求被拒绝。metrics-server 日志中的 x509: certificate signed by unknown authority 表示 kubelet 正在提供抓取器不信任的自签名证书。通常有两种处理方式:启用 kubelet 服务证书轮换,让集群 CA 为其签名,然后批准证书签名请求;或者在实验集群中接受这一风险,并使用 --kubelet-insecure-tls 运行 metrics-server。
如果一条消息同时包含 Forbidden 和 nodes/proxy 或 nodes/metrics,则表示发生了 RBAC(基于角色的访问控制)失败。调用方已到达 kubelet,kubelet 向 API server 查询该身份是否可以使用此子资源,但查询结果为否。请修复调用方的 ClusterRole。防火墙配置无法解决此问题,因为没有流量被阻止。
如果您只需要一个小型集群
如果您是在单台 VPS 上首次使用 kubeadm 搭建集群时遇到这些错误,请考虑是否确实需要 kubeadm。VPS 上的单节点 k3s 集群只需一条命令即可提供可用的 Kubernetes API,并预先配置好 kubelet、kube-proxy 和 CNI。该环境仍使用 10250 端口,相关规则也同样适用,但您不再需要自行组装控制平面。
FAQ
Kubernetes 中的 10250 端口用于什么?
这是每个节点上的 kubelet 身份验证 HTTPS API,控制平面节点和工作节点都提供该 API。API server 通过它执行 kubectl logs、kubectl exec、kubectl attach 和 kubectl port-forward,metrics-server 也会从该端口抓取 /metrics/resource,以提供 kubectl top。节点状态不使用该端口,因为 kubelet 会通过 6443 端口向 API server 主动发送心跳。因此,10250 被阻断时,节点会显示为 Ready,但日志和 exec 操作会失败。
如何查找正在监听 10250 端口的进程?
在节点上运行 sudo ss -lntp | grep 10250。行末的 users:((...)) 字段会显示进程名称及其 PID。sudo 很重要,因为没有 root 权限时,进程列会为空。如果所有者是 kubelet,sudo systemctl status kubelet --no-pager 可以帮助您判断 kubelet 是否运行正常,还是在循环重启。如果所有者是 k3s,说明同一台服务器上安装了两个 Kubernetes 发行版,您需要卸载其中一个。
必须在防火墙中开放 10250 端口吗?
必须在节点之间开放。控制平面必须能够访问每个节点上的 10250 端口,包括控制平面自身所在的节点,否则日志、exec、端口转发和指标都会失败。应将来源限制为节点共用的网络,例如 sudo ufw allow from 10.0.0.0/24 to any port 10250 proto tcp。不要将该端口开放到互联网:任何能够通过身份验证访问该端口的对象,都可以在该节点上的任意容器中运行进程。
为什么只有一个节点上的 Pod 执行 kubectl logs 会失败?
因为阻断是按节点生效的,API server 会连接承载该 Pod 的特定节点。先读取错误文本,其中包含它尝试访问的节点 IP 地址。然后从控制平面节点运行 nc -zv <node-ip> 10250。如果请求超时,问题可能出在该节点的防火墙或云服务商的网络防火墙上。connection refused 表示该节点上的 kubelet 未运行,因此应改为在该节点上检查 systemctl status kubelet。