一、引言

在 K8s 这个庞大而强大的体系中,Deployment 又是管理无状态应用的核心组件。它赋予了我们对应用部署的强大掌控力,通过简单的配置,就能实现应用的自动化部署、无缝升级、灵活扩展以及便捷回滚。可以说,掌握了 Deployment,就相当于掌握了开启 K8s 强大功能之门的钥匙,能够充分利用 K8s 的优势,提升应用的运维效率和稳定性。接下来,就让我们一起深入探索 K8s Deployment 的奇妙世界。

二、Deployment 是什么

Deployment 是 Kubernetes 中的一种声明式控制器,它主要负责管理 Pod 副本集,以确保实际运行的 Pod 数量、版本和配置与用户定义的期望状态一致 ,是管理无状态应用的利器。通过 Deployment,你可以轻松地实现应用的部署、更新、回滚以及扩展和收缩,极大地简化了应用的运维工作。

举个例子,假如你要部署一个后端服务,使用 Deployment,你只需编写一个简单的配置文件,声明你希望运行 3 个该服务的 Pod 副本,使用某个特定版本的镜像,Kubernetes 就会自动帮你创建这 3 个 Pod,并时刻监控它们的状态。如果某个 Pod 因为服务器故障或其他原因意外终止,Deployment 会立即创建一个新的 Pod 来替代它,保证服务始终可用。​

在生产环境中,Deployment 的应用场景极为广泛。无论是 Web 应用、API 服务,还是各种中间件,只要是无状态的应用,都可以借助 Deployment 来实现高效、可靠的部署和管理。比如,一个大型电商平台在促销活动期间,通过 Deployment 快速扩展后端服务的 Pod 数量,以应对突增的用户流量;活动结束后,再轻松缩容,节省资源成本。又比如,一个内容分发网络(CDN)服务提供商,利用 Deployment 实现了全球范围内节点的快速部署和更新,确保用户能够快速获取内容 。

三、Deployment 的工作原理

Deployment 的工作原理基于 Kubernetes 的控制器模式,其核心是一个持续运行的控制循环,不断地将集群中应用的实际状态与用户定义的期望状态进行比对,并努力使两者保持一致 。​

当用户创建一个 Deployment 对象时,其实就是在定义应用的期望状态,这个状态信息会被存储在 Kubernetes 的 etcd 数据库中。这些期望状态信息包括但不限于:期望的 Pod 副本数量、每个 Pod 使用的容器镜像版本、Pod 的标签以及其他各种配置参数 。​

接下来,Deployment 控制器会创建或更新对应的 ReplicaSet(副本集) 。ReplicaSet 是 Deployment 的得力助手,它负责确保在任何时刻,集群中实际运行的 Pod 副本数量都能与 Deployment 定义的期望副本数量一致。举个例子,假如 Deployment 期望运行 5 个 Pod 副本,而当前集群中只有 3 个 Pod 在运行,那么 ReplicaSet 就会立即创建 2 个新的 Pod;反之,如果当前有 7 个 Pod 在运行,超过了期望的 5 个,ReplicaSet 就会删除 2 个多余的 Pod。​

在 Pod 的整个生命周期中,Deployment 控制器会持续监控它们的状态。一旦发现某个 Pod 出现故障,比如容器崩溃、节点故障导致 Pod 不可用等情况,Deployment 控制器会马上采取行动。它会指挥 ReplicaSet 创建一个新的 Pod 来替代出现问题的 Pod,从而保证应用的整体可用性不受影响。​

当需要对应用进行更新时,比如更换容器镜像版本、修改容器的配置参数等,Deployment 会按照预先定义的更新策略来执行更新操作。默认情况下,Deployment 使用滚动更新策略,这种策略就像是一场有条不紊的接力赛。它会先逐步创建新的 Pod 副本,并且在确保新 Pod 正常运行后,才会开始逐步删除旧的 Pod 副本 。这样一来,在整个更新过程中,应用始终能够对外提供服务,不会出现长时间的中断,极大地保障了业务的连续性 。​

四、Deployment 的使用方法

