Kubernetes中pod绑定node节点

固定节点nodeName和nodeSelector调度

一、Kubernetes Pod调度说明

Scheduler 是 Kubernetes 的调度器,主要任务是把定义的Pod分配到集群的节点上,听起来非常简单,但要考虑需要方面的问题:

  • 公平:如何保证每个节点都能被分配到资源

  • 资源高效利用:集群所有资源最大化被使用

  • 效率:调度性能要好,能够尽快的对大批量的Pod完成调度工作

  • 灵活:允许用户根据自己的需求控制调度的流程

Scheduler 是作为单独的服务运行的,启动之后会一直监听API Server,获取 podSpec.NodeName为空的Pod,对每个Pod都会创建一个binding,表明该Pod应该放在哪个节点上。

调度过程

调度流程:

1)、首先过滤掉不满足条件的节点,这个过程称为predicate
2)、然后对通过的节点按照优先级的顺序,这个是priority
3)、最后从中选择优先级最高的节点。如果中间有任何一步报错,则直接返回错误信息。(对于调度过程分为两部分,一部分是预选,一部分是优选)

Predicate有一系列的算法可以使用:

  • PodFitsResources:节点上剩余的资源是否大于 Pod 请求的资源
  • PodFitsHost:如果Pod指定了nodeName,检查节点名称是否和nodeName匹配
  • PodFitsHostPort:节点上已经使用的port是否和Pod申请的port冲突
  • PodSelectorMatches:过滤和Pod指定的 label 不匹配的节点
  • NoDiskConflict:已经 mount 的 volume 和 Pod 指定的volume不冲突,除非他们都是只读

如果在predicate过程中没有适合的节点,Pod会一直处于Pending状态,不断重新调度,直到有节点满足条件,经过这个步骤,如果多个节点满足条件,就会进入priority过程:按照优先级大小对节点排序,优先级由一系列键值对组成,键是该优先级的名称,值是它的权重,这些优先级选项包括:

  • LeastRequestedPriority:通过计算CPU和Memory的使用率来决定权重,使用率越低权重越高,换句话说,这个优先级倾向于资源使用率低的节点
  • BalanceResourceAllocation:节点上CPU和Memory使用率非常及接近,权重就越高,这个要和上边的一起使用,不可单独使用
  • ImageLocalityPriority:倾向于已经要使用镜像的节点,镜像的总大小值越大,权重越高

通过算法对所有的优先级项目和权重进行计算,得出最终的结果

二、自定义调度器

除了Kubernetes自带的调度器,也可以编写自己的调度器,通过spec.schedulername参数指定调度器的名字,可以为Pod选择某个调度器进行调度,比如下边的Pod选择my-scheduler进行调度,而不是默认的default-scheduler。

apiVersion: v1
kind: Pod
metadata:
  name: scheduler-test
  labels:
    name: example-scheduler
spec:
  schedulername: my-scheduler
  containers:
  - name: Pod-test
    image: nginx:v1

三、指定调度节点

1、nodeName

Pod.spec.nodeName 将 Pod 直接调度到指定的 Node 节点上,会跳过 Scheduler 的调度策略,该匹配规则是强制匹配

apiVersion: apps/v1
kind: Deployment
metadata:  
  name: myapp
  namespace: default
  labels:
    app: busybox
spec:  
  replicas: 8
  selector:
    matchLabels:
      app: busybox
  template:   
     metadata:      
       labels:        
         app: busybox    
     spec:
       nodeName: k8s-node1    
       containers:      
       - name: buxybox        
         image: busybox   
         ports:
         - containerPort: 22
         command: ["/bin/sh","-c","sleep 3600"]
