容器化技术(十一):Kubernetes中Pod的调度策略-标签与污点实战

一、Pod的调度策略总述

Kubernetes中的容器编排功能的最大需要解决的问题就是将创建的Pod,调度到Node上去。那么Pod在选择调度到哪个Node上去时,是如何决策的。这就涉及到了我们在前面安装Kubernetes集群时,其中介绍的kube-scheduler组件。

kube-scheduler 给一个 pod 做调度选择的依据包含两个步骤:

(1)过滤:根据Pod对调度的要求,过滤掉一些Node,例如资源是否匹配,标签是否匹配,污点是否容忍等。

(2)打分:在满足了过滤要求的情况下,给这些Node进行打分,打分高的Node,会被确立为整个Pod调度的节点。

Pod对于资源的需求,我们在前面的内容中已经给读者介绍过。只有当对应的Node的资源满足Pod对于资源的请求时,才具备调度的可能性。

还有两种策略的指定,会直接影响到我们对于Pod的调度,那就是:

(1)标签

(2)污点

二、Label的调度实战

标签label,可以定义在Kubernetes集群中的各种资源上,上层的资源可以根据标签来选择对应的资源。常见的有三种对于标签的使用:

(1)Deployment,与Service根据标签选择对应的Pod。后文会介绍。

(2)PVC可以根据标签选择器选择满足资源要求的打了此标签的的PV。后文会介绍。

(3)Pod在定义的时候可以显示定义,部署在满足什么样的要求的标签的节点上面。

接下来,我们通过一个例子,来给节点打标签,并在Pod中定义部署在什么样标签的节点上,来验证效果。

给我们在Kubernetes集群中的资源添加标签,可以通过在yaml文件的metadata中定义,如下所示:

metadata: 
  labels: 
    key1: value1
    key2: value2

对于Kubernetes集群中的Worker节点,我们可以通过命令操作,给节点打标签,与去除标签的命令如下:

(1)kubectl label nodes node1 key1=value1

(2)kubectl label nodes node1 key1-

第一步,先给节点打标签:

kubectl label nodes kubernetes-worker01 scheduler-node=node1

验证下节点上是否打上了标签,查询的结果,如图所示,发现已经成功的打上了标签。

[root@kubernetes-master01 ~]# kubectl describe node kubernetes-worker01
Name:               kubernetes-worker01
Roles:              <none>
Labels:             beta.kubernetes.io/arch=amd64
                    beta.kubernetes.io/os=linux
                    kubernetes.io/arch=amd64
                    kubernetes.io/hostname=kubernetes-worker01
                    kubernetes.io/os=linux
                    scheduler-node=node1

第二步,我们用yaml来定义一个Pod,文件内容如下:

apiVersion: v1
kind: Pod
metadata:
  name: pod-nginx
spec:
  containers:
  - name: nginx
    image: nginx
    ports:
    - containerPort: 80
      hostPort: 80
  nodeSelector:
    scheduler-node: node1

创建这个Pod:

kubectl apply -f pod-nginx-select.yaml

我们来查看下Pod调度到哪个节点上了,如图所示,此Pod已经被定义到了对应的节点上。

[root@kubernetes-master01 ~]# kubectl get pod -o wide
NAME        READY   STATUS    RESTARTS   AGE    IP            NODE                  
pod-nginx   1/1     Running   0          102s   10.244.1.28   kubernetes-worker01

第三步,修改Pod的定义,将节点定义的标签 scheduler-node 的值改为node2,yaml文件改动的地方如下:

nodeSelector:
    scheduler-node: node2

第四步,删除之前的Pod,并重新创建这个Pod,如图所示,执行对应的命令。

[root@kubernetes-master01 ~]# kubectl delete pod pod-nginx
pod "pod-nginx" deleted
[root@kubernetes-master01 ~]# kubectl apply -f pod-nginx-select.yaml
pod/pod-nginx created

第五步,我们来查看下Pod的状态,如图所示,Pod的状态为Pending状态。

[root@kubernetes-master01 ~]# kubectl get pod -o wide
NAME        READY   STATUS    RESTARTS   AGE   IP       NODE     
pod-nginx   0/1     Pending   0          7s    <none>   <none>   

第六步,查看Pod的具体运行情况,为什么没有调度成功,如图所示,因为没有找到nodeSelector对应的node。

Events:
  Type     Reason            Age                  From               Message
  ----     ------            ----                 ----               -------
  Warning  FailedScheduling  78s (x4 over 4m11s)  default-scheduler  0/3 nodes are available: 3 node(s) didn't match node selector.

接下来,我们来总结下标签的相关内容,标签是在对应的资源上打上对应的标签,本例中是在node上打上对应的标签,进而让Pod根据标签选择参数nodeSelector来决定部署到特定类型的节点node上面。

