☰
K8s集群搭建避坑指南:从初始化到生产可用的验证清单
2026/10/2 3:06:28 网站建设 项目流程

1. 集群搭建完,先别急着庆祝:怎么才算"真成功"

k8s集群搭建这件事,很多人把"看到节点Ready"当成了终点,实际上那只是起点。我见过太多人卡在奇怪的地方:明明所有节点都Ready了,dashboard也打开了,但一部署业务就出问题;也有的人初始化master的时候报错,折腾了半天发现是某个小配置的问题。这篇东西我尽量把"搭建完成之后的那几步验证"讲透,顺带把搭建过程中最高频的几个坑位给排掉。

先说结论:一个k8s集群"搭建成功",至少要满足四个条件。第一,所有节点状态都是Ready,并且角色标识正确,master节点不能跑业务负载(除非你是单机测试环境),工作节点要能正常调度Pod。第二,控制平面组件全部健康,apiserver、etcd、scheduler、controller-manager这四个核心组件缺一不可。第三,网络插件正常工作,Pod和Pod之间、Pod和Service之间、节点和Pod之间的网络都要通,这是很多人忽略的地方。第四,DNS可用,CoreDNS跑起来并且能解析Service名,否则你部署的应用互相访问会各种超时。

这四个条件看起来简单,但实际验证起来每个都能踩到坑。这篇不是新手速成教程,而是"我亲手搭过好几套集群之后的经验笔记",适合你已经大概知道k8s是什么、docker和k8s的区别也基本搞明白了、正准备动手搭集群或者正在搭的过程中卡壳了的人。

2. 环境准备里的隐形决定因素:版本搭配和容器运行时选型

很多人一上来就kubeadm init,报错了才回头查环境。实际上环境准备阶段就决定了后面80%的排错工作量。

2.1 操作系统和内核版本:别用太新的,也别用太旧的

我最近一次搭建用的是Rocky Linux 9.x系列,内核版本默认5.14,配套k8s 1.36系列完全没问题。但要注意一个细节:如果你用的是CentOS 7的老内核(3.10),就别想装太新的k8s版本了,因为kubelet对内核有一些特性要求,比如cgroup v2支持。k8s 1.27之后默认走cgroup v2,老内核装新版会直接出现kubelet起不来或者节点反复NotReady。

选操作系统的原则一句话:生产环境选一个你熟悉的、社区资料多的发行版,Ubuntu Server 22.04/24.04或者Rocky Linux 9都行,别蹭最新的。用最新版系统搭生产集群,遇到问题你连资料都搜不到几条。

2.2 容器运行时的选择:containerd还是其他

k8s从1.24版本之后彻底移除了dockershim,你这会儿还纠结docker和k8s区别,属于没抓住重点。现在默认的、也是官方推荐的就是containerd。CRI-O也可以,但用得少,遇到问题求助渠道窄。

containerd的安装有个容易出错的点:配置文件默认不生成,你需要手动创建/etc/containerd/config.toml。大多数人复制默认配置之后,没改SystemdCgroup这个参数,结果Pod起来了但cgroup资源限制不生效,表现就是Pod一直Pending或者被OOM Killer杀掉。

我的做法是装完containerd后执行两条命令:

containerd config default | sed 's/SystemdCgroup = false/SystemdCgroup = true/' | sudo tee /etc/containerd/config.toml sudo systemctl restart containerd

把SystemdCgroup改成true,让containerd直接使用systemd的cgroup驱动,和kubelet保持一致。这一个参数不统一,后面你会遇到非常诡异的资源统计错乱问题。

另外需要确认/etc/containerd/config.toml里sandbox镜像是能拉下来的。国内网络环境可能需要替换sandbox_image字段的内容,否则初始化集群的时候kube-system命名空间里的Pod一直拉不起来,报的错是ImagePullBackOff。这一项属于环境准备里的"隐形地雷",很多人忽略,因为不初始化集群根本发现不了。

