你还在无脑上K8s?你的服务正在被它慢慢杀死

2026-09-16 9 0

你还在无脑上K8s?你的服务正在被它慢慢杀死

去年某电商平台的技术选型会上,一位从某大厂跳槽来的架构师信誓旦旦:"不用Kubernetes,你们的系统永远算不上现代化。"然后CTO拍板,全量迁移。

结果迁移后第三周,第一次大促,订单服务开始频繁重启。查监控:OOMKilled。查资源配置:limits设置了,requests没设置。查K8s调度:Pod被调度到一个实际内存只有2G的节点,但容器以为自己可以随便用。

这不是个案。我见过太多团队,上K8s是因为"别人都在用",而不是真的需要它的能力。今天咱们就说说,Kubernetes那些让你服务变慢的隐形杀手。


杀手一:资源限制——你配的limits可能正在制造OOM

很多团队第一次写K8s deployment yaml,都是抄的:

resources:
  limits:
    memory: "512Mi"
    cpu: "500m"
  requests:
    memory: "0"    # 没设置,K8s默认等于limits
    cpu: "0"

requests没设置的时候,K8s默认把requests设成limits的值。这看起来很美好——实际上是个坑王。

因为requests是调度依据,limits是实际上限。如果你的应用启动时需要600Mi内存来加载缓存,但limits只写了512Mi,Pod会被调度到一个"看起来够用"的节点,实际跑起来直接OOMKilled。

更隐蔽的是:K8s调度器只看requests,不看limits。调度时它认为"这个Pod最多用512Mi",但你的应用启动时瞬间需要600Mi,OOM就在启动的一瞬间发生。监控都来不及报警,Pod已经被Kill了。

正确的配置:

resources:
  requests:
    memory: "512Mi"   # 调度依据:必须保证的最小资源
    cpu: "200m"
  limits:
    memory: "1Gi"     # 上限:超过就Kill,但启动时可以用更多
    cpu: "1000m"

另一个常见坑是CPU throttling。当limits设置的cpu值小于单核时,如果你的应用有突发CPU使用需求,Linux CFS会强制进行CPU限流,导致P99延迟莫名奇妙地飙升。spec.containers[].resources.limits.cpu本质上是一个CFS quota,不是硬上限。


杀手二:Liveness Probe——那个让你服务雪崩的小可爱

K8s官方文档说,Liveness Probe是"检测服务是否活着,如果死了就重启"。听起来很合理。

现实是:配置不当的Liveness Probe是生产环境中制造雪崩的第一大元凶

最常见的错误:

livenessProbe:
  httpGet:
    path: /health
    port: 8080
  initialDelaySeconds: 0   # 服务还没启动就开始检查
  periodSeconds: 5
  failureThreshold: 1        # 失败一次就重启

你的Java服务启动需要30秒,但Liveness Probe从第0秒就开始问"你还活着吗",到第5秒没回应,直接标记为死亡并重启。然后这个服务就陷入了启动→被Kill→启动→被Kill的死亡循环

正确的姿势:

livenessProbe:
  httpGet:
    path: /health
    port: 8080
  initialDelaySeconds: 60   # 等服务完全启动后再开始检查
  periodSeconds: 10
  failureThreshold: 3       # 连续3次失败才重启
  successThreshold: 1
  timeoutSeconds: 5

还有一个更隐蔽的问题:Liveness Probe和Readiness Probe共用同一个接口。当你的服务负载高时,健康检查接口响应变慢,被Liveness判定为死亡,然后——重启,然后——更大负载,然后——更多重启。这就是经典的正反馈雪崩。

解法是分开:/livez只做进程存活检查,/readyz做真正的就绪检查(包括依赖检查)。Liveness永远不要包含依赖检查。


杀手三:Pod调度开销——比你想的要贵得多

很多人以为Pod调度就是"找个节点放上去",实际上K8s的调度过程相当复杂:

  1. 预选阶段:过滤掉不满足资源/标签/亲和性条件的节点
  2. 优选阶段:对通过预选的节点打分(涉及资源利用率计算)
  3. 绑定阶段:将Pod和节点绑定,写入etcd

这个过程在Pod数量少的时候不明显。但当集群里有上千个Pod的时候,每次调度的计算量是惊人的。调度器是单线程的,10000个Pod的集群,这个时间可能上升到秒级。

更关键的是:一个Node上的Pod越多,kubelet的心跳和资源报告开销就越大。kubelet每10秒向API Server报告一次节点状态,每秒发送一次心跳。节点上Pod越多,控制平面的压力越大。

你以为部署了10个副本就能扛10倍流量?实际上这10个Pod本身的管理开销就已经吃掉了一部分资源。对于有状态的服务,这个开销更明显——某些Java应用会频繁通过K8s API获取ConfigMap,100个Pod就是100次并发API调用。如果你的ConfigMap更新频繁,API Server可能就成了新的瓶颈。


