Z.S.K.'s Records

Kube-batch学习(queue及podgroup)

上篇简单说了下在集群中安装kube-batch且成功运行起来之后, 现在开始对kube-batch中的一些概念进行阐明, 先从podgroup及queue说起

Queue

queue在kube-batch中属于集群级别的资源,配置如下:

1
2
3
4
5
6
apiVersion: scheduling.incubator.k8s.io/v1alpha1
kind: Queue
metadata:
name: default
spec:
weight: 1

默认,如果集群中不存在queue,kube-batch会默认创建一个如上的queue,这个默认的queue主要用来存放没有绑定queue的job(这个job不是kubernetes中job资源,而是特指使用了kube-batch的对象,有可能是kubernetes job,也可能是pod等)

前面提到过,kube-batch对多租户的支持、多租户间的资源分配就是通过queue来实现的

queue本身就是一种排除机制,kube-batch也是如此,对于资源的消耗,如果资源得不到满足,同样会进行排除处理.

这里举个最简单的例子:

假如一个k8s集群中存在的资源有,cpu: 12c, mem: 120Gi, GPU: 4c

假如现在创建了2个queue

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: scheduling.incubator.k8s.io/v1alpha1
kind: Queue
metadata:
name: default-1
spec:
weight: 1
---
apiVersion: scheduling.incubator.k8s.io/v1alpha1
kind: Queue
metadata:
name: default-2
spec:
weight: 2

其中,weight表示权重,那么集群中的所有资源会被分成3等份,所属default-1的queue占1/3, 即cpu:4c,mem:40ci,gpu:4c,而所属default-2的queue占2/3, 即cpu:8c,mem:80ci,gpu:8c

当然,前面也提过,这个不是绝对的,因为kube-batch存在插件机制,租户之间可以指定优先级等插件对其它租户的资源进行抢占,使资源的资源更加偏向重要的任务

如果集群中没有多租户的概念,则可以不进行资源的划分,所有的任务都属于一个queue.

PodGroup

podgroup在kube-batch是个很重要的资源对象,kube-batch会监听集群中所有podgroup,然后计算(由插件决定)所属这个group中的pod所需要的资源,如果集群中没有这些资源,则所有的pod会处理pendding状态,在queue中进行排队,如果存在,则进行调度

使用方法来看官方一个简单的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
---
apiVersion: scheduling.incubator.k8s.io/v1alpha1
kind: PodGroup
metadata:
name: qj-1
spec:
minMember: 6
---
apiVersion: batch/v1
kind: Job
metadata:
name: qj-1
spec:
backoffLimit: 6
completions: 6
parallelism: 6
template:
metadata:
annotations:
scheduling.k8s.io/group-name: qj-1 # 指定所属podgroup
spec:
containers:
- image: busybox
imagePullPolicy: IfNotPresent
name: busybox
resources:
requests:
cpu: "1" # 指明需要的资源
restartPolicy: Never
schedulerName: kube-batch # 指定使用kube-batch做为调度器

The yaml file means a Job named qj-01 to create 6 pods(it is specified by parallelism), these pods will be scheduled by scheduler kube-batch (it is specified by schedulerName). kube-batch will watch PodGroup, and the annotation scheduling.k8s.io/group-name identify which group the pod belongs to. kube-batch will start .spec.minMember pods for a Job at the same time; otherwise, such as resources are not sufficient, kube-batch will not start any pods for the Job.

上面的yaml文件生成了个podgroup对象及一个kubernetes中的job对象

其中:

  • podgroup中的minMember表明至少需要满足6个pods的资源时才会进行调度,pods则是通过annotations进行绑定
  • 在job中存在有annotations,通过scheduling.k8s.io/group-name绑定的podgroup.
  • 在job中指定了schedulerName为Kube-batch

通过这样的绑定关系就将podgroup与pod进行了关联,当然,podgroup中的minMember只是最小的pod数,如果满足这个条件也只是表明这个podgroup可以调度,但是并不一定调度成功,但是如果不满足最小pod数,则一定不会调度

