2026/10/22 9:52:48

跨局域网多台服务器组建K8s集群的完整实战指南

跨局域网多台服务器组建K8s集群的完整实战指南 先把结论放在前面不同局域网内的多台服务器组建k8s集群说难不难说简单也绝不是一台机器上敲两条命令就能完事的。我在实际项目中碰到过很多次这类需求——云上几台节点、办公室内网几台物理机、异地机房几台高配服务器业务希望它们组成一套Kubernetes集群统一调度。这类需求的本质是节点间物理二层不可达默认路由也互不相同但k8s的控制面组件、kubelet、CNI都需要一个彼此可达的三层网络。所以第一步不是装容器运行时而是先解决“不同局域网之间如何互通”这个底层问题。如果所有节点都在同一个二层网络kubeadm init一把过拉起来就能用一旦跨局域网控制面endpoint填什么地址、证书里要放哪些SAN、容器网络插件选哪种模式、节点上报的IP用哪个网卡全都变成坑。这篇文章就把这些点拆开讲包含我多次部署中验证过的步骤和踩过的坑。准备在混合机房、多园区、多云节点上搭k8s的读者可以按这个思路直接落地。1. 先想清楚跨局域网组集群到底难在哪1.1 看起来是“多台机器”实则是一张“独立网络”Kubernetes的架构是主从模式控制面节点上的apiserver要被所有节点访问kubelet要主动向apiserver回连上报状态etcd还要在控制面节点之间同步数据。这里面涉及的端口很多我列一下常用端口组件端口用途apiserver6443所有客户端和kubelet都要访问kubelet10250kubelet与apiserver之间的通信etcd2379、2380etcd客户端通信和节点间peer通信节点端口30000-32767NodePort类型Service对外暴露在同一局域网内这些端口天然互通。但不同局域网之间它们通常是隔离的你所在办公室的内网地址可能是192.168.10.x另一个机房的内网可能是192.168.20.x云上的VPC可能是10.0.x.x。想让这些网段直接互访要么靠路由表打通要么把每台节点塞进同一个虚拟网络平面要么把所有关键端口暴露到公网——第三种方案在生产环境中极其痛苦。用生活类比理解这件事就好比三个小区各自有门禁快递员要互相送件各自的小区门禁不认对方的出入证。最省事的做法是给所有快递员发一张通用的门禁卡让他们在同一个“虚拟小区”里活动。跨局域网组建k8s这张门禁卡就是一套节点间共用的虚拟网络地址。1.2 三条路线别急着动手先选型围绕“互通”我见过三类常用方案。第一类是给所有节点装上虚拟组网工具把每台服务器的IP放到同一个虚拟网段里节点之间用封装转发通信。说白点就是“软件定义网络”的思路。这类方案对已有物理网络改动最小也最容易满足k8s对“所有节点彼此三层可达”的强要求。我下面整篇的核心流程也围绕这条路展开。第二类是用专线把不同机房的内网路由发布到一起。这种方式带宽高、时延低、可靠性也更好但成本高而且需要网络设备配合一般只有大团队才会用。就算用了专线跨网段后容器网络插件依然要仔细选型IPIP、BGP还是VXLAN封装各有各的适用前提。第三类则是把apiserver的地址直接指向某个公网IP再用端口转发映射到内网。我强烈不建议生产环境这么干。一是每个节点的10250端口也要暴露二是证书SAN必须包含公网IP三是kubelet的node-ip如果处理不当插件全乱。短期demo可以节点一多排查网络问题的成本会吞掉所有时间。从我的经验看在“多台服务器分布在不同局域网”这种场景下最通用、成本最低、也最容易复现的是第一类搭一个跨地域的虚拟网络平面然后按k8s标准流程初始化。下面所有实操细节都以这个方案为准。2. 网络规划与基础环境准备2.1 节点角色与地址规划先画好表格再动手以前我搭过一套四节点集群分布大概是这样的控制平面1位于A园区物理内网192.168.10.0/24控制平面2位于B机房物理内网192.168.20.0/24工作节点1位于B机房物理内网192.168.20.0/24工作节点2位于云端VPC物理内网172.16.0.0/16如果直接拿这些物理内网地址去初始化集群三个网段互相不认识后面所有组件都会乱。所以节点规划表必须分成两列物理网卡IP负责节点本身的管理虚拟网络IP负责k8s组件的互相通信。我通常把虚拟网络段规划为10.20.0.0/16规划表大致如下角色主机名物理网卡IP虚拟网络IP备注控制平面k8s-ctl-1192.168.10.1110.20.0.11第一主节点控制平面k8s-ctl-2192.168.20.1110.20.0.12第二主节点工作节点k8s-wk-1192.168.20.2110.20.0.21业务节点工作节点k8s-wk-2172.16.0.2110.20.0.22云端节点为什么虚拟段选10.20.0.0/16而不是常见的192.168.0.0/16因为绝大多数办公网、家宽、机房默认使用192.168.x.x再选192.168容易跟现有网络撞车一旦冲突路由立刻混乱。10.20.0.0/16很少被默认占用相对安全。除了虚拟网络平面还要给Pod网络和Service网络留段。我这里用的是Pod网段10.244.0.0/16这是Flannel默认值也适合大多数情况Service网段10.96.0.0/12kubeadm默认值虚拟网络平面10.20.0.0/16物理网段192.168.10.0/24、192.168.20.0/24、172.16.0.0/16这四个段互不重叠是我在多个环境里验证过的组合。如果你自己规划唯一原则是所有网段之间不能有任何交集。尤其是Pod网段和Service网段不能和任何一台服务器的物理内网、虚拟网络内网冲突否则CNI路由直接废掉。2.2 把各局域网拉进同一张虚拟网络平面跨局域网组集群我最终选了虚拟组网方案。需要说明的是我不在这里点名任何具体商业产品因为这类工具太多了选择哪家取决于你的网络环境、所在地区和安全要求。不同工具实现原理相通装上之后每台服务器会多出一块虚拟网卡分配一个虚拟网络IP节点之间的流量会被封装后在现有物理网络上转发对应用层透明。操作层面你要做的就是把上面表格里的“虚拟网络IP”填进工具的节点配置里。安装完成后先在每台机器上互相ping一下虚拟IP确认四个节点都在同一张网络内再继续下一步。不要急着装k8s先保证底层是通的。这一步如果漏了后面所有问题都会堆到一起来。同时要记得在物理局域网的安全组或防火墙规则里放通虚拟组网工具会用到的通信端口。不同工具有不同端口我通常统一放通TCP和UDP的高位端口确保封装流量不被拦。如果公司网络比较严格还得提前和网络管理员沟通让防火墙允许虚拟网络报文的封装协议通过。2.3 时间同步、主机名、内核参数和镜像源跨局域网的节点分布在不同物理环境系统时间不一致的概率很大。etcd对时间偏差非常敏感时间不同步会导致选举混乱、证书校验失败。我在每台节点上都配置了NTP时间同步并确认chronyd在工作之后用date命令对比各节点时间误差保持在毫秒级。接着是主机名和hosts。主机名建议全部用短名称比如k8s-ctl-1不要用带下划线或大写的主机名。然后在每个节点的/etc/hosts里写上四台机器的虚拟网络IP和主机名这样所有组件解析主机名时都能走到虚拟网络上。这一步听起来很基础但很多问题就是少写了hosts导致组件解析到物理网卡IP。内核参数方面需要开启iptables对桥接流量的转发modprobe br_netfilter echo 1 /proc/sys/net/bridge/bridge-nf-call-iptables echo 1 /proc/sys/net/ipv4/ip_forward这些参数要写进/etc/sysctl.d/k8s.conf里然后执行sysctl --system让其生效。另外建议关闭swapkubeadm会检查swap状态不关闭会直接初始化失败如果暂时无法关闭至少要在kubelet配置里显式允许swap使用但我更推荐直接关掉。容器运行时我用的是containerd。所有节点装好containerd后记得修改配置文件把SystemdCgroup设置为true否则kubelet和containerd的cgroup驱动不一致后面节点会一直NotReady。这些基础操作不复杂但漏任何一个都够你排查半天。3. kubeadm 初始化与节点加入的完整实操3.1 控制平面初始化参数的逐项拆解准备工作做完开始初始化第一个控制平面节点。在kubeadm init之前我会先准备一个kubeadm-config.yaml而不是直接敲一串命令行参数。因为跨局域网方案要配置证书SAN这些内容很难写在一行命令里。下面是一个实际用过的范例apiVersion: kubeadm.k8s.io/v1beta3 kind: InitConfiguration localAPIEndpoint: advertiseAddress: 10.20.0.11 bindPort: 6443 --- apiVersion: kubeadm.k8s.io/v1beta3 kind: ClusterConfiguration kubernetesVersion: v1.28.2 controlPlaneEndpoint: 10.20.0.11:6443 apiServer: certSANs: - 10.20.0.11 - 10.20.0.12 - 10.20.0.21 - 10.20.0.22 - 127.0.0.1 networking: podSubnet: 10.244.0.0/16 serviceSubnet: 10.96.0.0/12 --- apiVersion: kubelet.config.k8s.io/v1beta1 kind: KubeletConfiguration cgroupDriver: systemd几个容易被忽视的地方。advertiseAddress填的是虚拟网络IP。它决定了apiserver对外通告的地址所有kubectl、kubelet都会用这个地址来访问你。如果填成物理内网IP其他局域网节点根本连不上。controlPlaneEndpoint是后续所有节点join时统一的访问入口。单控制平面时我直接填第一个节点的虚拟IP。后面做多控制平面高可用时这里可以换成负载均衡的VIP而不是某个具体节点。certSANs里我提前放进了四台节点的虚拟IP甚至包含将来准备提升为控制平面的节点。为什么因为证书一旦生成再想加新IP非常麻烦。要么改配置重新生成要么手工处理证书而kubeadm后续判断集群是否健康时还会校验证书SAN。提前把可能用到的地址都放进去能省很多事。注意certSANs漏配是跨局域网集群最常见的翻车原因。初始化之前把集群内所有会参与通信的虚拟IP都枚举一遍宁可多写不要少写。执行初始化kubeadm init --config kubeadm-config.yaml --upload-certs--upload-certs这个参数用于多控制平面场景。它会将控制平面证书加密上传到集群后续第二个、第三个控制平面节点join时可以直接拉取证书避免手动拷贝。如果你只有一个控制面节点这个参数可以不写。初始化完成以后kubeadm会输出一段join命令包括token和discovery-token-ca-cert-hash。先别急着关终端保存好这段输出。紧接着配置kubectlmkdir -p $HOME/.kube cp -i /etc/kubernetes/admin.conf $HOME/.kube/config3.2 工作节点加入与证书校验在每一个工作节点上执行kubeadm输出的join命令例如kubeadm join 10.20.0.11:6443 \ --token xxxxx.yyyyy \ --discovery-token-ca-cert-hash sha256:xxxxxxxx \ --node-name k8s-wk-1 \ --cri-socket unix:///run/containerd/containerd.sock这里有两个容易踩坑的地方。一是token默认有效期24小时长时间不加入会过期过期后需要重新生成kubeadm token create --print-join-command二是node-name我建议手动指定为规划表里的名字避免kubelet根据机器hostname自动生成的名字和预期不一致。join执行完回到控制平面节点跑kubectl get nodes正常情况下能看到节点注册。但不能高兴太早下一步装CNI之前节点会一直处于NotReady状态这是正常现象。工作节点加入时kubelet会自动选择一个IP作为node-ip。跨局域网环境下它很可能选到物理网卡IP而不是虚拟网络IP。如果后续发现Pod网络不通首先要检查kubectl get nodes -o wide输出的INTERNAL-IP。如果显示的是物理IP而不是10.20.0.x就需要在/etc/sysconfig/kubelet中加上KUBELET_EXTRA_ARGS--node-ip10.20.0.21然后重启kubeletsystemctl daemon-reload systemctl restart kubelet这个操作要在所有上报IP不对的节点上来一遍。我早期搭建时就吃过亏CNI装好后Pod能创建但跨节点不通查了很久才发现是kubelet上报的IP不对导致Pod网段流量被路由到错误的物理接口。3.3 多控制平面的高可用扩展如果只是“不同局域网内的多台服务器”组集群工作节点加单控制平面已经能满足大部分场景。但控制平面只有一台时它所在的局域网一旦出问题整个集群的API就不可用。所以生产环境我习惯把控制平面也扩到两个或三个。第二个控制平面节点加入的语句在kubeadm init输出里已经给出形如kubeadm join 10.20.0.11:6443 \ --token xxxxx.yyyyy \ --discovery-token-ca-cert-hash sha256:xxxxxxxx \ --control-plane --certificate-key xxxxxxxxx \ --node-name k8s-ctl-2 \ --cri-socket unix:///run/containerd/containerd.sock注意后面的--control-plane和--certificate-key这是控制平面节点和普通工作节点加入的区别。certificate-key只有在kubeadm init时使用了--upload-certs才会生成。这里要多说一句etcd。kubeadm默认采用“堆叠式etcd”即每个控制平面节点本地跑一个etcd实例。跨局域网环境下etcd节点之间的peer地址如果依然选择物理内网IP其他机房的控制节点无法互访etcd集群就建立不起来。所以第二个控制平面节点加入并完成初始化后我马上检查/etc/kubernetes/manifests/etcd.yaml确认--listen-peer-urls和--initial-advertise-peer-urls使用的是虚拟网络IP而不只是127.0.0.1和物理IP。必要时手动修改这个yaml文件etcd容器会自动滚动重启。如果你希望在所有控制平面节点之前放一个统一的流量入口可以在虚拟网络内配置四层负载均衡把6443端口转发到两台控制平面的虚拟IP。controlPlaneEndpoint写成这个负载均衡的虚拟IP例如10.20.0.100:6443这样无论哪个控制面节点异常访问入口都不变。这是一个很实用的高可用方案但需要额外部署一套负载均衡服务。4. 跨局域网容器网络的选型与配置4.1 为什么默认网络方案会“装完不通”初始化完成只代表控制面起来了Pod之间的网络还得靠CNI插件。同一个局域网内很多人直接安装Flannel或Calico默认配置就能用因为默认配置假设所有节点处于同一个二层域或者至少三层路由互通。但跨局域网场景下不同机房之间的物理路由器并不知道Pod网段怎么走默认CNI配置装完Pod创建出来会一直ContainerCreatingkubelet日志里到处是network plugin not ready。所以跨局域网环境选CNI不仅仅是“选一个顺手的”而是要同时考虑“用哪种模式完成跨网段封装转发”。这也是整个集群搭建过程中网络维度最关键的一步。4.2 Flannel、Calico、Cilium 的取舍先看Flannel。它后端模式常用VXLAN或host-gw。host-gw模式要求所有节点二层互通跨局域网直接排除VXLAN模式通过UDP封装实现跨三层网络通信是跨局域网最省心的选择。Flannel优势是简单、配置少、资源占用低适合业务规模不大、不需要精细网络策略的场景。缺点是功能有限NetworkPolicy支持需要额外部署组件。再看Calico。跨局域网时它有两种常见模式IPIP和BGP。BGP模式要求底层网络设备能运行BGP协议或者节点之间能建立BGP邻居跨多个局域网时条件通常不成立IPIP模式则把Pod流量封进IPIP报文只要底层IP路由可达就能跑实现难度低很多。Calico的优点是支持完整的NetworkPolicy网络策略能力很强也是我多数生产环境的首选。最后是Cilium。它功能最丰富基于eBPF性能、可观测性、安全策略都做得很好但在跨局域网场景下通常也建议使用VXLAN封装模式。它的问题是组件更多、对内核版本要求高如果只是单纯想组个跨地域集群用它多少有点杀鸡用牛刀。我的选择习惯是控制面节点不多、网络策略需求简单时用Flannel的VXLAN需要NetworkPolicy和更细粒度管控时用Calico的IPIP模式对性能、可观测性有更高要求的团队再考虑Cilium。三者在跨局域网部署中都需要对外开放对应端口Flannel VXLANUDP 4789Calico IPIPIP协议号4Calico BGPTCP 179Cilium VXLANUDP 8472不管选哪个装完后都要立刻检查所有节点的Pod网段路由是否已经写入。最简单的方法是查看kube-system命名空间下CNI相关Pod是否Running再在一个节点上用临时Pod去ping另一个节点的Pod IP能通才算真通。4.3 Pod 网段和服务网段的避坑规划CNI插件本身不会自动避开冲突网段。很多人会把Pod网段设成10.244.0.0/16Service设成10.96.0.0/12这没问题前提是你的物理网络和虚拟网络都没占用这些段。之前遇到过某环境里业务机器本身就在10.96.x.x网段结果Service ClusterIP一分配就和物理IP冲突应用访问直接乱套。我一般这样规划物理网络、虚拟网络、Pod网络、Service网络四者完全独立Pod网段容量按“未来节点数乘以每节点最大Pod数”估算。比如预计50个节点每节点最多110个Pod需要5500个地址/24只给256个明显不够/16有65536个足够Service网段不需要太大/12已经很大但不要因此随意扩大。跨局域网环境下ClusterIP越多iptables规则越重不必要的服务可以清理如果使用Calico还可以通过IPPool控制Pod IP的分配给不同机房的节点分配不同子网段。这样路由更清晰也方便后续定位问题。但默认情况下一个Pod网段池也能工作不必为了复杂而复杂。5. 高频坑与排查实录5.1 API Server 地址变了证书却不认账症状你换了网络环境或者把controlPlaneEndpoint从物理IP改成虚拟IP然后kubectl访问报x509证书错误或者节点加入时报certificate signed by unknown authority。原因几乎都是apiserver证书的SAN里没有新地址。apiserver证书只对它生成时写入SAN的IP和域名生效你后来访问的IP不在里面证书自然不认账。解决办法有两个一是重新生成apiserver证书并追加SAN操作复杂且容易出错二是在初始化之前就规划好所有地址把虚拟IP、物理IP、将来准备用的VIP全部写进certSANs。这也是我在前面强调提前配SAN的原因。如果你已经初始化完了才发现漏配操作步骤是编辑/etc/kubernetes/kubeadm-config.yaml在apiServer.certSANs追加地址然后备份并删除旧的apiserver证书执行kubeadm init phase certs apiserver --config/etc/kubernetes/kubeadm-config.yaml重新生成证书后再重启apiserver。这一步要小心最好在维护窗口操作。5.2 kubelet 反复崩溃日志指向连接被拒症状节点加入后kubelet日志反复出现connection refused或者Failed to connect to apiserver。排查思路分四步。第一步检查控制平面地址在当前节点上是否通ping 10.20.0.11 telnet 10.20.0.11 6443如果通说明网络问题不大如果不通去查虚拟网络平面的连通性和防火墙放通规则。第二步检查kubelet配置里serverTLSBootstrap和node-ip设置。第三步检查token是否过期过期就用kubeadm token create重新生成。第四步检查当前时间证书校验对时间敏感时间差超过几分钟就会直接拒绝连接。这四步基本覆盖了跨局域网加入节点时最常见的路径。我遇到最多的是第二步也就是node-ip选错表现为节点能注册但状态反复NotReady或者kubelet日志里IP地址和规划不一致。5.3 节点 NotReady问题大多出在 CNI症状所有节点都能注册kubectl get nodes显示NotReady用describe查看Condition提示NetworkPluginNotReady。这个时候先别怀疑kubelet90%是CNI问题。按顺序排查kubectl get pods -n kube-system kubectl logs -n kube-system calico-node-pod ip a show tunl0 ip route | grep 10.244先确认CNI的Pod有没有Running比如Calico的calico-node、Flannel的kube-flannel-ds再看这些DaemonSet Pod的日志重点找报错里有没有“overlay”“network is not ready”等字样最后看每个节点的/etc/cni/net.d下配置文件是否存在权限是否正常。跨局域网场景里CNI Pod报错最多的是“Missing default route”或者“BGP peer not reachable”。前者通常是因为节点的默认路由指向物理内网网关CNI无法把Pod流量正确发出去后者是Calico BGP模式的典型跨网段问题我一般直接切换成IPIP模式。切换方式是在Calico的Installation资源里设置ipipMode为Always重新应用配置后重启calico-node Pod简单有效。5.4 跨地域访问时延高、吞吐低症状集群建好了Pod也能互通但跨地域Pod之间访问延迟高文件传输速度慢。跨局域网集群天然有物理距离时延不可消除。但有几个优化手段很实用尽量让高频互通的Pod调度到同一地域节点上用nodeSelector或节点亲和性约束减少跨地域流量控制面节点集中在一个低时延地域数据面节点分散在各处避免etcd的选举流量跨高延迟链路如果业务流量非常大考虑在两地之间使用带宽更高的物理链路而不是完全依赖虚拟网络封装转发适当调整TCP相关内核参数比如增大net.core.rmem_max和wmem_max提升长链路吞吐要合理预期跨地域集群的最大价值是“统一管理、统一调度”不是“跨地域网络性能对标本地机房”。把关键链路尽量拉到同一地域是更务实的做法。6. 最后说点个人体会跨局域网多台服务器组建k8s集群我实操下来最大的体会是所有看起来诡异的故障最后都能归到一个根因——每个组件实际使用的IP地址到底是不是同一张虚拟网络里的地址。IP规划一致CNI模式选对证书SAN提前留足这个集群就成功了一大半反过来这三件事里任何一件没做好排查时间都会成倍放大。还有一个小技巧值得分享初始化之前先在所有节点上把“虚拟网络IP、主机名、Pod网段、Service网段”写进一个表格每完成一个步骤就核对一次。我每次搭这类环境都会这样操作基本能在第一次就把最容易出错的地址问题消灭掉。如果你正在准备搭建多园区或多云节点的集群希望这篇文章能帮你少走弯路。按这个顺序推进先通虚拟网络再谈k8s初始化先把地址规划好再谈高可用。能做到这两句话你的跨局域网集群大概率第一次就能跑起来。