2.3 网络规划:Pod网段和Service网段不能冲突

这个错误特别隐蔽。kubeadm init的时候你会指定--pod-network-cidr和--service-cidr,这两个网段必须和你现有服务器的网段不冲突,并且互相不能重叠。

我之前有一台服务器的内网段是10.0.0.0/16,图省事把Pod网段也配成了10.0.0.0/16,结果Calico装完,Node之间路由就乱了,所有跨节点的Pod完全不通。排查半天才意识到网段重叠了。这个真不是危言耸听,很多线上集群故障都是IP段规划不好埋的雷。

建议的配法:

  • 物理机/虚拟机网段:192.168.x.0/24 这种(看你的实际环境)
  • Pod网段:10.244.0.0/16(Flannel常用)或10.20.0.0/16(自己规划)
  • Service网段:10.96.0.0/12(kubeadm默认)或10.100.0.0/16

这三个段,互相之间绝对不能重叠,更不能和物理网络重叠。

3. master节点初始化的那道坎:the api server is not healthy报错全复盘

标题里那个热搜词太真实了:k8s控制节点master初始化显示the api server is not healthy after 4m0.00747357s。这个报错我敢说,十个搭集群的人至少五个遇见过。kubeadm init卡在这里,等了几分钟给你一个红色报错,然后告诉你"Unfortunately, an error has occurred: timed out waiting for the condition"。

3.1 报错发生的完整背景和可能原因

这里的机制是:kubeadm init在初始化过程中,会持续探测localhost:6443端口上的kube-apiserver健康检查接口(/healthz)。默认超时时间是4分钟。如果4分钟内apiserver一直没健康,就认为初始化失败,并且把之前创建的部分组件回滚。

为什么apiserver会不健康?排查路径按顺序走:

  1. apiserver容器没起来:执行crictl ps -a | grep apiserver,看看容器状态是Running还是Exited。如果是Exited,看日志crictl logs <容器id>,高频原因是镜像拉取失败或者启动参数不对。
  2. etcd没起来:apiserver启动依赖etcd,etcd挂了apiserver也会起不来。执行crictl ps -a | grep etcd,重点看etcd的日志里有没有"failed to load data"之类的报错。
  3. 6443端口没监听:执行ss -lntp | grep 6443,如果端口没监听,说明apiserver进程压根没起来。
  4. Swap没关:这个老生常谈但是真的常见。kubelet和apiserver都是通过容器跑的,但kubelet进程本身在宿主机上。Swap开着会导致kubelet性能异常,间接影响apiserver的健康检查。你需要在所有节点上关闭swap:swapoff -a并注释掉/etc/fstab里的swap条目。
  5. 容器运行时cgroup驱动不一致:这个前面已经说过,SystemdCgroup没开,会导致kubelet和containerd之间的资源统计不一致,kubelet会报"failed to run Kubelet"之类的错,进而导致apiserver无法通过kubelet的健康上报。

3.2 我踩过的那次根因:apiserver端口和主机名解析

有一次我排查了半天,最后定位到的问题特别乌龙:/etc/hosts文件里没有写master节点自己的主机名映射。

kubeadm生成的apiserver证书里面包含的主机名是master01,但是服务器的/etc/hosts里只写了IP后面跟着一个完全不同的hostname。apiserver启动后会去绑定证书里的SAN(Subject Alternative Name),使用了错误的主机名解析之后监听地址和健康检查探测地址不匹配,于是kubeadm一直探测不到健康的apiserver,等到超时只能报错。

解决办法很简单:

# 在master节点上执行 echo "192.168.10.10 master01" >> /etc/hosts # 确保hostnamectl set-hostname master01 的设置和/etc/hosts一致

注意一个细节:hostnamectl set-hostname改完之后,当前shell里的$HOSTNAME还是旧值,需要bash重新登入或者exec bash刷新环境变量。但即便这样,/etc/hosts里还是要写映射。

3.3 如果你已经排了以上所有点还不行:重启再试,但别反复硬试

