1. Volcano 简介
Volcano 是华为开源的一个基于 Kubernetes 的资源调度系统,相较于原生的调度器,具有的显著特点有:
对于批量作业的调度,容易碰到死锁的问题,比如两个作业都需要同时运行 10 个 Pod 才能启动,当两个作业同时提交时,可能都只有部分 Pod 被调度,两个作业都无法正常运行,而处于互相等待状态。gang scheduling 就是为了解决这个问题。
配置不同的调度队列,能够实现对资源的抢占、配额的控制等。
Numa、GPU 等硬件资源的感知,能够让 Pod 对硬件资源更高效的使用。
Volcano 是在 Kubernetes 原生调度能力的基础上进行的扩展和优化,因此,对于基本的 nodeSelector 、nodeAffinity 等也是支持的。同时也支持 Extended Resource,这点对于 GPU、IB 网卡等资源的在调度层面的感知非常重要。
2. 安装
1
| helm repo add volcano-sh https://volcano-sh.github.io/helm-charts
|
1
| helm install volcano volcano-sh/volcano -n volcano-system --create-namespace --version 1.8.2
|
3. 相关 CRD 列表
1
2
3
4
5
6
7
8
9
| kubectl get crd |grep volcano
commands.bus.volcano.sh 2024-03-21T03:41:33Z
jobflows.flow.volcano.sh 2024-03-21T03:41:33Z
jobs.batch.volcano.sh 2024-03-21T03:41:33Z
jobtemplates.flow.volcano.sh 2024-03-21T03:41:33Z
numatopologies.nodeinfo.volcano.sh 2024-03-21T03:41:33Z
podgroups.scheduling.volcano.sh 2024-03-21T03:41:33Z
queues.scheduling.volcano.sh 2024-03-21T03:41:33Z
|
用于与 Volcano 系统交互。它允许用户通过创建 Command 对象来触发特定的操作,如暂停/恢复作业、重新调度等。
JobFlow 对象描述了多个作业之间的执行依赖关系。
常见的用例是数据处理流水线,通过 Jobflow 可以自动根据依赖关系正确编排作业执行顺序。
Job 是 Volcano 最核心的资源对象,用于提交和运行批处理作业。它支持多种工作负载类型,如单个 Job、Job 数组、周期性作业等。
Job 还可以挂载数据卷、配置资源需求等。
- jobtemplates.flow.volcano.sh
JobTemplate 为创建相似的作业提供了模板机制。用户只需定义一次作业的规格,就可以根据模板快速方便地创建多个实例。
特别适合需要同时运行数十或数百个相似作业的场景,大幅降低管理和运维成本。
- numatopologies.nodeinfo.volcano.sh
NumaTopology 对象用于描述节点的 Numa 信息。
- podgroups.scheduling.volcano.sh
PodGroup 对象将多个 Pod 作为一个整体进行调度。
当需要多个 Pod 同时运行时,可以使用 PodGroup 对象。
- queues.scheduling.volcano.sh
Queue 对象用于定义作业队列,实现资源隔离和公平调度。
在多租户场景下,不同团队或部门可以根据需要创建自己专属的队列,并为队列设置资源配额和优先级参数。通过队列可以避免互相影响,实现可预测和可控的资源分配。
4. Job Pluings 定制 Pod 运行
4.1 常用的三种插件
Volcano 提供了一些内置的 plugins,如果想要自定义开发插件,需要根据源码 https://github.com/volcano-sh/volcano/tree/master/pkg/controllers/job/plugins 实现 PluginInterface 接口。下面是一个示例:
1
2
3
4
5
6
7
8
9
| apiVersion: batch.volcano.sh/v1beta1
kind: Job
metadata:
name: my-job
spec:
plugins:
ssh: []
env: []
svc: []
|
这些插件能够实现一些定制化的需求:
配置 Pod 之间的 SSH 互信,提供免密登录
提供作业运行所需要的网络信息如 hosts 文件、headless service 等 ,来提供计算集群参数的自动化配置
提供作业运行所需要的环境变量
4.2 注入原理
- env 插件注入的是 VK_TASK_INDEX 、VC_TASK_INDEX。
注入原理:
1
2
3
4
5
6
7
| spec:
containers:
- env:
- name: VK_TASK_INDEX
value: "0"
- name: VC_TASK_INDEX
value: "0"
|
插件从 Pod 名字中获取到索引值,然后直接设置到 env 中。
- svc 插件注入的是 VC_DEMO_NUM、VC_DEMO_HOSTS
注入原理:
1
2
3
4
5
6
7
8
9
10
11
12
13
| spec:
containers:
- env:
- name: VC_DEMO_HOSTS
valueFrom:
configMapKeyRef:
key: VC_DEMO_HOSTS
name: my-plugins-job-svc
- name: VC_DEMO_NUM
valueFrom:
configMapKeyRef:
key: VC_DEMO_NUM
name: my-plugins-job-svc
|
1
2
3
4
| kubectl get cm my-plugins-job-svc
NAME DATA AGE
my-plugins-job-svc 3 15m
|
在 my-plugins-job-svc 中存储了 VC_DEMO_NUM 和 VC_DEMO_HOSTS 的值。
注入原理:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| spec:
containers:
volumes:
- name: my-plugins-job-ssh
secret:
defaultMode: 384
items:
- key: id_rsa
path: .ssh/id_rsa
- key: id_rsa.pub
path: .ssh/id_rsa.pub
- key: authorized_keys
path: .ssh/authorized_keys
- key: config
path: .ssh/config
secretName: my-plugins-job-ssh
|
1
2
3
4
| kubectl get secret my-plugins-job-ssh
NAME TYPE DATA AGE
my-plugins-job-ssh Opaque 4 22m
|
在 my-plugins-job-ssh 中存储了 ssh 公钥和私钥,Volcano 会将秘钥挂载到 Pod 中。
4.3 测试 Plugins
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| cat <<EOF | kubectl apply -f -
apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
name: my-plugins-job
spec:
minAvailable: 3
plugins:
ssh: []
env: []
svc: []
tasks:
- replicas: 3
name: demo
template:
spec:
containers:
- name: demo
image: shaowenchen/demo-sshd
EOF
|
这样就创建了三个同时运行的 Pod。
1
2
3
4
5
6
| kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP
my-plugins-job-demo-0 1/1 Running 0 79s 10.244.228.250
my-plugins-job-demo-1 1/1 Running 0 79s 10.244.8.240
my-plugins-job-demo-2 1/1 Running 0 79s 10.244.228.249
|
进入容器
1
| kubectl exec -it my-plugins-job-demo-0 -- bash
|
打印 Volcano 注入的环境变量
1
2
3
4
5
6
| env | grep -E '^VC_|^VK_'
VC_DEMO_NUM=3
VK_TASK_INDEX=0
VC_TASK_INDEX=0
VC_DEMO_HOSTS=my-plugins-job-demo-0.my-plugins-job,my-plugins-job-demo-1.my-plugins-job,my-plugins-job-demo-2.my-plugins-job
|
VC_DEMO_NUM
表示任务总数; VK_TASK_INDEX
、VC_TASK_INDEX
从源码看赋值是相等的,均表示 Task 的索引号,在每个 Pod 中各不相同,VC_DEMO_HOSTS
表示当前任务的 IP 列表。
进入容器
1
| kubectl exec -it my-plugins-job-demo-0 -- bash
|
免密 ssh 到其他 Pod
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| ssh 10.244.8.240
Warning: Permanently added '10.244.8.240' (ECDSA) to the list of known hosts.
Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.4.0-144-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
This system has been minimized by removing packages and content that are
not required on a system that users do not log into.
To restore this content, you can run the 'unminimize' command.
The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.
|
需要注意,这里只能通过 IP 访问,不能使用 Pod Name,因为 Volcano 并没有将其他 Pod 的 IP 和 Name 写入到 /etc/hosts
中。
5. 配置 Deployment 使用 Volcano 控制资源使用
这里举一个示例,限制 Deployment 最多仅能使用 2 核 CPU。
1
2
3
4
5
6
7
8
9
10
11
| cat <<EOF | kubectl apply -f -
apiVersion: scheduling.volcano.sh/v1beta1
kind: Queue
metadata:
name: my-node-queue
spec:
weight: 1
reclaimable: false
capability:
cpu: 2
EOF
|
创建一个仅有 2 核 CPU、并且绑定到节点组 my-node-group 的队列。
这里的 weight
表示集群资源划分中所占的相对比重,是软约束; reclaimable
表示是否允许被回收,由 weight
来决定; capability
表示队列的资源限制。
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
| cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: ubuntu-with-volcano
labels:
app: demo
spec:
replicas: 1
selector:
matchLabels:
app: demo
template:
metadata:
labels:
app: demo
spec:
schedulerName: volcano
containers:
- name: demo
image: shaowenchen/demo-ubuntu
resources:
requests:
cpu: 1
EOF
|
将 schedulerName
设置为 volcano
,表示使用 Volcano 调度器。
1
2
3
4
| kubectl get pods -l app=demo
NAME READY STATUS RESTARTS AGE
ubuntu-with-volcano-97c94f9fb-bfgrh 1/1 Running 0 6m24s
|
1
| kubectl scale deployment/ubuntu-with-volcano --replicas=3
|
此时,三个 Pod 只有两个处于 Running 状态,因为 Volcano 限制了 Deployment 最多仅能使用 2c CPU。
1
2
3
4
5
6
| kubectl get pods -l app=demo
NAME READY STATUS RESTARTS AGE
ubuntu-with-volcano-97c94f9fb-25nb7 1/1 Running 0 27s
ubuntu-with-volcano-97c94f9fb-6fd64 0/1 Pending 0 27s
ubuntu-with-volcano-97c94f9fb-bfgrh 1/1 Running 0 7m31s
|
6. 配置 Job 使用 Volcano 限流并发执行
这里创建一个 Job 并且要求至少 3 个 Pod 一起运行的 Job。
直接使用 Kubernetes batch/v1
中的 Job ,配置 completions 和 parallelism,也可以实现这个需求。但 Volcano 提供的 Queue 可以控制资源使用、Policy 可以控制 Task 的生命周期策略,能更精准控制 Job 的执行。
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
| cat <<EOF | kubectl apply -f -
apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
name: my-job
spec:
minAvailable: 3
schedulerName: volcano
queue: default
policies:
- event: PodEvicted
action: RestartJob
tasks:
- replicas: 30
name: demo
policies:
- event: TaskCompleted
action: CompleteJob
template:
spec:
containers:
- image: ubuntu
name: demo
command: ["sleep", "5"]
resources:
requests:
cpu: 20
restartPolicy: Never
EOF
|
其中:
1
2
3
| policies:
- event: PodEvicted
action: RestartJob
|
表示如果 Pod 被 Evict 了,就重启 Job。
1
2
3
| policies:
- event: TaskCompleted
action: CompleteJob
|
表示如果 Task 完成了,就完成 Job。
通过 Event 和 Action,可以控制 Job 的状态和行为。
1
2
3
4
5
6
7
8
9
| kubectl get pod
NAME READY STATUS RESTARTS AGE
my-job-demo-0 1/1 Running 0 7s
my-job-demo-1 1/1 Running 0 7s
my-job-demo-10 0/1 Pending 0 7s
...
my-job-demo-2 1/1 Running 0 7s
...
|
由于我设置了 Pod 的 CPU Request 为 20,集群上没有足够的资源,所以 30 个 Pod 每次只能运行 3 个。
执行完成之后,Pod 不会被删除而是处于 Completed 状态。由于 Pod 的 ownerReferences 是 Job,如果删除 Job,Pod 也会被删除。