Deployment:扩缩、自愈与滚动更新
运行一个 Pod 的多个副本,无停机地更新它们,并在发布出问题时回滚。
Pod 是可丢弃的,而一个裸 Pod 一旦死亡就永远死了。Deployment 解决了这个问题:你声明想要多少份副本以及运行哪个镜像,Kubernetes 就保持那么多在运行,并为你处理更新。本指南涵盖创建 Deployment、扩缩它、无停机地更新它,以及回滚它。
为什么你很少创建裸 Pod
如果节点失败或 Pod 被删除,没有任何东西会重新创建一个独立的 Pod。在实践中,你几乎从不直接创建 Pod。你创建一个 Deployment,它创建一个 ReplicaSet,再由 ReplicaSet 创建并维护 Pod。Deployment 添加了 ReplicaSet 所缺少的东西:受控的更新和版本历史。
编写 Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
labels:
app: web
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
这里有两个 metadata 部分,把它们分清楚会有帮助。上面那个描述 Deployment。template 之下的那个描述它创建的 Pod,而 template 就是一个没有名称的 Pod 清单。selector 告诉 Deployment 哪些 Pod 属于它,所以它必须与模板中的标签匹配,否则 Kubernetes 会拒绝该清单。
kubectl apply -f deployment.yaml
kubectl get deployment,replicaset,pods
你会看到一个 Deployment、一个以它命名并带有哈希的 ReplicaSet,以及三个以 ReplicaSet 命名的 Pod。
标签与选择器:对象如何找到彼此
标签是你可以附加到任何对象上的键值对。选择器查询它们,这正是 Kubernetes 连接事物的方式:Deployment 找到它的 Pod,Service 找到它的后端,网络策略找到它所管辖的 Pod。
kubectl get pods --show-labels
kubectl get pods -l app=web
要看到选择器有多重要,请从一个 Pod 上移除标签:
kubectl label pod POD_NAME app-
kubectl get pods --show-labels
ReplicaSet 不再把该 Pod 视为自己的 Pod,所以它会启动一个替代品以回到三个。被取消标签的 Pod 继续运行,但没有任何东西管理它。请用 kubectl delete pod POD_NAME 删除它,以免它一直留着。
扩缩并观察自愈
kubectl scale deployment web --replicas=5
kubectl get pods -w
在另一个终端中,删除一个 Pod,并观察替代品在片刻之内出现。若要永久更改副本数,请编辑清单中的 replicas 并再次应用,因为单独使用 kubectl scale 会让你的文件过时。你无法通过删除 Pod 来摆脱 Deployment 的 Pod,因为它们会不断回来。要删除 Deployment 本身,或把它缩容到零。
发布新版本
更改 Pod 模板中的任何内容,比如镜像,都会触发滚动更新:
kubectl set image deployment/web nginx=nginx:1.26
kubectl rollout status deployment/web
Deployment 会为新版本创建第二个 ReplicaSet,然后逐步扩容它,同时缩容旧的,所以始终有一些 Pod 在提供流量。默认情况下,它在变更期间允许最多 25% 的额外 Pod(maxSurge)和最多 25% 的不可用 Pod(maxUnavailable)。你可以在 spec 中调整它:
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
在 maxUnavailable: 0 下,只有当新 Pod 就绪后,旧 Pod 才会被移除。“就绪”的含义由就绪探针决定,后面的指南会介绍。更改副本数不是一次发布,因为它没有触碰 Pod 模板。
回滚
Kubernetes 保留旧的 ReplicaSet,这正是回滚很快的原因:
kubectl rollout history deployment/web
kubectl rollout undo deployment/web
kubectl rollout undo deployment/web --to-revision=1
undo 回到上一个修订版本,--to-revision 选择某个具体的版本。保留的旧修订版本数量由 revisionHistoryLimit 设定,默认为 10。一个提醒:如果你的清单存放在 Git 中,并由 Argo CD 之类的东西应用,请优先选择回退提交,而不是 rollout undo。undo 会改变集群而不改变 Git,所以两者会不一致,直到有人去调谐。