4.1 使用 YAML 描述 Deployment

通过编写 YAML 文件来创建 Deployment,能提供更丰富、更灵活的配置选项。下面是一个创建 nginx-deployment 的 YAML 文件示例:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.25
        ports:
        - containerPort: 80

在这个 YAML 文件中,apiVersion 指定了 Kubernetes API 的版本;kind 表明我们要创建的是一个 Deployment;metadata 部分定义了 Deployment 的名称和标签;spec 则详细描述了 Deployment 的配置,包括期望的副本数量(replicas)、选择器(selector)以及 Pod 模板(template)。Pod 模板中又定义了容器的相关信息,如容器名称、使用的镜像以及暴露的端口。

相信大家对其他关键字都有了一定的了解,下面我们对选择器(selector)做一点补充说明,selector,它的作用是“筛选”出要被 Deployment 管理的 Pod 对象,下属字段“matchLabels”定义了 Pod 对象应该携带的 label,它必须和“template”里 Pod 定义的“labels”完全相同,否则 Deployment 就会找不到要控制的 Pod 对象,apiserver 也 会告诉你 YAML 格式校验错误无法创建。

这个 selector 字段的用法初看起来好像是有点多余,为了保证 Deployment 成功创建,我们 必须在 YAML 里把 label 重复写两次:一次是在“selector.matchLabels”,另一次是在 “template.matadata”。像在这里,你就要在这两个地方连续写 app: nginx

Kubernetes 采用的是这种“贴标签”的方式,通过在 API 对象的“metadata”元信息里加各种标签 (labels),我们就可以使用类似关系数据库里查询语句的方式,筛选出具有特定标识的那些 对象。通过标签这种设计,Kubernetes 就解除了 Deployment 和模板里 Pod 的强绑定,把 组合关系变成了“弱引用”

接下来我们还是用一张图,用不同的颜色来区分 Deployment YAML 里的字段,并且用虚线特别 标记了 matchLabels 和 labels 之间的联系,希望能够帮助你理解 Deployment 与被它管理 的 Pod 的组合关系。

编写好 YAML 文件后,使用以下命令即可创建 Deployment:

kubectl apply -f nginx-deployment.yaml

这种方式适用于生产环境中对应用部署有复杂配置需求的场景,通过 YAML 文件可以清晰地定义应用的各种参数和配置,方便版本控制和管理。

4.2 查看 Deployment

创建 Deployment 后,我们常常需要查看它的相关信息,以了解其运行状态。使用 kubectl get deployments 命令(可简写为 kubectl get deploy),可以列出所有的 Deployment 及其基本信息,包括名称、命名空间、当前副本数量、最新可用副本数量以及创建时间等 。例如:

kubectl get deploy
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3/3     3            3           10m

上述输出表明 nginx-deployment 已经成功创建,当前有 3 个 Pod 副本,并且这 3 个副本都已更新到最新版本,处于可用状态,该 Deployment 创建时间为 10 分钟前。

如果想要查看某个特定 Deployment 的详细信息,比如 nginx-deployment,可以使用 kubectl describe deployment nginx-deployment 命令 。这个命令会输出非常详细的信息,包括 Deployment 的基本信息、副本控制器、事件记录、Pod 模板以及当前运行的 Pod 状态等。通过这些详细信息,我们能够深入了解 Deployment 的配置和运行情况,快速定位和解决可能出现的问题。例如,当我们怀疑某个 Pod 出现故障时,可以通过这个命令查看事件记录,了解 Pod 创建、启动过程中发生的详细事件,从而找到问题的根源。

另外,使用 kubectl get deploy nginx-deployment -o wide 命令,也能查看特定 Deployment 的详细信息,并且会额外显示每个 Pod 所在的节点等信息,这在排查跨节点部署问题时非常有用 。比如:

kubectl get deploy nginx-deployment -o wide
NAME               READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS   IMAGES         SELECTOR
nginx-deployment   3/3     3            3           10m   nginx        nginx:1.25     app=nginx

