搭建流程时,我们把各个模块脚本都写好了,现在通过编写主程序将模块串起来,那么怎么样依次(或者并行)将任务自动投递到集群呢?就是说这一步运行完之后,下一步自动运行。我们当然可以在脚本中设一个标志,反复检查这一个标志是否出现来决定是否运行下一步,但这种方法太原始,太多弊端了,耗内存,无法并行,且不可预料的出错。那么,有没有相应的工具来管理集群任务投递?有,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:集群任务管理的更多相关文章

  1. python连接redis哨兵集群

    一.redis集群模式有多种, 哨兵模式只是其中的一种实现方式, 其原理请自行谷歌或者百度 二.python 连接 redis 哨兵集群 1. 安装redis包 pip install redis 2 ...

  2. 二进制包部署Kubernetes集群

    今天这篇文章教给大家如何快速部署一套Kubernetes集群.K8S集群部署有几种方式:kubeadm.minikube和二进制包.前两者属于自动部署,简化部署操作,我们这里强烈推荐初学者使用二进制包 ...

  3. python 连接 redis cluster 集群

    一. redis集群模式有多种, cluster模式只是其中的一种实现方式, 其原理请自行谷歌或者百度, 这里只举例如何使用Python操作 redis cluster 集群 二. python 连接 ...

  4. 基于Python+Django的Kubernetes集群管理平台

    ➠更多技术干货请戳:听云博客 时至今日,接触kubernetes也有一段时间了,而我们的大部分业务也已经稳定地运行在不同规模的kubernetes集群上,不得不说,无论是从应用部署.迭代,还是从资源调 ...

  5. 公司jar包提交到集群的方法

    yarn -jar xx.jar 此时包会提交到集群上运行 也可以把jar包放到hbase 的lib下面用hbase jar 方式调用

  6. python连接redis sentinel集群

    安装 python redis 客户端 pip install redis #!/usr/bin/env python # -*- coding:utf-8 -*- #!/usr/bin/env py ...

  7. Python scan查找Redis集群中的key

    import redis import sys from rediscluster import StrictRedisCluster #host = "172.17.155.118&quo ...

  8. Linux Redis集群搭建与集群客户端实现(Python)

    硬件环境 本文适用的硬件环境如下 Linux版本:CentOS release 6.7 (Final) Redis版本: Redis已经成功安装,安装路径为/home/idata/yangfan/lo ...

  9. 有关python numpy pandas scipy 等 能在YARN集群上 运行PySpark

    有关这个问题,似乎这个在某些时候,用python写好,且spark没有响应的算法支持, 能否能在YARN集群上 运行PySpark方式, 将python分析程序提交上去? Spark Applicat ...

随机推荐

  1. Java RMI学习与解读(一)

    Java RMI学习与解读(一) 写在前面 本文记录在心情美丽的一个晚上. 嗯.就是心情很美丽. 那为什么晚上还要学习呢? emm... 卷... 卷起来. 全文基本都是根据su18师傅和其他师傅的文 ...

  2. 第二次Alpha Scrum Meeting

    本次会议为Alpha阶段第二次Scrum Meeting会议 会议概要 会议时间:2021年4月24日 会议地点:线上会议 会议时长:30min 会议内容简介:本次会议主要由每个人展示自己目前完成的工 ...

  3. OO_JAVA_JML系列作业_单元总结

    OO_JAVA_JML系列作业_单元总结 (1)梳理JML语言的理论基础.应用工具链情况 简单梳理 以下三者是jml规格里的核心,对一个方法功能和属性的限制: requires子句:规定方法的前置条件 ...

  4. 【BZOJ 1419】Red is good [概率DP]

    我 是 Z Z 概率好玄啊(好吧是我太弱.jpg Description 桌面上有R张红牌和B张黑牌,随机打乱顺序后放在桌面上,开始一张一张地翻牌,翻到红牌得到1美元,黑牌则付出1美元.可以随时停止翻 ...

  5. Go语言核心36讲(Go语言进阶技术十一)--学习笔记

    17 | go语句及其执行规则(下) 知识扩展 问题 1:怎样才能让主 goroutine 等待其他 goroutine? 我刚才说过,一旦主 goroutine 中的代码执行完毕,当前的 Go 程序 ...

  6. Python matplotlib 概率论与数理统计 伯努利分布 二项分布

    Python 代码实现 二项分布 import numpy as np import matplotlib.pyplot as plt import math from scipy import st ...

  7. vue2强制刷新,解决页面不会重新渲染的问题

    问题描述: 在使用Vue框架开发时,在函数中改变了页面中的某个值,在函数中查看是修改成功了,但在页面中没有及时刷新改变后的值: 解决: 运用 this.$forceUpdate();  //强制刷新, ...

  8. telnet IP 端口 的作用

    测试远程服务器的端口是否开启

  9. shell 脚本二进制安装mysql

    以下脚本的手动安装连接:https://www.cnblogs.com/leihongnu/p/12581793.html [ #/bin/bash#脚本安装 mysql,上传安装包至 /rootcd ...

  10. SSH 提示密码过期,如何通过 ansible 批量更新线上服务器密码

    起因 线上环境是在内网,登陆线上环境需要使用 VPN + 堡垒机 登陆,但是我日常登陆线上环境都是 VPN + 堡垒机 + Socks5常驻代理,在shell端只需要保存会话,会话使用socks5代理 ...