01 节点

Kubernetes中节点(node)指的是一个工作机器,曾经叫做 minion。不同的集群中,节点可能是虚拟机也可能是物理机。每个节点都由 master 组件管理,并包含了运行 Pod(容器组)所需的服务。这些服务包括:

  • 容器引擎
  • kubelet
  • kube-proxy 查看此文档可了解更多细节 The Kubernetes Node

1.1 节点状态

节点的状态包含如下信息:

  • Addresses
  • Conditions
  • Capacity and Allocatable
  • Info

执行以下命令可查看所有节点的列表:

  1. # kubectl get nodes -o wide
  2. NAME STATUS ROLES AGE VERSION EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
  3. 10.4.42.5 Ready <none> 17h v1.9.0 <none> CentOS Linux 7 (Core) 3.10.0-693.el7.x86_64 docker://1.13.1
  4. 10.4.42.6 Ready <none> 17h v1.9.0 <none> CentOS Linux 7 (Core) 3.10.0-693.el7.x86_64 docker://1.13.1

执行以下命令可查看节点状态以及节点的其他详细信息:kubectl describe node <your-node-name>

  1. # kubectl describe node 10.4.42.5
  2. Name: 10.4.42.5
  3. Roles: <none>
  4. Labels: beta.kubernetes.io/arch=amd64
  5. beta.kubernetes.io/os=linux
  6. kubernetes.io/hostname=10.4.42.5
  7. Annotations: node.alpha.kubernetes.io/ttl=0
  8. volumes.kubernetes.io/controller-managed-attach-detach=true
  9. Taints: <none>
  10. CreationTimestamp: Mon, 24 Feb 2020 22:58:16 +0800
  11. Conditions:
  12. Type Status LastHeartbeatTime LastTransitionTime Reason Message
  13. ---- ------ ----------------- ------------------ ------ -------
  14. OutOfDisk False Tue, 25 Feb 2020 16:43:59 +0800 Mon, 24 Feb 2020 22:58:16 +0800 KubeletHasSufficientDisk kubelet has sufficient disk space available
  15. MemoryPressure False Tue, 25 Feb 2020 16:43:59 +0800 Mon, 24 Feb 2020 22:58:16 +0800 KubeletHasSufficientMemory kubelet has sufficient memory available
  16. DiskPressure False Tue, 25 Feb 2020 16:43:59 +0800 Mon, 24 Feb 2020 22:58:16 +0800 KubeletHasNoDiskPressure kubelet has no disk pressure
  17. Ready True Tue, 25 Feb 2020 16:43:59 +0800 Tue, 25 Feb 2020 10:51:05 +0800 KubeletReady kubelet is posting ready status
  18. Addresses:
  19. InternalIP: 10.4.42.5
  20. Hostname: 10.4.42.5
  21. Capacity:
  22. cpu: 4
  23. memory: 8010172Ki
  24. pods: 110
  25. Allocatable:
  26. cpu: 4
  27. memory: 7907772Ki
  28. pods: 110
  29. System Info:
  30. Machine ID: 5ad801f3b9dc3735abbcbaa581e7b250
  31. System UUID: 5AD801F3-B9DC-3735-ABBC-BAA581E7B250
  32. Boot ID: 2ffef5dc-fcd5-4b93-85c0-0a95e1f063c4
  33. Kernel Version: 3.10.0-693.el7.x86_64
  34. OS Image: CentOS Linux 7 (Core)
  35. Operating System: linux
  36. Architecture: amd64
  37. Container Runtime Version: docker://1.13.1
  38. Kubelet Version: v1.9.0
  39. Kube-Proxy Version: v1.9.0
  40. PodCIDR: 172.20.0.0/24
  41. ExternalID: 10.4.42.5
  42. Non-terminated Pods: (4 in total)
  43. Namespace Name CPU Requests CPU Limits Memory Requests Memory Limits
  44. --------- ---- ------------ ---------- --------------- -------------
  45. default nginx-deployment-74c5794645-4vlsj 0 (0%) 0 (0%) 0 (0%) 0 (0%)
  46. default nginx-deployment-74c5794645-tl5nw 0 (0%) 0 (0%) 0 (0%) 0 (0%)
  47. default nginx-deployment-74c5794645-v2wmq 0 (0%) 0 (0%) 0 (0%) 0 (0%)
  48. kube-system kube-dns-697c8cb47f-26nst 260m (6%) 0 (0%) 110Mi (1%) 170Mi (2%)
  49. Allocated resources:
  50. (Total limits may be over 100 percent, i.e., overcommitted.)
  51. CPU Requests CPU Limits Memory Requests Memory Limits
  52. ------------ ---------- --------------- -------------
  53. 260m (6%) 0 (0%) 110Mi (1%) 170Mi (2%)
  54. Events: <none>