kubeadm init失败之后,机器状态并不干净,不清理就直接再次init,会遇到各种残留问题。正确操作是:

kubeadm reset -f rm -rf /etc/cni/net.d rm -rf $HOME/.kube systemctl restart containerd # 然后再执行kubeadm init

这里有个心理层面的建议:不要在一个报错上死磕超过三次。如果同样的报错出现三遍以上,大概率你排错的方向不对,跳出"修改参数-重试"的循环,把日志从头到尾打印出来,静下心看一遍。

查看完整apiserver日志的方法:

journalctl -u kubelet -f crictl logs $(crictl ps -a | grep apiserver | awk '{print $1}')

日志会直接告诉你问题在哪。有一次我看到的是"Unable to connect to etcd: dial tcp...connection refused",顺藤摸瓜发现etcd容器起不来是因为磁盘空间满了,apiserver只是连带受害者。所以记住:apiserver不健康只是结果,不是原因,永远要往后看一层。

4. 网络插件的选择与验证:集群能跑起来,网络未必通

master节点初始化成功之后,kubectl get nodes会发现master是NotReady状态,因为还没装网络插件。这一步是集群搭建里最影响体感的部分。

4.1 选择网络插件的决策依据

当前主流选择就是Calico和Flannel。我的建议:

  • 单集群、规模不大、追求简单:用Flannel。配置少,启动快,但功能也少,不支持网络策略。
  • 生产环境、需要NetworkPolicy控制东西向流量:用Calico。功能全、性能也够用,配置相对复杂一些,但长期看省心。

很多教程默认推Calico,但Calico的镜像更多、初始化更慢,对新手不够友好。如果你只是搭个测试环境验证业务,Flannel够用。如果这套集群要上生产,直接上Calico不纠结。

4.2 Calico部署中的常见坑

以Calico为例,最常见的问题就是安装完之后,Node一直是NotReady。用kubectl get pods -n kube-system看到calico-node的状态是CrashLoopBackOff。

查看单个Pod日志:

kubectl logs -n kube-system calico-node-xxxxx

高频报错是Error: calico/node is not ready: BIRD is not ready: BIRD has not established a session...,这个问题的根源通常是:

  • Pod网段和Calico默认网段冲突:如果你在kubeadm init时指定了--pod-network-cidr=10.244.0.0/16,安装Calico时用的默认IP池是192.168.0.0/16,这两者必须改一个保持一致。用kubectl edit ippool default-ipv4-ippool -n calico-system来改,或者装之前就先改好manifest。
  • 主机名没有解析:Calico的BGP需要节点间通过主机名通信,所以每个节点的/etc/hosts里要写清楚各个节点的主机名映射。只写了master的hosts是不行的,所有节点都要配置。

Flannel相对简单,通常是kubectl apply -f https://.../kube-flannel.yml一把过。但Flannel也有一个要注意的点:它的默认网段固定是10.244.0.0/16,所以kubeadm init时--pod-network-cidr就要指定这个,不要自己另想一个网段然后又去改Flannel配置,纯属给自己添麻烦。

我的建议是:要么全用默认网段,要么改配置的时候务必记得两处一起改,对齐之后再apply,就很少出幺蛾子。

4.3 网络验证的正确姿势:三个层次的通信测试

网络插件装完、Node全部变成Ready之后,别急着部署业务。网络层的验证要做三层,每一层都有对应的测试方法。

第一层:同节点Pod互通
kubectl run test-a --image=busybox -- sleep 3600 kubectl exec -it test-a -- sh -c 'wget -qO- http://<另一个Pod的IP>:端口'

这一层如果不同,说明容器运行时或者Pod IP段有问题。

第二层:跨节点Pod互通

这是最关键的测试。你的集群里有master01和worker01两个节点,分别跑一个测试Pod,然后从master01上的Pod去访问worker01上的Pod IP。如果不通,优先检查网络插件的BGP状态或者VXLAN隧道状态。

