
公网环境里搭一套能用的 Kubernetes和在本机用 kind、minikube 跑个玩具集群完全是两码事。前者你要面对的是真实的网络拓扑、真实的攻击面、真实的证书链和真实的账单后者你只需要敲一行命令然后等它自己好。我见过太多人本地玩得飞起一到公网就卡在节点 NotReady、Pod 拉不到镜像、Service 死活不通这几个经典坑上折腾两三天最后放弃。这篇就把公网 K8S 搭建这件事从头到尾拆开讲清楚它到底难在哪、每一步为什么这么做、哪些参数必须改、哪些默认值会要你的命以及我自己踩过的那些文档里不会写的坑。适合已经懂一点容器基础、准备把服务真正搬到公网集群上的朋友也适合被节点通信问题折磨过、想系统补一遍网络原理的人。1. 公网集群和本地玩具集群的本质差别很多人对公网 K8S 的第一印象是不就是把 minikube 换成云主机吗这个认知偏差是后面所有痛苦的根源。本地集群和公网集群在技术栈上看着一样但约束条件完全不同理解这些差别比记住任何命令都重要。1.1 网络模型从随便通变成处处要打通本地跑 minikube 的时候宿主机、虚拟机、容器三层网络基本是 NAT 叠加 NAT反正都在一台机器里怎么绕都能通。公网集群不一样你的控制平面和 worker 节点可能分布在不同可用区、不同网段甚至不同机房。K8S 的网络模型要求每个 Pod 有独立 IP且任意两个 Pod 之间可以直接通信不经过 NAT这条硬性要求在多主机公网环境下需要 CNI 插件来真正实现而不是像本地那样靠默认网桥糊弄过去。我一开始用某云厂商的两台机器搭集群节点之间内网互通没问题但忘了检查安全组kubelet 的 10250 端口被挡在外面结果控制平面一直报节点 NotReady日志里全是dial tcp ...:10250: i/o timeout。这个坑的迷惑性在于kubectl get nodes显示的是 NotReady但真正的原因在网络层不在节点本身。所以公网搭建的第一课是把所有需要的端口在安全组/防火墙层面先放行再谈装软件。1.2 证书和身份从自签凑合变成必须规划本地集群的证书基本是工具自动生成、自动过期、自动续你根本不用管。公网集群里API Server 的证书 SAN 必须包含所有节点可能访问它的地址——公网 IP、内网 IP、域名、集群内部 Service IP通常是 10.96.0.1 这类。少写一个对应的访问方式就会报x509: certificate is valid for ..., not ...。这个错误的坑在于它出现得很晚集群装完了节点也 Ready 了结果某个组件连 API Server 时突然证书校验失败。我建议在初始化之前把所有会用到的访问地址列成一张表一次性写进配置别指望后面补。1.3 资源与成本从无限变成要算账本地你开十个 Pod 无所谓公网每台机器都是真金白银。控制平面单独占一台小规格机器worker 按业务负载规划还要预留系统组件CNI、CoreDNS、metrics-server、ingress controller的资源。我一般给每个 worker 预留 10% 到 15% 的 CPU 和内存给系统组件否则业务 Pod 一多节点直接 OOMkubelet 开始驱逐 Pod整个集群像多米诺骨牌一样连锁反应。下面这张表是我总结的本地集群和公网集群的关键差异搭建前对照着过一遍能省掉大量返工维度本地玩具集群公网生产集群网络NAT 叠加随便通需 CNI 实现 Pod 互通安全组要放行证书工具自动生成必须手动规划 SAN 列表高可用单点无所谓控制平面建议多副本 负载均衡资源无限开需预留系统资源按账单规划安全基本不设防API Server 必须限制访问来源持久化本地目录凑合需独立存储方案2. 搭建前的硬性准备机器、系统与端口真正动手之前准备工作做扎实后面能少踩一半的坑。这一节讲的是装软件之前必须搞定的事很多人急着敲 kubeadm init结果卡在系统配置上。2.1 机器规格与操作系统选择控制平面节点我建议至少 2 核 4Gworker 节点按业务来但最低 2 核 4G 起步因为光系统组件就要吃掉不少。操作系统优先选主流的 Linux 发行版内核版本不要太老因为 K8S 对内核的 cgroup、iptables、overlayfs 支持有要求。我一般用较新的稳定版内核避免 CNI 插件因为内核特性缺失而工作异常。系统层面必须做的几件事我列成清单逐条确认关闭 swapK8S 的调度器假设节点内存是精确可用的swap 会让这个假设失效。执行swapoff -a并注释掉/etc/fstab里的 swap 行重启后确认free -h里 swap 是 0。加载内核模块overlay和br_netfilter两个模块必须加载写入/etc/modules-load.d/k8s.conf保证开机自动加载。调整内核参数开启 IP 转发让 iptables 能正确处理桥接流量。写入/etc/sysctl.d/k8s.conf内容包含net.bridge.bridge-nf-call-iptables1、net.ipv4.ip_forward1等然后sysctl --system生效。时间同步所有节点时间必须一致否则证书校验、日志排查都会出问题。配好时间同步服务确认timedatectl显示同步正常。提示关闭 swap 这一步在有些云主机上会被开机脚本重新打开务必重启后复查一次我吃过这个亏集群跑了一周突然开始报 swap 相关的告警。2.2 端口放行的完整清单公网环境最容易漏的就是端口。下面这张表是我实际搭建时用的放行清单控制平面和 worker 分开列安全组和系统防火墙都要配组件端口协议说明API Server6443TCP所有节点和外部 kubectl 访问etcd2379-2380TCP仅控制平面之间kubelet API10250TCP控制平面访问 workerkube-scheduler10259TCP控制平面内部kube-controller-manager10257TCP控制平面内部NodePort 服务30000-32767TCP按需放行CNI 插件按插件而定-如某 CNI 用 VXLAN 需放行 UDP 8472这里有个经验不要图省事把 6443 对全网开放。API Server 是集群的命门暴露在公网等于把大门钥匙挂在门口。正确做法是只允许你自己的管理 IP 和集群内部网段访问外部访问走跳板或专用管理通道。我见过有人图方便开了全网结果几天后 API Server 日志里全是扫描请求。2.3 容器运行时与 kubelet 的前置配置容器运行时的选择上现在主流是 containerd配置相对简单。安装完之后要生成默认配置并调整两处一是 cgroup 驱动必须设为systemd和 kubelet 保持一致否则会出现节点 Ready 但 Pod 起不来的诡异现象二是配置镜像加速或私有仓库地址公网拉取官方镜像经常超时。kubelet 的 cgroup 驱动在 kubeadm 配置文件里也要显式指定cgroupDriver: systemd。这两处不一致是新手最常见的坑之一症状是 kubelet 日志里反复报failed to create pod sandbox但错误信息不会直接告诉你驱动不匹配得自己去比对配置。3. 用 kubeadm 初始化控制平面参数背后的取舍kubeadm 是官方推荐的搭建工具命令看着简单但每个参数都有讲究。这一节把初始化过程拆开重点讲为什么这么配。3.1 初始化配置文件的完整写法不要直接用kubeadm init裸命令而是写一个配置文件把所有关键参数固化下来方便复现和排查。核心配置大概长这样apiVersion: kubeadm.k8s.io/v1beta3 kind: InitConfiguration localAPIEndpoint: advertiseAddress: 10.0.0.10 bindPort: 6443 nodeRegistration: criSocket: unix:///run/containerd/containerd.sock kubeletExtraArgs: cgroup-driver: systemd --- apiVersion: kubeadm.k8s.io/v1beta3 kind: ClusterConfiguration kubernetesVersion: v1.28.0 controlPlaneEndpoint: api.example.com:6443 networking: podSubnet: 10.244.0.0/16 serviceSubnet: 10.96.0.0/12 apiServer: certSANs: - api.example.com - 10.0.0.10 - 203.0.113.10几个关键点解释一下。advertiseAddress填节点的内网 IP不要填公网 IP因为节点间通信走内网更稳更快。controlPlaneEndpoint填一个域名指向控制平面的负载均衡地址这样以后扩容控制平面或者换 IP 都不用重新签证书。certSANs把所有可能访问 API Server 的地址都列上包括域名、内网 IP、公网 IP。podSubnet和serviceSubnet不能和节点所在网段重叠这是硬性要求。我一般用10.244.0.0/16给 Pod10.96.0.0/12给 Service这两个是社区常用值和大多数云主机内网网段不冲突。如果你的内网恰好用了 10.0.0.0/8 的大段那就要换别的网段比如172.16.0.0/16。3.2 初始化之后必须做的三件事kubeadm init成功后会打印一段提示告诉你接下来要做什么。很多人复制粘贴完就以为完事了其实还有三件事必须确认第一配置 kubectl 的 kubeconfig。把/etc/kubernetes/admin.conf复制到~/.kube/config并设置好权限。如果是多用户环境注意这个文件包含集群管理员凭证权限要收紧。第二安装 CNI 插件。没有 CNI节点会一直 NotReady因为 kubelet 在等网络就绪。CNI 的选择后面单独讲这里先记住装完 CNI 后等一两分钟kubectl get nodes才会变 Ready。第三确认 CoreDNS 起来了。kubectl get pods -n kube-system里应该有 CoreDNS 的 Pod如果它一直 Pending 或 CrashLoopBackOff多半是 CNI 没装好或者 Pod 网段配错了。3.3 控制平面高可用的现实考量单控制平面在公网环境是有风险的这台机器一挂整个集群就失去管理能力虽然已运行的 Pod 还能跑。生产环境建议至少三个控制平面节点前面挂一个负载均衡。kubeadm 支持通过controlPlaneEndpoint指向负载均衡地址来实现高可用。但高可用不是免费的三个控制平面意味着三份 etcdetcd 对磁盘 IO 敏感机器规格不能太寒酸而且 etcd 集群的维护备份、恢复、成员变更复杂度上升。我的建议是如果业务规模不大、能接受短暂管理中断单控制平面 定期 etcd 备份也够用如果业务关键老老实实上三节点高可用别省这个钱。4. CNI 插件选型Pod 网络到底怎么通CNI 是公网 K8S 搭建里最容易出问题的环节因为它直接决定了 Pod 之间能不能通、跨节点能不能通、网络性能如何。这一节把主流方案和选型逻辑讲透。4.1 三种主流 CNI 的适用场景市面上 CNI 插件很多但真正常用的就三类我按使用体验排一下第一类简单直接的 overlay 方案。这类插件通过封装比如 VXLAN在节点间建立隧道把 Pod 网络包封装在节点网络包里传输。优点是配置简单、跨网段也能通、对底层网络没要求缺点是封装有性能开销MTU 要相应调小通常从 1500 调到 1450否则大包会分片甚至丢包。适合节点分布在不同网段、底层网络不可控的场景。第二类基于 BGP 的路由方案。这类插件把 Pod 网段通过 BGP 协议通告给底层网络实现原生路由转发没有封装开销性能好。缺点是需要底层网络支持 BGP配置复杂度高跨网段场景下不一定适用。适合自建机房、网络可控的环境。第三类云厂商集成的方案。很多云平台有自己的 CNI 实现直接对接云网络性能和稳定性都不错但绑定平台迁移成本高。我个人的选择逻辑是公网多主机、底层网络不完全可控的情况下优先选 overlay 方案配置省心出问题好排查。性能敏感且网络可控再考虑 BGP 方案。4.2 装完 CNI 后的验证方法CNI 装完不能只看节点 Ready 就完事必须验证 Pod 跨节点通信。我的验证步骤是这样的在两个不同节点上各起一个测试 Pod用kubectl run加--overrides指定节点或者用 nodeSelector。进入其中一个 Podping另一个 Pod 的 IP确认能通。用curl或nc测试 TCP 端口连通性因为 ICMP 通不代表 TCP 通防火墙可能只挡了 TCP。检查 MTU用ping -M do -s 1472测试大包如果不通说明 MTU 设置有问题需要调小 CNI 的 MTU 配置。注意MTU 问题非常隐蔽小包正常、大包失败表现为某些 HTTP 请求卡住、数据库连接超时。我遇到过一次排查了半天才发现是 VXLAN 封装后 MTU 没调导致大响应体被丢弃。4.3 网络策略的必要性公网集群默认所有 Pod 之间可以互相访问这在多租户或对外暴露服务的场景下是安全隐患。网络策略NetworkPolicy可以限制 Pod 间的流量但前提是你的 CNI 支持它。选 CNI 时一定要确认它支持 NetworkPolicy否则后面想加隔离只能换插件。我一般会给命名空间加上默认拒绝策略然后按需放行。这样即使某个服务被攻破攻击者也不能在集群内横向移动。这个配置在搭建阶段就该规划好别等出了事再补。5. 节点加入与集群验收别急着上业务控制平面好了、CNI 通了接下来是加 worker 节点和全面验收。这一步做扎实后面上业务才稳。5.1 worker 节点加入的正确姿势kubeadm init会打印一条kubeadm join命令包含 token 和证书哈希。这条命令有时效性token 默认 24 小时过期过期了要用kubeadm token create --print-join-command重新生成。加入之前worker 节点要做和控制平面一样的前置配置关 swap、加载模块、调内核参数、装容器运行时、配 kubelet。这些步骤漏一个join 就会失败或者加入后节点异常。join 成功后在控制平面执行kubectl get nodes等节点变 Ready。如果一直 NotReady按这个顺序排查kubelet 服务状态 → kubelet 日志 → 容器运行时状态 → CNI Pod 状态 → 安全组端口。这个排查链路我用了很多次基本能定位九成问题。5.2 集群验收的完整清单节点 Ready 只是开始下面这些验收项我每次搭建都会过一遍验收项命令预期结果节点状态kubectl get nodes -o wide全部 Ready版本一致系统组件kubectl get pods -n kube-system全部 RunningDNS 解析起测试 Pod 解析 Service 名能解析到 ClusterIP跨节点通信两节点 Pod 互 ping通Service 访问创建 Service 后 curl ClusterIP通外部访问NodePort 或 LoadBalancer从集群外能访问存储创建 PVC 并挂载挂载成功可读写镜像拉取起一个用公共镜像的 Pod能拉下来这张表里DNS 解析和 Service 访问是最容易出问题的。CoreDNS 的 Pod 如果没起来所有服务发现都会失败kube-proxy 如果配置有问题Service 的 ClusterIP 就访问不通。这两个组件都在 kube-system 里验收时重点看。5.3 常见 NotReady 的排查链路节点 NotReady 是公网搭建最高频的问题我把完整的排查链路写出来照着走基本能定位第一步systemctl status kubelet看服务是否在跑。如果服务挂了journalctl -u kubelet -n 100看日志常见原因是容器运行时没起、cgroup 驱动不匹配、证书过期。第二步如果 kubelet 在跑但节点 NotReady看 kubelet 日志里有没有连不上 API Server 的报错。有的话检查 6443 端口是否放行、controlPlaneEndpoint是否可达。第三步看 CNI Pod 状态。CNI 没起来节点网络就绪不了节点自然 NotReady。CNI Pod 起不来的原因通常是镜像拉不下来或者配置错误。第四步检查节点上的kubelet配置和容器运行时配置是否一致特别是 cgroup 驱动。这个链路的关键是从下往上查先确认基础服务容器运行时、kubelet正常再查网络API Server 连通性、CNI最后查配置一致性。反过来查容易迷失在日志里。6. 公网暴露面收敛与日常维护集群搭起来能用只是及格线公网环境下的安全和维护才是长期考验。这一节讲怎么把暴露面收住以及日常要盯什么。6.1 API Server 的访问控制前面提过6443 端口绝对不能对全网开放。除了安全组限制来源 IP还可以在 API Server 层面加额外的认证和授权控制。比如启用审计日志记录所有 API 请求出问题时能追溯配置 RBAC给不同用户不同权限别所有人都用 admin 凭证。kubeconfig 的分发也要注意给开发人员的 kubeconfig 应该是受限权限的只能操作特定命名空间不能碰集群级资源。我见过把 admin kubeconfig 发给整个团队的等于把集群 root 权限人手一份风险极大。6.2 证书轮换与 etcd 备份K8S 的证书有有效期默认一年。到期不轮换集群直接不可用。kubeadm 提供了kubeadm certs check-expiration查看证书状态kubeadm certs renew轮换证书。建议在证书到期前一个月做轮换并把这个操作写进运维日历。etcd 备份更是重中之重。etcd 存着集群所有状态丢了等于集群重建。备份命令用etcdctl snapshot save建议每天定时备份备份文件存到集群外的独立存储。恢复流程也要提前演练别等真出事才发现备份文件不能用。6.3 监控与告警的最小配置公网集群至少要有基础监控节点资源CPU、内存、磁盘、网络、Pod 状态、API Server 健康度。metrics-server 提供基础指标再配一个监控系统采集和告警。磁盘尤其要盯etcd 对磁盘 IO 敏感磁盘满了整个集群会雪崩。我一般设几条关键告警节点磁盘使用率超过 80%、节点 NotReady 超过 5 分钟、API Server 不可达、etcd 备份失败。这几条覆盖了最常见的故障场景能让你在用户感知之前发现问题。7. 我踩过的几个真实坑与应对文档里不会写的经验往往是最值钱的。这一节分享几个我在公网搭建中真实踩过的坑以及后来总结的应对方法。7.1 镜像拉取超时导致的假性故障有一次集群装好所有系统组件都 Running但起业务 Pod 时一直 ImagePullBackOff。排查发现是节点访问公共镜像仓库超时。这个问题在公网环境很常见尤其是跨境访问。应对方法是配置镜像加速地址或者把常用镜像同步到私有仓库。containerd 的镜像配置在/etc/containerd/config.toml里改完重启 containerd 生效。这个坑的迷惑性在于它看起来像集群故障实际是网络问题。所以验收清单里我特意加了镜像拉取这一项提前暴露问题。7.2 安全组规则的方向搞反安全组规则分入站和出站很多人只配了入站忘了出站或者方向搞反。我遇到过一次节点间内网通信正常但 Pod 访问外网失败查了半天发现是出站规则限制了。公网集群的节点通常需要出站访问外网拉镜像、同步时间出站规则要放行。排查这类问题的技巧是在节点上用curl测试目标地址如果节点能通但 Pod 不通问题在 CNI 或 Pod 网络如果节点本身就不通问题在安全组或路由。7.3 资源预留不足引发的连锁驱逐早期搭建时我没给系统组件预留资源业务 Pod 一多节点内存吃紧kubelet 开始驱逐 Pod。驱逐是按优先级来的结果把 CoreDNS 这类关键组件也驱逐了导致整个集群 DNS 解析失败所有服务雪崩。后来我学乖了给 kubelet 配置system-reserved和kube-reserved把系统资源锁住业务 Pod 再怎么涨也动不了系统组件的份额。这个配置在 kubelet 的启动参数里搭建阶段就该配好。7.4 时间不同步导致的证书校验失败有次集群运行一段时间后某个节点突然连不上 API Server报证书校验失败。查下来是这台节点的时间漂移了几分钟导致证书有效期校验不通过。公网环境的机器时间漂移比想象中常见尤其是虚拟化环境。应对方法是确保时间同步服务正常运行并加监控告警。timedatectl能看同步状态chronyc tracking能看漂移量。这个坑提醒我基础设施的小事往往是大故障的根因。8. 从单机到生产的演进路径最后聊聊演进。公网 K8S 搭建不是一锤子买卖随着业务增长集群形态也要跟着变。这一节给几条演进建议都是我实际走过的路。8.1 单控制平面到高可用的平滑过渡如果一开始是单控制平面后面要升级到三节点高可用kubeadm 支持逐步加入控制平面节点。步骤是先准备好新节点用kubeadm join --control-plane加入然后配置负载均衡把controlPlaneEndpoint指过去。这个过程需要停机窗口建议在业务低峰期做并提前备份 etcd。过渡期间要注意 etcd 成员变更的顺序一次加一个确认集群健康再加下一个。etcd 集群对成员数量有要求奇数从 1 到 3 是安全的从 3 到 5 也可以但别搞成偶数。8.2 存储方案的选型演进初期可以用本地存储或简单的网络存储凑合业务上量后要换成更可靠的方案。选择存储方案时考虑三点一是是否支持动态供给PVC 自动创建 PV二是是否有副本数据可靠性三是性能是否满足业务。公网环境下跨可用区的存储方案能提升容灾能力但延迟会高一些要权衡。8.3 集群拆分与多集群管理单集群规模大了之后会遇到调度瓶颈、故障域过大、升级风险高等问题。这时候可以考虑按业务拆分多集群或者用多集群管理工具统一纳管。拆分的维度可以是环境测试/生产、业务线、地域。多集群管理的复杂度不低建议在单集群确实遇到瓶颈时再考虑别为了架构而架构。我个人在实际操作中的体会是公网 K8S 搭建的难点从来不在装而在配和守。装的过程照着文档走基本能成但配置的每一个参数背后都有原因守的每一天都要盯着证书、备份、资源和安全。把这些想清楚集群才能真的稳。