杀手四:网络策略——那些你以为没问题的网络可能在"假装工作"

K8s的Service和CoreDNS是让很多人"无脑上K8s"的核心动力——IP变了不用改配置,DNS解析自动搞定。

但代价是:每一次服务间通信,都多了一层Kube-Proxy的iptables/IPVS转发

在物理机或者VM里,服务A调用服务B是直连。在K8s里,如果用ClusterIP默认方式,是A → Kube-Proxy → B。对于高并发场景,这个转发延迟是累积的。Kube-Proxy基于iptables,规则越多,查找越慢。当你的Service数量超过100个的时候,每条请求的iptables查找时间可以从亚微秒级上升到毫秒级。

有人会说:用IPVS模式啊,O(1)查找。对,但IPVS的负载均衡算法在某些场景下会导致负载不均——某些Pod连接数爆了,某些Pod空闲着。更要命的是,IPVS的连接跟踪会消耗内核conntrack表的空间,高并发场景下容易触发nf_conntrack: table full错误。


杀手五:存储——PV/PVC的"挂载延迟"会让你抓狂

有状态服务跑在K8s里,最常遇到的问题就是存储挂载。

Local PV看起来很美:本地SSD,低延迟。但问题是Local PV不支持动态分配,Pod漂移时数据不会跟着走。如果节点挂了,Pod被调度到另一台机器,Local PV里的数据就丢了。

Network PV(NFS/云存储)支持动态分配,但延迟高。AWS EBS的Attach操作在高峰期可能需要30秒以上。这30秒里,你的Pod卡在Pending状态,Service没有后端,健康检查失败,然后——Liveness Probe又要动手了。

对于MySQL这种对延迟极其敏感的服务,K8s官方都不推荐跑在容器里。Percona和MySQL社区的主流方案都明确说:MySQL on K8s的性能表现远不如跑在裸机上。但架不住团队"统一基础设施"的执念,非要跑,然后遇到性能问题,再花大量时间调优——最后发现:还不如跑在物理机上。


杀手六:etcd——那个被所有人忽视的单点瓶颈

K8s的所有控制平面状态都存在etcd里。Pod创建、Service变更、ConfigMap更新、RBAC策略——全部都要写etcd。

随着集群规模扩大,etcd会成为事实上的瓶颈。官方建议etcd使用SSD,因为etcd的写入延迟直接决定了Pod调度的速度。如果etcd写入延迟超过50ms,新的Pod可能需要几分钟才能完成调度。

更可怕的是etcd的一致性保证。每次API Server和etcd通信,都是一次网络往返。在某些多可用区部署中,etcd网络延迟可能成为整个集群性能的决定性因素。

很多团队用K8s,跑着几十个服务,但etcd只给了8G SSD和最便宜的云盘。然后抱怨说"K8s好慢"——这不是K8s的锅,是预算的锅。


什么时候真的需要K8s?

说了这么多,不是说K8s不好。K8s是个好工具,但它解决的是"大规模容器编排"的问题,不是"让我服务跑起来"的问题

如果你满足以下条件,K8s是值得的:

  • 服务数量超过50个,需要统一治理
  • 多团队协作,需要权限隔离和资源配额
  • 需要灰度发布、蓝绿部署、滚动升级的自动化能力
  • 有多云/混合云的需求
  • 团队有专职的SRE和K8s运维人员

如果你的服务数量在10个以内,团队没有专职运维,还在用"kubectl apply -f deployment.yaml"手动挡的方式跑K8s——那你不是在用K8s,是K8s在用你。


写在最后

我见过最讽刺的一幕是:一个日活不到1万的小应用,为了"云原生",上了K8s,然后雇了一个全职K8s运维。运维成本比服务器成本还高。

技术选型这件事,最怕的不是"用错了技术",而是"因为焦虑而选择了不需要的技术"。别人都在用K8s、Service Mesh、微服务,你就觉得自己不用就落伍了——这种焦虑比技术债务还可怕。

真正成熟的工程师,不是会用最潮的技术,而是能在合适的场景选择合适的技术,在不需要的地方保持克制。

K8s很好。但让它杀死你的服务之前,先问自己一句:我真的需要它吗?

别让"现代化"变成"慢性服毒"。共勉。 🦞

相关文章

不想折腾了?让小龙虾帮你一键部署AI工具,省心又省力!
写API这事儿:那些年我踩过的坑和良心建议
写API这事儿:那些年我踩过的坑和良心建议
为什么你的REST API总被嫌弃?聊聊那些让人崩溃的设计
AI圈最近太热闹了:我在用OpenClaw搞的那些骚操作,顺便吐槽几个噱头货
不想折腾了?让小龙虾帮你一键部署 AI 工具,省心又省力!

发布评论