kubernetes Scheduling

Kubernetes Scheduling

Kubernetes作为一个容器编排调度引擎,资源调度是它的最基本也是最重要的功能。当我们部署一个应用时它运行在哪个节点?这个节点满不满足我们的运行要求?Kubernetes又是如何进行资源调度的呢?今天将通过本文带你了解到以下信息:

  • 了解资源请求及限制对pod调度的影响
  • 查看调度事件events
  • 了解label选择器对pod调度的影响
  • 了解节点亲和性和Pod亲和性对调度的影响
  • 不使用调度器, 手动调度一个pod
  • 了解Daemonset的角色
  • 了解如何配置kubernetes scheduler

在Kubernetes中有一个kube-scheduler组件,该组件运行在master节点上,它主要负责pod的调度。kube-scheduler监听kube-apiserver中是否有还未调度到node上的pod(即Spec.NodeName为空的Pod),再通过特定的算法为pod指定分派node运行。如果分配失败,则将该pod放置调度队列尾部以重新调度。调度主要分为几个部分:首先是预选过程,过滤不满足Pod要求的节点。然后是优选过程,对通过要求的节点进行优先级排序,最后选择优先级最高的节点分配。其中涉及到的两个关键点是过滤和优先级评定的算法。调度器使用一组规则过滤不符合要求的节点,其中包括设置了资源的request和指定了Nodename或者其他亲和性设置等等。优先级评定将过滤得到的节点列表进行打分,调度器考虑一些整体的优化策略,比如将Deployment控制的多个副本集分配到不同节点上等等。

资源请求及限制对pod调度的影响

在部署应用时,我们会考虑到使这个应用运行起来需要多少的内存和CPU资源的使用量,这样才能判断应将他运行在哪个节点上。在部署文件resource属性中添加requests字段用于说明运行该容器所需的最少资源,当调度器开始调度该Pod时,调度程序确保对于每种资源类型,计划容器的资源请求总和必须小于节点的容量才能分配该节点运行Pod,resource属性中添加limits字段用于限制容器运行时所获得的最大资源。如果该容器超出其内存限制,则可能被终止。 如果该容器可以重新启动,kubelet会将它从新启动。如果调度器找不到合适的节点运行Pod时,就会产生调度失败事件,调度器会将Pod放置调度队列以循环调度,直到调度完成。

在下面例子中,运行一个nginx Pod,资源请求了256Mi的内存和100m的CPU,调度器将判断哪个节点还剩余这么多的资源,寻找到了之后就会将这个Pod调度上去。同时也设置了512Mi的内存和300m的CPU的使用限制,如果该Pod运行之后超出了这一限制就将被重启甚至被驱逐。

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      requests:
        memory: "256Mi"
        cpu: "100m"
      limits:
        memory: "512Mi"
        cpu: "300m"

参考文档:

查看调度事件events

在部署应用后,可以使用kubectl describe命令进行查看Pod的调度事件,下面是一个coredns被成功调度到node3运行的事件记录。

$ kubectl describe po coredns-5679d9cd77-d6jp6 -n kube-system
...
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  29s   default-scheduler  Successfully assigned kube-system/coredns-5679d9cd77-d6jp6 to node3
  Normal  Pulled     28s   kubelet, node3     Container image "grc.io/kubernetes/coredns:1.2.2" already present on machine
  Normal  Created    28s   kubelet, node3     Created container
  Normal  Started    28s   kubelet, node3     Started container

下面是一个coredns被调度失败的事件记录,根据记录显示不可调度的原因是没有节点满足该Pod的内存请求。

$ kubectl describe po coredns-8447874846-5hpmz -n kube-system
...
Events:
  Type     Reason            Age                From               Message
  ----     ------            ----               ----               -------
  Warning  FailedScheduling  22s (x3 over 24s)  default-scheduler  0/3 nodes are available: 3 Insufficient memory.

label选择器对pod调度的影响

例如我们需要部署一个ES集群,由于ES对磁盘有较高的要求,而集群中只有一部分节点有SSD磁盘,那么我们就需要将标记一下带有SSD磁盘的节点即给这些节点打上Lable,让ES的pod只能运行在带这些标记的节点上。
Lable是附着在K8S对象(如Pod、Service等)上的键值对。它可以在创建对象的时候指定,也可以在对象创建后随时指定。Kubernetes最终将对labels最终索引和反向索引用来优化查询和watch,在UI和命令行中会对它们排序。通俗的说,就是为K8S对象打上各种标签,方便选择和调度。

  1. 查看节点信息
$ kubectl get nodes
NAME    STATUS   ROLES            AGE    VERSION
node1   Ready    etcd,master      128m   v1.12.4
node2   Ready    etcd,lb,master   126m   v1.12.4
node3   Ready    etcd,lb,worker   126m   v1.12.4
  1. 选择出有SSD磁盘的节点, 并给这个节点打上标记(label)
