一文吃透 K8s Deployment:资源创建、扩缩容与节点自愈实战
前言
Kubernetes(简称 K8s)作为当前容器编排领域的事实标准,其核心能力之一就是通过多种控制器(Controller)来管理应用的生命周期。在实际生产环境中,我们很少直接操作 Pod —— 因为 Pod 本身不具备自愈、扩缩容、滚动更新等能力。为此,Kubernetes 提供了 Deployment、ReplicaSet、DaemonSet、StatefulSet、Job 等一系列更高层次的对象,其中Deployment是最常用、也最基础的工作负载控制器。
无论是部署一个简单的 Web 服务,还是维护一个复杂的微服务集群,Deployment 都能帮助我们轻松实现声明式管理:你只需要告诉 Kubernetes “我想要运行 3 个实例,使用 httpd 镜像”,它就会自动创建所需的 ReplicaSet 和 Pod,并确保实际状态始终与期望状态一致。此外,Deployment 还天然支持滚动更新、版本回滚、副本扩缩容、节点故障自动迁移等高级功能。
本文将从 Deployment 的基本概念出发,结合实际的命令行输出和 YAML 配置文件,逐步分析 Deployment 的执行流程、创建资源的方式、配置文件编写方法、在线扩缩容操作、节点故障时的自动切换,以及如何通过 Label 控制 Pod 的调度位置。文章内容基于 Kubernetes v1.18.6 环境,所有示例均来自真实操作记录。希望通过这篇详解,能帮助读者从原理到实践全面掌握 Deployment 的使用。
一、Deployment介绍
1. Deployment的概念
为了满足不同业务场景,Kubernetes 开发了 Deployment、ReplicaSet、DaemonSet、StatefulSet、Job 等多种 Controller。其中Deployment是最常用的一种。
Deployment 为 Pod 和 ReplicaSet 提供了一个声明式定义(declarative)方法,用来替代以前的 Replication Controller,从而更方便地管理应用。
作为最常用的 Kubernetes 对象,Deployment 经常被用来创建 ReplicaSet 和 Pod。我们通常不会直接在集群中使用 ReplicaSet 部署微服务,因为 ReplicaSet 功能不够强大,缺乏更新、扩缩容等常见运维操作。Deployment 的引入正是为了支持这些复杂操作。
2. Deployment执行状态分析
查看 Deployment 的状态,可以使用以下命令:
[root@master k8s]# kubectl get deployment NAME READY UP-TO-DATE AVAILABLE AGE http-deployment 3/3 3 3 160m可以看到,集群中有一个名为http-deployment的 Deployment,它有三个副本且全部正常运行。
要查看某个 Deployment 的详细信息,可以执行:
[root@master k8s]# kubectl describe deployment http-deployment从输出中发现,存在一个 ReplicaSethttp-deployment-749876cdf4,这说明 Deployment 是通过 ReplicaSet 来管理 Pod 的。
接着查看 ReplicaSet 的状态:
[root@master k8s]# kubectl get replicaset http-deployment-749876cdf4使用kubectl describe replicaset可以查看更详细的信息:
[root@master ~]# kubectl describe replicaset Name: http-deployment-749876cdf4 Namespace: default Selector: app=http_server, pod-template-hash=749876cdf4 Labels: app=http_server pod-template-hash=749876cdf4 Annotations: deployment.kubernetes.io/desired-replicas: 3 deployment.kubernetes.io/max-replicas: 4 deployment.kubernetes.io/revision: 1 Controlled By: Deployment/http-deployment Replicas: 3 current / 3 desired Pods Status: 3 Running / 0 Waiting / 0 Succeeded / 0 Failed Pod Template: Labels: app=http_server pod-template-hash=749876cdf4 Containers: http-web: Image: httpd Port: <none> Host Port: <none> Environment: <none> Mounts: <none> Volumes: <none> Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal SuccessfulCreate 51m replicaset-controller Created pod: http-deployment-749876cdf4-vlfrp Normal SuccessfulCreate 51m replicaset-controller Created pod: http-deployment-749876cdf4-k2xSt Normal SuccessfulCreate 51m replicaset-controller Created pod: http-deployment-749876cdf4-kq9dt输出中的Controlled By指明该 ReplicaSet 是由 Deploymenthttp-deployment创建的。Events 部分记录了三个副本 Pod 的创建过程。
然后查看 Pod 的状态:
[root@master k8s]# kubectl get pod NAME READY STATUS RESTARTS AGE http-deployment-749876cdf4-k2x5t 1/1 Running 0 101s http-deployment-749876cdf4-kq9dt 1/1 Running 0 101s http-deployment-749876cdf4-vlfrp 1/1 Running 0 101s可以看到三个副本 Pod 都处于 Running 状态。
使用kubectl describe pod查看更详细的信息:
Controlled By: ReplicaSet/http-deployment-749876cdf4 Containers: http-web: Container ID: docker://3043f79fa114... Image: httpd Image ID: docker-pullable://httpd@sha256:2a9ae199b5... State: Running Started: Fri, 24 Jul 2020 16:53:17 +0800 Ready: True Restart Count: 0 Environment: <none> ... Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled <unknown> default-scheduler Successfully assigned default/http-deployment-749876cdf4-89c29 to node2 Normal Pulling 67s kubelet, node2 Pulling image "httpd" Normal Pulled 52s kubelet, node2 Successfully pulled image "httpd" Normal Created 52s kubelet, node2 Created container http-web Normal Started 52s kubelet, node2 Started container http-web输出中Controlled By指明此 Pod 是由 ReplicaSethttp-deployment-749876cdf4创建。最后的 Events 记录了 Pod 的启动过程。如果操作失败(比如镜像不存在),也可以从这里查看到失败原因。
3. Deployment执行流程总结
Deployment 的实现过程可以总结为以下几步:
用户通过
kubectl创建 Deployment。Deployment 创建 ReplicaSet。
ReplicaSet 创建 Pod。
Pod 在每个节点上通过 kubelet 调用容器运行时(如 docker)完成容器创建。
对象的命名方式为:子对象的名字 = 父对象名字 + 随机字符串或数字。
二、K8s中创建资源的方式
1. 基于命令的方式
优点:简单直观快捷,上手快。
适用场景:适合临时测试或实验。
2. 基于配置文件的方式
优点:资源的属性写在 YAML 配置文件中,描述了最终要达到的状态。配置文件提供了创建资源的模板,能够重复部署,可以像管理代码一样管理部署。
适用场景:适合正式的、跨环境、规模化部署。
注意:这种方式要求熟悉配置文件的语法,有一定难度。后续我们主要采用这种方式,需要尽快熟悉和掌握。
3. 用 kubectl 命令直接创建资源(已部分废弃)
[root@master k8s]# kubectl run nginx-deployment --image=nginx --port=80 [root@master k8s]# kubectl get pod nginx-deployment 1/1 Running 0 31s从 Kubernetes 1.18 版本开始,已经不支持在命令行中通过参数指定资源的属性(例如--replicas参数已弃用),推荐使用 Deployment 配置文件方式创建 Pod。
4. 通过配置文件和kubectl apply创建资源
[root@master k8s]# more http.yml apiVersion: apps/v1 kind: Deployment metadata: name: http-deployment spec: replicas: 3 selector: matchLabels: app: http_server template: metadata: labels: app: http_server spec: containers: - name: http-web image: httpdkubectl apply不仅可以创建 Kubernetes 资源,还能对资源进行更新,非常方便。虽然 Kubernetes 还提供了kubectl create、kubectl replace、kubectl edit和kubectl patch等命令,但kubectl apply已经能够应对超过 90% 的场景。
三、通过 YAML 文件编写 Deployment 配置
Deployment 的配置格式详解
以http.yml为例进行说明:
| 字段 | 说明 |
|---|---|
apiVersion | 当前配置格式的版本。可以通过kubectl api-resources找到所有资源,再用kubectl explain deploy获取版本和类型信息。 |
kind | 要创建的资源类型,这里是Deployment。 |
metadata | 该资源的元数据,name是必需的,可任意指定。 |
spec(第一层) | Deployment 的规格说明。 |
replicas | 副本数量,默认为 1,这里指定为 3。 |
selector | 选择器,matchLabels用于匹配标签。发布 Service 时,selector需要与此处对应。 |
template | 定义 Pod 的模板,是配置文件的重点部分。 |
template.metadata | 定义 Pod 的元数据,至少要定义一个label,key/value 可任意指定。 |
template.spec | 描述 Pod 的规格,定义 Pod 中每个容器的属性,name和image是必需的。 |
注意:containers后面是一个列表,所以每项前面需要加一个短横线-。
四、在线增加或减少 Pod 的副本数
1. 增加 Pod 副本数
修改http.yml文件,将replicas从 3 增加到 5,然后重新应用配置:
[root@master k8s]# kubectl apply -f http.yml deployment.apps/http-deployment configured [root@master k8s]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE http-deployment-749876cd4f-89c29 1/1 Running 0 76m 10.244.2.5 node2 http-deployment-749876cd4f-bj7v9 1/1 Running 0 76m 10.244.1.7 node1 http-deployment-749876cd4f-gv8cw 1/1 Running 0 22s 10.244.1.8 node1 http-deployment-749876cd4f-hxkks 1/1 Running 0 76m 10.244.1.6 node1 http-deployment-749876cd4f-ldq8q 1/1 Running 0 22s 10.244.2.8 node2可以看到 Pod 数量由 3 个增加到了 5 个。
2. 减少 Pod 副本数
修改http.yml文件,将replicas从 5 减少到 3(或 2),重新应用即可。Kubernetes 会自动终止多余的 Pod。
五、Node 节点故障时的 Pod 切换
假设当前有 3 个 httpd 副本分别运行在 node1 和 node2 上:
[root@master k8s]# kubectl get nodes NAME STATUS ROLES AGE VERSION master Ready master 8h v1.18.6 node1 Ready <none> 8h v1.18.6 node2 Ready <none> 8h v1.18.6 [root@master k8s]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE http-deployment-749876cdf4-brmt7 1/1 Running 0 16s 10.244.1.12 node1 http-deployment-749876cdf4-jt4bd 1/1 Running 0 16s 10.244.1.11 node1 http-deployment-749876cdf4-p56tb 1/1 Running 0 16s 10.244.2.9 node2现在模拟 node1 故障,关闭该节点,然后查看状态:
[root@master k8s]# kubectl get nodes NAME STATUS ROLES AGE VERSION master Ready master 8h v1.18.6 node1 NotReady <none> 8h v1.18.6 node2 Ready <none> 8h v1.18.6 [root@master k8s]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE http-deployment-749876cd4-rmt7 1/1 Terminating 0 7m56s 10.244.1.12 node1 http-deployment-749876cd4-jt4bd 1/1 Terminating 0 7m56s 10.244.1.11 node1 http-deployment-749876cd4-17jkd 1/1 Running 0 83s 10.244.2.11 node2 http-deployment-749876cd4-ndb9g 1/1 Running 0 83s 10.244.2.10 node2 http-deployment-749876cd4-p56tb 1/1 Running 0 7m56s 10.244.2.9 node2可以看到,node1 状态变为NotReady。等待一段时间,Kubernetes 会检测到 node1 不可用,将 node1 上的 Pod 标记为Terminating状态,并在 node2 上新建两个 Pod,使总副本数维持在 3 个。
当 node1 恢复后,Terminating的 Pod 会被删除,但已经在 node2 上运行的 Pod 不会重新调度回 node1。
六、通过 Label 控制 Pod 的位置
默认配置下,Scheduler 会将 Pod 调度到所有可用的 Node。但有时我们希望将 Pod 部署到指定的 Node(例如将大量磁盘 I/O 的 Pod 部署到配置了 SSD 的节点,或需要 GPU 的 Pod 部署到 GPU 节点)。
Kubernetes 通过Label实现这个功能。Label 是 key-value 对,各种资源都可以设置 Label,灵活添加自定义属性。
1. 给 Node 添加 Label
执行如下命令,标注 node1 是配置了 SSD 磁盘的节点:
[root@master k8s]# kubectl label node node1 disk=ssd [root@master k8s]# kubectl get node --show-labels注意:disk=ssd中disk是 key,ssd是 value,都是自定义的。除了自定义 label,Node 上还有几个 Kubernetes 自己维护的 label。
2. 编写带 nodeSelector 的 Deployment
编写一个nginx.yml文件,内容如下:
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: replicas: 3 selector: matchLabels: app: nginx_server template: metadata: labels: app: nginx_server spec: containers: - name: nginx-web image: nginx nodeSelector: disk: ssd在 Pod 模板的spec中通过nodeSelector指定将此 Pod 部署到具有 labeldisk=ssd的 Node 上。
注意:nodeSelector需要与containers保持同级,且其中的S必须大写。
3. 删除 Label
要删除 label,执行:
[root@master k8s]# kubectl label node node1 disk-删除之后,已存在的 Pod 并不会重新部署,依然会在原节点上运行。要改变 Pod 运行的节点,需要修改nodeSelector设置,然后通过kubectl apply重新部署。
结语
通过本文的详细介绍,我们系统性地学习了 Kubernetes Deployment 的方方面面:从它作为 ReplicaSet 和 Pod 上层控制器的基本定位,到实际的创建、查看、更新操作;从 YAML 配置文件的逐字段解析,到在线扩缩容、节点故障自愈等强大特性;最后还掌握了如何通过 Label 和 nodeSelector 精准控制 Pod 的调度位置。
Deployment 的设计思想体现了 Kubernetes 声明式 API 的精髓 —— 用户只需要描述“想要什么”,系统会持续不断地将实际状态调整为期望状态。这种模式极大地降低了运维复杂度,也为自动化运维和 GitOps 实践奠定了坚实基础。
在实际生产环境中,Deployment 通常还会结合 Service、Ingress、ConfigMap、Secret 等对象共同工作,形成一个完整的应用部署方案。作为 Kubernetes 学习的起点,深入理解 Deployment 的使用方法和原理,将帮助你更轻松地掌握 StatefulSet(有状态应用)、DaemonSet(守护进程)、Job/CronJob(任务型应用)等其它控制器,从而从容应对各种复杂业务场景。
希望本文能成为你 Kubernetes 学习之路上的一块坚实垫脚石。如果你有任何疑问或建议,欢迎在评论区留言交流。