1、Addresses

  1. Addresses:
  2. InternalIP: 10.4.42.5
  3. Hostname: 10.4.42.5

依据你集群部署的方式(在哪个云供应商部署,或是在物理机上部署),Addesses 字段可能有所不同。

  • HostName: 在节点命令行界面上执行 hostname 命令所获得的值。启动 kubelet 时,可以通过参数 --hostname-override 覆盖
  • ExternalIP:通常是节点的外部IP(可以从集群外访问的内网IP地址;上面的例子中,此字段为空)
  • InternalIP:通常是从节点内部可以访问的 IP 地址

2、Conditions

Conditions 描述了节点的状态。Condition的例子有:

  1. Conditions:
  2. Type Status LastHeartbeatTime LastTransitionTime Reason Message
  3. ---- ------ ----------------- ------------------ ------ -------
  4. OutOfDisk False Tue, 25 Feb 2020 16:43:59 +0800 Mon, 24 Feb 2020 22:58:16 +0800 KubeletHasSufficientDisk kubelet has sufficient disk space available
  5. MemoryPressure False Tue, 25 Feb 2020 16:43:59 +0800 Mon, 24 Feb 2020 22:58:16 +0800 KubeletHasSufficientMemory kubelet has sufficient memory available
  6. DiskPressure False Tue, 25 Feb 2020 16:43:59 +0800 Mon, 24 Feb 2020 22:58:16 +0800 KubeletHasNoDiskPressure kubelet has no disk pressure
  7. Ready True Tue, 25 Feb 2020 16:43:59 +0800 Tue, 25 Feb 2020 10:51:05 +0800 KubeletReady kubelet is posting ready status
Node Condition 描述
OutOfDisk 如果节点上的空白磁盘空间不够,不能够再添加新的节点时,该字段为 True
,其他情况为 False
Ready 如果节点是健康的且已经就绪可以接受新的 Pod。则节点Ready字段为 True
False
表明了该节点不健康,不能够接受新的 Pod。
MemoryPressure 如果节点内存紧张,则该字段为 True
,否则为False
PIDPressure 如果节点上进程过多,则该字段为 True
,否则为 False
DiskPressure 如果节点磁盘空间紧张,则该字段为 True
,否则为 False
NetworkUnvailable 如果节点的网络配置有问题,则该字段为 True
,否则为 False

如果 Ready 类型Condition 的 status 持续为 Unkown 或者 False 超过 pod-eviction-timeoutkube-controller-manager的参数)所指定的时间,节点控制器(node controller)将对该节点上的所有 Pod 执行删除的调度动作。默认的 pod-eviction-timeout 时间是 5 分钟。某些情况下(例如,节点网络故障),apiserver 不能够与节点上的 kubelet 通信,删除 Pod 的指令不能下达到该节点的 kubelet 上,直到 apiserver 与节点的通信重新建立,指令才下达到节点。这意味着,虽然对 Pod 执行了删除的调度指令,但是这些 Pod 可能仍然在失联的节点上运行。

在 kubernetes v1.5 以前,节点控制器将从 apiserver 强制删除这些失联节点上的 Pod。在 v1.5 及以后的版本中,节点控制器将不会强制删除这些 Pod,直到已经确认他们已经停止运行为止。您可能会发现失联节点上的 Pod 仍然在运行(在该节点上执行 docker ps 命令可查看容器的运行状态),然而 apiserver 中,他们的状态已经变为 Terminating 或者 Unknown。如果 Kubernetes 不能通过 cloud-controller-manager 判断失联节点是否已经永久从集群中移除(例如,在虚拟机或物理机上自己部署 Kubernetes 的情况),集群管理员需要手工(通过 kubectl delete node your-node-name 命令)删除 apiserver 中的节点对象。此时,Kubernetes 将删除该节点上的所有 Pod。

