Kubernetes 设计哲学折腾手记
YAML 写对了,Pod 还在 CrashLoopBackOff——从 Swarm 迁过来之后,我才真正搞懂 K8s 说的「声明式」和「控制器」不是在炫概念。
Swarm 时期习惯的是「一条命令把容器拉起来」:docker service scale web=5,当场生效。迁到 Kubernetes 之后,团队还是这套脑子——上线前 kubectl scale deployment web --replicas=5,过一会儿又变回 3;有人手工 kubectl delete pod 清故障,Deployment 立刻补一个新的,删错版本的话旧 Pod 又回来。
这些都不是 K8s 坏了,是设计哲学和 Swarm 根本不一样。这篇不打算复述官方架构图,只记迁移后真正帮上忙的几件事:声明式到底声明了什么、控制器在后台干什么、以及 spec 和 status 为什么经常对不上。
命令式脑子撞上声明式系统
Swarm 的 docker service update 本质是发指令:你现在给我改成 5 个副本。Kubernetes 的 Deployment 是交期望状态:我希望始终有 5 个副本在跑,至于中间删了 Pod、节点挂了、镜像拉失败,控制器自己想办法凑齐。
第一次被教育,是有人手工扩缩容:
# 手工改成 5 副本
kubectl scale deployment web --replicas=5
kubectl get deployment web
# READY 5/5
# 十分钟后
kubectl get deployment web
# READY 3/3 —— 又回去了
根因是 YAML 里还写着 replicas: 3。Deployment Controller 一直在跑 reconcile 循环:实际状态 ≠ spec,就改实际状态。手工 scale 只改了集群里的对象,没改 Git 里的 manifest,下一轮 Argo CD 同步或者有人 kubectl apply -f,数字就被打回去。
这和 Swarm 最大的体感差异:改集群不算完,改「期望状态」才算。后来我们定规矩:生产变更只走 PR 改 YAML,禁止裸 kubectl scale(紧急止血除外,且必须回头补 YAML)。
控制器:不是魔法,是死循环
文档里写 Controller 监控资源、消除差异——听起来很抽象。排障时看 Event 才具体:
kubectl describe deployment web
# Events:
# ScalingReplicaSet deployment/web Scaled up replica set web-7d4f9 to 3
# ScalingReplicaSet deployment/web Scaled down replica set web-6a2b1 to 0
Deployment 自己不直接管 Pod,它管 ReplicaSet;ReplicaSet 再管 Pod。发布新镜像时,旧 ReplicaSet 缩到 0、新 ReplicaSet 拉到 3——不是原地改容器,是换一套对象。我们有一次滚动更新卡住,查半天发现是新 ReplicaSet 的 Pod 一直 ImagePullBackOff,旧 Pod 已经被缩掉了,服务半死不活。
理解控制器之后,排查路径就固定了:
kubectl get deploy,rs,pod -l app=web—— 看三层对象是否对齐kubectl describe看 Events —— 控制器最后一步想干什么- 改 spec,别跟 status 较劲 —— status 是控制器写回来的
自定义 CRD / Operator 也是同一套:你声明期望,它写 reconcile 逻辑。我们后来给 Redis 集群上了 Operator,备份窗口、主从切换不再靠 Cron + 手工脚本,但调试思路一样——看 CR 的 status.phase,看 Operator 日志里 reconcile 失败原因。
metadata / spec / status:三个字段各管什么
API 统一模型听起来像面试题,写 YAML 时老踩坑:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
labels:
app: web
env: prod # 给 Service selector、NetworkPolicy 用
annotations:
deployment.kubernetes.io/revision: "3" # 别手改,控制器维护
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web # 必须和 selector 对得上,否则 ReplicaSet 认不出 Pod
spec:
containers:
- name: nginx
image: nginx:1.25
status: # 用户一般不写,kubectl get 看到的是这里
replicas: 3
readyReplicas: 2 # 2 个 Ready —— 和 spec 不一致时说明还在收敛或有问题
我们踩过两个典型坑:
label selector 对不上 —— 改了 Pod template 的 label 忘了改 Deployment selector,新 ReplicaSet 创建 0 个 Pod,旧 Pod 还在跑,看起来「服务正常、发布没生效」。
把 status 当 spec 改 —— 有人 export 现网 Deployment,status 整段贴进 Git,apply 时直接 ignored 或报 warning。正确做法是只版本管理 spec;现网状态用 kubectl get -o yaml 看,别往回写。
最终一致:apply 成功 ≠ 立刻能用
Swarm 里 docker service ls 看到 RUNNING 基本就能打流量。K8s 里 kubectl apply 返回 success,只说明 API Server 接受了 spec,Pod 调度、拉镜像、探针通过还要时间。
我们有过一次「发布成功但 502」:
kubectl rollout status deployment/web --timeout=120s
# error: timed out waiting for the condition
kubectl get pod -l app=web
# web-xxx 0/1 CrashLoopBackOff
Deployment 的 status.conditions 里 Available=False,Ingress 后端已经切到新 Pod,但 readinessProbe 没过。教训:对外宣告发布完成,要看 Ready 副本数,不是看 apply 有没有报错。
这也解释了为什么 K8s 选最终一致而不是处处强一致——API 要快、控制面要可扩展,收敛延迟交给控制器和探针去兜。
哪些「哲学」真的影响日常写法
| 概念 | 以前怎么理解 | 现在怎么用 |
|---|---|---|
| 声明式 | 写 YAML 代替 shell | 所有变更进 Git;手工改集群会被打回 |
| 控制器 | 自动化的黑盒 | 排障看 Events + 三层对象(Deploy/RS/Pod) |
| 不可变基础设施 | 口号 | 改镜像 = 新 ReplicaSet,不 ssh 进容器改文件 |
| 自愈 | Pod 挂了会自动重启 | 先查为什么挂;盲目 delete pod 可能滚到更糟的版本 |
| 水平扩展 | HPA 很酷 | 无状态服务先上 HPA;有状态得先想 PVC 和拓扑 |
没上来就用 Service Mesh、没急着写 Operator。三节点集群、二十几个 Deployment,把 Deployment + Service + Ingress + ConfigMap/Secret 用顺,比背 Borg 论文管用。
和 Swarm 对照:不是谁更强,是默认假设不同
| Docker Swarm | Kubernetes | |
|---|---|---|
| 心智模型 | 命令改集群状态 | 提交期望,控制器收敛 |
| 发布 | service update 原地改 | 新 RS 滚动替换 |
| 扩缩容 | scale 命令即时 | 改 spec;HPA 改 spec |
| 排障 | docker service ps | describe events + 多层对象 |
| 适合 | 容器少、团队小、要快 | 要多环境、多租户、生态和扩展 |
我们迁移到 K8s 不是因为 Swarm「不能用」,而是灰度、监控、网络策略、团队技能栈——这些问题 K8s 默认就带假设,Swarm 得自己拼。理解设计哲学,本质是理解这些默认假设,少跟系统对着干。
收束
Kubernetes 的设计哲学不是 PPT 里的「云原生赋能」,而是几件事反复撞墙后才记住:
- 改 spec,别跟 status 搏斗
- 控制器一直在 reconcile,手工 patch 集群是临时方案
- apply 成功只是开始,Ready 才是真的
Borg 论文、API 版本策略那些,知道有这回事就行;日常运维真正用的是 kubectl describe 里的 Events 和 Deployment 三层对象。哲学落地了,CrashLoopBackOff 才不会每次都像玄学。
版权声明: 本文首发于 指尖魔法屋-Kubernetes 设计哲学折腾手记(https://blog.thinkmoon.cn/post/4-kubernetes-design-philosophy-notes/) 转载或引用必须申明原指尖魔法屋来源及源地址!
评论
使用 GitHub 账号登录后即可留言,支持 Markdown。