[root@k8s-master ~]# kubectl get pod -o wide
NAME                     READY   STATUS    RESTARTS   AGE     IP            NODE         NOMINATED NODE   READINESS GATES
myapp-544cb6f798-5fjdh   1/1     Running   0          88s     10.244.1.9    k8s-node1    <none>           <none>
myapp-544cb6f798-7wfjz   1/1     Running   0          88s     10.244.1.10   k8s-node1    <none>           <none>
myapp-544cb6f798-chlxz   1/1     Running   0          88s     10.244.1.13   k8s-node1    <none>           <none>
myapp-544cb6f798-cl8x8   1/1     Running   0          7m12s   10.244.1.7    k8s-node1    <none>           <none>
myapp-544cb6f798-gdqbg   1/1     Running   0          88s     10.244.1.12   k8s-node1    <none>           <none>
myapp-544cb6f798-q54fk   1/1     Running   0          88s     10.244.1.8    k8s-node1    <none>           <none>
myapp-544cb6f798-tdhxz   1/1     Running   0          88s     10.244.1.11   k8s-node1    <none>           <none>
myapp-544cb6f798-tq448   1/1     Running   0          7m12s   10.244.1.6    k8s-node1    <none>           <none>

可以看到全部在node1上面,公平调度没有发挥作用,不经过调度器,即使节点上有污点也忽略,因为不经过调度器。

前面亲和性,nodeselector,污点都是经过调度器。之前配置相关调度的属性它都不起作用了,如果调度器出现故障,希望pod可以快速部署到某个节点,可以使用nodename

2、nodeSelector

Pod.spec.nodeSelector:通过 kubernetes 的 label-selector 机制选择节点,由调度器调度策略匹配 label,而后调度 Pod 到目标节点,该匹配规则属于强制约束

nodeSelector:用于将Pod调度到匹配Label的Node上,如果没有匹配的标签会调度失败。

作用:

  • 约束Pod到特定的节点运行
  • 完全匹配节点标签

应用场景:

  • 专用节点:根据业务线将Node分组管理
  • 配备特殊硬件:部分Node配有SSD硬盘、GPU

默认配置下,Scheduler 会将 Pod 调度到所有可用的 Node。不过有些情况我们希望将 Pod 部署到指定的 Node,比如将有大量磁盘 I/O 的 Pod 部署到配置了 SSD 的 Node,或者 Pod 需要 GPU,需要运行在配置了 GPU 的节点上。

Kubernetes 是通过 label 来实现这个功能的。

示例:确保Pod分配到具有SSD硬盘的节点上

第一步:给节点添加标签
格式: kubectl label nodes <node-name> <label-key>=<label-value>
例如: kubectl label nodes k8s-node1 disktype=ssd
验证: kubectl get nodes --show-labels

第二步:添加nodeSelector字段到Pod配置中

apiVersion: v1
kind: Pod
metadata:
  name: pod-example
spec:
  nodeSelector:
    disktype: "ssd"
  containers:
    - name: nginx
      image: nginx

最后,验证:
kubectl get pods -o wide

label 是 key-value 对,各种资源都可以设置 label,灵活添加各种自定义属性。比如执行如下命令标注 k8s-node2 是配置了 SSD 的节点。

[root@k8s-master ~]# kubectl label node k8s-node2 disktype=ssd
node/k8s-node2 labeled

然后通过 kubectl get node --show-labels 查看节点的 label

[root@k8s-master ~]# kubectl get node --show-labels
NAME         STATUS   ROLES    AGE   VERSION   LABELS
k8s-master   Ready    <none>   51d   v1.18.3   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-master,kubernetes.io/os=linux
k8s-node1    Ready    <none>   50d   v1.18.3   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node1,kubernetes.io/os=linux
k8s-node2    Ready    <none>   50d   v1.18.3   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disktype=ssd,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node2,kubernetes.io/os=linux

disktype=ssd 已经成功添加到 k8s-node2,除了 disktype,Node 还有几个 Kubernetes 自己维护的 label。

有了 disktype 这个自定义 label,接下来就可以指定将 Pod 部署到 k8s-node2。编辑 nginx.yml:

apiVersion: apps/v1
kind: Deployment
metadata:  
  name: myapp
  namespace: default
  labels:
    app: nginx
