2018年的时候,我还是个坚定的Kubernetes布道者。当时我们公司只有二十几台物理机,老板看上了K8s的弹性伸缩,说上云之后能把资源利用率拉高到80%以上。我没有做充分的评估就直接冲了,选了一个1.12版本搭三个master和四个node。那一阵子etcd几乎每隔两天就要报警,有时候是磁盘同步太慢,有时候是心跳超时掉leader。折腾了整整三个月才把应用跑稳,而对比当初我们用Docker Swarm部署到生产环境只花了两个星期。每次有人跟我说K8s是最先进的调度系统,我就想起那三个月的每一个凌晨,说实话,那是真的崩溃。
后来我仔细研究,发现Kubernetes的核心优势并不在于调度效率,而在于声明式API把基础设施的期望状态固化下来。可这个代价是把原本简单的运维变成了分布式系统的排障。举个例子,有次一个node节点直接NotReady,我查了两天,最终发现是conntrack表满了,内核参数net.netfilter.nf_conntrack_max默认值在2.4.19内核版本下只支持65535,而Pod一多一建立TCP连接就丢包。传统虚拟机环境根本碰不到这种底层细节,你只需要设置iptables规则就行。K8s把内核特性变成日常踩坑,对不熟悉Netfilter的人来说完全是黑盒。后来我加了net.bridge.bridge-nf-call-iptables=1,又调整了超时参数,才算解决。
另一个我不会再忽略的点是K8s本身极其吃资源。我们生产环境大概跑了两百个Pod,Prometheus加各种exporter每个node吃掉了8%到15%的CPU,一个cAdvisor每天生成的指标超过100GB,我那台16GB内存的worker节点,光监控agent就占了3GB。后来把采集频率从15秒降到45秒,内存才勉强降下来。相比之下,以前裸机部署传统的Java应用,每个节点只用跑一个系统监控,总开销不到2%。我开始反思,Kubernetes带给我们的可观测性进步是事实,但它把“监控自身”变成了一个不小的成本。小公司没有独立的容量规划团队,这15%的资源浪费不一定承受得起。
更讽刺的是,我们原本最吹嘘的弹性伸缩,在实际业务里让人哭笑不得。一次流量突增,HPA配置从3个副本扩到10个,但K8s默认的缩扩容周期是每15秒同步一次指标,再等待pod完成初始化,加上Java应用冷启动就要至少40秒,整个扩缩完成花了超过300秒。那段时间前端已经超时扔掉了三成请求。而以前在VM里部署时,用nginx加脚本自动加两台机器,只要负载均衡器检测到CPU超过80%,就立刻在宿主机上启动新容器,大概20秒内就能对外服务。K8s的伸缩有更优雅的机制,但优雅的前提是你需要prometheus-adapter自定义指标、预设PodDisruptionBudget、以及优化过镜像大小的基础镜像。这种复杂度,很多中小团队根本没有时间和能力去闭环。
如果你问我Kubernetes到底值不值,我的看法是:它更像是一次基础架构的标准化变革,而不是单纯的部署工具升级。对于拥有专职SRE、流量峰值明显且有多个团队需要同时交付的公司,K8s带来的平台化收益远远大于维护成本。可如果你的团队不超过二十个人,没有专人去盯etcd备份、证书轮换和版本升级,我劝你用云平台上的托管K8s或者干脆回到编排简单的方式。我们后来做了一次1.16升到1.22,前前后后花了两个星期,期间还因为v1beta2 API变更和存储驱动不兼容导致两个服务中断。Kubernetes本身没有罪,它只是一个中立的调度系统,错的是我们在还没有准备好承受它复杂性的时候就把它当成银弹。省下的那点机器成本,远不够支付我们半夜爬起来看kubelet日志的时间和脑细胞。