一、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专栏
本专栏特点:
- 理论结合实战
- 讲解深入浅出