上一章讲了 Pod。本章讲生产环境最常用的控制器 Deployment:副本管理、滚动更新、回滚、扩缩容。
3.1 为什么需要 Deployment
Pod 有几个致命缺陷:
- 手动创建,没人盯着它,挂了不会自动重建
- 想扩到 10 个副本,要手动创建 10 次
- 升级版本只能先删后建,服务中断
- 出问题了没法快速回滚
Deployment 的出现,就是为了让 Pod 变得"可管理":
Deployment(管版本、管策略)
└── ReplicaSet(管副本数)
└── Pod(跑容器)
Deployment 不直接管理 Pod,而是管理 ReplicaSet。每次发布新版本,Deployment 都会创建一个新的 ReplicaSet,老 ReplicaSet 上的 Pod 逐步被替换。
3.2 Deployment 的核心字段
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: dev
labels:
app: web
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25% # 升级时最多多出多少个 Pod
maxUnavailable: 0 # 升级时最少允许多少个 Pod 不可用
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web # 必须和 selector.matchLabels 一致
spec:
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
resources:
requests:
memory: "64Mi"
cpu: "100m"
limits:
memory: "128Mi"
cpu: "200m"
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 3
periodSeconds: 5
重点字段解读:
| 字段 | 含义 | 建议 |
|---|---|---|
replicas | 期望副本数 | 至少 2,生产建议 3+ |
strategy.type | 升级策略,RollingUpdate 或 Recreate | 生产默认 RollingUpdate |
maxSurge | 升级时最多比期望副本多出多少 | 25% 兼顾速度和资源 |
maxUnavailable | 升级时最多允许多少不可用 | 设为 0 保证业务不中断 |
selector.matchLabels | 选择哪些 Pod 归这个 Deployment 管 | 创建后不可修改 |
3.3 创建与日常操作
# 命令方式创建
$ kubectl create deployment web --image=nginx:alpine --replicas=3
# 查看
$ kubectl get deploy,rs,pod -l app=web
NAME READY UP-TO-DATE AVAILABLE
deployment.apps/web 3/3 3 3
NAME DESIRED CURRENT READY
replicaset.apps/web-7c4d9f8b5c 3 3 3
NAME READY STATUS AGE
pod/web-7c4d9f8b5c-2jxmt 1/1 Running 1h
UP-TO-DATE 表示当前模板版本最新的 Pod 数量。发布过程中如果看到 3/3 但 UP-TO-DATE 1/3,说明还在滚动更新。
3.3.1 扩缩容
# 手动扩容
$ kubectl scale deployment web --replicas=5
# 自动扩缩容 HPA(第 9 章详细讲)
$ kubectl autoscale deployment web --min=2 --max=10 --cpu-percent=70
3.3.2 更新镜像
# 方式一:命令行
$ kubectl set image deployment/web nginx=nginx:1.27
# 方式二:编辑 YAML
$ kubectl edit deployment/web
3.4 滚动更新与回滚
滚动更新的本质:
初始状态:
RS-old: 3/3(v1)
第 1 步:
RS-old: 2/3 RS-new: 1/1
第 2 步:
RS-old: 1/3 RS-new: 2/2
完成:
RS-old: 0/3 RS-new: 3/3
在更新过程中:
$ kubectl get rs
NAME DESIRED CURRENT READY
web-7c4d9f8b5c 2 2 2 ← 老版本
web-6f8d5c9b7a 2 2 2 ← 新版本
查看发布进度:
$ kubectl rollout status deployment/web
Waiting for deployment "web" rollout to finish: 2 of 3 updated replicas are available...
deployment "web" successfully rolled out
3.4.1 发布历史与回滚
# 查看发布历史
$ kubectl rollout history deployment/web
deployment.apps/web
REVISION CHANGE-CAUSE
1 <none>
2 <none>
3 kubectl set image deployment/web nginx=nginx:1.27
# 回滚到上一个版本
$ kubectl rollout undo deployment/web
# 回滚到指定版本
$ kubectl rollout undo deployment/web --to-revision=2
# 查看新状态
$ kubectl get rs
NAME DESIRED CURRENT READY
web-7c4d9f8b5c 3 3 3 ← 老版本又回来了
web-6f8d5c9b7a 0 0 0
重要:Deployment 默认保留 10 个 ReplicaSet 历史版本,方便回滚。老版本不会立即删除。
3.5 零停机发布的配置技巧
要实现真正的零停机,光靠默认配置不够。核心是:新 Pod Ready 之前,老 Pod 不退出。
推荐配置:
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 最多多 1 个 Pod
maxUnavailable: 0 # 不允许任何时刻少于 3 个可用 Pod
配合 readinessProbe 使用:新 Pod 没 Ready,就不算"可用",老的继续跑。
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
failureThreshold: 3
完整的优雅发布流程:
1. 创建新 RS,起 1 个新 Pod
2. readinessProbe 通过 → 新 Pod 加入 Service Endpoints
3. 老 Pod 收到 SIGTERM
4. preStop 钩子等一会儿,让 Endpoints 列表更新
5. 老 Pod 处理完已有请求后退出
6. 继续下一轮,直到全部替换完成
3.6 金丝雀发布(进阶)
如果你想让 10% 的流量先访问新版本,验证没问题再全量发布:
# 先只发布 1 个新版本 Pod
$ kubectl set image deployment/web nginx=nginx:1.27
$ kubectl scale deployment web --replicas=10
# 这样新版本 1 个,老版本 9 个,约 10% 流量
# 验证没问题,继续全量
$ kubectl scale deployment web --replicas=10
# 后续设置 maxSurge: 100%, maxUnavailable: 0,一次性全切
更专业的金丝雀可以用 Argo Rollouts、Istio、Nginx Ingress canary 注解等。第 11 章 ArgoCD 会涉及相关的发布策略思路。
3.7 常见 Deployment 故障
| 现象 | 原因 | 排查 |
|---|---|---|
kubectl apply 成功但 Pod 没更新 | 镜像 tag 没变 | 必须改镜像名或 tag 才会触发滚动更新 |
| 滚动更新卡住 | readinessProbe 一直失败 | kubectl describe pod 看探针 |
| 新版本 rollout 后旧 RS 还在 | 正常,保留历史版本用于回滚 | kubectl get rs |
| 回滚失败 | 老版本 RS 被清理了 | 调大 revisionHistoryLimit |
| Pod 反复重启 | livenessProbe 太敏感或应用本身问题 | 调整探针阈值或修 bug |
3.8 本章小结
- Deployment 通过 ReplicaSet 管理 Pod,实现自愈、扩缩、滚动更新
- 核心字段:
replicas、strategy、selector、template - 滚动更新 = 新 RS 扩容 + 老 RS 缩容,历史版本保留用于回滚
- 零停机发布三要素:
maxUnavailable: 0+readinessProbe+preStop钩子 - 常用命令:
scale、set image、rollout status、rollout undo
3.9 课后练习
- 创建一个 3 副本的 Deployment,更新镜像版本,观察
kubectl get rs中新旧 RS 数量变化。 - 在 Deployment 中配置
maxUnavailable: 0和readinessProbe,实现零停机发布。 - 故意把镜像 tag 写错,看 Deployment 卡住后如何查看失败原因。
- 练习
kubectl rollout history和kubectl rollout undo回滚。