还是以上面的例子来说明:

假如集群中只有4个CPU, 这时minMember设置为1,那么这个podgroup会进行调度,但是由于CPU只有4个,而job中需要同时启动6个(job的parallelism参数指定 )pod,也就是需要6个cpu,显然有2个无法获取到cpu,也即会有2外pod处于pendding状态,其它4个运行正常

而如果将minMember指定为6,则一个pod都不会调度,整个job会处理pendding状态.

上面这种最简单的场景在训练任务中非常常见, 往往一个训练任务由N多个pod一起进行,且每个pod都负责其中的一部分,如果不考虑增量训练的话,其中其中任一个pod的资源不能满足的话,则就不应该对其它pod进行调度,这样可以合理地利用资源

shadowPodGroup

从上面的例子可以看到,每一个任务都需要创建一个podgroup,对于懒人来说,有没有办法直接关联podgroup呢?

答案是肯定的,这就是shadowpodgroup,顾名思义就是podgroup的影子,作用就是不再需要手工地创建podgroup对象

只需要在annotation中指定minMemeber,kube-batch即会自动创建shadowPodgroup对象

因此,上面的例子则直接变成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: batch/v1
kind: Job
metadata:
name: qj-1
spec:
backoffLimit: 6
completions: 6
parallelism: 6
template:
metadata:
annotations:
scheduling.k8s.io/group-min-member: 6 # 指定所属podgroup
spec:
containers:
- image: busybox
imagePullPolicy: IfNotPresent
name: busybox
resources:
requests:
cpu: "1" # 指明需要的资源
restartPolicy: Never
schedulerName: kube-batch # 指定使用kube-batch做为调度器

通过在annotation中指定scheduling.k8s.io/group-min-member,则会由kube-batch自动生成逻辑的podgroup,是不是很方便.

这里要注意的是,podgroup在kubernetes中是属于crd对象,是可以通过kubectl get 命令查询的,而shadowpodgroup则是kube-batch逻辑对象,通过kubectl get 命令无法查询

以下是shawpodgroup的源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
func createShadowPodGroup(pod *v1.Pod) *api.PodGroup {
jobID := api.JobID(utils.GetController(pod))
if len(jobID) == 0 {
jobID = api.JobID(pod.UID)
}
// Deriving min member for the shadow pod group from pod annotations.
//
// Annotation from the newer API has priority over annotation from the old API.
//
// By default, if no annotation is provided, min member is 1.
minMember := 1
if annotationValue, found := pod.Annotations[v1alpha1.GroupMinMemberAnnotationKey]; found {
if integerValue, err := strconv.Atoi(annotationValue); err == nil {
minMember = integerValue
} else {
glog.Errorf("Pod %s/%s has illegal value %q for annotation %q",
pod.Namespace, pod.Name, annotationValue, v1alpha1.GroupMinMemberAnnotationKey)
}
}
if annotationValue, found := pod.Annotations[v1alpha2.GroupMinMemberAnnotationKey]; found {
if integerValue, err := strconv.Atoi(annotationValue); err == nil {
minMember = integerValue
} else {
glog.Errorf("Pod %s/%s has illegal value %q for annotation %q",
pod.Namespace, pod.Name, annotationValue, v1alpha2.GroupMinMemberAnnotationKey)
}
}
return &api.PodGroup{
ObjectMeta: metav1.ObjectMeta{
Namespace: pod.Namespace,
Name: string(jobID),
Annotations: map[string]string{
shadowPodGroupKey: string(jobID),
},
},
Spec: api.PodGroupSpec{
MinMember: int32(minMember),
},
}
}

从这段代码也可以看出,最后其实还是生成的podgroup对象.

这篇文章中介绍了queue及podgroup的使用方法,相信大家有一定的了解,但是还没有一些重要的东西未涉及,比如podgroup中的资源是如何计算的,插件又是如何工作的,这些都会在后续进行更新.

参考文章:

转载请注明原作者: 周淑科(https://izsk.me)

 wechat
Scan Me To Read on Phone
I know you won't do this,but what if you did?