华为云原生Kubernetes运行Volcano高性能作业的深度使用和实践【与云原生的故事】

网友投稿 1308 2022-05-30

一、Volcano 简介

① 什么是 Volcano ?

Volcano 是 CNCF 下首个也是唯一的基于 Kubernetes 的容器批量计算平台,主要用于高性能计算场景,提供了机器学习、深度学习、生物信息学、基因组学及其他大数据应用所需要而 Kubernetes 当前缺失的一系列特性。

Volcano 提供高性能任务调度引擎、高性能异构芯片管理、高性能任务运行管理等通用计算能力,通过接入 AI、大数据、基因、渲染等诸多行业计算框架服务终端用户。

Volcano 针对计算型应用提供了作业调度、作业管理、队列管理等多项功能,主要特性包括:

丰富的计算框架支持:通过 CRD 提供了批量计算任务的通用 API,通过提供丰富的插件及作业生命周期高级管理,支持 TensorFlow,MPI,Spark 等计算框架容器化运行在 Kubernetes 上;

高级调度:面向批量计算、高性能计算场景提供丰富的高级调度能力,包括成组调度,优先级抢占、装箱、资源预留、任务拓扑关系等;

队列管理:支持分队列调度,提供队列优先级、多级队列等复杂任务调度能力。

② Volcano 的特性

Volcano 支持各种调度策略,包括 Gang-scheduling、Fair-share scheduling、Queue scheduling、Preemption scheduling、Topology-based scheduling、Reclaims、Backfill、Resource Reservation 等,得益于可扩展性的架构设计,Volcano 支持用户自定义 plugin 和 action 以支持更多调度算法;

Volcano 提供了增强型的 Job 管理能力以适配高性能计算场景,如多 pod 类型job、增强型的异常处理、可索引 Job;

Volcano 提供了基于多种架构的计算资源的混合调度能力:如 x86、ARM、鲲鹏、昇腾、GPU;

Volcano 已经支持几乎所有的主流计算框架:Spark、TensorFlow、PyTorch、Flink、Argo、MindSpore、PaddlePaddle、OpenMPI、Horovod、mxnet、Kubeflow、KubeGene、Cromwell 等。

③ Volcano 的系统架构

Volcano 与 Kubernetes 天然兼容,并为高性能计算而生,它遵循 Kubernetes 的设计理念和风格,如下所示:

Volcano 由 scheduler、controllermanager、admission 和 vcctl 组成:

Scheduler Volcano scheduler 通过一系列的 action 和 plugin 调度Job,并为它找到一个最适合的节点,与 Kubernetes default-scheduler 相比,Volcano 与众不同的地方是它支持针对 Job 的多种调度算法;

Controllermanager Volcano controllermanager 管理 CRD 资源的生命周期,它主要由 Queue ControllerManager、 PodGroupControllerManager、 VCJob ControllerManager 构成;

Admission Volcano admission 负责对 CRD API 资源进行校验;

Vcctl Volcano vcctl 是 Volcano 的命令行客户端工具。

④ Volcano 的应用场景

⑤ Volcano 的安装

Deployment Yaml 安装方式支持 x86_64/arm64 两种架构,在 kubernetes 集群上,执行如下的 kubectl 指令:

For x86_64: kubectl apply -f https://raw.githubusercontent.com/volcano-sh/volcano/master/installer/volcano-development.yaml For arm64: kubectl apply -f https://raw.githubusercontent.com/volcano-sh/volcano/master/installer/volcano-development-arm64.yaml

也可以将 master 替换为指定的标签或者分支(比如 release-1.5 分支表示最新的 v1.5.x 版本,v1.5.1 标签表示 v1.5.1 版本)以安装指定的 Volcano 版本。

如果没有 kubernetes 集群,可以选择在 github 下载 volcano 源代码压缩包,解压后运行 volcano 的安装脚本,这种安装方式暂时只支持 x86_64 平台:

# git clone https://github.com/volcano-sh/volcano.git # tar -xvf volcano-{Version}-linux-gnu.tar.gz # cd volcano-{Version}-linux-gnu # ./hack/local-up-volcano.sh

