资讯动态

K8s集群初始化实战:kubeadm搭建生产级可用集群

发布时间:2026/10/21 5:57:01 来源:尧图企业网站定制
1. 项目概述从零开始搭一套真正能干活的K8s集群“k8s的初始及搭建”这六个字是过去三年里我被问得最多的问题之一。不是“怎么部署一个Demo”也不是“跑个Hello World就完事”而是——搭一套能进测试环境、能接CI/CD流水线、能扛住中等规模微服务压测、出了问题自己能快速定位的K8s集群。很多人卡在第一步以为kubeadm init敲完就结束了结果发现Node起不来、Pod卡在Pending、Service死活不通、Ingress 404满天飞……最后不得不回退到Docker Compose说“K8s太重”。其实不是K8s重是初始搭建时漏掉了那些文档里不写、但生产环境天天踩的“隐性前提”。我带过的几个模拟项目X都是从零开始建集群某高校AI实验室要调度GPU训练任务某公司内部要迁移12个Java微服务还有个IoT边缘网关项目需要轻量离线能力。它们起点不同但共性极强——初始搭建阶段的决策直接锁死了后续半年的运维成本、扩缩容弹性、安全加固路径和故障排查效率。比如你选错CNI插件后期想换就得滚动重建所有节点你忽略etcd备份机制一次磁盘故障就全集群不可逆你用默认token不做轮换安全审计直接亮红灯。所以这篇不是“手把手教你用kubeadm装K8s”的速成教程而是以一名实际操刀过7套不同规模K8s集群最小3节点最大67节点的从业者视角还原真实搭建现场每个命令背后为什么这么写、哪些参数看似可选实则关键、哪些检查项必须在init前完成、哪些“成功提示”其实是埋雷信号。你会看到完整的网络拓扑设计逻辑、证书体系如何避免30天后自动失效、kube-proxy到底该用iptables还是ipvs、CoreDNS怎么防单点、以及最关键的——如何用一条命令验证集群是否真的“可用”而不仅是“能启动”。适合谁看如果你正准备在测试环境部署第一套K8s希望少走两周弯路已经搭过但总出诡异问题想回溯初始配置找根因要给团队写搭建SOP需要知道哪些步骤必须加checklist或者只是想搞懂kubeadm init --pod-network-cidr10.244.0.0/16里这个CIDR为什么不能随便改……那接下来的内容就是你该逐行抄下来的实操笔记。2. 整体设计与思路拆解为什么必须放弃“一键安装”幻觉2.1 拒绝黑盒化K8s初始搭建的本质是“基础设施契约签署”很多人把K8s搭建理解为“装软件”这是根本性误判。K8s不是MySQL或Nginx它不提供独立服务而是定义了一套运行时契约Runtime Contract容器怎么启停、网络怎么互通、存储怎么挂载、健康怎么探测……这些契约的履行依赖底层基础设施OS内核、网络设备、存储系统的严格配合。所谓“初始搭建”本质是在物理/虚拟机上用配置文件和命令向K8s组件明确声明“我承诺提供以下能力”。比如你执行kubeadm init时指定--pod-network-cidr10.244.0.0/16这不是在分配IP而是在签协议“我保证所有Node的内核路由表里会有一条指向这个网段的规则且CNI插件会确保Pod IP落在这个范围内”。如果后续你用Flannel却忘了在每台Node上开启net.bridge.bridge-nf-call-iptables1那协议就违约了——Pod之间ping不通因为内核没把网桥流量交给iptables处理。再比如etcd数据目录默认是/var/lib/etcd。但如果你的服务器磁盘是NVMe SSD而系统盘只有100GB那这个目录很快就会被快照塞爆。这时候你不能怪K8s而要意识到初始设计时你已把etcd的持久化责任契约式地绑定到了这块小磁盘上。真正的生产级搭建必须在init前就规划好etcd数据放哪块盘、备份策略怎么触发、快照保留几份。所以我的搭建流程永远分三阶段契约准备期2小时检查所有节点内核参数、SELinux/AppArmor状态、时间同步精度、磁盘IO性能、网络MTU一致性契约签署期30分钟执行kubeadm init但全程盯着日志里的WARNING对每个“[WARNING]”手动验证是否可接受契约验真期1小时不只看kubectl get nodes显示Ready而是用5个真实场景测试跨Node Pod通信、Headless Service解析、Ingress路由、PV动态供给、etcd健康快照。提示kubeadm init输出的最后几行常被当成“成功标志”直接复制。但其中kubeadm join命令里的--discovery-token-ca-cert-hash sha256:xxx如果节点时间误差超过10分钟这个hash会校验失败——因为证书时间戳不匹配。这不是bug是K8s用密码学强制你遵守“时间同步”这条基础设施契约。2.2 方案选型为什么坚持kubeadm而非Minikube/K3s/RKE当前主流搭建工具有四类Minikube单机虚拟机适合学习API但kubectl exec进容器看到的不是真实Linux内核cgroup v2支持残缺无法测试节点亲和性、污点容忍等调度特性K3s轻量但默认禁用很多安全特性如PodSecurityPolicyetcd被封装成SQLite备份恢复流程和标准K8s完全不同RKE/Rancher企业级但抽象层太厚rke up报错时你得先查Rancher日志再查kubeadm日志最后查容器引擎日志三层嵌套排查kubeadm官方推荐配置透明错误日志直指组件如kubelet[1234]: F0321 10:22:33.123456 1234 server.go:274] failed to run Kubelet: unable to load bootstrap kubeconfig所有参数都能在/etc/kubernetes/manifests/里找到对应YAML。我坚持kubeadm是因为它强迫你直面K8s的“脏细节”。比如当你发现kubelet反复重启日志里出现failed to run Kubelet: misconfiguration: kubelet cgroup driver: systemd is different from docker cgroup driver: cgroupfs这问题在K3s里会被自动修复但在kubeadm里你必须亲手去/etc/docker/daemon.json里把exec-opts: [native.cgroupdriversystemd]加上然后systemctl restart docker。这个过程很烦但它让你彻底理解容器运行时和K8s节点代理必须用同一套cgroup管理器否则资源隔离就形同虚设。注意kubeadm不是万能的。它不处理高可用Master需额外搭etcd集群负载均衡也不管云厂商VPC路由配置。所以我的方案是kubeadm负责K8s控制平面初始化其他基础设施问题用Ansible Playbook预置。比如在init前用Ansible统一设置所有节点的sysctl参数、关闭swap、配置chrony时间同步——这些都不是K8s的事但没做好K8s就跑不稳。2.3 网络模型选择CNI不是插件是集群的“神经系统”K8s网络有四个必须满足的条件所有Pod能互相直接通信无需NAT所有Node能与所有Pod通信Pod看到的自己的IP就是其他Pod/Node访问它的IPNode上的进程能直接访问Pod IP。满足这四点的方案核心在于CNIContainer Network Interface插件的选择和配置。目前主流有Flannel、Calico、Cilium、Weave。我的选型逻辑很直接维度FlannelCalicoCilium学习成本极低UDP/VXLAN封装中BGP路由概念高eBPF编程模型性能损耗UDP封装约5%~8%BGP路由查表1%eBPF内核态处理几乎零损耗网络策略仅支持基础NetworkPolicy完整NetworkPolicy Iptables扩展NetworkPolicy L7 HTTP/gRPC策略 服务网格适用场景测试环境、小规模集群50节点生产环境主力金融、电商高性能场景实时风控、高频交易对于首次搭建我推荐Flannel的host-gw模式非默认VXLAN理由很实在host-gw模式不封包直接在Node路由表里加静态路由性能接近原生它要求所有Node在二层互通同一VLAN这恰好逼你检查物理网络——很多“网络不通”问题根源是交换机STP阻塞了某个端口而不是K8s配置错了配置只需一行kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml但前提是你的kubeadm init用了--pod-network-cidr10.244.0.0/16且Flannel的ConfigMap里Network字段必须严格一致。实操心得Flannel的kube-flannel.yml里有个关键字段Backend: {Type: host-gw}。如果你复制的是VXLAN版本又没开UDP端口Pod会卡在ContainerCreating。我见过最惨的一次客户在阿里云VPC里用VXLAN但安全组默认禁止UDP 8472端口排查了两天才发现是云平台防火墙挡的——这再次证明K8s网络问题70%在云网络层30%在K8s层。3. 核心细节解析与实操要点那些文档里不会写的“魔鬼参数”3.1 初始化前的硬性检查清单必须逐项确认kubeadm init不是“一键安装”而是“契约签署仪式”。以下12项检查少一项后续都可能引发连锁故障。我在某公司搭建时因跳过第7项导致集群上线3天后所有Pod DNS解析超时回溯发现是/etc/resolv.conf里配置了内网DNS服务器而该服务器无法访问公网——CoreDNS启动时会尝试连接8.8.8.8做健康检查失败后进入CrashLoopBackOff。内核版本 ≥ 4.18低于此版本cgroup v2支持不全kubelet会报failed to run Kubelet: unsupported cgroup version: 2。检查命令uname -rSELinux状态必须为permissive或disabled。enforcing模式下kubelet无法挂载/var/lib/kubelet/pods。检查命令getenforce临时关闭sudo setenforce 0swap关闭K8s 1.8强制要求关闭swap否则kubelet拒绝启动。检查命令swapon --show关闭命令sudo swapoff -a并注释/etc/fstab里swap行iptables FORWARD链默认策略必须是ACCEPT否则Pod间通信被拦截。检查命令sudo iptables -L FORWARD若为DROP执行sudo iptables -P FORWARD ACCEPT时间同步精度 ≤ 1秒etcd对时间极其敏感误差超1秒会导致leader选举失败。检查命令chronyc tracking偏移量Offset列应1000mshostname唯一性与合法性所有Node的hostname不能含下划线_且长度≤63字符。kubeadm会用hostname生成证书CN非法字符导致证书生成失败DNS配置合理性/etc/resolv.conf里不能有无法访问的nameserver。coredns启动时会尝试连接列表中第一个nameserver做连通性测试失败则崩溃磁盘空间/var/lib/etcd所在分区至少预留20GB。etcd快照wal日志增长极快100节点集群日均产生3GB数据内核模块加载br_netfilter、overlay必须已加载。检查命令lsmod | grep -E br_netfilter|overlay缺失则执行sudo modprobe br_netfilter sudo modprobe overlaysysctl参数net.bridge.bridge-nf-call-iptables1、net.ipv4.ip_forward1必须为1。检查命令sysctl net.bridge.bridge-nf-call-iptables net.ipv4.ip_forward容器运行时Docker CE 20.10或containerd 1.6。kubeadm会检测/var/run/dockershim.sock或/run/containerd/containerd.sock缺失则报错端口开放Master节点需开放6443API Server、2379-2380etcd、10250kubelet、10259kube-scheduler、10257kube-controller-managerWorker节点需开放10250、30000-32767NodePort。提示别信“一键脚本”。我写过一个precheck.sh把上述12项做成自动检测每项失败都给出修复命令。比如检测到swap未关脚本会输出❌ SWAP detected: /dev/sdb1. Run: sudo swapoff -a sudo sed -i /swap/d /etc/fstab。这种“检测即修复”的思路比人工逐条执行高效十倍。3.2 kubeadm init核心参数详解每个flag都是契约条款kubeadm init命令看似简单但每个参数都在定义集群行为边界。以下是生产环境必设的7个参数附带原理和坑点--pod-network-cidr10.244.0.0/16原理K8s不管理Pod IP分配而是把这个网段告诉CNI插件由CNI负责子网划分。Flannel会为每个Node分配一个/24子网如Node1: 10.244.1.0/24Node2: 10.244.2.0/24坑点此网段不能与Node宿主机网段、公司内网网段重叠。曾有客户用192.168.1.0/24结果Node上跑的数据库服务IP和Pod IP冲突kubectl exec进容器后ping 192.168.1.100竟通向了数据库——网络完全混乱。--service-cidr10.96.0.0/12原理Service ClusterIP的地址池。K8s会为每个Service分配一个该网段内的VIPkube-proxy通过iptables/ipvs规则将VIP流量转发到后端Pod坑点/12意味着4096个IP10.96.0.0 ~ 10.111.255.255足够支撑2000 Service。但若你用/1665536个IP看似更宽裕实则kube-proxy的iptables规则数会爆炸导致iptables-restore耗时超2分钟Service更新延迟极高。--cri-socket/run/containerd/containerd.sock原理显式指定容器运行时socket路径。Docker CE 20.10默认用containerd但kubeadm仍会优先找/var/run/dockershim.sock若不存在则报错坑点某些云厂商镜像预装了Docker但dockershim已被废弃必须用此参数指向containerd socket。--upload-certs原理为后续kubeadm join添加Master节点时自动生成并加密传输etcd证书。不加此参数添加新Master需手动拷贝/etc/kubernetes/pki/etcd/下所有证书坑点生成的--certificate-key有效期2小时超时需重新kubeadm init phase upload-certs --upload-certs。--control-plane-endpointlb.k8s.local:6443原理高可用集群必备。所有Master节点API Server注册到同一个DNS名如lb.k8s.local前端挂负载均衡器如HAProxy。Worker节点join时kubelet连接此Endpoint而非具体Master IP坑点此DNS名必须能被所有Node解析且lb.k8s.local的A记录需指向负载均衡器VIP不能指向某台Master物理IP。--image-repositoryregistry.cn-hangzhou.aliyuncs.com/google_containers原理国内网络无法拉取k8s.gcr.io镜像必须替换为国内镜像源。阿里云、中科大、网易都提供镜像坑点镜像源必须与K8s版本严格匹配。K8s 1.25用registry.cn-hangzhou.aliyuncs.com/google_containers/kube-apiserver:v1.25.0若错用v1.24镜像kube-apiserver容器启动失败。--ignore-preflight-errorsNumCPU,Mem原理跳过CPU核心数2、内存2GB的预检。测试环境常用坑点仅用于开发机生产环境严禁使用。kube-scheduler在单核CPU上会因调度循环阻塞导致Pod长时间Pending。实操心得我从不手敲kubeadm init。而是用kubeadm config print init-defaults kubeadm-config.yaml生成默认配置然后编辑YAML。这样所有参数集中管理版本升级时只需改YAML不用记命令行。YAML里还能加nodeRegistration.criSocket、clusterName等高级字段比命令行灵活得多。3.3 证书体系深度解析为什么30天后集群会“突然死亡”K8s组件间通信全部基于TLS双向认证证书是集群的生命线。kubeadm init生成的证书默认有效期365天但有一个致命例外front-proxy-client证书只有30天。这个证书用于API Server与Aggregated API Server如metrics-server通信。30天后kubectl top node会报x509: certificate has expired or is not yet validmetrics-server Pod持续CrashLoopBackOff整个监控体系瘫痪。证书位置与作用一览证书文件存放路径用途默认有效期关键风险ca.crtca.key/etc/kubernetes/pki/根CA签发所有其他证书10年私钥泄露集群沦陷apiserver.crt/etc/kubernetes/pki/API Server服务端证书1年过期后kubectl完全无法连接front-proxy-client.crt/etc/kubernetes/pki/API Server访问聚合API的客户端证书30天最常见“突然死亡”元凶etcd/ca.crt/etc/kubernetes/pki/etcd/etcd根CA10年—admin.conf/etc/kubernetes/admin.confkubectl配置含client cert1年过期后kubectl命令失效续期操作不是“重装”而是“证书轮换”# 1. 备份原证书重要 sudo cp -r /etc/kubernetes/pki /etc/kubernetes/pki-backup-$(date %Y%m%d) # 2. 轮换所有证书除etcd外 sudo kubeadm certs renew all # 3. 更新admin.confkubectl配置 sudo kubeadm init phase kubeconfig admin --cert-dir /etc/kubernetes/pki # 4. 重启API Server等静态Pod自动触发 sudo systemctl restart kubelet注意kubeadm certs renew不会动etcd证书需单独轮换# 进入etcd容器假设用docker docker exec -it etcd sh # 在etcd容器内生成新证书 etcdctl --endpointshttps://127.0.0.1:2379 --cacert/etc/kubernetes/pki/etcd/ca.crt --cert/etc/kubernetes/pki/etcd/server.crt --key/etc/kubernetes/pki/etcd/server.key \ snapshot save /tmp/etcd-snapshot.db这个过程必须在集群健康时操作否则快照失败。4. 实操过程与核心环节实现从init到“真正可用”的完整链路4.1 初始化Master节点不只是复制粘贴那条命令我们以K8s 1.27.0为例假设3台CentOS 7.9服务器master01, worker01, worker02内网IP分别为10.0.1.10, 10.0.1.11, 10.0.1.12。Step 1在master01执行预检与初始化# 下载kubeadm/kubelet/kubectl国内源 curl -fsSL https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | sudo gpg --dearmor -o /usr/share/keyrings/kubernetes-apt-keyring.gpg echo deb [arch$(dpkg --print-architecture) signed-by/usr/share/keyrings/kubernetes-apt-keyring.gpg] https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main | sudo tee /etc/apt/sources.list.d/kubernetes.list sudo apt-get update sudo apt-get install -y kubelet kubeadm kubectl sudo apt-mark hold kubelet kubeadm kubectl # 执行预检会输出所有WARNING sudo kubeadm init --config kubeadm-config.yamlkubeadm-config.yaml内容如下关键参数已标注apiVersion: kubeadm.k8s.io/v1.27 kind: InitConfiguration bootstrapTokens: - token: abcdef.0123456789abcdef # 自定义token避免默认随机token泄露 ttl: 24h nodeRegistration: criSocket: /run/containerd/containerd.sock # 显式指定containerd taints: [] # 移除master默认taint允许调度Pod测试环境 --- apiVersion: kubeadm.k8s.io/v1.27 kind: ClusterConfiguration kubernetesVersion: v1.27.0 controlPlaneEndpoint: lb.k8s.local:6443 # 高可用Endpoint networking: podSubnet: 10.244.0.0/16 # 必须与Flannel一致 serviceSubnet: 10.96.0.0/12 imageRepository: registry.cn-hangzhou.aliyuncs.com/google_containers # 国内镜像源 certificatesDir: /etc/kubernetes/pki clusterName: k8s-clusterStep 2初始化后必做的5件事配置kubectlmkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config部署Flannelhost-gw模式# 下载并修改为host-gw curl -O https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml # 编辑kube-flannel.yml将Backend.Type改为host-gw sed -i s/Type: vxlan/Type: host-gw/g kube-flannel.yml kubectl apply -f kube-flannel.yml验证Pod网络# 查看flannel Pod是否Running kubectl get pods -n kube-flannel # 在master01上ping worker01的Pod网段10.244.2.0/24 # 先查worker01的flannel.1接口IP kubectl get nodes -o wide # 得到worker01内网IP 10.0.1.11 ssh worker01 ip addr show flannel.1 | grep inet # 应返回类似inet 10.244.2.0/32 scope global flannel.1 # 然后从master01 ping这个IP ping 10.244.2.0部署CoreDNS并验证# 检查CoreDNS是否Running kubectl get pods -n kube-system -l k8s-appkube-dns # 创建测试Pod验证DNS kubectl run dns-test --imagebusybox:1.31 --rm -it --restartNever -- nslookup kubernetes.default # 应返回Server: 10.96.0.10Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local标记Master可调度仅测试环境kubectl taint nodes --all node-role.kubernetes.io/control-plane- # 移除所有节点的control-plane污点让Pod可调度到Master提示kubeadm init输出的kubeadm join命令务必保存。它包含--token和--discovery-token-ca-cert-hash后者是证书哈希值用于验证Master身份。如果丢失只能用kubeadm token create --print-join-command重新生成。4.2 添加Worker节点为什么join后Node状态是NotReady在worker01和worker02上执行kubeadm join命令后kubectl get nodes常显示NotReady。这不是失败而是CNI插件尚未部署完成的正常状态。K8s设计如此Node注册后kubelet会报告Ready但CNI插件如Flannel的DaemonSet Pod需先启动为Node配置网络之后Node才真正Ready。排查NotReady的黄金三步法查kubelet日志sudo journalctl -u kubelet -n 100 --no-pager | grep -i cni\|network # 常见错误failed to load network config: no networks found in /etc/cni/net.d # 意味着Flannel DaemonSet还没拉起镜像查Flannel Pod状态kubectl get pods -n kube-flannel # 如果是ImagePullBackOff检查镜像源是否正确如果是Pending检查Node是否有足够资源查Node的CNI配置文件ls /etc/cni/net.d/ # 正常应有10-flannel.conflist文件。若为空说明Flannel DaemonSet没成功写入 cat /etc/cni/net.d/10-flannel.conflist | jq .plugins[0].network # 应返回10.244.0.0/16与init时--pod-network-cidr一致Worker节点加入后的终极验证# 1. 部署一个跨Node的Nginx kubectl create deploy nginx --imagenginx:alpine --replicas2 kubectl expose deploy nginx --port80 --typeNodePort # 2. 查看Pod分布 kubectl get pods -o wide # 应显示一个在master01一个在worker01 # 3. 从master01 curl另一个Node上的Pod IP # 获取worker01上Pod的IP POD_IP$(kubectl get pods -o wide | grep worker01 | awk {print $6}) curl http://$POD_IP # 应返回Nginx欢迎页 # 4. 从worker01 curl master01上Pod的IP反向验证 # 获取master01上Pod的IP POD_IP$(kubectl get pods -o wide | grep master01 | awk {print $6}) ssh worker01 curl http://$POD_IP实操心得我习惯在所有Node上部署tcpdump抓flannel.1接口的包。当curl不通时直接看是否有ARP请求发出、是否有ICMP响应回来。网络问题80%靠抓包定位而不是猜配置。4.3 高可用集群搭建为什么不能只加Master节点单Master是测试玩具生产必须高可用。但高可用≠多加几个Master。核心是控制平面组件API Server、Scheduler、Controller Manager和etcd的分离部署。标准高可用架构3台Master运行kube-apiserver、kube-scheduler、kube-controller-manager、etcd嵌入式1台专用etcd集群可选3节点独立etcdMaster只运行控制平面组件1台负载均衡器HAProxy或云厂商SLB监听6443端口后端指向3台Master的6443。关键配置差异kubeadm-config.yaml中controlPlaneEndpoint必须设为LB的DNS名每台Master的kubeadm init命令需加--upload-certs第一台Master init后其余Master用kubeadm join --control-plane --certificate-key key加入LB配置必须开启tcp-check健康检查检测6443端口是否存活。HAProxy配置片段/etc/haproxy/haproxy.cfgfrontend k8s-api bind *:6443 mode tcp default_backend k8s-master backend k8s-master mode tcp balance roundrobin option tcp-check tcp-check connect tcp-check send PING\r\n tcp-check expect string PONG server master01 10.0.1.10:6443 check server master02 10.0.1.11:6443 check server master03 10.0.1.12:6443 check注意tcp-check里send PING\r\n和expect string PONG是K8s API Server的健康检查协议。如果用HTTP检查API Server会返回400错误导致LB认为后端宕机。5. 常见问题与排查技巧实录那些让我凌晨三点还在敲命令的故障5.1 故障速查表按现象归类精准定位根因现象可能原因排查命令解决方案kubectl get nodes显示NotReadyFlannel未部署/配置错误kubectl get pods -n kube-flannells /etc/cni/net.d/检查Flannel YAML中podSubnet是否匹配init参数重装Flannelkubectl get pods返回The connection to the server X.X.X.X:6443 was refusedAPI Server未启动/端口被占sudo ss -tlnp | grep :6443sudo journalctl -u kubelet -n 50检查/etc/kubernetes/manifests/kube-apiserver.yaml中--advertise-address是否为本机IP杀掉占用6443的进程Pod状态ContainerCreatingCNI插件未就绪/镜像拉取失败kubectl describe pod pod-namesudo crictl ps -a | grep ContainerCreating查Events字段检查/etc/containerd/config.toml中registry.mirrors是否配置国内镜像源CoreDNS PodCrashLoopBackOffDNS上游不可达/内存不足kubectl logs -n kube-system coredns-podkubectl top pod -n kube-system修改CoreDNS ConfigMap将forward . /etc/resolv.conf改为forward . 114.114.114.114增加resources limitskubectl top node

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