容器编排中一个重要且复杂的方面是调度应用程序容器。如何将容器适当放置到可用的共享基础架构资源上,是在最佳计算资源使用情况下实现最大性能的关键所在。


Kubernetes容器调度策略_第1张图片


Cattle是Rancher 1.6的默认编排引擎,提供了各种调度功能来有效地放置服务:

https://rancher.com/docs/rancher/v1.6/en/cattle/scheduling/#scheduling-services


随着基于Kubernetes的Rancher 2.0版本的发布,Rancher现在使用原生的Kubernetes调度。在本文中,我们将看看Rancher 2.0中可用的调度方法与Cattle的调度的比较。


节点调度


根据原生的Kubernetes行为,默认情况下,Rancher 2.0工作负载中的pod将分布在可调度且具有足够可用容量的节点(主机)上。但就像1.6版本一样,Rancher 2.0也有助于:


  • 在特定节点上运行所有pod

  • 使用标签进行节点调度


以下是1.6 UI中的调度方式。Rancher允许您在特定主机上运行所有容器,指定硬/软主机标签,或在部署服务时使用亲和性/反亲和性规则。


Kubernetes容器调度策略_第2张图片


以下是Rancher 2.0中对应的节点调度UI,它在部署工作负载时提供相同的功能。


Kubernetes容器调度策略_第3张图片


Rancher使用底层的原生Kubernetes构造来指定节点的亲和性/反亲和性。相应的Kubernetes的详细文档参考此处:

https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity


下面的示例中我们将来看看如何使用节点调度选项来调度工作负载pod,然后看看Kubernetes YAML规范和Rancher 1.6 Docker Compose配置的对比。


示例:在特定节点上运行所有Pod


在部署工作负载(导航到Cluster> Project> Workloads)时,可以将工作负载中的所有pod调度到特定节点。


在这里,我使用特定节点上的nginx镜像部署scale = 2的工作负载。


Kubernetes容器调度策略_第4张图片


如果某节点有足够的计算资源可用,Rancher将选择该节点;如果使用hostPort,则不会发生端口冲突。如果该工作负载使用与另一个工作负载冲突的nodePort来对外暴露,那么部署是可以成功创建的,但它不会创建nodePort服务。如此一来,工作负载则完全不会暴露了。


“工作负载/Workload”选项卡上,您可以按节点列出工作负载。在此,我可以看到我的Nginx工作负载的两个pod都安排在指定的节点上了:


Kubernetes容器调度策略_第5张图片


Kubernetes pod规范中的调度规则如下所示:


Kubernetes容器调度策略_第6张图片


示例:主机标签的亲和性/反亲和性


我在Rancher 2.0集群中向node1添加了标签foo = bar,以测试基于标签的节点调度规则。


Kubernetes容器调度策略_第7张图片


主机标签亲和性:硬


下图展示了如何在Rancher 2.0 UI中指定主机标签的亲和性规则。硬亲和性规则意味着所选主机必须满足所有调度规则。如果找不到此类主机,则工作负载将无法部署。


Kubernetes容器调度策略_第8张图片


在PodSpec YAML中,此规则将转换为字段nodeAffinity。另外请注意,我已经包含了Rancher 1.6 docker-compose.yml以使用标签实现相同的调度行为。


Kubernetes容器调度策略_第9张图片


主机标签亲和性:软


如果您是Rancher 1.6用户,那么您一定知道软亲和性规则意味着调度程序会尝试按规则部署应用程序,但即使有主机不满足规则也可以成功部署。以下是如何在Rancher 2.0 UI中指定此规则:


Kubernetes容器调度策略_第10张图片


pod的相应YAML规范如下所示:



Kubernetes容器调度策略_第11张图片

主机标签反亲和性


除了key = value主机标签匹配规则外,Kubernetes调度结构还支持以下运算符:


Kubernetes容器调度策略_第12张图片


因此,要实现反亲和性,可以使用运算符NotInDoesNotExist作为节点标签。


Rancher 1.6的其他调度功能


如果您是Cattle用户,想必您很熟悉Rancher 1.6中提供的一些其他调度功能:



  • 选择使用容器标签的主机:

    https://rancher.com/docs/rancher/v1.6/en/cattle/scheduling/#finding-hosts-with-container-labels

  • 能够根据资源约束进行调度:

    https://rancher.com/docs/rancher/v1.6/en/rancher-services/scheduler/#resource-constraints

  • 能够仅在主机上调度特定服务:https://rancher.com/docs/rancher/v1.6/en/rancher-services/scheduler/#restrict-services-on-host


如果您是在Rancher 1.6设置中使用这些功能,则可以使用原生的Kubernetes调度选项在Rancher 2.0中复制它们。在Rancher v2.0.8中,在部署工作负载时暂时没有对这些选项的UI支持,但您始终可以通过在Rancher集群上导入Kubernetes YAML规范来使用它们。


使用容器标签进行调度