第三层:Service和DNS

部署一个简单的nginx服务:

kubectl create deployment nginx --image=nginx kubectl expose deployment nginx --port=80 --target-port=80

然后从任意Pod里访问Service名:

kubectl run curl-test --image=curlimages/curl --rm -it -- sh # 里面执行 curl http://nginx.default.svc.cluster.local

能通,说明CoreDNS和kube-proxy的iptables/IPVS规则都正常。这一步通过了,你的集群才算真正"通了"。

5. 从"搭成功"到"能扛事":生产环境必备的验证清单

集群通了,节点Ready了,这只能叫"搭成功",离"可用"还有距离。下面这份清单是我每次搭建完集群后必做的检查,也是从"能跑"到"扛得住"之间的关键一步。

5.1 控制平面组件的健康检查

kubectl get componentstatus命令现在虽然还能用但是输出不完整,推荐直接看组件Pod状态:

kubectl get pods -n kube-system -o wide

重点关注etcd、kube-apiserver、kube-controller-manager、kube-scheduler这四类Pod的状态。都处于Running并且重启次数为0,才算健康。重启次数大于0,说明启动过程中有异常,需要继续看日志。

还要检查etcd的集群健康状态:

ETCDCTL_API=3 etcdctl --cacert=/etc/kubernetes/pki/etcd/ca.crt \ --cert=/etc/kubernetes/pki/etcd/server.crt \ --key=/etc/kubernetes/pki/etcd/server.key \ endpoint health --cluster

如果输出healthy就正常。etcd是整个集群的"元数据库",它的稳定性就是集群的稳定性,这一步值得花两分钟做。

5.2 资源请求与限制:别再当"裸奔"集群

很多新搭的集群,业务部署的时候连resources都不写,这是生产环境大忌。你在验证阶段就可以先把集群自身的系统组件检查一遍,看有没有关键组件没设资源限制。同时给自己定个规矩:部署任何业务应用,必须设置requests和limits,否则调度器无法合理分配资源,一台节点出问题就可能引发雪崩。

如果想要生产环境更稳,建议给kube-system命名空间加上默认的LimitRange:

apiVersion: v1 kind: LimitRange metadata: name: kube-system-default-limit namespace: kube-system spec: limits: - default: cpu: 500m memory: 512Mi defaultRequest: cpu: 100m memory: 128Mi type: Container

这样就算某个组件忘了写资源限制,也有个兜底。

5.3 节点安全与高可用:别把所有鸡蛋放一个篮子

单master节点的集群,etcd就一份数据,master挂了整个集群的控制面就瘫了。测试环境无所谓,生产环境你至少要做三master的高可用架构。用kubeadm搭高可用集群,核心是VIP(虚拟IP)方案,Keepalived加HAProxy或者直接上云厂商的负载均衡。这一步我没有展开实操的打算,因为篇幅不允许,但你必须知道:单机控制面不能进生产。

另外,节点安全方面,至少要确认kubelet的匿名访问是关闭的,/etc/kubernetes/kubelet.conf里不能允许未授权请求。你可以测一下:

curl -k https://node-ip:10250/pods

如果返回Unauthorized,说明配置正常。如果直接返回Pod列表,说明匿名访问开着,必须关闭。

5.4 监控、日志和告警:晚装不如早装

我建议集群一搭完就部署监控体系,哪怕只是最简配。目前的常用选择还是Prometheus搭Grafana,再加一套Loki或者ELK收日志。说个实际感受:集群出问题的时候,没有监控日志,你就是瞎子摸象。

热度词里出现"k8s生产环境中常见的故障影响到用户"这句话,说明在生产环境中故障排查是多么痛的领悟。哪怕你现在只是测试集群,也建议先把metrics-server装上,至少让kubectl top node能输出资源使用量:

kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

装好之后需要注意:metrics-server默认不配置证书跳过校验,需要在部署的yaml里给容器加一个参数--kubelet-insecure-tls(测试环境)或者配置好证书。否则metrics-server的Pod会一直CrashLoopBackOff。