这里的 CONTAINERS 字段显示了 Pod 中包含的容器名称,IMAGES 字段显示了容器使用的镜像版本,SELECTOR 字段则显示了 Deployment 的标签选择器,而通过 NODE 字段(在完整输出中会显示),我们可以清楚地看到每个 Pod 运行在哪个节点上,方便进行资源管理和故障排查 。

4.3 更新 Deployment

在应用的生命周期中,更新是不可避免的,比如需要升级应用版本、修复漏洞、调整配置等。Kubernetes 的 Deployment 提供了强大且灵活的更新策略,主要有两种:RollingUpdate 和 Recreate。​

RollingUpdate 是 Deployment 的默认更新策略,它就像一场精心编排的接力赛,能够实现零停机更新 。在更新过程中,它会逐步创建新的 Pod 副本,同时逐步淘汰旧的 Pod 副本。具体来说,它会先启动一些新的 Pod,等这些新 Pod 通过健康检查(Readiness Probe)后,才会开始终止旧的 Pod。这样一来,在整个更新过程中,服务始终保持可用状态,极大地保障了业务的连续性 。例如,我们有一个正在运行的 nginx-deployment,现在要将其镜像版本从 nginx:1.25 更新到 nginx:1.26,可以使用以下命令:

kubectl set image deployment/nginx-deployment nginx=nginx:1.26

这条命令会触发 RollingUpdate 策略,Kubernetes 会按照预设的节奏,逐步用 nginx:1.26 镜像的新 Pod 替换 nginx:1.25 镜像的旧 Pod。在更新过程中,我们可以通过 kubectl rollout status deployment/nginx-deployment 命令来查看更新进度,确保更新顺利进行 。​

Recreate 策略则相对简单直接,它会先一次性终止所有旧版本的 Pod,然后再一次性创建所有新版本的 Pod。在旧 Pod 完全终止到新 Pod 启动并准备好这段时间内,服务会中断 。这种策略适用于那些可以容忍短暂中断的应用场景,比如开发环境中的快速迭代,或者一些批处理任务。虽然它会导致服务中断,但在某些特定情况下,比如数据库 schema 迁移等需要独占操作的场景,这种策略能够确保迁移时只有新版本应用在运行,避免旧版本应用访问新 schema 导致错误 。在定义 Deployment 的 YAML 文件中,可以通过修改 spec.strategy.type 字段来指定使用 Recreate 策略,如下所示:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.26
        ports:
        - containerPort: 80

修改完 YAML 文件后,使用 kubectl apply -f nginx-deployment.yaml 命令应用更改,Deployment 就会按照 Recreate 策略进行更新 。​

除了更新镜像版本,我们还可以更新 Deployment 的其他配置,比如修改副本数量。假设我们要将 nginx-deployment 的副本数量从 3 个增加到 5 个,可以使用以下命令:

kubectl scale deployment nginx-deployment --replicas=5

这条命令会立即调整 Deployment 的副本数量,Kubernetes 会根据需要创建或删除 Pod,以确保实际运行的 Pod 数量与我们指定的副本数量一致 。

4.4 回滚 Deployment

在应用更新过程中,难免会遇到各种问题,比如新的版本引入了严重的缺陷,导致服务不可用。这时,回滚 Deployment 就成为了保障服务稳定运行的关键手段 。回滚操作能够让我们将 Deployment 快速恢复到之前的某个稳定版本,从而避免因更新失败而造成的业务损失。​

Kubernetes 提供了非常便捷的回滚命令。要回滚到前一个版本,只需执行以下命令:

kubectl rollout undo deployment/nginx-deployment

这条命令会撤销最近的一次更新,将 Deployment 恢复到上一个稳定版本。Kubernetes 会自动创建新的 Pod,并使用上一个版本的配置,同时逐步淘汰当前有问题的 Pod。在回滚过程中,我们可以通过 kubectl rollout status deployment/nginx-deployment 命令实时查看回滚进度,确保回滚操作顺利完成 。​

如果我们想要回滚到特定的版本,首先需要查看 Deployment 的更新历史,以确定要回滚到的版本号。使用以下命令可以查看更新历史:

kubectl rollout history deployment/nginx-deployment

