前情概要

在科学计算领域, 早些年的程序语言基本都是C/C++或者FORTRAN的天下, 是因为科学计算本身非常耗时, 选择一门运行速度比较快的语言能大大的节约对数据的计算时间.

今天本文的主角就是一段gfortran编写的祖传代码,gcc编译,运行依赖sundials库,主函数通过一个配置文件注入3个参数,运行旷日持久至少一小时,CPU高占用,最后生成的程序结果是12个结果文件,大约20MB左右,重点是程序三个参数需要改变进行4 * 5 * 6 组合运行,也就是需要120次左右的运算,如果程序支持持续计算,至少也需要5天整,然而程序并不支持连续计算,另外持续运行5天也实在是丧心病狂了,当听到整个事件的来龙去脉的时候我觉得可以拿出docker的思想来解决

当然这段gfortran程序并不重要,重点是如何使用容器的思路去解决这样的场景.实现一个简单分布式运算方案

思路

  1. 拉取一个centos系统镜像,然后在系统中安装程序依赖环境,保证程序单独运行无误

  2. 创建一个master服务,用以生成任务,分发任务,统计任务完成情况

  3. 创建一个worker程序,在容器内部运行,用以访问master服务,获取任务,解析参数,并触发程序运行,并将结果输出到指定文件中,然后返回完成通知给master服务

  4. 构建新的镜像,并推送到阿里云仓库中,使用k8s集群启动多组容器,并挂载映射结果输出文件目录,待任务全部完成后去实例目录中将结果文件下载到本地

    每一个程序在运行前都是配置无关的,它不知道自己要运行具体内容,当容器启动运行获取任务后,它才知道自己要运行的具体内容

动手

系统镜像创建

这次的重点不是代码,而是思路,所以代码少之又少

docker pull centos
docker run -dit --name mycentos centos
docker exec -it mycentos

进入镜像后就开始安装程序依赖环境,总之就是要保证程序首先可以在容器中正常运行计算,如果连这一步都完不成,后续都是无用功

创建master服务

创建一个master服务,用以生成任务,分发任务,统计任务完成情况,可以用java也可以用python,用你最擅长的语言就行,最重要的一个点就是分发任务要保证同步,不可重复分发,以免造成任务重复获取,计算浪费

创建worker程序

创建一个worker程序,在容器内部运行,用以访问master服务,获取任务,解析参数,并触发程序运行,并将结果输出到指定文件中,然后返回完成通知给master服务

这里我的worker程序使用java实现的springboot项目,当worker项目启动后访问master服务,获取具体需要运行的参数,然后修改gfortran程序的配置文件,然后调用Runtime.getRuntime().exec(shell)去运行具体的gfortran命令,最后根据入参将结果复制到指定目录,比如参数分别为1 , 2, 3,程序将结果文件复制到/opt/out/1_2_3/文件中,然后容器挂载/opt/out/到宿主机/home/out/目录下,这样在每一个容器运行完成后结果都会储存在宿主机中,而不会丢失计算结果

简单演示

镜像构建

将worker程序复制到宿主机后,需要将当前的容器打成一个镜像,用以推送到阿里云仓库

#用法
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
#例子
$ docker commit c3f279d17e0a  svendowideit/testimage:version3
f5283438590d
$ docker images
REPOSITORY                        TAG                 ID                  CREATED             SIZE
svendowideit/testimage            version3            f5283438590d        16 seconds ago      335.7 MB

commit 命令文档

官网建议使用Dockerfile构建,不过不适合这次,因为需要构建编译环境,比较麻烦

推送阿里云镜像

登入阿里云控制台

image-20200625195247338

去镜像服务中,首先创建一个命名空间,然后再去镜像仓库中创建一个仓库

image-20200625195519975

然后点击管理镜像可以看到镜像操作指南

image-20200625195844623

我们需要推送镜像,所以只看第三

#将镜像推送到Registry
$ sudo docker login --username=xxxxxx registry.cn-beijing.aliyuncs.com
#用于登录的用户名为阿里云账号全名,密码为开通服务时设置的密码
$ sudo docker tag [ImageId] registry.cn-beijing.aliyuncs.com/xxxx-docker/hub:[镜像版本号]
$ sudo docker push registry.cn-beijing.aliyuncs.com/xxxxxx-docker/hub:[镜像版本号]

image-20200625200333995

到此推送镜像完成

使用K8S集群

到了这里提一句为什么使用阿里云k8s集群,其实主要原因就是没有牛逼的服务器,本文中的案例使用了三台12vcpu24G内存配置的ECS实例,也就是36核72G内存也只能同时跑60个容器,再多一个实例,容器就会被干掉,反复重启

阿里云的K8S服务创建首先先要选3台以上的ECS实例,然后k8s集群会创建在这三台服务上,顺带附加各种服务比如net网关,负载均衡等等,费用综合下来就是ECS的按小时使用费用和然后其他功能的按量收费

image-20200625201523628

这里我们选择标准托管集群

image-20200625201703877

然后勾选虚拟交换机,别的不用管,下一步

image-20200625201823697

然后根据自己的运算场景选择合适的虚拟机,比如这里我选择的就是sn1ne型

点击下一步,创建集群,等待十分钟左右集群大概就会创建完成,

image-20200625232041036

具体的容器部署,请选择应用无状态中的使用镜像创建,然后选择最一开始推送上去的镜像,选择要部署容器数量,点击创建即可,需要注意容器目录挂载

具体的运行状态可以在节点列表中查看

592fb25af2c865d98b03e6dfca5b5bb

性能图表可以在监控查看

77472f372383cf8c0d71210e76a5a5f

最终120组任务在6个小时能计算完成,使用k8s做分布式计算真的很强大

原理思路很简单,考验的是动手能力

欢迎关注公众号<无聊看风景>,获取更多有趣(无聊)的技术文章

img

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