$ kubectl label nodes <your-node-name> disktype=ssd
node/<your-node-name> labeled
  1. 验证节点上是否有成功打上对应label
$ kubectl get nodes --show-labels
NAME    STATUS   ROLES            AGE    VERSION   LABELS
node1   Ready    etcd,master      139m   v1.12.4   ...disktype=ssd,kubernetes.io/hostname=node1...
node2   Ready    etcd,lb,master   137m   v1.12.4   ...kubernetes.io/hostname=node2...
node3   Ready    etcd,lb,worker   137m   v1.12.4   ...kubernetes.io/hostname=node3...
  1. 创建一个ES的pod, 调度到有SSD磁盘标记的节点上
    在pod的配置里, 要指定nodeSelector属性值为disktype:ssd。 这意味着pod启动后会调度到打上了disktype=ssd标签的node上
apiVersion: v1
kind: Pod
metadata:
  name: es
spec:
  containers:
  - name: es
    image: es
  nodeSelector:
    disktype: ssd
  1. 验证pod启动后是否调度到指定节点上
$ kubectl get pods -o wide
NAMESPACE  NAME                   READY   STATUS    RESTARTS   AGE    IP              NODE    NOMINATED NODE
default    es-5679d9cd77-sbmcx    1/1     Running   0          134m   10.244.2.3      node1   <none>

节点亲和性和Pod亲和性对调度的影响

上小节讲述的nodeSelector提供了一种非常简单的方法,可以将pod限制为具有特定标签的节点。而更为强大的表达约束类型则可以由Affinity和Anti-affinity来配置。即亲和性与反亲和性的设置。亲和性和反亲和性包括两种类型:节点(反)亲和性与Pod(反)亲和性。Node affinity与NodeSelector很相似,它允许你根据节点上的标签限制你的pod可以在哪些节点上进行调度。目前有两种类型的节点关联,称为requiredDuringSchedulingIgnoredDuringExecution和 preferredDuringSchedulingIgnoredDuringExecution。可以将它们分别视为“硬规则”和“软规则”,前者指定了要将 pod调度到节点上必须满足的规则,而后者指定调度程序将尝试强制但不保证的首选项。名称中的“IgnoredDuringExecution”部分意味着,类似于nodeSelector工作方式,如果节点上的标签在运行时更改,不再满足pod上的关联性规则,pod仍将继续在该节点上运行。Pod affinity强调的是同一个节点中Pod之间的亲和力。可以根据已在节点上运行的pod上的标签来约束pod可以调度哪些节点上。比如希望运行该Pod到某个已经运行了Pod标签为app=webserver的节点上,就可以使用Pod affinity来表达这一需求。目前有两种类型Pod亲和力和反亲和力,称为requiredDuringSchedulingIgnoredDuringExecution以及 preferredDuringSchedulingIgnoredDuringExecution,其中表示“硬规则”与“软规则”的要求。类似于Node affinity,IgnoredDuringExecution部分表示如果在Pod运行期间改变了Pod标签导致亲和性不满足以上规则,则pod仍将继续在该节点上运行。无论是Selector还是Affinity,都是基于Pod或者Node的标签来表达约束类型。从而让调度器按照约束规则来调度Pod运行在合理的节点上。
节点亲和性如下所示,其中亲和性定义为:该pod只能放置在一个含有键为kubernetes.io/hostname并且值为node1或者node2标签的节点上。此外,在满足该标准的节点中,具有其键为app且值为webserver的标签的节点应该是优选的。

apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/hostname
            operator: In
            values:
            - node1
            - node2
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: app
            operator: In
            values:
            - webserver
  containers:
  - name: with-node-affinity
    image: k8s.gcr.io/pause:2.0

Pod反亲和性如下所示,其中反亲和性定义为:在此拓扑域(相当于以topologyKey的值进行的节点分组)中,命名空间为default下有标签键为app,标签值为redis的Pod时不在此Node上运行。

apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
spec:
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - redis
        namespaces:
        - default
        topologyKey: kubernetes.io/hostname
  containers:
  - name: with-node-affinity
    image: k8s.gcr.io/pause:2.0

不使用调度器, 手动调度一个pod

Scheduling过程的本质其实就是给Pod赋予nodeName属性合适的值。那么在我们进行Pod部署时就直接指定这个值是否可行呢?答案是肯定的。如下配置,将nginx直接分配到node1上运行。

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - image: nginx
    name: nginx
  nodeName: node1