在 Kubernetes v1.12 中,TaintNodesByCondition 特性进入 beta 阶段,此时 node lifecycle controller 将自动创建该 Condition 对应的 污点。相应地,调度器在选择合适的节点时,不再关注节点的 Condition,而是检查节点的污点和 Pod 的容忍。

3 Capacity and Allocatable (容量和可分配量)

  1. Capacity:
  2. cpu: 4
  3. memory: 8010172Ki
  4. pods: 110
  5. Allocatable:
  6. cpu: 4
  7. memory: 7907772Ki
  8. pods: 110

容量和可分配量(Capacity and Allocatable)描述了节点上的可用资源的情况:

  • CPU
  • 内存
  • 该节点可调度的最大 pod 数量

Capacity 中的字段表示节点上的资源总数,Allocatable 中的字段表示该节点上可分配给普通 Pod 的资源总数。

reserve compute resources

4 Info

描述了节点的基本信息,例如:

  • Linux 内核版本
  • Kubernetes 版本(kubelet 和 kube-proxy 的版本)
  • Docker 版本
  • 操作系统名称
  1. System Info:
  2. Machine ID: 5ad801f3b9dc3735abbcbaa581e7b250
  3. System UUID: 5AD801F3-B9DC-3735-ABBC-BAA581E7B250
  4. Boot ID: 2ffef5dc-fcd5-4b93-85c0-0a95e1f063c4
  5. Kernel Version: 3.10.0-693.el7.x86_64
  6. OS Image: CentOS Linux 7 (Core)
  7. Operating System: linux
  8. Architecture: amd64
  9. Container Runtime Version: docker://1.13.1
  10. Kubelet Version: v1.9.0
  11. Kube-Proxy Version: v1.9.0
  12. - Linux 内核版本
  13. - Kubernetes 版本(kubelet kube-proxy 的版本)
  14. - Docker 版本
  15. - 操作系统名称

这些信息由节点上的 kubelet 收集。

1.2 节点管理

与 Pod 和 Service 不一样,节点并不是由 Kubernetes 创建的,节点由云供应商(例如,Google Compute Engine、阿里云等)创建,或者节点已经存在于您的物理机/虚拟机的资源池。向 Kubernetes 中创建节点时,仅仅是创建了一个描述该节点的 API 对象。节点 API 对象创建成功后,Kubernetes将检查该节点是否有效。例如,假设您创建如下节点信息:

  1. kind: Node
  2. apiVersion: v1
  3. metadata:
  4. name: "10.240.79.157"
  5. labels:
  6. name: "my-first-k8s-node"

Kubernetes 在 APIServer 上创建一个节点 API 对象(节点的描述),并且基于 metadata.name 字段对节点进行健康检查。如果节点有效(节点组件正在运行),则可以向该节点调度 Pod;否则,该节点 API 对象将被忽略,直到节点变为有效状态。

TIP

Kubernetes 将保留无效的节点 API 对象,并不断地检查该节点是否有效。除非您使用 kubectl delete node my-first-k8s-node 命令删除该节点

手工创建,随便瞎玩的

  1. # kubectl get nodes
  2. NAME STATUS ROLES AGE VERSION
  3. 10.4.42.5 Ready <none> 18h v1.9.0
  4. 10.4.42.6 Ready <none> 18h v1.9.0
  5. # vi node3.yaml
  6. kind: Node
  7. apiVersion: v1
  8. metadata:
  9. name: "10.4.42.8"
  10. labels:
  11. name: "node3"
  12. # kubectl apply -f node3.yaml
  13. node "10.4.42.8" created
  14. # kubectl get nodes
  15. NAME STATUS ROLES AGE VERSION
  16. 10.4.42.5 Ready <none> 18h v1.9.0
  17. 10.4.42.6 Ready <none> 18h v1.9.0
  18. 10.4.42.8 Unknown <none> 11s
  19. # kubectl delete node 10.4.42.8
  20. node "10.4.42.8" deleted
  21. # kubectl get nodes
  22. NAME STATUS ROLES AGE VERSION
  23. 10.4.42.5 Ready <none> 18h v1.9.0
  24. 10.4.42.6 Ready <none> 18h v1.9.0