对于Deployment与Service等高级资源,他们在选择Pod的策略上,也是同样的道理,可以在Pod上面打上对应的标签,在Deployment或Service等资源上设定对应的Pod的标签范围,就可以让这些高级资源找到对应的Pod了。

在yaml文件中,上层资源指定匹配标签的格式如下:

selector:
  matchLabels:
    component: redis
  matchExpressions:
    - {key: tier, operator: In, values: [cache]}
    - {key: environment, operator: NotIn, values: [dev]}

其中:

(1)matchLabels 是由 {key,value} 对组成的映射。

(2)matchLabels 映射中的单个 {key,value } 等同于 matchExpressions 的元素, 其 key 字段为 “key”,operator 为 “In”,而 values 数组仅包含 “value”。

(3)matchExpressions 是 Pod 选择算符需求的列表。

(4) 有效的运算符包括 In、NotIn、Exists 和 DoesNotExist。

(5) 在 In 和 NotIn 的情况下,设置的值必须是非空的。

(6)来自 matchLabels 和 matchExpressions 的所有要求都按逻辑与的关系组合到一起 – 它们必须都满足才能匹配。

三、污点的调度实战

我们除了使用标签让资源进行相应的匹配外,还有一种叫做污点的机制,污点一般都是不好的东西,当我们的节点因为一些情况,不想让Pod调度到上面的时候,就可以给节点打上污点。打了污点的节点,Pod不会调度到上面去,除非是Pod上明确标明,可以容忍对应的污点。

给节点打污点的命令为:

kubectl taint nodes node1 key1=value1:NoSchedule

其中:

(1)node1:是节点的名字。

(2)key1:是污点的可以。

(3)value1:是污点的value。

(4)NoSchedule:代表污点起到的效果。

污点起到的效果的值可以为:

(1)NoSchedule:Pod不会调度到这个节点上。

(2)PreferNoSchedule:Pod可能会调度到这个节点上,都是不会优先调度到这个节点上。

(3)NoExecute:Pod如果没在这个节点上运行,那么不会调度到这个节点上,如果Pod已经在这个节点上运行,则这个Pod将会被驱逐。

我们用一个例子,来带领读者去实践和理解下污点的概念。例子中我们给一个节点打上污点,正常的创建一个Pod,然后查看Pod的调度情况。然后我们将Pod的定义修改为可以容忍这个污点,再次查看Pod的调度情况。

第一步:给两个Worker节点都打上污点。

kubectl taint node kubernetes-worker01 key1=value1:NoSchedule
kubectl taint node kubernetes-worker02 key1=value1:NoSchedule

查看节点上的污点的情况,如图所示,已经成功的给节点打上了污点。

[root@kubernetes-master01 ~]# kubectl describe node kubernetes-worker01
Name:               kubernetes-worker01
Roles:              <none>
Labels:             beta.kubernetes.io/arch=amd64
                    beta.kubernetes.io/os=linux
                    kubernetes.io/arch=amd64
                    kubernetes.io/hostname=kubernetes-worker01
                    kubernetes.io/os=linux
                    scheduler-node=node1
Annotations:        flannel.alpha.coreos.com/backend-data: {"VNI":1,"VtepMAC":"82:5b:4d:5a:49:f8"}
                    flannel.alpha.coreos.com/backend-type: vxlan
                    flannel.alpha.coreos.com/kube-subnet-manager: true
                    flannel.alpha.coreos.com/public-ip: 192.168.8.22
                    kubeadm.alpha.kubernetes.io/cri-socket: /var/run/dockershim.sock
                    node.alpha.kubernetes.io/ttl: 0
                    volumes.kubernetes.io/controller-managed-attach-detach: true
CreationTimestamp:  Sun, 14 Mar 2021 10:47:25 +0800
Taints:             key1=value1:NoSchedule
[root@kubernetes-master01 ~]# kubectl describe node kubernetes-worker02
Name:               kubernetes-worker02
Roles:              <none>
Labels:             beta.kubernetes.io/arch=amd64
                    beta.kubernetes.io/os=linux
                    kubernetes.io/arch=amd64
                    kubernetes.io/hostname=kubernetes-worker02
                    kubernetes.io/os=linux
Annotations:        flannel.alpha.coreos.com/backend-data: {"VNI":1,"VtepMAC":"96:a9:e7:6c:c6:32"}
                    flannel.alpha.coreos.com/backend-type: vxlan
                    flannel.alpha.coreos.com/kube-subnet-manager: true
                    flannel.alpha.coreos.com/public-ip: 192.168.8.33
                    kubeadm.alpha.kubernetes.io/cri-socket: /var/run/dockershim.sock
                    node.alpha.kubernetes.io/ttl: 0
                    volumes.kubernetes.io/controller-managed-attach-detach: true