还有一种指定节点的部署方式——static pod,就像它名称一样,他是一个“静态”的Pod,它不通过apiserver,直接由kubelet进行托管。在kubelet的启动参数中--pod-manifest-path=DIR,这里的DIR就是放置static pod的编排文件的目录。把static pod的编排文件放到此目录下,kubelet就可以监听到变化,并根据编排文件创建pod。还有一个启动参数--manifest-url=URL,kubelet会从这个URL下载编排文件,并创建pod。static pod有一个特性是我们使用docker或kubectl删除static pod后, static pod还能被kubelet进程拉起。通过这种方式保证了应用的可用性。有点相当于systemd的功能, 但比systemd好的一点是, static pod的镜像信息会在apiserver中注册。 这样的话, 我们就可以统一对部署信息进行可视化管理。 此外static pod是容器, 无需拷贝二进制文件到主机上, 应用封装在镜像里也保证了环境的一致性, 无论是应用的编排文件还是应用的镜像都方便进行版本管理和分发。
在使用kubeadm部署kubernetes集群时, static pod得到了大量的应用,比如etcd、 kube-scheduler, kube-controller-manager, kube-apiserver等都是使用 static pod的方式运行的。
使用static pod部署出来的pod名称与其他pod有很大的不同点,名称中没有“乱码”,只是简单的将pod的name属性值与它运行在的node的name属性值相连接而成。如下所示,coredns是通过Deployment部署出来的名称中就有部分“乱码”,而etcd,kube-apiserver这种Pod就是static pod。

 $ kubectl get po --all-namespaces
NAMESPACE     NAME                            READY   STATUS    RESTARTS   AGE
kube-system   coredns-5679d9cd77-d6jp6        1/1     Running   0          6m59s
kube-system   etcd-node1                      1/1     Running   0          6m58s
kube-system   etcd-node2                      1/1     Running   0          6m58s
kube-system   etcd-node3                      1/1     Running   0          6m54s
kube-system   kube-proxy-nxj5d                1/1     Running   0          6m52s
kube-system   kube-proxy-tz264                1/1     Running   0          6m56s
kube-system   kube-proxy-zxgxc                1/1     Running   0          6m53s

了解Daemonset角色

DaemonSet是一种控制器,它确保在一些或全部Node上都运行一个指定的Pod。这些Pod就相当于守护进程一样不期望被终止。当有Node加入集群时,也会为他们新增一个Pod。当有Node从集群移除时,对应的Pod也会被回收。当删除DaemonSet时将会删除它创建的所有Pod。一般情况下,Pod运行在哪个节点上是由Kubernates调度器选择的。但是在Kubernates 1.11版本之前由DaemonSet Controller创建的Pod在创建时已经确定了在哪个节点上运行(pod在创建的时候.spec.nodeName字段就指定了, 因此会被scheduler忽略),所以即使调度器没有启动DaemonSet Controller创建的Pod仍然也可以被分配node。直到Kubernates 1.11版本,DaemonSet的pod由scheduler调度才作为alpha特性引入。在上小节中kube-proxy就是以DaemonSet的方式进行运行的。

配置Kubernetes scheduler

如果需要配置一些高级的调度策略以满足我们的需要,可以修改默认调度程序的配置文件。kube-scheduler在启动的时候可以通过--policy-config-file参数来指定调度策略文件,我们可以根据我们自己的需要来组装Predicates和Priority函数。选择不同的过滤函数和优先级函数。调整控制优先级函数的权重和过滤函数的顺序都会影响调度结果。
官方的Policy文件如下:

kind: Policy
apiVersion: v1
predicates:
- {name: PodFitsHostPorts}
- {name: PodFitsResources}
- {name: NoDiskConflict}
- {name: NoVolumeZoneConflict}
- {name: MatchNodeSelector}
- {name: HostName}
priorities:
- {name: LeastRequestedPriority, weight: 1}
- {name: BalancedResourceAllocation, weight: 1}
- {name: ServiceSpreadingPriority, weight: 1}
- {name: EqualPriority, weight: 1}

其中predicates区域是调度的预选阶段所需要的过滤算法。priorities区域是优选阶段的评分算法。

总结:

我们再来回顾一下调度的主要构成部分:首先是预选过程,过滤掉不满足Pod要求的节点,然后是优选过程,对通过要求的节点进行优先级排序,最后选择优先级最高的节点进行分配。当调度器不工作时或有临时需求可以手动指定nodeName属性的值,让其不通过调度器进行调度直接运行在指定的节点上。

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

推荐阅读更多精彩内容

  • 经过上周周末两天的遭遇,真的是好痛苦的感觉。一方面详尽办法节约时间,增加学习的时间,另一方面,家里有个两岁的娃,一...
    zazabo阅读 637评论 0 0
  • 呼吸禅:还是压脚时进行呼吸练习,有点急促,因为脚疼,可能觉得快点呼吸,能够快点到20分钟,我想,明天还是要单独进行...
    我是爱和自由阅读 162评论 0 0
  • 定义 以终为始,就是说从现在开始,以你的人生目标作为衡量一切的标准,你的一言一行,一举一动,无论发生在何时,都必须...
    印钞机阅读 1,025评论 0 1
  • 今天或许是个全新的开始,仿佛回到了学生时代。遗憾的是不能一起享受大汗淋漓的过程,听朋友们在群里聊着跑步的感受,心里...
    幸福鸟_350e阅读 157评论 0 1