Kubernetes Kubernetes

Deployment:扩缩、自愈与滚动更新

运行一个 Pod 的多个副本,无停机地更新它们,并在发布出问题时回滚。

14 min 初级

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,所以两者会不一致,直到有人去调谐。

热门指南