最近临近春节,工作节奏忙碌,很难静下心来写东西。不过我还是想尽量记录一些思考,也算是对这个不同寻常时期的一个注脚。
学习 Kubernetes 的人,迟早会经历三个灵魂拷问:
第一问:我明明把 Pod 修好了,为什么又坏了?
第二问:为什么我手动改的配置,总是莫名其妙消失?
第三问:Kubernetes 到底听谁的?
如果你已经了解过它的基本理念,你应该知道:
- Kubernetes 的设计哲学不依赖于人的持续干预(声明式与自动化)
- 它要的是规则,不是一次性的命令
- 它会通过控制循环,不断将偏离的现实拉回期望状态
但这里有个更扎心的问题:
你辛辛苦苦改的配置,为什么在 K8s 眼里“不算数”?
今天我们就来深入聊聊这件事。
一、一个似曾相识的运维夜晚
想象一下这个场景:
凌晨三点。
Pod 挂了。
你睡眼惺忪,咖啡已经凉透,但还是凭借着肌肉记忆敲下命令:
kubectl exec -it my-pod -- /bin/bash
进入容器,修改配置,重启相关进程。
看着服务恢复,你在工作群里留下一句:“已修复。”
然后便疲惫地趴在桌上睡着了。
早上九点,电话铃声刺耳地响起。
“服务怎么又挂了?”
你愣住了。明明昨晚日志显示修复成功,配置也确实改过了。
直到同事问了一句:“你是进 Pod 里面改的吧?”
你沉默了两秒。
对方回了一句,语气平静却像一盆冷水:
“那等于没修。”
那一刻,你可能开始自我怀疑:是不是我技术不行?是不是 Kubernetes 有 Bug?是不是这系统在针对我?
但事实只有一个:
你昨晚做的那一切,从 Kubernetes 的角度看——从未发生过。
二、Kubernetes 不讲“人情”,只认“合同”
在传统的运维世界里,你的操作会被系统“默认承认”。
你改了配置文件,服务就按新配置跑;你重启了进程,它就会以新的状态运行。
但 Kubernetes 的世界观截然不同。它只承认一种东西:
写进规则里的事实。
你没有写进去,没有留下可验证、可复现、可追溯的痕迹,那么无论你多么努力,都只是无法被采信的“现场口供”。而 Kubernetes,从来不相信口供。
三、YAML 在 Kubernetes 中的真实身份
很多教程会告诉你:“YAML 是 Kubernetes 的配置文件。”
这句话在技术层面没错,但在认知层面却可能引入一个危险的误解:让人以为 YAML 只是“多种配置方式之一”。
然而在 Kubernetes 的语境下,YAML 的真实身份是:
唯一具有法律效力的、对系统期望状态的正式描述。
你在 YAML 里写的不是建议,也不是临时性的偏好。
你不是在说“我觉得可以这样”或“先这么改着试试”,而是在宣告:
我规定,系统必须长成这个样子。
Kubernetes 的角色不是一个会体谅你辛苦的助手,而是一个严格、死板、毫无弹性的执行者。它不会问你为什么,不会理解你的加班,更不会因为你昨晚没合眼而网开一面。
它只做一件事:
把现实世界,一次又一次、坚定不移地拉回到 YAML 所描述的样子。
四、Pod 会重建,节点会更换,但 YAML 永存
这是理解 Kubernetes 运维模式的一个关键转折点。
在传统运维思维里:
- 物理服务器或虚拟机是核心资产
- 上面运行的服务是核心业务
- 运维人员的记忆和经验是隐形的核心依赖
而在 Kubernetes 的设计中:
- Pod 是临时的,生命期短暂
- 容器是一次性的,用完即弃
- 节点(Node)是可被替换的耗材
唯一被系统视为“长期事实”和“真理来源”的,只有 YAML 文件。
你可以把 Kubernetes 世界里的实体分为两类:
会消失的:
- Pod(今天的“张三”,明天重建可能就是“李四”)
- 容器(像一次性餐盒,用完即弃)
- 进程(随时可能被重启)
- 节点(机器故障或升级时说换就换)
不会消失的:
这就解释了那个很“反直觉”的现象:你费尽心思在容器内部调试了半天,结果节点一次重启,所有修改荡然无存。这不是 Kubernetes 冷血,而是它从一开始就没打算记住你的任何手工操作。
就像你在沙滩上刻字,海浪一来,字迹就消失了。不是海浪故意和你作对,而是沙滩本身就不是用来铭刻永久碑文的地方。
五、为什么 Kubernetes 必须“只认 YAML”?
还记得控制循环(Control Loop)的核心逻辑吗?
观察现实 -> 对比期望 -> 执行纠偏
这里引出一个根本性问题:
如果没有一个稳定、可重复、不受临时操作干扰的“期望来源”,控制循环拿什么作为对比的基准?
答案只能是:
一份不依赖于任何特定人员、不会被临机操作搞乱的、文本化的声明。
也就是 YAML。
把 Kubernetes 想象成一个严谨的法庭:
- 你(手动改 Pod):法官大人,他昨天答应还我钱,真的!
- Kubernetes(法官):有白纸黑字、双方签字的借款合同吗?
- 你:这个……当时是口头说的……
- Kubernetes:口头承诺无效,不予采纳。下一个案子。
YAML 就是那份具有法律效力的合同。你可以有一万次口头承诺,但只要没写进合同,在法律上就等于从未发生。
你通过 kubectl exec 现场修改 Pod:
- 不可追溯(谁改的?什么时候改的?)
- 不可复现(下次出问题还能一模一样地改吗?)
- 不可验证(改得对不对?有没有引入副作用?)
- 不可持久(Pod 一重建,修改就灰飞烟灭)
从系统工程和可靠性的角度看,这种“运维”方式是灾难性的。 Kubernetes 正是为了避免这种灾难,才设计成了这个“只认白纸黑字”的系统。
六、Kubernetes 世界里的“合法”操作路径
理解了上述核心,很多之前觉得“别扭”的 Kubernetes 设计,瞬间就顺理成章了。
在 Kubernetes 的世界里,只有下面这条路径是“合法”且被系统承认的:
修改 YAML 文件
↓
通过 kubectl apply 提交给 API Server
↓
相应的 Controller 感知到期望状态变化
↓
控制循环开始工作
↓
驱动现实世界(Pod、配置等)向新的期望状态对齐
而不是我们熟悉但不可靠的路径:
发现服务有问题
↓
SSH/exec 进入环境
↓
手动修改配置、重启服务
↓
祈祷它别再出问题
对比一下两种方式:
| 操作方式 |
是否持久? |
Pod 重建后 |
K8s 承认吗? |
能否方便回滚? |
kubectl exec 手动改 |
❌ 否 |
修改丢失 |
❌ 不承认 |
❌ 几乎不能 |
改 YAML 后 apply |
✅ 是 |
配置仍在 |
✅ 承认 |
✅ 可以 |
用一句话总结这种思维的转变:
你不是在“操作”一个系统,你是在“修订”管理这个系统的法律条文。
七、kubectl exec:临时止血工具,而非根治方案
说句大实话:kubectl exec 在 Kubernetes 世界里,其定位是临时诊断与止血工具,绝不是最终的治疗方案。
它可以:
- ✅ 帮你快速定位问题根因
- ✅ 验证你的故障假设
- ✅ 在紧急救火时实现快速止血
但它永远不应该成为你的问题解决方案。
为什么?
因为它提供了一种虚假的掌控感。你以为修复了问题,实际上只是给一个随时可能“死亡并重生”的 Pod 化了个妆。几天后,这个 Pod 因为调度、更新或故障而重建,新生的 Pod 又是“素颜”,你之前的所有“化妆”痕迹丝毫未留。
一个类比:
kubectl exec 就像你在租住的房子里钉了个钉子挂画。某天,房东(Kubernetes)来检查或维护,直接把这面墙重新粉刷了一遍。你的钉子?不好意思,它从未在租赁合同里存在过。
想要合法地挂画?正确做法是去修改你的“租赁合同”(YAML),让房东书面同意你可以在指定位置钉钉子。
一个真实的故事:
运维工程师小明发现线上 MySQL 的 max_connections 设置过低,导致连接数不足。
他的操作是:
kubectl exec 进入 MySQL Pod。
- 修改
/etc/mysql/my.cnf 配置文件。
- 重启 MySQL 服务。
- 连接数恢复正常,安心下班。
一周后,因节点维护,Pod 被驱逐并重建。
新建的 Pod 加载的是原始的、未修改的 my.cnf 配置。
早高峰来临,数据库连接数瞬间被打满,整个系统瘫痪。
老板在群里质问:“这配置谁改的?怎么又回去了?!”
正确的声明式做法应该是:
- 创建一个 ConfigMap,包含正确的
my.cnf 配置。
- 修改 Deployment 的 YAML,将 Pod 挂载的配置文件指向这个 ConfigMap。
- 执行
kubectl apply -f deployment.yaml。
- Kubernetes 会自动滚动更新 Pod,新 Pod 会加载正确的配置。
这样,无论节点重启、Pod 重建还是版本回滚,你的配置都永远是预期中的样子。因为它被写进了 YAML——那份 Kubernetes 唯一承认的“法律条文”里。
八、记住这一句就够了
在 Kubernetes 的世界里,任何未写入 YAML(或通过其 API 声明)的修改,在系统眼中都等同于从未发生。
所有这类修改,都只是系统运行过程中短暂的幻觉,随着 Pod 的生命周期结束而湮灭。
收尾
传统运维模式下,你的工作是:“哪里坏了修哪里。”
而在掌握了 Kubernetes 的声明式哲学后,你的工作应转变为:“定义规则,让系统自动地、持续地按规则运行。”
打一个比方:
- 以前的你:是村里的赤脚医生,头疼医头,脚疼医脚。
- 现在的你:是卫生部的官员,负责制定《医疗规范》和《诊疗流程》,让全国的医院都按标准执行。
以前你治疗的是单个病人,现在你定义的是保障全民健康的规则。
所以,下次当你下意识地想敲 kubectl exec 进去手动修改时,先问问自己:
“我此刻是在紧急救火,还是在制定长期有效的规则?”
救火时,可以使用 exec 快速探查和临时处理。
但若要制定规则,请务必去修改 YAML。这正是云原生时代运维工程师思维转变的核心,也是保障系统长期稳定运行的基石。更多关于云原生运维的实践与探讨,欢迎访问云栈社区,那里有丰富的实战经验和技术文档分享。
思考题
假设线上一个服务的 Pod,需要临时将其日志级别从 INFO 调整为 DEBUG,以便排查一个偶发问题。通过修改 YAML 并触发滚动更新需要10分钟,而你只需要看5分钟 DEBUG 日志就能定位问题。
你会怎么办?
A. 先 kubectl exec 进入 Pod 修改日志配置,排查完再改回来。
B. 老老实实改 YAML,等待10分钟的滚动更新完成。
C. 有没有更优雅的第三种办法?
我的答案:
这种情况,使用 kubectl exec 是可以接受的,但你必须非常清醒地认识到:
- ✅ 这是临时性、诊断性的操作,绝非最终解决方案。
- ✅ 排查完毕后,应立即将配置恢复,或者将正确的配置正式更新到 YAML 中。
- ⚠️ 要清楚风险:如果 Pod 在你排查的这5分钟内发生了重启,你的临时修改会全部丢失。
更优雅、更“Kubernetes”的办法:
使用 kubectl set env 或 kubectl patch 命令,临时修改 Deployment 的环境变量(假设应用通过环境变量控制日志级别),触发一次快速的、受控的 Pod 滚动更新。排查完毕后,使用 kubectl rollout undo 一键回滚。
# 临时将日志级别改为 DEBUG,触发滚动更新
kubectl set env deployment/my-app LOG_LEVEL=DEBUG
# 观察日志,排查问题...
# 排查完毕后,回滚到上一个版本
kubectl rollout undo deployment/my-app
这种方式的好处是,你的修改是“合法”的(通过 Kubernetes API 修改了期望状态),记录在案,并且可以瞬间回滚,完美契合声明式运维的理念。