5.5 GPU调用和存储就更远了?不,提前规划

如果准备跑AI类业务,GPU的调度是必须提前验证的。k8s调用GPU的方式目前比较主流的是安装NVIDIA Device Plugin,在节点上装了NVIDIA驱动和nvidia-container-toolkit之后,用DaemonSet方式部署插件,然后Pod声明nvidia.com/gpu资源。验证方法:

kubectl describe node worker-gpu-01 | grep nvidia.com/gpu

如果有输出,说明GPU资源已经被kubelet识别。

存储方面,测试环境可以先不用太纠结,但生产环境一定要提前规划好用什么存储方案。如果你的存储方案是自建NFS,记得装NFS CSI驱动或者用自带的nfs-client-provisioner。StorageClass不配好,PVC一直Pending,业务根本部署不起来。

6. 踩坑实录总结:我搭集群踩过的那些"低级"错误

写到最后,分享几个我最想穿越回去告诉当年自己的经验,希望你少走弯路。

6.1 系统防火墙真的是第一杀手

我有一台节点,启动之后始终连不上apiserver,Kubelet日志里没有任何有效报错,后来发现是firewalld默认规则把6443端口deny了。在测试学习阶段,最简单的办法是暂时关闭防火墙:

systemctl stop firewalld systemctl disable firewalld

但生产环境请不要直接关防火墙,而是开放必要的端口:6443(apiserver)、2379/2380(etcd)、10250(kubelet)、10256(kube-proxy),以及网络插件需要的端口。OpenStack或云环境下,安全组也要放通这些端口,很多人用的云主机安全组把内网端口挡了,怎么配都不通,最后发现是安全组的事。

6.2 时钟同步没做好,证书会闹鬼

k8s整个体系太吃证书,证书又太吃系统时间。节点之间时间差超过5分钟,证书验证就会失败,各种"x509: certificate has expired or is not yet valid"报错。这个报错会把你绕晕,因为明明刚生成的证书怎么会过期?其实就是时钟问题。所有节点统一配置chrony或ntpd,这个钱和时间一定不能省。

6.3 生产环境升级前,永远先看官方变更日志

k8s每个版本的API都有变化,升级前不看变更日志,代价可能是一堆旧资源全部失效。1.16版本移除了很多extensions/v1beta1的API,1.22移除了很多v1beta1的API,1.25之后又移除了PodSecurityPolicy。未经测试的集群升级比不升级危险得多。如果只是个人学习,大版本升级倒也不用太紧张,但至少先跑一遍kubectl convert相关工具,检查一下现有资源的API版本是否合规。

6.4 分布式存储别自己造轮子

你要是搭Redis集群或者数据库这类有状态服务,存储方案的选型会直接影响上层的运行效果。别上来就自己搭一个分布式的什么存储,先用NFS这种最简单的方案跑通验证,等你在k8s上积累了一定经验,再考虑用更专业的分布式存储,特别是生产环境,存储方案切换的成本高到你不想试第二次。

7. 集群搭完之后的下一步规划

k8s集群搭建完成只是开始。从热度词里看,很多人还会继续研究namespace、Redis集群部署、GPU调用这些方向,说明大家的基本路径都是:搭集群、熟悉核心概念、上业务、做运维。顺着这个路径走下去,你迟早要面对的问题包括:怎么规范namespace的使用、怎么设计多集群方案、怎么做弹性伸缩、怎么在集群里部署大数据生态(Spark、Hadoop都在热词里出现了)。

我最后再给一个建议:从搭建到跑业务的整个过程中,每踩一个坑,就把报错信息、排查过程、最终根因记下来。不用写得很正式,几句话就行。积累过几十个坑之后,你会发现自己对k8s的理解会上一个大台阶。这套集群从"能跑"到"能扛事",靠的其实就是这些实打实的经验沉淀。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询