tags:
– Kubernetes
– K8s系列
– DevOps
– Deployment
– ReplicaSet
– 声明式管理
– 容器编排
K8s 系列 | 第 4 天:Deployment 与 ReplicaSet:声明式应用管理
第 4/30 天
一、引言
当我们进入 Kubernetes 的世界,核心目标是将容器化应用可靠地运行在集群中。第 3 天我们学习了 Pod——K8s 中最小的调度单元。但直接管理 Pod 并不现实:Pod 是临时性的,可能因节点故障、资源不足或人为误操作而消失。如何确保应用始终维持期望的运行副本数?如何实现无损更新和快速回滚?这正是 Deployment 和 ReplicaSet 要解决的核心问题。
Deployment 是 Kubernetes 中最常用的工作负载资源之一,它提供声明式更新能力——你只需描述”最终状态”,K8s 的控制循环便会自动将实际状态收敛到期望状态。而 ReplicaSet 则是 Deployment 的底层基石,负责确保指定数量的 Pod 副本始终运行。
本文将从原理到实战,深入剖析 Deployment 和 ReplicaSet 的工作机制,并通过多个 YAML 示例展示声明式应用管理的全流程。
二、核心概念
2.1 ReplicaSet:Pod 副本的守护者
ReplicaSet 是 Kubernetes 中的一个控制器,它的职责很简单:确保集群中运行着指定数量的 Pod 副本。当 Pod 因故障退出或被删除时,ReplicaSet 会立即创建新的 Pod 来补足;当副本数超出预期时,它会终止多余的 Pod。
ReplicaSet 的工作原理基于标签选择器(Label Selector)和控制器循环(Control Loop):
- 用户通过 YAML 定义
replicas(期望副本数)和selector.matchLabels(选择哪些 Pod) - ReplicaSet 控制器持续通过 API Server 监听 Pod 状态
- 当实际副本数偏离期望值时,控制器调用 API 创建或删除 Pod
# replicaset-example.yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: myapp-replicaset
labels:
app: myapp
tier: frontend
spec:
replicas: 3
selector:
matchLabels:
app: myapp
tier: frontend
template:
metadata:
labels:
app: myapp
tier: frontend
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 200m
memory: 256Mi
关键要点:
– spec.selector 必须与 spec.template.metadata.labels 匹配,否则 API Server 会拒绝创建
– ReplicaSet 本身不支持滚动更新——更新 Pod 模板需要手动删除并重建所有 Pod
– 这也就是为什么实际生产中几乎不会直接使用 ReplicaSet,而是使用更高级的 Deployment
2.2 Deployment:声明式更新的终极抽象
Deployment 在 ReplicaSet 之上增加了版本管理和滚动更新能力。当你定义一个 Deployment,K8s 会自动为其创建对应的 ReplicaSet,并由 Deployment 控制器协调 ReplicaSet 的版本迭代。
Deployment 的核心职责:
- 声明式更新:你描述”想要什么状态”,控制器负责”如何到达”
- 滚动更新(RollingUpdate):逐步替换旧版本 Pod,保持服务可用
- 回滚(Rollback):一键回到任意历史版本
- 扩缩容(Scale):调整副本数,增加或减少运行实例
- 自愈(Self-healing):Pod 故障时自动重建
# deployment-example.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deployment
labels:
app: myapp
tier: frontend
spec:
replicas: 3
selector:
matchLabels:
app: myapp
tier: frontend
template:
metadata:
labels:
app: myapp
tier: frontend
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 200m
memory: 256Mi
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
revisionHistoryLimit: 10
看到这里的 strategy 字段了吗?它定义了 Deployment 的更新策略,我们稍后会详细讲解。
2.3 Deployment 与 ReplicaSet 的关系
Deployment 和 ReplicaSet 之间的关系可以通过这张逻辑图理解:
Deployment
├── ReplicaSet v1 ─── Pod v1 (x3)
├── ReplicaSet v2 ─── Pod v2 (x3) ← 当前活跃版本
└── ReplicaSet v3 ─── Pod v3 (x3) ← 最新版本
每次 Deployment 更新 Pod 模板时,K8s 不会修改已有的 ReplicaSet,而是创建一个新的 ReplicaSet,将旧 ReplicaSet 的副本数逐渐缩为 0,同时扩增新 ReplicaSet 的副本数。旧版本的 ReplicaSet 仍然保留(受 revisionHistoryLimit 控制),用于快速回滚。
三、实战步骤
3.1 创建 Deployment
首先,将上面的 YAML 保存为文件并应用到集群:
# 应用 Deployment
kubectl apply -f deployment-example.yaml
# 查看 Deployment 状态
kubectl get deployments -o wide
# 查看对应的 ReplicaSet
kubectl get replicasets -o wide
# 查看 Pod 详情
kubectl get pods -l app=myapp
# 输出示例:
# NAME READY STATUS RESTARTS AGE
# myapp-deployment-7d9f8c6b4-abc12 1/1 Running 0 30s
# myapp-deployment-7d9f8c6b4-def34 1/1 Running 0 30s
# myapp-deployment-7d9f8c6b4-ghi56 1/1 Running 0 30s
注意:Pod 名称中的 7d9f8c6b4 正是对应 ReplicaSet 的哈希值,它由 Deployment 的 Pod 模板内容计算得出。
3.2 滚动更新实战
现在我们来模拟一次应用版本升级——将 Nginx 从 1.25 升级到 1.26:
# 方式一:直接修改 YAML 并重新 apply
# 修改 image: nginx:1.25 → nginx:1.26
kubectl apply -f deployment-example.yaml
# 方式二:使用 kubectl set image 在线更新(更便捷)
kubectl set image deployment/myapp-deployment nginx=nginx:1.26 --record
# 查看滚动更新过程
kubectl rollout status deployment/myapp-deployment
# 输出示例:
# Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
# deployment "myapp-deployment" successfully rolled out
在滚动过程中,Deployment 控制器会:
- 创建新的 ReplicaSet(例如
myapp-deployment-8f2e1c4a5),副本数为 0 - 根据
maxSurge和maxUnavailable策略逐步调整新旧 ReplicaSet 的副本数 - 当新 Pod 通过就绪探针检查后,继续下一批替换
- 直到所有旧 Pod 被替换完毕
# 查看更新前后的 ReplicaSet
kubectl get replicasets -l app=myapp
# 输出示例:
# NAME DESIRED CURRENT READY AGE
# myapp-deployment-7d9f8c6b4 0 0 0 10m
# myapp-deployment-8f2e1c4a5 3 3 3 1m
可以看到,旧的 ReplicaSet 副本数已缩减为 0,但仍然存在——这就是回滚的”回退点”。
3.3 回滚操作
如果新版本出现问题,可以快速回滚:
# 查看发布历史
kubectl rollout history deployment/myapp-deployment
# 输出示例:
# REVISION CHANGE-CAUSE
# 1 <none>
# 2 kubectl set image deployment/myapp-deployment nginx=nginx:1.26 --record=true
# 回滚到上一个版本
kubectl rollout undo deployment/myapp-deployment
# 回滚到指定版本
kubectl rollout undo deployment/myapp-deployment --to-revision=1
# 确认回滚完成
kubectl rollout status deployment/myapp-deployment
⚠️ 注意: 回滚后 Deployment 控制器会创建一个新的 ReplicaSet,而非直接复用旧的。但旧版本 Pod 模板与新 ReplicaSet 的模板一致,因此效果相同。
3.4 扩缩容操作
# 将副本数扩展到 5
kubectl scale deployment/myapp-deployment --replicas=5
# 缩减到 2
kubectl scale deployment/myapp-deployment --replicas=2
# 查看实时变化
kubectl get pods -l app=myapp -w
四、深入理解:更新策略与参数详解
4.1 RollingUpdate 策略参数
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 更新时允许超出期望副本数的最大 Pod 数(可以是数字或百分比)
maxUnavailable: 0 # 更新时允许不可用 Pod 的最大数量(0 表示始终保持全部可用)
maxSurge: 1:当期望副本数为 3 时,更新期间最多同时运行 4 个 Pod(3+1)maxUnavailable: 0:保证始终至少有 3 个 Pod 提供服务,零中断发布
这种配置适合对可用性要求高的生产环境。如果希望节省资源,可以设置为:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
这表示更新过程中最多超出期望值 25%,同时最多允许 25% 的 Pod 不可用。对于 4 副本 Deployment,意味着可以最多有 1 个额外的 Pod(4 × 25% = 1),同时最多 1 个 Pod 可以被关闭。
4.2 Recreate 策略
如果应用不能同时运行多个版本(例如使用了独占资源或有状态应用),可以使用 Recreate 策略:
strategy:
type: Recreate
注意: Recreate 策略会先终止所有旧 Pod,再创建新 Pod,这会导致短暂的停机时间。
4.3 就绪探针(Readiness Probe)的重要性
滚动更新的核心依赖于就绪探针。如果没有配置就绪探针,K8s 认为 Pod 一启动就是”就绪”状态,这会导致新 Pod 还没准备好服务就切入了流量,引发 502 错误:
# 在 Deployment 的 Pod template 中添加就绪探针
spec:
template:
spec:
containers:
- name: nginx
image: nginx:1.26
readinessProbe:
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 5
periodSeconds: 10
failureThreshold: 3
livenessProbe:
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 15
periodSeconds: 20
五、常见问题与最佳实践
Q1:为什么我的 Deployment 一直处于 “progressing” 状态?
- 原因:新 Pod 未能通过就绪探针检查
- 排查:
kubectl describe deployment/myapp-deployment查看事件,或kubectl logs <pod-name>检查容器日志
Q2:滚动更新太慢怎么办?
- 增大
maxSurge和maxUnavailable的值 - 减少
readinessProbe.periodSeconds和failureThreshold - 使用更快的镜像仓库(配置 imagePullPolicy: IfNotPresent 减少拉取)
Q3:如何暂停滚动更新?
kubectl rollout pause deployment/myapp-deployment
# 做一些金丝雀验证...
kubectl rollout resume deployment/myapp-deployment
Q4:Deployment 和 StatefulSet 有什么区别?
- Deployment 无状态:所有 Pod 可互换,使用共享存储或云盘
- StatefulSet 有状态:每个 Pod 有唯一标识和稳定的网络标识,适合数据库
- 第 10 天我们会详细讲解 StatefulSet
最佳实践清单
| 实践 | 说明 |
|---|---|
| ✅ 总是配置就绪探针 | 确保滚动更新中流量无损切换 |
✅ 使用 --record |
在修订历史中记录变更原因 |
✅ 设置 revisionHistoryLimit |
控制历史版本数量,避免 etcd 存储膨胀 |
| ✅ 配置资源请求与限制 | 帮助调度器做出更好的放置决策 |
| ✅ 避免直接修改 ReplicaSet | 始终通过 Deployment 管理副本 |
❌ 不要在生产使用 latest 标签 |
导致不可控的版本漂移 |
六、总结
今天我们系统学习了:
- ReplicaSet 确保 Pod 副本数恒定,是控制器模式的基础实现
- Deployment 在 ReplicaSet 之上提供了声明式更新、滚动发布和回滚能力
- 滚动更新策略通过
maxSurge和maxUnavailable精确控制更新期间的容量 - 就绪探针是零中断发布的关键依赖
Deployment 是 Kubernetes 中最常用的工作负载资源,理解了它,你就掌握了声明式运维的核心思想——告诉系统你想去哪,而不是手把手教它每一步怎么走。
下期预告
第 5 天我们将深入 Service 与网络基础,解析 ClusterIP、NodePort、LoadBalancer 三种 Service 类型的区别与适用场景,并手把手搭建一个从集群内到集群外的完整网络通路。
👉 系列目录:K8s 30 天系列文章















暂无评论内容