1 节点控制 (Node Controller)

节点控制器是一个负责管理节点的 Kubernetes master 组件。在节点的生命周期中,节点控制器起到了许多作用。

  • 首先,节点控制器在注册节点时为节点分配 CIDR 地址块
  1. [root@master1 deploy]# kubectl apply -f node3.yaml
  2. node "10.4.42.8" created
  3. [root@master1 deploy]# kubectl describe node 10.4.42.8
  4. Name: 10.4.42.8
  5. Roles: <none>
  6. Labels: name=node3
  7. Annotations: kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"v1","kind":"Node","metadata":{"annotations":{},"labels":{"name":"node3"},"name":"10.4.42.8","namespace":""}}
  8. node.alpha.kubernetes.io/ttl=0
  9. Taints: <none>
  10. CreationTimestamp: Tue, 25 Feb 2020 17:19:05 +0800
  11. Addresses:
  12. System Info:
  13. Machine ID:
  14. System UUID:
  15. Boot ID:
  16. Kernel Version:
  17. OS Image:
  18. Operating System:
  19. Architecture:
  20. Container Runtime Version:
  21. Kubelet Version:
  22. Kube-Proxy Version:
  23. PodCIDR: 172.20.3.0/24
  24. ExternalID: 10.4.42.8
  25. Non-terminated Pods: (0 in total)
  26. Namespace Name CPU Requests CPU Limits Memory Requests Memory Limits
  27. --------- ---- ------------ ---------- --------------- -------------
  28. Allocated resources:
  29. (Total limits may be over 100 percent, i.e., overcommitted.)
  30. CPU Requests CPU Limits Memory Requests Memory Limits
  31. ------------ ---------- --------------- -------------
  32. 0 (-9223372036854775808%) 0 (-9223372036854775808%) 0 (-9223372036854775808%) 0 (-9223372036854775808%)
  33. Events: <none>
  34. [root@master1 deploy]#
  • 第二,节点控制器通过云供应商(cloud-controller-manager)接口检查节点列表中每一个节点对象对应的虚拟机是否可用。在云环境中,只要节点状态异常,节点控制器检查其虚拟机在云供应商的状态,如果虚拟机不可用,自动将节点对象从 APIServer 中删除。
  • 第三,节点控制器监控节点的健康状况。当节点变得不可触达时(例如,由于节点已停机,节点控制器不再收到来自节点的心跳信号),节点控制器将节点API对象的 NodeStatus Condition 取值从 NodeReady 更新为 Unknown;然后在等待 pod-eviction-timeout 时间后,将节点上的所有 Pod 从节点驱逐。
  • 默认40秒未收到心跳,修改 NodeStatus Condition 为 Unknown
  • 默认 pod-eviction-timeout 为 5分钟
  • 节点控制器每隔 --node-monitor-period 秒检查一次节点的状态

大多数情况下,节点控制器限制了驱逐 Pod 的速率为 --node-eviction-rate (默认值是0.1)每秒,即节点控制器每 10 秒驱逐 1 个 Pod。

当节点所在的高可用区出现故障时,节点控制器驱逐 Pod 的方式将不一样。节点控制器驱逐Pod前,将检查高可用区里故障节点的百分比(NodeReady Condition 的值为 UnknownFalse):

  • 如果故障节点的比例不低于
    (默认为 0.55),则降低驱逐 Pod 的速率

    1. --unhealthy-zone-threshold
    • 如果集群规模较小(少于等于 --large-cluster-size-threshold 个节点,默认值为 50),则停止驱逐 Pod
    • 如果集群规模大于 --large-cluster-size-threshold 个节点,则驱逐 Pod 的速率降低到 --secondary-node-eviction-rate (默认值为 0.01)每秒

针对每个高可用区使用这个策略的原因是,某一个高可用区可能与 master 隔开了,而其他高可用区仍然保持连接。如果您的集群并未分布在云供应商的多个高可用区上,此时,您只有一个高可用区(即整个集群)。

将集群的节点分布到多个高可用区最大的原因是,在某个高可用区出现整体故障时,可以将工作负载迁移到仍然健康的高可用区。因此,如果某个高可用区的所有节点都出现故障时,节点控制器仍然使用正常的驱逐 Pod 的速率(--node-eviction-rate)。

