IT策士 10余年一线大厂经验,专注 IT 思维、架构、职场进阶。我会在各个平台持续发布最新文章,助你少走弯路。
在前四篇中,我们围绕 Pod 做了大量工作:编写 YAML、理解生命周期、配置探针、实践 Sidecar 模式。但你有没有发现一个问题?我们一直在手动管理裸 Pod。
裸 Pod 就像没有劳动合同的临时工------一旦被删除(节点宕机、手动误删、资源驱逐),它就彻底消失了,没有人会自动帮你重新招一个回来。生产环境中,你不可能靠手动 kubectl run 和盯着监控来保证服务的高可用。
这时候需要一位"工头"------Deployment。它负责管理 Pod 的副本数量、确保指定数量的 Pod 始终在运行、在更新时控制 Pod 的替换节奏。今天这篇,我们从裸 Pod 的痛点出发,引出 Deployment 的设计逻辑,然后将贯穿案例的 Flask + Redis 应用从裸 Pod 升级为 Deployment 管理。
一、裸 Pod 的三大痛点
在第 20 篇中,我们用 kubectl create deployment my-nginx --image=nginx:alpine 创建了一个 Deployment,体验了自愈和扩容。但你有没有想过:如果当时用的是裸 Pod,删掉之后会怎样?
bash
# 创建一个裸 Pod(无 Deployment 管理)
kubectl run nginx-pod --image=nginx:alpine
kubectl get pod nginx-pod
# NAME READY STATUS RESTARTS AGE
# nginx-pod 1/1 Running 0 10s
# 删除它
kubectl delete pod nginx-pod
# pod "nginx-pod" deleted
kubectl get pod nginx-pod
# Error from server (NotFound): pods "nginx-pod" not found
Pod 消失了,除非你手动重新创建,否则服务就中断了。这正是裸 Pod 的三大痛点:
-
无自愈能力:Pod 被删了、节点宕机了,没人帮你重建。
-
无法扩容 :流量突增时,你需要手动
kubectl run一个个 Pod。 -
无法滚动更新:新版本发布时,你只能先删旧 Pod 再建新 Pod,服务中断是必然的。
解决方案:把 Pod 的"管理权"交给 Deployment。你只需要在 YAML 中声明"我要 3 个 Nginx Pod",Deployment Controller 会持续监控,确保实际 Pod 数量与你的声明一致。这就是 K8s 声明式管理的核心------你不再对 Pod 下命令,而是告诉控制器"我想要这样",控制器自己想办法达成目标。
二、Deployment、ReplicaSet、Pod 的三层关系
Deployment 并非直接管理 Pod,而是通过一个中间层------ReplicaSet。这三者的层级关系如下:
bash
Deployment (声明:我要 3 个 Nginx Pod,版本 v1.0)
│
└── ReplicaSet (执行:维护 3 个 Pod 副本)
│
├── Pod 1 (nginx:v1.0)
├── Pod 2 (nginx:v1.0)
└── Pod 3 (nginx:v1.0)
-
Deployment:负责声明期望状态(副本数、镜像版本、更新策略),是最上层的抽象。
-
ReplicaSet:由 Deployment 自动创建和管理,负责确保指定数量的 Pod 副本始终运行。
-
Pod :被 ReplicaSet 管理,通过
ownerReference字段关联到所属的 ReplicaSet。
在日常操作中,你只需要和 Deployment 打交道。ReplicaSet 由 Deployment Controller 自动维护,Pod 由 ReplicaSet Controller 自动维护。这正是第 19 篇讲的控制循环的实际体现:
-
Deployment Controller 发现"期望的 Deployment 定义了 3 个副本",创建 ReplicaSet。
-
ReplicaSet Controller 发现"期望 3 个 Pod,实际只有 1 个",创建更多 Pod。
-
节点上的 kubelet 发现"有新 Pod 分配给我",拉取镜像并启动容器。
三、编写 Deployment YAML
Deployment 的 YAML 结构与 Pod 高度相似,遵循同样的四段式骨架。区别在于多了 spec.replicas 和 spec.selector,而 Pod 模板则嵌套在 spec.template 中。
bash
apiVersion: apps/v1
kind: Deployment
metadata:
name: flask-deployment
labels:
app: flask-counter
spec:
replicas: 3
selector:
matchLabels:
app: flask-counter
template:
metadata:
labels:
app: flask-counter
spec:
containers:
- name: flask
image: flask-redis-counter:2.0
ports:
- containerPort: 5000
env:
- name: REDIS_HOST
value: redis-service
startupProbe:
httpGet:
path: /health
port: 5000
periodSeconds: 5
failureThreshold: 12
livenessProbe:
httpGet:
path: /health
port: 5000
periodSeconds: 15
failureThreshold: 3
readinessProbe:
httpGet:
path: /health
port: 5000
periodSeconds: 5
failureThreshold: 2
关键字段说明:
-
spec.replicas: 3:声明期望的 Pod 副本数。Deployment Controller 会持续确保实际运行的 Pod 数量与此一致。 -
spec.selector.matchLabels:定义 Deployment 如何找到它管理的 Pod。必须与template.metadata.labels保持一致,否则创建时会报错。K8s 通过标签选择器实现松耦合------Pod 只需要有正确的标签,Deployment 就能找到它。 -
spec.template:Pod 模板。内嵌的metadata.labels和spec与裸 Pod YAML 完全一致。
matchLabels 与 template.labels 的一致性:如果两者不匹配,Deployment 会拒绝创建。因为控制器无法找到自己创建的 Pod,会导致副本数统计混乱。
四、部署与核心操作
4.1 部署 Deployment
bash
kubectl apply -f flask-deployment.yaml
# deployment.apps/flask-deployment created
kubectl get deployment,pods
# NAME READY UP-TO-DATE AVAILABLE AGE
# deployment.apps/flask-deployment 3/3 3 3 30s
#
# NAME READY STATUS RESTARTS AGE
# pod/flask-deployment-5c6d7f8b9f-abcde 1/1 Running 0 30s
# pod/flask-deployment-5c6d7f8b9f-def34 1/1 Running 0 30s
# pod/flask-deployment-5c6d7f8b9f-ghi56 1/1 Running 0 30s
三个 Pod 的名称都以 flask-deployment 开头,后面跟着 ReplicaSet 生成的随机后缀(5c6d7f8b9f)和 Pod 的唯一 ID(abcde)。这是 Deployment → ReplicaSet → Pod 命名链路的直观体现。
bash
# 查看 ReplicaSet
kubectl get replicaset
# NAME DESIRED CURRENT READY AGE
# flask-deployment-5c6d7f8b9f 3 3 3 1m
4.2 自愈验证
bash
# 手动删除一个 Pod
kubectl delete pod flask-deployment-5c6d7f8b9f-abcde
# pod "flask-deployment-5c6d7f8b9f-abcde" deleted
# 立即查看
kubectl get pods
# 一个新的 Pod 被自动创建,名字后半段不同(新的唯一 ID)
# flask-deployment-5c6d7f8b9f-xyz12 1/1 Running 0 5s
ReplicaSet Controller 检测到实际 Pod 数量(2)< 期望数量(3),立即创建了一个新 Pod。整个过程对上层应用透明------只要还有一个 Pod 在运行,服务就不会中断。
4.3 扩容
bash
# 将副本数从 3 扩到 5
kubectl scale deployment flask-deployment --replicas=5
# deployment.apps/flask-deployment scaled
kubectl get pods -w
# flask-deployment-5c6d7f8b9f-xyz12 1/1 Running 0 2m
# flask-deployment-5c6d7f8b9f-new1 0/1 ContainerCreating 0 2s
# flask-deployment-5c6d7f8b9f-new2 0/1 ContainerCreating 0 2s
# flask-deployment-5c6d7f8b9f-new1 1/1 Running 0 10s
# flask-deployment-5c6d7f8b9f-new2 1/1 Running 0 10s
-w 参数持续监听 Pod 状态变化。你会看到两个新 Pod 从 ContainerCreating 过渡到 Running 的完整过程------这就是声明式管理的直观体现。你只需修改期望副本数,Controller 负责创建新 Pod,Scheduler 分配节点,kubelet 拉取镜像启动容器。
对比 Docker Compose:docker compose up -d --scale flask-app=3 也能扩容,但 Compose 只能在本机扩容,且没有自愈能力。如果某个容器被手动 docker rm -f 删除,Compose 不会自动重建它------除非你在 Compose 文件中配置了 restart: always(这是容器级别的重启,而非控制器驱动的副本保持)。而 K8s Deployment 中的 Pod 无论因为什么原因消失,ReplicaSet Controller 都会立刻补充,保持期望副本数不变。
五、Deployment 的常用运维操作
5.1 查看 Deployment 详情
bash
kubectl describe deployment flask-deployment
输出会包含 Deployment 的事件日志:何时创建、何时扩容、滚动更新等操作的历史记录。
5.2 查看 Deployment 状态
bash
kubectl rollout status deployment/flask-deployment
# deployment "flask-deployment" successfully rolled out
5.3 编辑 Deployment(在线修改)
bash
kubectl edit deployment flask-deployment
这会在默认编辑器中打开 Deployment 的完整 YAML,修改后保存退出,K8s 会自动触发更新。适合快速调整副本数、环境变量等参数。但不推荐直接在生产环境使用 ------原因有三:它绕过了 Git 版本控制,无法审计变更历史;如果误改关键字段(如镜像版本),可能触发非预期的滚动更新;手动编辑的 YAML 可能与 Git 中的声明式配置产生漂移。正确的做法是修改 YAML 文件后执行 kubectl apply -f。
5.4 查看更新历史
bash
kubectl rollout history deployment/flask-deployment
# deployment.apps/flask-deployment
# REVISION CHANGE-CAUSE
# 1 <none>
REVISION 是版本号,CHANGE-CAUSE 可以通过 --record 参数记录(K8s v1.22+ 推荐使用 annotation 记录变更原因)。每一次镜像更新或配置变更都会生成一个新 Revision,你可以随时回滚到任意历史版本(第 26 篇会讲到)。
5.5 暂停与恢复 Deployment
bash
# 暂停 Deployment(停止自动调和)
kubectl rollout pause deployment/flask-deployment
# 恢复 Deployment
kubectl rollout resume deployment/flask-deployment
暂停期间,你对 Deployment 的修改不会立即触发 Pod 更新,适合在执行多个配置修改后再统一应用。
六、Deployment 与 Compose 的对比
从 Docker Compose 到 K8s Deployment 的跃迁,可以总结为以下几点:
Compose 的思维是"我给你下命令,你执行"(命令式),Deployment 的思维是"我告诉你我想要什么,你自己想办法保持"(声明式)。这正是我们在第 18 篇强调过的根本转变。
七、命令速查表
八、本篇总结
-
裸 Pod 的三大痛点:无自愈、无扩容、无滚动更新。生产环境必须使用控制器管理。
-
Deployment → ReplicaSet → Pod 三层架构:你只和 Deployment 打交道,ReplicaSet 和 Pod 由 Controller 自动管理。
-
核心 YAML 字段 :
replicas(期望副本数)、selector(标签选择器)、template(Pod 模板),三者必须协调一致。 -
自愈和扩容 :删除 Pod 自动补充,
kubectl scale一键扩容,控制器持续调和实际状态与期望状态。 -
与 Compose 的关键差异:Deployment 是声明式、跨节点、有自愈能力的管理模式,Compose 是命令式、单机、手动干预的管理模式。
你现在已经拥有了一个"高可用"的 Flask 应用------3 个 Pod 副本同时运行,即使删掉其中 1 个,ReplicaSet Controller 也会立刻补回来。但如何更新镜像版本?如何保证更新过程中服务不中断?这些正是下一篇------第 26 篇:Deployment 进阶:滚动更新、回滚与暂停------要解决的问题。
想了解更多还可以去各个平台搜索「IT策士」,一起升级 IT 思维 !