ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

K8s集群搭建避坑指南:从初始化到生产可用的验证清单

K8s集群搭建避坑指南:从初始化到生产可用的验证清单 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/16Flannel常用或10.20.0.0/16自己规划Service网段10.96.0.0/12kubeadm默认或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会不健康排查路径按顺序走apiserver容器没起来执行crictl ps -a | grep apiserver看看容器状态是Running还是Exited。如果是Exited看日志crictl logs 容器id高频原因是镜像拉取失败或者启动参数不对。etcd没起来apiserver启动依赖etcdetcd挂了apiserver也会起不来。执行crictl ps -a | grep etcd重点看etcd的日志里有没有failed to load data之类的报错。6443端口没监听执行ss -lntp | grep 6443如果端口没监听说明apiserver进程压根没起来。Swap没关这个老生常谈但是真的常见。kubelet和apiserver都是通过容器跑的但kubelet进程本身在宿主机上。Swap开着会导致kubelet性能异常间接影响apiserver的健康检查。你需要在所有节点上关闭swapswapoff -a并注释掉/etc/fstab里的swap条目。容器运行时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启动后会去绑定证书里的SANSubject 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-cidr10.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 --imagebusybox -- 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 --imagenginx kubectl expose deployment nginx --port80 --target-port80然后从任意Pod里访问Service名kubectl run curl-test --imagecurlimages/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_API3 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命名空间加上默认的LimitRangeapiVersion: 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 系统防火墙真的是第一杀手我有一台节点启动之后始终连不上apiserverKubelet日志里没有任何有效报错后来发现是firewalld默认规则把6443端口deny了。在测试学习阶段最简单的办法是暂时关闭防火墙systemctl stop firewalld systemctl disable firewalld但生产环境请不要直接关防火墙而是开放必要的端口6443apiserver、2379/2380etcd、10250kubelet、10256kube-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的API1.22移除了很多v1beta1的API1.25之后又移除了PodSecurityPolicy。未经测试的集群升级比不升级危险得多。如果只是个人学习大版本升级倒也不用太紧张但至少先跑一遍kubectl convert相关工具检查一下现有资源的API版本是否合规。6.4 分布式存储别自己造轮子你要是搭Redis集群或者数据库这类有状态服务存储方案的选型会直接影响上层的运行效果。别上来就自己搭一个分布式的什么存储先用NFS这种最简单的方案跑通验证等你在k8s上积累了一定经验再考虑用更专业的分布式存储特别是生产环境存储方案切换的成本高到你不想试第二次。7. 集群搭完之后的下一步规划k8s集群搭建完成只是开始。从热度词里看很多人还会继续研究namespace、Redis集群部署、GPU调用这些方向说明大家的基本路径都是搭集群、熟悉核心概念、上业务、做运维。顺着这个路径走下去你迟早要面对的问题包括怎么规范namespace的使用、怎么设计多集群方案、怎么做弹性伸缩、怎么在集群里部署大数据生态Spark、Hadoop都在热词里出现了。我最后再给一个建议从搭建到跑业务的整个过程中每踩一个坑就把报错信息、排查过程、最终根因记下来。不用写得很正式几句话就行。积累过几十个坑之后你会发现自己对k8s的理解会上一个大台阶。这套集群从能跑到能扛事靠的其实就是这些实打实的经验沉淀。
返回列表