该命令会列出 Deployment 的所有修订版本,以及每个版本的更新原因。例如:

REVISION  CHANGE-CAUSE
1         <none>
2         kubectl set image deployment/nginx-deployment nginx=nginx:1.26

从输出中可以看出,版本 1 是初始版本,版本 2 是由于执行了 kubectl set image 命令将镜像更新到 nginx:1.26 而产生的。假设我们要回滚到版本 1,可以使用以下命令:

kubectl rollout undo deployment/nginx-deployment --to-revision=1

这样,Deployment 就会回滚到指定的版本 1,确保应用恢复到之前的稳定状态 。回滚操作完成后,我们还可以通过 kubectl describe deployment nginx-deployment 命令查看 Deployment 的详细信息,确认回滚后的配置是否正确 。

4.5 扩展和缩减 Deployment

在实际的生产环境中,应用的负载情况往往是动态变化的。为了应对这种变化,我们需要能够灵活地调整 Deployment 中 Pod 的副本数量,这就是扩展和缩减 Deployment 的意义所在。通过合理地扩展和缩减 Pod 副本数量,我们可以确保应用在不同的负载情况下都能保持良好的性能和稳定性,同时避免资源的浪费 。​

水平扩展 Deployment 非常简单,只需使用 kubectl scale 命令即可。例如,我们要将 nginx-deployment 的 Pod 副本数量从 3 个扩展到 6 个,命令如下:

kubectl scale deployment nginx-deployment --replicas=6

执行这条命令后,Kubernetes 会立即根据 Deployment 的 Pod 模板创建 3 个新的 Pod,使总副本数量达到 6 个。这些新创建的 Pod 会自动加入到负载均衡池中,开始分担流量,从而提高应用的处理能力 。​

当负载降低时,我们可以通过缩减 Deployment 来减少 Pod 副本数量,节省资源成本。比如,要将 nginx-deployment 的副本数量从 6 个缩减回 3 个,命令如下:

kubectl scale deployment nginx-deployment --replicas=3

此时,Kubernetes 会按照一定的规则,逐步删除 3 个 Pod,使副本数量恢复到 3 个 。​

这里需要提及的是 Horizontal Pod Autoscaler(HPA),它可以根据 CPU 使用率等指标自动扩缩容 Pod 副本数量 。与手动使用 kubectl scale 命令不同,HPA 是基于实时的监控数据进行动态调整的。例如,我们可以配置 HPA,当 CPU 使用率超过 70% 时,自动增加 Pod 副本数量;当 CPU 使用率低于 30% 时,自动减少 Pod 副本数量 。通过 kubectl autoscale 命令可以创建 HPA,如下所示:

kubectl autoscale deployment nginx-deployment --min=3 --max=10 --cpu-percent=70

这条命令会创建一个针对 nginx-deployment 的 HPA,它会确保 Pod 副本数量在 3 到 10 个之间动态调整,以维持 CPU 使用率在 70% 左右 。手动扩展和缩减适用于我们已知负载变化情况,或者需要对资源进行精细控制的场景;而 HPA 则更适合在负载波动较大且难以预测的情况下,实现自动化的资源管理 。在实际应用中,我们可以根据具体的业务需求和场景,灵活地选择使用手动扩展缩减还是 HPA,或者将两者结合使用,以达到最佳的资源利用和服务性能 。

五、小结

通过对 Kubernetes Deployment 的深入探索,我们已经领略到它在容器化应用部署和管理中的强大威力。从基础概念到高级应用,Deployment 以其声明式的配置方式、灵活的更新策略以及便捷的扩缩容和回滚机制,为我们提供了高效、可靠的应用运维解决方案 。它不仅简化了应用的部署流程,还极大地提升了应用的稳定性和可用性,让我们能够更加从容地应对复杂多变的业务需求 。

 Tips: 为了大家快速高效的学习,已经将文章提交到了git仓库,涵盖后端大部分技术,以及后端学习路线,仓库内容会持续更新,建议 Star 收藏 以便随时查看https://gitee.com/bxlj/java-article

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