前言

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

从输出中发现,存在一个 ReplicaSet http-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 是由 Deployment http-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 是由 ReplicaSet http-deployment-749876cdf4 创建。最后的 Events 记录了 Pod 的启动过程。如果操作失败(比如镜像不存在),也可以从这里查看到失败原因。

3. Deployment执行流程总结

Deployment 的实现过程可以总结为以下几步:

  1. 用户通过 kubectl 创建 Deployment。

  2. Deployment 创建 ReplicaSet。

  3. ReplicaSet 创建 Pod。

  4. 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: httpd

kubectl apply 不仅可以创建 Kubernetes 资源,还能对资源进行更新,非常方便。虽然 Kubernetes 还提供了 kubectl createkubectl replacekubectl 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 部署到具有 label disk=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 学习之路上的一块坚实垫脚石。如果你有任何疑问或建议,欢迎在评论区留言交流。

更多推荐