spec:  
  replicas: 8
  selector:
    matchLabels:
      app: myapp
  template:   
     metadata:      
       labels:        
         app: myapp    
     spec:
       nodeSelector:
         disktype: ssd
       containers:      
       - name: nginx        
         image: nginx   
         ports:
         - containerPort: 80
[root@k8s-master ~]# kubectl apply -f test2.yml 
deployment.apps/myapp created

在 Pod 模板的 spec 里通过 nodeSelector 指定将此 Pod 部署到具有 label disktype=ssd 的 Node 上。

部署 Deployment 并查看 Pod 的运行节点:

[root@k8s-master ~]# kubectl get pod -o wide
NAME                     READY   STATUS    RESTARTS   AGE     IP            NODE         NOMINATED NODE   READINESS GATES
myapp-9bf5d74f5-4jlhr    1/1     Running   0          112s    10.244.2.12   k8s-node2    <none>           <none>
myapp-9bf5d74f5-5b94h    1/1     Running   0          2m39s   10.244.2.9    k8s-node2    <none>           <none>
myapp-9bf5d74f5-6wvrv    1/1     Running   0          3m      10.244.2.5    k8s-node2    <none>           <none>
myapp-9bf5d74f5-7tbpk    1/1     Running   0          3m      10.244.2.6    k8s-node2    <none>           <none>
myapp-9bf5d74f5-l5h9w    1/1     Running   0          3m      10.244.2.7    k8s-node2    <none>           <none>
myapp-9bf5d74f5-n8zs8    1/1     Running   0          2m23s   10.244.2.10   k8s-node2    <none>           <none>
myapp-9bf5d74f5-sf8j6    1/1     Running   0          3m      10.244.2.8    k8s-node2    <none>           <none>
myapp-9bf5d74f5-vs4v6    1/1     Running   0          2m8s    10.244.2.11   k8s-node2    <none>           <none>

全部 8个副本都运行在 k8s-node2上,符合我们的预期。

要删除 label disktype,执行如下命令:

[root@k8s-master ~]# kubectl label node  k8s-node2 disktype-
node/k8s-node2 labeled
[root@k8s-master ~]# kubectl get  node  k8s-node2 --show-labels
NAME        STATUS   ROLES    AGE   VERSION   LABELS
k8s-node2   Ready    <none>   51d   v1.18.3   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node2,kubernetes.io/os=linux
[root@k8s-master ~]# kubectl get pod -o wide
NAME                     READY   STATUS    RESTARTS   AGE     IP            NODE         NOMINATED NODE   READINESS GATES
myapp-9bf5d74f5-4jlhr    1/1     Running   0          5m23s   10.244.2.12   k8s-node2    <none>           <none>
myapp-9bf5d74f5-5b94h    1/1     Running   0          6m10s   10.244.2.9    k8s-node2    <none>           <none>
myapp-9bf5d74f5-6wvrv    1/1     Running   0          6m31s   10.244.2.5    k8s-node2    <none>           <none>
myapp-9bf5d74f5-7tbpk    1/1     Running   0          6m31s   10.244.2.6    k8s-node2    <none>           <none>
myapp-9bf5d74f5-l5h9w    1/1     Running   0          6m31s   10.244.2.7    k8s-node2    <none>           <none>
myapp-9bf5d74f5-n8zs8    1/1     Running   0          5m54s   10.244.2.10   k8s-node2    <none>           <none>
myapp-9bf5d74f5-sf8j6    1/1     Running   0          6m31s   10.244.2.8    k8s-node2    <none>           <none>
myapp-9bf5d74f5-vs4v6    1/1     Running   0          5m39s   10.244.2.11   k8s-node2    <none>           <none>

不过此时 Pod 并不会重新部署,依然在 k8s-node2 上运行。除非在 nginx.yml 中删除 nodeSelector 设置,然后通过 kubectl apply 重新部署。Kubernetes 会删除之前的 Pod 并调度和运行新的 Pod。

如果多个节点都有该标签会选择一个最合适的,因为这几个节点都可以匹配,根据其调度算法选择最合适的匹配。如果没有集群当中没有该标签,那么就无法调度,会一直处于pending。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容