在集群中下载 Helm,可以根据以下指南安装 Helm:[https://helm.sh/docs/intro/](安装 Helm)(仅当使用 Helm 模式进行安装时需要)。

如果想使用 Helm 部署 Volcano,请先确认已经在集群中安装了 Helm。

创建一个新的命名空间:

# kubectl create namespace volcano-system namespace/volcano-system created

使用 Helm 进行安装:

# helm install helm/chart/volcano --namespace volcano-system --name volcano NAME: volcano LAST DEPLOYED: Tue Jul 23 20:07:29 2019 NAMESPACE: volcano-system STATUS: DEPLOYED RESOURCES: ==> v1/ClusterRole NAME AGE volcano-admission 1s volcano-controllers 1s volcano-scheduler 1s ==> v1/ClusterRoleBinding NAME AGE volcano-admission-role 1s volcano-controllers-role 1s volcano-scheduler-role 1s ==> v1/ConfigMap NAME DATA AGE volcano-scheduler-configmap 2 1s ==> v1/Deployment NAME READY UP-TO-DATE AVAILABLE AGE volcano-admission 0/1 1 0 1s volcano-controllers 0/1 1 0 1s volcano-scheduler 0/1 1 0 1s ==> v1/Job NAME COMPLETIONS DURATION AGE volcano-admission-init 0/1 1s 1s ==> v1/Pod(related) NAME READY STATUS RESTARTS AGE volcano-admission-b45b7b76-84jmw 0/1 ContainerCreating 0 1s volcano-admission-init-fw47j 0/1 ContainerCreating 0 1s volcano-controllers-5f66f8d76c-27584 0/1 ContainerCreating 0 1s volcano-scheduler-bb4467966-z642p 0/1 Pending 0 1s ==> v1/Service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE volcano-admission-service ClusterIP 10.107.128.208 443/TCP 1s ==> v1/ServiceAccount NAME SECRETS AGE volcano-admission 1 1s volcano-controllers 1 1s volcano-scheduler 1 1s ==> v1beta1/CustomResourceDefinition NAME AGE podgroups.scheduling.sigs.dev 1s queues.scheduling.sigs.dev 1s NOTES: Thank you for installing volcano. Your release is named volcano. For more information on volcano, visit: https://volcano.sh/

验证 Volcano 组件的状态:

# kubectl get all -n volcano-system NAME READY STATUS RESTARTS AGE pod/volcano-admission-5bd5756f79-p89tx 1/1 Running 0 6m10s pod/volcano-admission-init-d4dns 0/1 Completed 0 6m10s pod/volcano-controllers-687948d9c8-bd28m 1/1 Running 0 6m10s pod/volcano-scheduler-94998fc64-9df5g 1/1 Running 0 6m10s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/volcano-admission-service ClusterIP 10.96.140.22 443/TCP 6m10s NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/volcano-admission 1/1 1 1 6m10s deployment.apps/volcano-controllers 1/1 1 1 6m10s deployment.apps/volcano-scheduler 1/1 1 1 6m10s NAME DESIRED CURRENT READY AGE replicaset.apps/volcano-admission-5bd5756f79 1 1 1 6m10s replicaset.apps/volcano-controllers-687948d9c8 1 1 1 6m10s replicaset.apps/volcano-scheduler-94998fc64 1 1 1 6m10s NAME COMPLETIONS DURATION AGE job.batch/volcano-admission-init 1/1 28s 6m10s

⑥ 使用 Volcano CRD 资源

创建一个名为 “test” 的自定义队列:

# cat <

创建一个名为 “job-1” 的 Volcano Job:

# cat <

检查自定义 job 的状态:

# kubectl get vcjob job-1 -oyaml apiVersion: batch.volcano.sh/v1alpha1 kind: Job metadata: creationTimestamp: "2022-05-18T12:59:37Z" generation: 1 managedFields: - apiVersion: batch.volcano.sh/v1alpha1 fieldsType: FieldsV1 fieldsV1: f:spec: .: {} f:minAvailable: {} f:policies: {} f:queue: {} f:schedulerName: {} manager: kubectl operation: Update time: "2022-05-18T12:59:37Z" - apiVersion: batch.volcano.sh/v1alpha1 fieldsType: FieldsV1 fieldsV1: f:spec: f:tasks: {} f:status: .: {} f:minAvailable: {} f:running: {} f:state: .: {} f:lastTransitionTime: {} f:phase: {} manager: vc-controller-manager operation: Update time: "2022-05-18T12:59:45Z" name: job-1 namespace: default resourceVersion: "850500" selfLink: /apis/batch.volcano.sh/v1alpha1/namespaces/default/jobs/job-1 uid: 215409ec-7337-4abf-8bea-e6419defd688 spec: minAvailable: 1 policies: - action: RestartJob event: PodEvicted queue: test schedulerName: volcano tasks: - name: nginx policies: - action: CompleteJob event: TaskCompleted replicas: 1 template: spec: containers: - command: - sleep - 10m image: nginx:latest name: nginx resources: limits: cpu: 1 requests: cpu: 1 status: minAvailable: 1 running: 1 state: lastTransitionTime: "2022-05-18T12:59:45Z" phase: Running

检查名为 ”job-1“ 的 PodGroup 的状态:

# kubectl get podgroup job-1 -oyaml apiVersion: scheduling.volcano.sh/v1beta1 kind: PodGroup metadata: creationTimestamp: "2022-05-18T12:59:37Z" generation: 5 managedFields: - apiVersion: scheduling.volcano.sh/v1beta1 fieldsType: FieldsV1 fieldsV1: f:metadata: f:ownerReferences: .: {} k:{"uid":"215409ec-7337-4abf-8bea-e6419defd688"}: .: {} f:apiVersion: {} f:blockOwnerDeletion: {} f:controller: {} f:kind: {} f:name: {} f:uid: {} f:spec: .: {} f:minMember: {} f:minResources: .: {} f:cpu: {} f:queue: {} f:status: {} manager: vc-controller-manager operation: Update time: "2022-05-18T12:59:37Z" - apiVersion: scheduling.volcano.sh/v1beta1 fieldsType: FieldsV1 fieldsV1: f:status: f:conditions: {} f:phase: {} f:running: {} manager: vc-scheduler operation: Update time: "2022-05-18T12:59:45Z" name: job-1 namespace: default ownerReferences: - apiVersion: batch.volcano.sh/v1alpha1 blockOwnerDeletion: true controller: true kind: Job name: job-1 uid: 215409ec-7337-4abf-8bea-e6419defd688 resourceVersion: "850501" selfLink: /apis/scheduling.volcano.sh/v1beta1/namespaces/default/podgroups/job-1 uid: ea5b4f87-b750-440b-a41a-5c9944a7ae43 spec: minMember: 1 minResources: cpu: "1" queue: test status: conditions: - lastTransitionTime: "2022-05-18T12:59:38Z" message: '1/0 tasks in gang unschedulable: pod group is not ready, 1 minAvailable.' reason: NotEnoughResources status: "True" transitionID: 606145d1-660f-4e01-850d-ed556cebc098 type: Unschedulable - lastTransitionTime: "2022-05-18T12:59:45Z" reason: tasks in gang are ready to be scheduled status: "True" transitionID: 57e6ba9e-55cc-47ce-a37e-d8bddd99d54b type: Scheduled phase: Running running: 1

检查队列 “test” 的状态:

# kubectl get queue test -oyaml apiVersion: scheduling.volcano.sh/v1beta1 kind: Queue metadata: creationTimestamp: "2022-05-18T12:59:30Z" generation: 1 managedFields: - apiVersion: scheduling.volcano.sh/v1beta1 fieldsType: FieldsV1 fieldsV1: f:spec: .: {} f:capability: {} f:reclaimable: {} f:weight: {} manager: kubectl operation: Update time: "2022-05-18T12:59:30Z" - apiVersion: scheduling.volcano.sh/v1beta1 fieldsType: FieldsV1 fieldsV1: f:spec: f:capability: f:cpu: {} f:status: .: {} f:running: {} f:state: {} manager: vc-controller-manager operation: Update time: "2022-05-18T12:59:39Z" name: test resourceVersion: "850474" selfLink: /apis/scheduling.volcano.sh/v1beta1/queues/test uid: b9c9ee54-5ef8-4784-9bec-7a665acb1fde spec: capability: cpu: 2 reclaimable: false weight: 1 status: running: 1 state: Open

二、Kubernetes 运行作业的问题分析及解决方案

① 报错 cannot allocate memory 或者 no space left on device,修复 Kubernetes 内存泄露问题

当 Kubernetes 集群运行日久以后,有的 Node 无法再新建 Pod,并且出现如下错误,当重启服务器之后,才可以恢复正常使用,查看 Pod 状态的时候会出现以下报错:

applying cgroup … caused: mkdir …no space left on device 或者在 describe pod 的时候出现 cannot allocate memory

这时候 Kubernetes 集群可能就存在内存泄露的问题,当创建的 Pod 越多的时候内存会泄露的越多越快。具体查看是否存在内存泄露:

cat /sys/fs/cgroup/memory/kubepods/memory.kmem.slabinfo 当出现 cat: /sys/fs/cgroup/memory/kubepods/memory.kmem.slabinfo: Input/output error 则说明不存在内存泄露的情况 如果存在内存泄露会出现 slabinfo - version: 2.1 # name : tunables : slabdata

可以考虑关闭 runc 和 kubelet 的 kmem,因为升级内核的方案改动较大,此处不采用。

kmem 导致内存泄露的原因:内核对于每个 cgroup 子系统的的条目数是有限制的,限制的大小定义在 kernel/cgroup.c #L139,当正常在 cgroup 创建一个 group 的目录时,条目数就加 1。

我们遇到的情况就是因为开启 kmem accounting 功能,虽然 cgroup 的目录删除,但是条目没有回收,这样后面就无法创建 65535 个 cgroup。也就是说,在当前内核版本下,开启 kmem accounting 功能,会导致 memory cgroup 的条目泄漏无法回收。

需要重新编译 runc:

配置 go 语言环境:

wget https://dl.google.com/go/go1.12.9.linux-amd64.tar.gz tar xf go1.12.9.linux-amd64.tar.gz -C /usr/local/ 写入 bashrc vim ~/.bashrc export GOPATH="/data/Documents" export GOROOT="/usr/local/go" export PATH="$GOROOT/bin:$GOPATH/bin:$PATH" export GO111MODULE=off 验证 source ~/.bashrc go env

下载 runc 源码:

mkdir -p /data/Documents/src/github.com/opencontainers/ cd /data/Documents/src/github.com/opencontainers/ git clone https://github.com/opencontainers/runc cd runc/ git checkout v1.0.0-rc9 # 切到 v1.0.0-rc9 tag

编译:

安装编译组件 sudo yum install libseccomp-devel make BUILDTAGS='seccomp nokmem' 编译完成之后会在当前目录下看到一个runc的可执行文件,等kubelet编译完成之后会将其替换

编译 kubelet:

下载 Kubernetes 源码:

mkdir -p /root/k8s/ cd /root/k8s/ git clone https://github.com/kubernetes/kubernetes cd kubernetes/ git checkout v1.15.3

制作编译环境的镜像 Dockerfile 如下:

FROM centos:centos7.3.1611 ENV GOROOT /usr/local/go ENV GOPATH /usr/local/gopath ENV PATH /usr/local/go/bin:$PATH RUN yum install rpm-build which where rsync gcc gcc-c++ automake autoconf libtool make -y \ && curl -L https://studygolang.com/dl/golang/go1.12.9.linux-amd64.tar.gz | tar zxvf - -C /usr/local

在制作好的 go 环境镜像中来进行编译 kubelet:

docker run -it --rm -v /root/k8s/kubernetes:/usr/local/gopath/src/k8s.io/kubernetes build-k8s:centos-7.3-go-1.12.9-k8s-1.15.3 bash cd /usr/local/gopath/src/k8s.io/kubernetes # 编译 GO111MODULE=off KUBE_GIT_TREE_STATE=clean KUBE_GIT_VERSION=v1.15.3 make kubelet GOFLAGS="-tags=nokmem"

替换原有的 runc 和 kubelet:

将原有 runc 和 kubelet 备份:

mv /usr/bin/kubelet /home/kubelet mv /usr/bin/docker-runc /home/docker-runc

停止 Docker 和 kubelet:

systemctl stop docker systemctl stop kubelet

将编译好的 runc 和 kubelet 进行替换:

cp kubelet /usr/bin/kubelet cp kubelet /usr/local/bin/kubelet cp runc /usr/bin/docker-runc

检查 kmem 是否关闭前需要将此节点的 Pod 杀掉重启或者重启服务器,当结果为 0 时成功:

cat /sys/fs/cgroup/memory/kubepods/burstable/memory.kmem.usage_in_bytes

是否还存在内存泄露的情况:

cat /sys/fs/cgroup/memory/kubepods/memory.kmem.slabinfo

② Kubernetes 证书过期问题的解决

出现 Kubernetes API 无法调取的现象,使用 kubectl 命令获取资源均返回如下报错:

Unable to connect to the server: x509: certificate has expired or is not yet valid

可以猜测 Kubernetes 集群的证书过期,使用命令排查证书的过期时间:

kubeadm alpha certs check-expiration

由于使用 kubeadm 部署的 Kubernetes 集群,所以更新起证书也是比较方便的,默认的证书时间有效期是一年,集群的 Kubernetes 版本是 1.15.3版本是可以使用以下命令来更新证书的,但是一年之后还是会到期,这样就很麻烦,所以需要了解一下 Kubernetes 的证书,然后来生成一个时间很长的证书,这样就可以不用去总更新证书:

kubeadm alpha certs renew all --config=kubeadm.yaml systemctl restart kubelet kubeadm init phase kubeconfig all --config kubeadm.yaml 然后将生成的配置文件替换,重启 kube-apiserver、kube-controller、kube-scheduler、etcd 这 4 个容器即可

华为云原生Kubernetes之运行Volcano高性能作业的深度使用和实践【与云原生的故事】

另外 kubeadm 会在控制面板升级的时候自动更新所有证书,所以使用 kubeadm 搭建得集群最佳的做法是经常升级集群,这样可以确保集群保持最新状态并保持合理的安全性。但是对于实际的生产环境我们可能并不会去频繁得升级集群,所以这个时候就需要去手动更新证书:

首先在/etc/kubernetes/manifests/kube-controller-manager.yaml文件加入配置 spec: containers: - command: - kube-controller-manager # 设置证书有效期为10年 - --experimental-cluster-signing-duration=87600h - --client-ca-file=/etc/kubernetes/pki/ca.crt

修改完成后 kube-controller-manager 会自动重启生效,然后需要使用下面的命令为 Kubernetes 证书 API 创建一个证书签名请求,如果设置例如 cert-manager 等外部签名者,则会自动批准证书签名请求(CSRs)。否者,必须使用 kubectl certificate 命令手动批准证书,以下 kubeadm 命令输出要批准的证书名称,然后等待批准发生。如下所示,通过调用 Kubernetes 的 API 来实现更新一个 10 年的证书:

kubeadm alpha certs renew all --use-api --config kubeadm.yaml &

需要将全部 pending 的证书全部批准,还不能直接重启控制面板的几个组件,这是因为使用 kubeadm 安装的集群对应的 etcd 默认是使用的 /etc/kubernetes/pki/etcd/ca.crt 这个证书进行前面的,而上面用命令 kubectl certificate approve 批准过后的证书是使用的默认的 /etc/kubernetes/pki/ca.crt 证书进行签发的,因此需要替换 etcd 中的 CA 机构证书:

# 先拷贝静态Pod资源清单 cp -r /etc/kubernetes/manifests/ /etc/kubernetes/manifests.bak vi /etc/kubernetes/manifests/etcd.yaml ...... spec: containers: - command: - etcd # 修改为CA文件 - --peer-trusted-ca-file=/etc/kubernetes/pki/ca.crt - --trusted-ca-file=/etc/kubernetes/pki/ca.crt ...... volumeMounts: - mountPath: /var/lib/etcd name: etcd-data - mountPath: /etc/kubernetes/pki # 更改证书目录 name: etcd-certs volumes: - hostPath: path: /etc/kubernetes/pki # 将 pki 目录挂载到etcd中去 type: DirectoryOrCreate name: etcd-certs - hostPath: path: /var/lib/etcd type: DirectoryOrCreate name: etcd-data ......

由于 kube-apiserver 要连接 etcd 集群,因此也需要重新修改对应的 etcd ca 文件:

vi /etc/kubernetes/manifests/kube-apiserver.yaml ...... spec: containers: - command: - kube-apiserver # 将etcd ca文件修改为默认的ca.crt文件 - --etcd-cafile=/etc/kubernetes/pki/ca.crt ......

除此之外还需要替换 requestheader-client-ca-file 文件,默认是 /etc/kubernetes/pki/front-proxy-ca.crt 文件,现在也需要替换成默认的 CA 文件,否则使用聚合 API,比如安装 metrics-server 后执行 kubectl top 命令就会报错:

cp /etc/kubernetes/pki/ca.crt /etc/kubernetes/pki/front-proxy-ca.crt cp /etc/kubernetes/pki/ca.key /etc/kubernetes/pki/front-proxy-ca.key

这样就得到了一个 10 年证书的 Kubernetes 集群,还可以通过重新编译 kubeadm 来实现一个 10 年证书。

三、Volcano 容器在气象行业 HPC 高性能计算场景的应用

① 什么是 HPC ?

HPC 是 High Performance Computing(高性能计算)的缩写,平时提到的 HPC,一般指代高性能计算机群(HPCC),它将大量的计算机软件/硬件整合起来,将大的计算作业分解成一个个小部分,通过并行计算的方式加以解决。HPC 高性能计算在 CAE 仿真、动漫渲染、物理化学、石油勘探、生命科学、气象环境等领域有广泛的应用。

一般来说,高性能计算集群(HPCC)包含如下部分:

PBS:Protable Batch System,资源管理器,负责管理集群中所有节点的资源,除了 PBS 意外,常用的资源管理系统还有 Slurm,LSF 等;

Maui:第三方任务调度器,支持资源预留,支持各种复杂的优先级策略,支持抢占机制等,资源管理器中内置了默认的任务调取器,但功能往往比较简单;

OpenMPI:上层通信环境,兼顾通信库,编译,分布式启动任务的功能。

PBS 和 Maui 对于用户来说是完全透明的,用户只需要按照 PBS 提供的方式提交作业即可,不需要了解内部细节,而 OpenMPI 则需要用户进行相关了解,来编写能够并行计算的应用。

以 mpirun -np 4 ./mpi_hello_world 为例介绍 mpi 作业是如何运行的:

说明:

调用 openmpi 或者其他 mpi 的库来编写源代码,示例就是输出 hello world 字符串;

使用支持 MPI 的编译器来编译出可执行程序 mpi_hello_world;

将 mpi_hello_world 分发到各个节点,也可以通过共享文件系统来实现对 mpi_hello_world 的访问;

运行 mpirun 来并行执行 mpi_hello_world。

② 什么是 WRF ?

WRF 是 Weather Research and Forecasting Model(天气研究和预报模型)的简称,是一种比较常见的 HPC 应用,WRF 是一种中尺度数值天气预报系统,设计用于大气研究和业务预报应用,可以根据实际的大气条件或理想化的条件进行模拟。

由于 WRF 包含多个模块,因此处理流程可能不尽相同,这里仅以 WPS 和 WRF 这两个模块为例介绍一下完整的 WRF 流程:

外部数据源:包含静态地理数据,网络数据等,静态地理数据可以理解为某区域内的地理信息,例如山川,河流,湖泊,森林等等。网络数据是某区域内的气象环境数据,例如气温,风速风向,空气湿度,降雨量等。

前处理系统 WRF Pre-processing System:前处理系统用于载入地理和气象数据,对气象数据进行插值,为 WRF 提供输入数据,该部分包含 3 个程序:

geogrid.exe:定义模型投影、区域范围,嵌套关系,对地表参数进行插值,处理地形资料和网格数据;

ungrib.exe:从 grib 数据中提取所需要的气象参数;

metgrid.exe:将气象参数插值到模拟区域。

核心模拟系统(WRF):核心模拟系统对前处理系统生成的气象信息进行模拟和预报,是 WRF 的核心模块,该部分包含 2 个程序:

real.exe:初始化实际气象数据;

wrf.exe:模拟及预报结果;

real.exe 和 wrf.exe 可以通过 mpi 并行运算来提升计算速度,如下所示,wrfinput_d0X 和 wrfbdy_d0X 为 real.exe 的运算结果,wrf.exe 以该结果为输入进行模拟演算,生成最终的气象模拟结果 wrfout_dxx_yyyy-mm-dd_hh:mm:ss,并由后处理系统进行验证展示:

后处理系统用来验证和显示核心模拟系统的计算结果,主要由各种第三方图像和验证工具组成。如下所示展示 Conus 2.5km 算例中各个地区相对湿度的模拟预报结果,Conus 2.5km 是指美国本土气象数据,分辨率为 2.5km(将整个区域分成一个个 2.5km2.5km2.5km 的方格,每个方格中的气象信息被认为是完全一致的):

③ HPC Volcano

如下所示,一个 HPCC 包括资源管理器,调度器和 mpi 并行计算库三部分,其中资源管理器由 Kubernetes 负责,调度器由 Volcano 负责:

在 Kubernetes+Volcano 环境中运行 HPC 应用,本质上就是在容器中运行 HPC 作业,示意图如下:

将运行的容器分为 Master 容器和 Worker 容器两种,Master 容器负责启动 mpirun/mpiexec 命令,Worker 容器负责运行真正的计算作业。因此 Volcano 为了支持 MPI 作业运行,添加了如下功能:

Volcano job 支持定义多个 pod 模板,能够同时定义 master pod 和 worker pod;

支持 Gang scheduling,保证作业中所有的 pod 能够同时启动;

Master/Worker pod 内部主机 IP 映射;

Master/Workerpod 之间 ssh 免密登录;

作业生命周期管理。

Volcano mpi 作业配置 mpi_sample.yaml:

apiVersion: batch.Volcano.sh/v1alpha1 kind: Job metadata: name: mpi-job labels: # 根据业务需要设置作业类型 "Volcano.sh/job-type": "MPI" spec: # 设置最小需要的服务 (小于总replicas数) # 这里等于mpimaster和mpiworker的总数 minAvailable: 3 # 指定调度器为Volcano schedulerName: Volcano plugins: # 提供 ssh 免密认证 ssh: [] # 提供运行作业所需要的网络信息,hosts文件,headless service等 svc: [] # 如果有pod被 杀死,重启整个作业 policies: - event: PodEvicted action: RestartJob tasks: - replicas: 1 name: mpimaster # 当 mpiexec 结束,认为整个mpi作业结束 policies: - event: TaskCompleted action: CompleteJob template: spec: # Volcano的信息会统一放到 /etc/Volcano 目录下 containers: # master容器中 # 1. 启动sshd服务 # 2. 通过/etc/Volcano/mpiworker.host获取mpiworker容器列表 # 3. 运行mpirun/mpiexec - command: - /bin/sh - -c - | MPI_HOST=`cat /etc/Volcano/mpiworker.host | tr "\n" ","`; mkdir -p /var/run/sshd; /usr/sbin/sshd; mpiexec --allow-run-as-root --host ${MPI_HOST} -np 2 mpi_hello_world; image: Volcanosh/example-mpi:0.0.1 imagePullPolicy: IfNotPresent name: mpimaster ports: - containerPort: 22 name: mpijob-port workingDir: /home resources: requests: cpu: "100m" memory: "1024Mi" limits: cpu: "100m" memory: "1024Mi" restartPolicy: OnFailure imagePullSecrets: - name: default-secret - replicas: 2 name: mpiworker template: spec: containers: # worker容器中只需要启动sshd服务 - command: - /bin/sh - -c - | mkdir -p /var/run/sshd; /usr/sbin/sshd -D; image: Volcanosh/example-mpi:0.0.1 imagePullPolicy: IfNotPresent name: mpiworker ports: - containerPort: 22 name: mpijob-port workingDir: /home resources: requests: cpu: "100m" memory: "2048Mi" limits: cpu: "100m" memory: "2048Mi" restartPolicy: OnFailure imagePullSecrets: - name: default-secret

提交 mpi Volcano job:

作业执行完毕:

查看 master pod 的结果:

通过上述执行结果可以看出,在作业执行结束后,Volcano 只清理 worker pod,保留 master pod,这样用户 kubectl 命令获取执行结果。

此外,由于网络构建可能会出现延迟,在作业运行开始时,master pod 会出现连接 worker pod 失败的情况。对于这种情况,Volcano 会自动重启 master pod,保证作业能够正确运行。

通过以上示例,可以看出 Volcano 想要运行 WRF 作业的话,理论上需要将其中的 mpi_hello_world 替换为 real.exe/wrf.exe,此外,用户还需要进行如下准备:

自建 docker images,包含完整的 WRF 运行环境;

将计算所需要的数据(原生数据或者中间结果数据)挂载到相应的容器中。

这样就能在 Kubernetes+Volcano 上运行气象模拟作业。

四、附录

本文正在参与【与云原生的故事】有奖征文火热进行中:https://bbs.huaweicloud.com/blogs/345260 。

Kubernetes Volcano 云原生 云端实践

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:【论文翻译】DeepWalk: Online Learning of Social Representations(论文翻译网)
下一篇:云原生之下,视频云业务的10大实践经验分享(谈谈使用云服务的经验)
相关文章