Rancher 1.6中的这一功能允许您将容器调度到具有特定标签的容器的主机。要在Rancher 2.0上执行此操作,请使用Kubernetes inter-pod亲和和反亲和功能:

https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity


如文档中所述,Kubernetes允许您根据pod标签而不是节点标签来约束pod可以被调度到哪些节点。


Rancher 1.6中最常用的调度功能之一是使用容器上的标签对服务本身进行反亲和。要在Rancher 2.0中复制此行为,我们可以在Kubernetes YAML规范中使用pod反亲和构造。例如,可以考虑使用Nginx Web工作负载。要确保此工作负载中的pod不在同一主机上,您可以使用podAntiAffinity构造,如下所示。通过使用标签指定podAntiAffinity,我们可以确保每个Nginx副本不在单个节点上共存。


Kubernetes容器调度策略_第13张图片


使用Rancher CLI,可以将此工作负载部署到Kubernetes集群上。请注意,上面的部署指定了三个副本,并且我在Kubernetes集群中有三个可调度节点。


Kubernetes容器调度策略_第14张图片


由于指定了podAntiAffinity,因此三个pod最终位于不同的节点上。为了进一步检查podAntiAffinity的应用方式,我可以将部署扩展到四个pod。请注意,由于调度程序无法找到满足podAntiAffinityrule的另一个节点,因此无法调度第四个pod。

Kubernetes容器调度策略_第15张图片


基于资源的调度


在Rancher 1.6中创建服务时,可以在UI的“安全/主机”选项卡中指定内存预留和mCPU预留。Cattle会将服务的容器安排到具有足够可用计算资源的主机上。


在Rancher 2.0中,您可以使用pod容器规范下的resources.requests.memoryresources.requests.cpu指定工作负载pod所需的内存和CPU资源。您可以在此处找到有关这些规范的更多详细信息:

https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container


指定这些资源请求时,Kubernetes调度程序会将pod分配给具有足够容量的节点。


仅给主机调度特定服务


Rancher 1.6能够在主机上指定容器标签,从而只将特定容器调度给它。


要在Rancher 2.0中实现此目的,可以在pod规范中使用相应的Kubernetes的“添加节点taints(如主机标签)并使用容差”的功能:

https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/


全局服务


在Rancher 1.6中,全局服务是指在环境中的每个主机上部署容器的服务:

https://rancher.com/docs/rancher/v1.6/en/cattle/scheduling/#global-service


如果服务的标签为io.rancher.scheduler.global:'true',则Rancher 1.6调度程序将在环境中的每个主机上调度服务容器。如文档中所述,如果将新主机添加到环境中,并且主机满足全局服务的主机要求,则Rancher将自动启动该服务。


下面的示例是Rancher 1.6中的全局服务示例。请注意,只需放置所需标签就足以使服务全局化。


Kubernetes容器调度策略_第16张图片


我们如何使用Kubernetes在Rancher 2.0中部署全局服务?


为此,Rancher为用户的工作负载部署了Kubernetes DaemonSet对象(https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/)。DaemonSet的功能与Rancher 1.6全局服务完全相同。Kubernetes调度程序将在集群的每个节点上部署一个pod,并且随着新节点的添加,调度程序将在它们上启动新的pod,前提是它们与工作负载的调度要求相匹配。


此外,在2.0中,您还可以将DaemonSet限制为部署到具有特定标签的节点,具体可参考文档:

https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/


使用Rancher 2.0 UI部署DaemonSet


如果您是Rancher 1.6用户,要使用UI将全局服务迁移到Rancher 2.0,请导航到Cluster> Project> Workloads视图。部署工作负载时,您可以选择以下工作负载类型:


Kubernetes容器调度策略_第17张图片


这就是上面的DaemonSetworkload相应的Kubernetes YAML规范:


Kubernetes容器调度策略_第18张图片


Docker Compose Kubernetes YAML


要使用Compose配置将Rancher 1.6全局服务迁移到Rancher 2.0,请按照下列步骤操作。


您可以使用Kompose工具将docker-compose.yml文件从Rancher 1.6转换为Kubernetes YAML,然后使用Kubernetes集群中的Kubectl客户端工具或Rancher CLI部署应用程序。


回头想想上面提到的docker-compose.yml规范,其中的Nginx服务就是全局服务。如下是使用Kompose将其转换为Kubernetes YAML的方法:


Kubernetes容器调度策略_第19张图片


下面开始针对您的Kubernetes集群配置Rancher CLI,并部署生成的* -daemonset.yaml文件。


Kubernetes容器调度策略_第20张图片


如上所示,我的Kubernetes集群有两个可以调度工作负载的工作节点,并且部署global-daemonset.yaml为Daemonset启动了两个pod,每个节点上有一个pod。


总  结   


在本文中,我们回顾了如何将Rancher 1.6的各种调度功能迁移到Rancher 2.0。大多数调度技术在Rancher 2.0中都有相同的选项,或者它们可以通过原生的Kubernetes结构实现。