最极端的情况是,所有的高可用区都完全不可用(例如,集群中一个健康的节点都没有),此时节点控制器 master 节点的网络连接出现故障,并停止所有的驱逐 Pod 的动作,直到某些连接得到恢复。

自 Kubernetes v1.6 开始,节点控制器同时也负责为带有 NoExecute 污点的节点驱逐其上的 Pod。此外,节点控制器还负责根据节点的状态(例如,节点不可用,节点未就绪等)为节点添加污点。参考 NoExecute) 获取更多信息。

自 Kubernetes v1.8 开始,节点控制器可以根据节点的 Condition 为节点添加污点,此特性处于 alpha 阶段

2 节点自注册 (Set-Registration)

如果 kubelet 的启动参数 --register-node为 true(默认为 true),kubelet 会尝试将自己注册到 API Server。kubelet自行注册时,将使用如下选项:

  • --kubeconfig:向 apiserver 进行认证时所用身份信息的路径
  • --cloud-provider:向云供应商读取节点自身元数据
  • --register-node:自动向 API Server 注册节点
  • --register-with-taints:注册节点时,为节点添加污点(逗号分隔,格式为 =:
  • --node-ip:节点的 IP 地址
  • --node-labels:注册节点时,为节点添加标签
  • --node-status-update-frequency:向 master 节点发送心跳信息的时间间隔
  1. # ps -ef | grep kubelet
  2. root 16641 1 1 10:50 ? 00:04:58
  3. /root/k8s/bin/kubelet \
  4. --address=10.4.42.5 \
  5. --hostname-override=10.4.42.5
  6. --pod-infra-container-image=registry.cn-hangzhou.aliyuncs.com/imooc/pause-amd64:3.0 - --kubeconfig=/etc/kubernetes/kubelet.kubeconfig #向 apiserver 进行认证时所用身份信息的路径
  7. --network-plugin=cni
  8. --cni-conf-dir=/etc/cni/net.d
  9. --cni-bin-dir=/root/k8s/bin
  10. --cluster-dns=10.68.0.2
  11. --cluster-domain=cluster.local.
  12. --allow-privileged=true
  13. --fail-swap-on=false
  14. --logtostderr=true --v=2
  15. --cgroup-driver=systemd

3 手工管理节点

集群管理员可以创建和修改节点API对象。

如果管理员想要手工创建节点API对象,可以将 kubelet 的启动参数 --register-node 设置为 false。

管理员可以修改节点API对象(不管是否设置了 --register-node 参数)。可以修改的内容有:

  • 增加/减少标签
  • 标记节点为不可调度(unschedulable)

节点的标签与 Pod 上的节点选择器(node selector)配合,可以控制调度方式,例如,限定 Pod 只能在某一组节点上运行。请参考 将容器组调度到指定的节点

执行如下命令可将节点标记为不可调度(unschedulable),此时将阻止新的 Pod 被调度到该节点上,但是不影响任何已经在该节点上运行的 Pod。这在准备重启节点之前非常有用。

  1. # kubectl cordon $NODENAME

TIP

DaemonSet Controller 创建的 Pod 将绕过 Kubernetes 调度器,并且忽略节点的 unschedulable 属性。因为我们假设 Daemons 守护进程属于节点,尽管该节点在准备重启前,已经排空了上面所有的应用程序。

4 节点容量 (Node Capacity)

节点API对象中描述了节点的容量(Capacity),例如,CPU数量、内存大小等信息。通常,节点在向 APIServer 注册的同时,在节点API对象里汇报了其容量(Capacity)。如果您 手动管理节点,您需要在添加节点时自己设置节点的容量。

Kubernetes 调度器在调度 Pod 到节点上时,将确保节点上有足够的资源。具体来说,调度器检查节点上所有容器的资源请求之和不大于节点的容量。此时,只能检查由 kubelet 启动的容器,不包括直接由容器引擎启动的容器,更不包括不在容器里运行的进程。

02 集群内的通信

2.1 master-Node 之间的通信

本文描述了Kubernetes集群和Master节点(实际上是 apiserver)之间的通信路径。用户在自定义集群的安装之前,或者调整集群的网络配置之前必须理解这部分内容。例如:

Master-Node 之间的通信可以分为如下两类:

2.2 Cluster to Master

所有从集群访问 Master 节点的通信,都是针对 apiserver 的(没有任何其他 master 组件发布远程调用接口)。通常安装 Kubernetes 时,apiserver 监听 HTTPS 端口(443),并且配置了一种或多种 客户端认证方式 authentication。至少需要配置一种形式的 授权方式 authorization,尤其是 匿名访问 anonymous requestsService Account Tokens 被启用的情况下。

节点上必须配置集群(apiserver)的公钥根证书(public root certificate),此时,在提供有效的客户端身份认证的情况下,节点可以安全地访问 APIServer。例如,在 Google Kubernetes Engine 的一个默认 Kubernetes 安装里,通过客户端证书为 kubelet 提供客户端身份认证。请参考 kubelet TLS bootstrapping,了解如何自动为 kubelet 提供客户端证书。

对于需要调用 APIServer 接口的 Pod,应该为其关联 Service Account,此时,Kubernetes将在创建Pod时自动为其注入公钥根证书(public root certificate)以及一个有效的 bearer token(放在HTTP请求头Authorization字段)。所有名称空间中,都默认配置了名为 kubernetes Kubernetes Service,该 Service对应一个虚拟 IP(默认为 10.96.0.1),发送到该地址的请求将由 kube-proxy 转发到 apiserver 的 HTTPS 端口上。请参考 Service连接应用程序 了解 Kubernetes Service 是如何工作的。

得益于这些措施,默认情况下,从集群(节点以及节点上运行的 Pod)访问 master 的连接是安全的,因此,可以通过不受信的网络或公网连接 Kubernetes 集群

2.3 Master to Cluster

从 master(apiserver)到Cluster存在着两条主要的通信路径:

  • apiserver 访问集群中每个节点上的 kubelet 进程
  • 使用 apiserver 的 proxy 功能,从 apiserver 访问集群中的任意节点、Pod、Service

1、apiserver to kubelet

apiserver 在如下情况下访问 kubelet:

  • 抓取 Pod 的日志
  • 通过 kubectl exec -it 指令(或 kuboard 的终端界面)获得容器的命令行终端
  • 提供 kubectl port-forward 功能
    这些连接的访问端点是 kubelet 的 HTTPS 端口。默认情况下,apiserver 不校验 kubelet 的 HTTPS 证书,这种情况下,连接可能会收到 man-in-the-middle 攻击,因此该连接如果在不受信网络或者公网上运行时,是 不安全 的。
    如果要校验 kubelet 的 HTTPS 证书,可以通过 --kubelet-certificate-authority 参数为 apiserver 提供校验 kubelet 证书的根证书

如果不能完成这个配置,又需要通过不受信网络或公网将节点加入集群,则需要使用 SSH隧道 连接 apiserver 和 kubelet。

同时,Kubelet authentication/authorization 需要激活,以保护 kubelet API

2、apiserver ot nodes pods services

从 apiserver 到 节点/Pod/Service 的连接使用的是 HTTP 连接,没有进行身份认证,也没有进行加密传输。您也可以通过增加 https 作为 节点/Pod/Service 请求 URL 的前缀,但是 HTTPS 证书并不会被校验,也无需客户端身份认证,因此该连接是无法保证一致性的。目前,此类连接如果运行在非受信网络或公网上时,是 不安全

3、ssh 隧道

Kubernetes 支持 SSH隧道(tunnel)来保护 Master —> Cluster 访问路径。此时,apiserver 将向集群中的每一个节点建立一个 SSH隧道(连接到端口22的ssh服务)并通过隧道传递所有发向 kubelet、node、pod、service 的请求。

2.4 控制器

在机器人技术和自动化技术中,控制循环 是一个控制系统状态的无限循环。房间里的恒温器就是控制循环的一个例子

  • 在恒温器上设定好目标温度,就是在告诉该 控制循环 你想要的 目标状态
  • 房间里的实际温度,是 当前状态
  • 恒温器通过打开或关闭加热装置,不断地使 当前状态 接近于 目标状态

在 Kubernetes 中,控制器 就是上面所说的 控制循环,它不断监控着集群的状态,并对集群做出对应的变更调整。每一个控制器都不断地尝试着将 当前状态 调整到 目标状态

1、控制器模式

在 Kubernetes 中,每个控制器至少追踪一种类型的资源。这些资源对象中有一个 spec 字段代表了目标状态。资源对象对应的控制器负责不断地将当前状态调整到目标状态。

理论上,控制器可以自己直接执行调整动作,然而,在Kubernetes 中,更普遍的做法是,控制器发送消息到 API Server,而不是直接自己执行调整动作

A、通过APIServer进行控制

以 Kubernetes 中自带的一个控制器 Job Controller 为例。Kubernetes 自带的控制器都是通过与集群中 API Server 交互来达到调整状态的目的。

Job 是一种 Kubernetes API 对象,一个 Job 将运行一个(或多个)Pod,执行一项任务,然后停止。当新的 Job 对象被创建时,Job Controller 将确保集群中有合适数量的节点上的 kubelet 启动了指定个数的 Pod,以完成 Job 的执行任务。Job Controller 自己并不执行任何 Pod 或容器,而是发消息给 API Server,由其他的控制组件配合 API Server,以执行创建或删除 Pod 的实际动作。

当新的 Job 对象被创建时,目标状态是指定的任务被执行完成。Job Controller 调整集群的当前状态以达到目标状态:创建 Pod 以执行 Job 中指定的任务

控制器同样也会更新其关注的 API 对象。例如:一旦 Job 的任务执行结束,Job Controller 将更新 Job 的 API 对象,将其标注为 Finished。(这有点儿像是恒温器将指示灯关闭,以表示房间里的温度已经到达指定温度。)

B、直接控制

某些特殊的控制器需要对集群外部的东西做调整。例如,您想用一个控制器确保集群中有足够的节点,此时控制器需要调用云供应商的接口以创建新的节点或移除旧的节点。这类控制器将从 API Server 中读取关于目标状态的信息,并直接调用外部接口以实现调整目标。

Kubernetes中,真的提供了一个控制器可以水平伸缩集群中的节点。请参考 Cluster autoscaling

2、目标状态 vs 当前状态

Kubernetes 使用了 云原生(cloud-native)的视角来看待系统,并且可以持续应对变化。您的集群在运行的过程中,任何时候都有可能发生突发事件,而控制器则自动地修正这些问题。这就意味着,本质上,您的集群永远不会达到一个稳定不变的状态。

这种通过控制器监控集群状态并利用负反馈原理不断接近目标状态的系统,相较于那种完成安装后就不再改变的系统,是一种更高级的系统形态,尤其是在您将运行一个大规模的复杂集群的情况下

3、设计

作为一个底层设计原则,Kubernetes使用了大量的控制器,每个控制器都用来管理集群状态的某一个方面。普遍来说,任何一个特定的控制器都使用一种 API 对象作为其目标状态,并使用和管理多种类型的资源,以达到目标状态。

使用许多个简单的控制器比使用一个全能的控制器要更加有优势。控制器可能会出故障,而这也是在设计 Kubernetes 时要考虑到的事情。

TIP

可能存在多种控制器可以创建或更新相同类型的 API 对象。为了避免混淆,Kubernetes 控制器在创建新的 API 对象时,会将该对象与对应的控制 API 对象关联,并且只关注与控制对象关联的那些对象。

  • 例如,Deployment 和 Job,这两类控制器都创建 Pod。Job Controller 不会删除 Deployment Controller 创建的 Pod,因为控制器可以通过标签信息区分哪些 Pod 是它创建的。

4、 运行控制器的方式

Kubernetes 在 kube-controller-manager 中运行了大量的内建控制器(例如,Deployment Controller、Job Controller、StatefulSet Controller、DaemonSet Controller 等)。这些内建控制器提供了 Kubernetes 非常重要的核心功能。Kubernetes 可以运行一个 master 集群,以实现内建控制器的高可用。

您也可以安装一些运行在 kube-controller-manager 之外的控制器,这些控制器通常是对 Kubernetes 已有功能的一些扩展。或者,在必要的情况下,您也可以自己编写自己需要的控制器,将其部署为一组 Pod,或者在 Kubernetes 集群之外部署。如何选择,取决于您想要用这个控制器做什么。