python包之drmaa:集群任务管理
搭建流程时,我们把各个模块脚本都写好了,现在通过编写主程序将模块串起来,那么怎么样依次(或者并行)将任务自动投递到集群呢?就是说这一步运行完之后,下一步自动运行。我们当然可以在脚本中设一个标志,反复检查这一个标志是否出现来决定是否运行下一步,但这种方法太原始,太多弊端了,耗内存,无法并行,且不可预料的出错。那么,有没有相应的工具来管理集群任务投递?有,python的drmaa包可以实现。
1. drmaa简介
Distributed Resource Management Application API (DRMAA),即分布式资源管理应用程序API,是一种高级 开放网格论坛(Open_Grid_Forum)应用程序接口规范,用于向分布式资源管理(DRM)系统(例如集群或网格计算提交和控制作业)。API的范围涵盖了应用程序提交,控制和监视DRM系统中执行资源上的作业所需的所有高级功能。DRMAA API已在Sun的Grid Engine(SGE)和Condor等作业管理调度系统中实现。关于SGE可参考我的推文:集群SGE作业调度系统
C、C++、Perl、Python等程序语言都开发有相应的drmaa包来实现SGE集群的任务管理。这里记录下drmaa-python:
Github:drmaa-python
PyPi:https://pypi.org/project/drmaa/
2. 安装和配置
要求:Python2.7+;与DRMAA兼容的集群,如SGE。
#安装
pip install drmaa
#设置路径
export SGE_ROOT=/path/to/gridengine #SGE安装的路径
export SGE_CELL=default
#设置库
export DRMAA_LIBRARY_PATH=/usr/lib/libdrmaa.so.1.0
#libdrmaa.so.1.0 C动态库,是libdrmaa-dev包的一部分
3. 示例
3.1 开始和终止会话
Session
#!/usr/bin/env python
import drmaa
def main():
"""Create a drmaa session and exit"""
with drmaa.Session() as s: #自动初始化,组织工作提交
print('A session was started successfully')
#with结束自动exit(),大部分函数都要在exit()前执行,如runJob/wait,getContact可在exit()后。
if __name__=='__main__':
main()
使用可重新连接的会话,可以将DRMAA库初始化为上一个会话,从而允许该库访问该会话的作业列表.
#!/usr/bin/env python
import drmaa
def main():
"""
Create a session, show that each session has an ID, use session ID to
disconnect, then reconnect. Finally, exit.
"""
s = drmaa.Session()
s.initialize()
print('A session was started successfully')
response = s.contact
print('session contact returns: %s' % response)
s.exit()
print('Exited from session')
s.initialize(response) #初始化上个session
print('Session was restarted successfullly')
s.exit()
if __name__=='__main__':
main()
3.2 运行工作
假设已知当前目录有一个sleeper.sh
脚本,后接两个参数:
#!/bin/bash
echo "Hello world, the answer is $1"
sleep 3s
echo "$2 Bye world!"
drmaa将sleeper.sh提交到SGE:
#!/usr/bin/env python
import drmaa
import os
def main():
"""
Submit a job.
Note, need file called sleeper.sh in current directory.
"""
with drmaa.Session() as s:
print('Creating job template')
jt = s.createJobTemplate() #分配工作模板(存储提交作业的信息结构)
jt.remoteCommand = os.path.join(os.getcwd(), 'sleeper.sh') #设置remoteCommand属性,找到要运行的程序。
#路径默认为用户的主目录,相对路径用workingDirectory属性
jt.args = ['42', 'Simon says:'] #执行文件的参数
jt.joinFiles=True
jobid = s.runJob(jt) #将分配给作业的ID放入我们传递给的字符数组中runJob()
print('Your job has been submitted with ID %s' % jobid)
# jobid = s.runBulkJobs(jt, 1, 30, 2) #提交一个数组作业
# print('Your jobs have been submitted with IDs %s' % jobid)
print('Cleaning up')
s.deleteJobTemplate(jt) #删除作业模板,释放作业模板保留的DRMAA内存,但对提交的作业没有影响
if __name__=='__main__':
main()
3.3 等待工作
即等待任务完成
#!/usr/bin/env python
import drmaa
import os
def main():
"""
Submit a job and wait for it to finish.
Note, need file called sleeper.sh in home directory.
"""
with drmaa.Session() as s:
print('Creating job template')
jt = s.createJobTemplate()
jt.remoteCommand = os.path.join(os.getcwd(), 'sleeper.sh')
jt.args = ['42', 'Simon says:']
jt.joinFiles = True
jobid = s.runJob(jt)
print('Your job has been submitted with ID %s' % jobid)
retval = s.wait(jobid, drmaa.Session.TIMEOUT_WAIT_FOREVER) #调用wait()等待作业结束
print('Job: {0} finished with status {1}'.format(retval.jobId, retval.hasExited))
#以下是提交多个作业的等待处理,synchronize替代wait
#joblist = s.runBulkJobs(jt, 1, 30, 2)
#print('Your jobs have been submitted with IDs %s' % joblist)
#s.synchronize(joblist, drmaa.Session.TIMEOUT_WAIT_FOREVER, True)
print('Cleaning up')
s.deleteJobTemplate(jt)
if __name__=='__main__':
main()
wait()返回一个JobInfo元组,其具有下面的属性: jobId,hasExited,hasSignal,terminatedSignal,hasCoreDump, wasAborted,exitStatus,resourceUsage
synchronize()的第3个参数是该synchronize()的调用是否在工作后清除。工作完成后,它会留下一些统计信息,如退出状态和用途,直到wait() 或synchronize()的处理状态变为True。确保每一项任务对这两个函数之一调用是很有必要的,否则可能引起内存泄漏。如果想要每一项任务恢复统计信息,可将synchronize()设置False。如下:
joblist = s.runBulkJobs(jt, 1, 30, 2)
print('Your jobs have been submitted with IDs %s' % joblist)
s.synchronize(joblist, drmaa.Session.TIMEOUT_WAIT_FOREVER, False) #False,每一项工作等待一次
for curjob in joblist:
print('Collecting job ' + curjob)
retval = s.wait(curjob, drmaa.Session.TIMEOUT_WAIT_FOREVER)
print('Job: {0} finished with status {1}'.format(retval.jobId,retval.hasExited))
3.4 控制工作
#!/usr/bin/env python
import drmaa
import os
def main():
"""Submit a job, then kill it.
Note, need file called sleeper.sh in home directory.
"""
with drmaa.Session() as s:
print('Creating job template')
jt = s.createJobTemplate()
jt.remoteCommand = os.path.join(os.getcwd(), 'sleeper.sh')
jt.args = ['42', 'Simon says:']
jt.joinFiles = True
jobid = s.runJob(jt)
print('Your job has been submitted with ID %s' % jobid)
# options are: SUSPEND, RESUME, HOLD, RELEASE, TERMINATE
s.control(jobid, drmaa.JobControlAction.TERMINATE) #删除刚提交的作业
print('Cleaning up')
s.deleteJobTemplate(jt)
if __name__=='__main__':
main()
还可以用control()来暂停,恢复,保留或释放工作。control()还可用于控制未通过DRMAA提交的作业,可以将任何有效的SGE作业ID传递control()为要删除的作业ID。
3.5 查询工作状态
#!/usr/bin/env python
import drmaa
import time
import os
def main():
"""
Submit a job, and check its progress.
Note, need file called sleeper.sh in home directory.
"""
with drmaa.Session() as s:
print('Creating job template')
jt = s.createJobTemplate()
jt.remoteCommand = os.path.join(os.getcwd(), 'sleeper.sh')
jt.args = ['42', 'Simon says:']
jt.joinFiles=True
jobid = s.runJob(jt)
print('Your job has been submitted with ID %s' % jobid)
# Who needs a case statement when you have dictionaries?
decodestatus = {drmaa.JobState.UNDETERMINED: 'process status cannot be determined',
drmaa.JobState.QUEUED_ACTIVE: 'job is queued and active',
drmaa.JobState.SYSTEM_ON_HOLD: 'job is queued and in system hold',
drmaa.JobState.USER_ON_HOLD: 'job is queued and in user hold',
drmaa.JobState.USER_SYSTEM_ON_HOLD: 'job is queued and in user and system hold',
drmaa.JobState.RUNNING: 'job is running',
drmaa.JobState.SYSTEM_SUSPENDED: 'job is system suspended',
drmaa.JobState.USER_SUSPENDED: 'job is user suspended',
drmaa.JobState.DONE: 'job finished normally',
drmaa.JobState.FAILED: 'job finished, but failed'}
for ix in range(10):
print('Checking %s of 10 times' % ix)
print decodestatus(s.jobStatus(jobid)) #jobStatus()获取作业的状态
time.sleep(5)
print('Cleaning up')
s.deleteJobTemplate(jt)
if __name__=='__main__':
main() #确定工作状态并报告
其他更多关于JobInfo,JobTemplate,Session等方法的属性可参考:https://drmaa-python.readthedocs.io/en/latest/drmaa.html
4. 应用
4.1 写一个简单应用
#!/usr/bin/env python
import drmaa
import os
class SGE():
def __init__(self):
self.__sgeProject="Test"
self.__sgeQueue="test.q"
self.__maxvmen="1G"
self.__proc="1"
self.__script=""
self.__workdir=""
self.__session=""
def setSgeProject(self, p):
self.__sgeProject=p
def getSgeProject(self):
return self.__sgeProject
def setSgeQueue(self, q):
self.__sgeQueue=q
def getSgeQueue(self):
return self.__sgeQueue
def setMaxvmem(self, m):
self.__maxvmem=m
def setNumproc(self, proc):
self.__proc=proc
def getMaxvmem(self):
return self.__maxvmem
def setScript(self, s):
self.__script=s
def getScript(self):
return self.__script
def setWorkDir(self, w):
self.__workdir=w
def getWorkDir(self):
return self.__workdir
def setSession(self, ss):
self.__session=ss
def getSession(self):
return self.__session
def submit(self):
st=os.stat(self.__script) #系统 stat 的调用,返回stat结构
os.chmod(self.__script, st.st_mode | stat.S_IEXEC | stat.S_IXGRP) #S_IEXEC是S_IXUSR同义词,所有者具有执行权限;S_IXGRP,组具有执行权限
jt = self.__session.createJobTemplate() ##分配工作模板
jt.remoteCommand = self.__script #remoteCommand属性找到要执行的脚本
jt.workingDirectory = self.__workdir #设定当前工作目录
par4qsub="".join(["-binding linear:",self.__proc," -P ",self.__sgeProject," -q ",self.__sgeQueue," -cwd -l ","vf=",self.__maxvmem," -l p=",self.__proc])
print('qsub {0} {1}'.format(par4qsub,self.__script))
jt.nativeSpecification = par4qsub #传递给jt的指令
jobid =self.__session.runJob(jt) #将分配给作业的ID传递给的字符数组
self.__session.deleteJobTemplate(jt)
return jobid
def main():
with drmaa.Session() as s:
sgeObj = SGE()
sgeObj.setSession(session)
sgeObj.setSgeProject("SGEProject")
sgeObj.setSgeQueue("SGEQueue")
dict_qsub_id={}
joblist=[]
cwdir=os.path.join(getcwd())
sgeObj.setWorkDir(cwdir)
sgeObj.setScript(os.path.join(cwdir,"test.sh"))
sgeObj.setMaxvmem("Memory")
sgeObj.setNumproc("1")
jobid=sgeObj.submit()
dict_qsub_id[jobid]=os.path.join(cwdir,"test.sh")
joblist.append(jobid)
s.synchronize(joblist, drmaa.Session.TIMEOUT_WAIT_FOREVER, False) #设为false
for curjob in joblist:
retval = session.wait(curjob, drmaa.Session.TIMEOUT_WAIT_FOREVER)
print('Job: {0} finished with status {1}'.format(retval.jobId,retval.hasExited))
if __name__=="__main__":
main()
4.2 应用示例2
说明:用MEGAN做微生物物种注释时,blast nr得到的结果太多,一次性注释太久,因此将其拆分开来。Linux环境中使用MEGAN注释需要调用xvfb-run(相当于一个wrapper, 给应用程序提供虚拟的 X server),但xvfb不能并行,当我同时运行多个注释时,MEGAN生成的临时文件rma会发生冲突,因而无法同时得到注释结果。不能并行就只能串行,但我拆分了上百份文件,不可能手动一个个投递,如何一个个任务依次运行呢?可以用drmaa写个循环。
#继承上面的SGE类
def check_status(retval,running_log,path,email):
if(retval.exitStatus != 0): #出错的要发邮件通知
running_log.write('{0}\nError job: {1}\n exitStatus: {2}\n wasAborted: {3}\n maxvmem: {4}Gb\n Qsub_id: {5}\n\n'.format("="*40, path, retval.exitStatus, retval.wasAborted, str(float(retval.resourceUsage['maxvmem'])/1000000000), retval.jobId ))
running_log.close()
emailObj = Email()
emailObj.setReceiver(email)
emailObj.sendMail('<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body><p>Error job: {0}</p><p> exitStatus: {1}</p><p>wasAborted: {2}</p><p>maxvmem: {3}</p></body></html>'.format(path, retval.exitStatus, retval.wasAborted, str(float(retval.resourceUsage['maxvmem'])/1000000000) ))
os._exit(0)
elif(retval.wasAborted == True): #手工终止的不需要发邮件通知
running_log.write('{0}\nAborted job: {1}\n exitStatus: {2}\n wasAborted: {3}\n maxvmem: {4}Gb\n Qsub_id: {5}\n\n'.format("="*40, path, retval.exitStatus, retval.wasAborted, str(float(retval.resourceUsage['maxvmem'])/1000000000) , retval.jobId))
running_log.close()
os._exit(0)
else:
running_log.write('{0}\nFinished job: {1}\n exitStatus: {2}\n wasAborted: {3}\n maxvmem: {4}Gb\n Qsub_id: {5}\n\n'.format("="*40, path, retval.exitStatus, retval.wasAborted, str(float(retval.resourceUsage['maxvmem'])/1000000000), retval.jobId ))
running_log.flush() #立即写到文件中。
def main():
project_dir=os.getcwd()
running_log=open("running.log", "w")
qsub_id_log=open("qsub_id.log", "w")
dict_qsub_id={}
with drmaa.Session() as session:
sgeObj = Sge()
sgeObj.setSession(session)
for i in range(1,101): #拆分的一百份任务
joblist=[]
subdir="tax_"+str(i)
cwdir=os.path.join(project_dir,subdir)
sgeObj.setWorkDir(cwdir)
shell="run_tax_"+str(i)+".sh"
sgeObj.setScript(os.path.join(cwdir,shell))
sgeObj.setMaxvmem("1G")
sgeObj.setNumproc("1")
jobid=sgeObj.submit()
qsub_id_log.write(jobid+"\n")
qsub_id_log.flush()
dict_qsub_id[jobid]=os.path.join(cwdir,shell)
joblist.append(jobid)
session.synchronize(joblist, drmaa.Session.TIMEOUT_WAIT_FOREVER, False)
for curjob in joblist:
retval = session.wait(curjob, drmaa.Session.TIMEOUT_WAIT_FOREVER)
check_status(retval,running_log,dict_qsub_id[retval.jobId],"123456@qq.com")
emailObj = Email()
emailObj.setReceiver("123456@qq.com")
emailObj.sendMail('<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body><p>Finished job: {0}</p></body></html>'.format(project_dir))
print ("{0} All work is done! {0}>".format("="*30))
running_log.close()
qsub_id_log.close()
if __name__=="__main__":
main()
Ref:https://drmaa-python.readthedocs.io/en/latest/tutorials.html#starting-and-stopping-a-session
python包之drmaa:集群任务管理的更多相关文章
- python连接redis哨兵集群
一.redis集群模式有多种, 哨兵模式只是其中的一种实现方式, 其原理请自行谷歌或者百度 二.python 连接 redis 哨兵集群 1. 安装redis包 pip install redis 2 ...
- 二进制包部署Kubernetes集群
今天这篇文章教给大家如何快速部署一套Kubernetes集群.K8S集群部署有几种方式:kubeadm.minikube和二进制包.前两者属于自动部署,简化部署操作,我们这里强烈推荐初学者使用二进制包 ...
- python 连接 redis cluster 集群
一. redis集群模式有多种, cluster模式只是其中的一种实现方式, 其原理请自行谷歌或者百度, 这里只举例如何使用Python操作 redis cluster 集群 二. python 连接 ...
- 基于Python+Django的Kubernetes集群管理平台
➠更多技术干货请戳:听云博客 时至今日,接触kubernetes也有一段时间了,而我们的大部分业务也已经稳定地运行在不同规模的kubernetes集群上,不得不说,无论是从应用部署.迭代,还是从资源调 ...
- 公司jar包提交到集群的方法
yarn -jar xx.jar 此时包会提交到集群上运行 也可以把jar包放到hbase 的lib下面用hbase jar 方式调用
- python连接redis sentinel集群
安装 python redis 客户端 pip install redis #!/usr/bin/env python # -*- coding:utf-8 -*- #!/usr/bin/env py ...
- Python scan查找Redis集群中的key
import redis import sys from rediscluster import StrictRedisCluster #host = "172.17.155.118&quo ...
- Linux Redis集群搭建与集群客户端实现(Python)
硬件环境 本文适用的硬件环境如下 Linux版本:CentOS release 6.7 (Final) Redis版本: Redis已经成功安装,安装路径为/home/idata/yangfan/lo ...
- 有关python numpy pandas scipy 等 能在YARN集群上 运行PySpark
有关这个问题,似乎这个在某些时候,用python写好,且spark没有响应的算法支持, 能否能在YARN集群上 运行PySpark方式, 将python分析程序提交上去? Spark Applicat ...
随机推荐
- poi实现生成下拉选联动
在我们实际的程序开发中,经常需要用到从excel导入数据中系统中,而为了防止用户在excel中乱输入文字,有些需要用到下拉选的地方,就需要从程序中动态生成模板.本例子简单的讲解一下,如何生成级联下拉选 ...
- 连续子序列的最大和 牛客网 剑指Offer
连续子序列的最大和 牛客网 剑指Offer 题目描述 HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学.今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量 ...
- nod_1009 数字1的数量(分析题)
题意: 给定一个十进制正整数N,写下从1开始,到N的所有正数,计算出其中出现所有1的个数. 例如:n = 12,包含了5个1.1,10,12共包含3个1,11包含2个1,总共5个1. Input 输入 ...
- java随手记 基础
import java.util.Scanner; //Scanner is in this package 明确导入 import java.util.*; //通配符导入 两者性能上无区别 pub ...
- xxx.app已损坏无法打开、来自身份不明的开发者解决办法
在 Mac 上安装非 App Store 软件时,可能会遇到一些这样或那样的问题,这篇文章就 Mac 从 .dmg 安装软件时可能遇到的问题提一些解决方法. 状况一:双击 .dmg 安装软件出现以下情 ...
- MySQL:由于找不到VCRUNTIME140_1.dll,无法继续执行代码。重新安装程序可能会解决此问题
我只是搬用工,记录一下 方法一: 安装这个微软常用运行库合集(https://www.repaik.com/), 链接:https://pan.baidu.com/s/1r4JJaUKjw-y1g3l ...
- Oracle四大语言DDL DML DCL TCL
DDL(数据定义语言) creater 创建数据表 ceater table 表名 (); alter 修改表结构 添加字段:alter table 表名 add 列名 数据类型 null 删除字段: ...
- 为什么不直接去Arraylist list = new Arraylist();而是直接通过List list = new ArrayList();使用接口的好处
ArrayList不是继承List接口,是实现了List接口. 你写成ArrayList arrayList = new ArrayList();这样不会有任何问题.和List list = new ...
- Oracle中对数字加汉字的排序
需求:有一列NAME, varchar2类型,内容如下 以上就是已经按order by name进行排序的,但不是我们想要的结果 现在需要只按数字进行排序 第一步:抽取数字由于数字有是一位的有是两位的 ...
- 【JAVA】笔记(6)--- toString方法;equals方法;finalize方法;package与import;内部类;
toString: 1.每创建一个类时,都要重写 toString 方法,这是敲代码的基本素养: 2.重写规则:简单明了: 3.String 类中也有toString方法(SUN公司写的): equa ...