CreationTimestamp:  Sun, 14 Mar 2021 10:47:25 +0800
Taints:             key1=value1:NoSchedule

第二步:正常的定义一个Pod,其yaml文件内容如下:

apiVersion: v1
kind: Pod
metadata:
  name: pod-nginx
spec:
  containers:
  - name: nginx
    image: nginx
    ports:
    - containerPort: 80
      hostPort: 80

创建这个Pod:

kubectl apply -f pod-nginx-toleration.yaml

第三步:查看Pod的状态,如图所示,Pod没有调度成功。

[root@kubernetes-master01 k8s-yaml]# kubectl get pod
NAME        READY   STATUS    RESTARTS   AGE
pod-nginx   0/1     Pending   0          67s

第四步:查看Pod没有调度成功的原因,如图所示, 因为master上面有污点,两个node上面也有污点,所以调度失败。

Events:
  Type     Reason            Age               From               Message
  ----     ------            ----              ----               -------
  Warning  FailedScheduling  3s (x3 over 93s)  default-scheduler  0/3 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate, 2 node(s) had taint {key1: value1}, that the pod didn't tolerate.

第五步:我们删除之前的Pod。

kubectl delete pod pod-nginx

第六步:修改Pod的定义,让其可以容忍对应的污点。

apiVersion: v1
kind: Pod
metadata:
  name: pod-nginx
spec:
  containers:
  - name: nginx
    image: nginx
    ports:
    - containerPort: 80
      hostPort: 80
  tolerations:
  - key: "key1"
    operator: "Exists"
    effect: "NoSchedule"

创建这个Pod:

kubectl apply -f pod-nginx-toleration.yaml

第七步:查看Pod的调度状态,如图所示,Pod已经调度成功。

[root@kubernetes-master01 k8s-yaml]# kubectl get pod
NAME        READY   STATUS    RESTARTS   AGE
pod-nginx   1/1     Running   0          56s

查看具体的Pod的运行状态,如图所示,发现已经调度成功到节点kubernetes-worker01上面。

Events:
  Type    Reason     Age   From                          Message
  ----    ------     ----  ----                          -------
  Normal  Scheduled  21s   default-scheduler             Successfully assigned default/pod-nginx to kubernetes-worker01
  Normal  Pulling    20s   kubelet, kubernetes-worker01  Pulling image "nginx"
  Normal  Pulled     4s    kubelet, kubernetes-worker01  Successfully pulled image "nginx"
  Normal  Created    4s    kubelet, kubernetes-worker01  Created container nginx
  Normal  Started    4s    kubelet, kubernetes-worker01  Started container nginx

接下里,总结一下关于污点的相关情况,一般污点会定义某些节点的运行不良的状况,正常的情况下Pod不会调度到有污点的节点上。除非Pod中定义了可以容忍对应的污点。Kubernetes集群中的节点会根据节点的运行情况,会自动的给节点添加上对应的污点:

(1)node.kubernetes.io/not-ready:节点未准备好。这相当于节点状态 Ready 的值为 “False”。

(2)node.kubernetes.io/unreachable:节点控制器访问不到节点. 这相当于节点状态 Ready 的值为 “Unknown”。

(3)node.kubernetes.io/out-of-disk:节点磁盘耗尽。

(4)node.kubernetes.io/memory-pressure:节点存在内存压力。

(5)node.kubernetes.io/disk-pressure:节点存在磁盘压力。

(6)node.kubernetes.io/network-unavailable:节点网络不可用。

(7)node.kubernetes.io/unschedulable: 节点不可调度。

(8)node.cloudprovider.kubernetes.io/uninitialized:如果 kubelet 启动时指定了一个 “外部” 云平台驱动, 它将给当前节点添加一个污点将其标志为不可用。在 cloud-controller-manager 的一个控制器初始化这个节点后,kubelet 将删除这个污点。

那么什么情况下,我们需要在生产中主动的给节点添加污点呢?一般基于以下两种情况:

(1)专用节点:当我们需要某些Pod运行在特定的节点上,那么我们可以在这些节点上打上污点,让这些对应的Pod容忍这些污点。

(2)配备了特殊硬件的节点:因为某些Pod对资源的使用有一些特殊的要求,这时候,可以使用污点机制来解决,达到Pod运行在这些特殊资源上的目标。

本节中,为读者说明了Pod的调度策略,首先需要对应的Node满足Pod对于资源你的要求,然后基于标签以及污点的机制,可以达到干预Pod的调度的目的,使Pod可以决定运行或不能运行或不优先运行在某些节点上,最后根据打分的机制,在满足要求的Node节点中,选择最优的Node来运行。

四、猜你喜欢

如果你对容器化技术的相关知识感兴趣,可以阅读:秀丽的容器化技术Kubernetes专栏

本专栏特点:

  • 理论结合实战
  • 讲解深入浅出
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值