标题说用了三年是夸张的说法,K8s 1.28是2023年8月才发布的,不可能用三年。但我用K8s确实有几年了,从1.20一路用到1.28,中间踩了无数的坑。

有些道理,看文档的时候觉得懂了,真正踩过坑之后才发现,原来根本没懂。这篇文章分享几个我用了很久K8s之后才真正明白的道理。

简单比复杂重要

刚接触K8s的时候,觉得功能越多越好。什么Service Mesh、GitOps、Serverless,能上的都上,集群里堆了一堆组件,看起来很高级。

后来出了一次故障,排查了半天,发现问题出在某个不熟悉的组件上。那时候才意识到,系统越复杂,出问题的概率越大,排查难度也越高。

现在我的原则是,能不用的组件就不用,能简单的方案就不搞复杂。K8s本身已经很复杂了,不要再给它增加不必要的复杂度。需要什么功能再加什么,而不是先把所有功能都堆上再说。

比如服务发现,K8s自带的Service和DNS已经够用了,没必要一上来就搞Service Mesh。等真的有灰度发布、流量治理这些需求了,再考虑也不迟。

资源限制一定要设

这是个老生常谈的问题,但真的踩过坑才知道有多重要。

有一次线上服务突然全部变慢,查了半天发现是某个新上线的服务没有设资源限制,内存泄漏把节点的内存吃光了,导致同节点上的其他服务全部被OOM Kill。

从那以后,我们规定所有Deployment必须设requests和limits,不设的不让上线。虽然设资源限制需要花时间调优,但这是值得的。一个服务出问题,不会影响整个节点的其他服务,这就是资源隔离的意义。

CPU的limits也要注意。设得太低,服务会被throttle,响应变慢;设得太高,节点CPU超卖,高峰期大家抢资源。我们的经验是,requests按实际使用量设,limits设成requests的两到三倍,给突发流量留一点空间。

健康检查不是摆设

很多人写健康检查就是随便写个接口返回200,觉得有就行。直到出了一次事故,才明白健康检查的重要性。

那次是一个服务依赖的数据库挂了,但健康检查还是返回200,因为检查接口根本没查数据库。结果K8s以为服务正常,继续把流量打过去,用户全部报错。

后来我们把健康检查改了,liveness检查进程是否存活,readiness检查依赖是否正常。数据库连不上的时候,readiness返回失败,K8s就会把这个Pod从Service里摘掉,不再打流量过去。虽然服务还是不可用,但至少不会把错误的流量打过去,也不会让K8s不停地重启Pod。

健康检查要根据服务的实际情况来写,不能一刀切。有的服务依赖数据库,有的依赖缓存,有的依赖外部API,检查的时候要把这些依赖都覆盖到。

配置和代码要分开

以前我们把配置写在代码里,改个配置还要重新打包发布。后来用了ConfigMap,配置和代码分开了,改配置不用重新构建镜像,方便很多。

但用了ConfigMap之后又踩了坑。ConfigMap更新之后,已经运行的Pod不会自动加载新配置,要么重启Pod,要么应用自己监听配置变化。我们有个服务改了ConfigMap之后没重启,结果配置一直没生效,排查了半天才发现。

现在我们的做法是,配置改动也走发布流程,更新ConfigMap之后滚动重启相关Pod。虽然多了一步,但保证了配置的一致性。重要的配置还会加版本号,出了问题可以快速回滚。

Secret的管理也要注意。不要把敏感信息写在镜像里,也不要写在ConfigMap里,要用Secret。虽然Secret本质上也只是base64编码,但至少权限控制和审计会方便一些。更敏感的信息可以用外部的密钥管理服务,不要存在K8s里。

监控和日志是生命线

K8s集群里跑着几十个服务,上百个Pod,出了问题如果没有监控和日志,根本无从下手。

我们吃过一次亏。那时候监控只搭了基础的CPU内存指标,日志也没集中收集。有个服务间歇性报错,因为没有完整的日志,查了好几天才定位到问题。

从那以后,我们把监控和日志当成基础设施来建设。Prometheus加Grafana监控指标,ELK收集日志,告警规则覆盖了服务可用性、资源使用率、错误率这些关键指标。现在出了问题,看一眼监控面板就知道大概哪里不对,查日志也能快速定位。

监控不是搭好就完事了,告警规则要持续优化。太多无效告警会让人麻木,真正重要的告警反而被忽略。我们定期 review 告警规则,把噪音大的关掉或者调整阈值,保证告警都是有用的。

升级要谨慎

K8s版本升级是个大事。小版本升级还好,大版本升级经常有API废弃和行为变化。

我们有一次从1.24升到1.25,因为没仔细看release notes,升级之后发现有个用了很久的API被移除了,相关的服务全部起不来。紧急回滚之后,花了一周时间改代码,才重新升级成功。

现在升级之前,我们会仔细读release notes,列出所有废弃的API和行为变化,逐个检查我们的服务有没有用到。先在测试环境升级,跑一遍完整的测试,确认没问题了再上生产。生产升级选在业务低峰期,提前做好回滚预案。

1.28这个版本整体比较平稳,没有特别大的破坏性变化。但即便如此,升级之前还是要做足准备,不能掉以轻心。

写在最后

K8s是个强大但复杂的工具。用得好,它能帮你管理大规模的容器集群;用不好,它本身就会成为最大的问题。

简单比复杂重要,资源限制一定要设,健康检查不是摆设,配置和代码要分开,监控和日志是生命线,升级要谨慎。这些道理看起来简单,但都是踩过坑之后才真正理解的。

技术的学习就是这样,看书看文档能知道概念,但只有真正用了、踩坑了、解决了,才能变成自己的经验。希望这些经验能帮你少走一点弯路。