近一个月来做的工作,主要围绕 m5C 实验进行,要点一是快速根据项目文档配置环境、运行测试,二是配置 nfs、DAOS 文件系统,并在多节点下运行,三是分析运行性能和功耗情况。本文大致记录这段经历,也将踩的坑列出来供自己参考。
一、关于配置环境
本次实践主要的工作,也就是从零开始配置运行项目所需的环境,即根据给出的信息安装所需的多种工具。例如,单机运行 m5C 实验需要的环境主体基于 Snakemake,另外需要 hisat-3n、samtools、UMICollapse、splitbam、fastqsplitter、cutseq 等工具和 OpenJDK 依赖。
其中部分工具可用 conda 或 pip 直接安装(需要注意要从正确的源安装所需的版本,尤其是当不同的源提供同名的包时——本次实验开始时就因不注意而导致配置错误,无法运行),部分工具必须编译安装,又需要根据其所需编译工具链正确选择版本......简单总结,就是按照项目要求去配置,先达成跑通的基本目标。
部分过程实录
首先是配置环境:
conda create -n m5c python=3.11 -y && conda activate m5c # 创建环境
git clone https://gh-proxy.com/github.com/DaehwanKimLab/hisat2.git hisat-3n
cd hisat-3n && git checkout -b hisat-3n origin/hisat-3n && make # 编译 hisat-3n
conda install -c bioconda -c conda-forge samtools=1.21 # 安装 samtools
conda install -c conda-forge -c bioconda snakemake # 安装 snakemake
cd .. && git clone https://github.com/Daniel-Liu-c0deb0t/UMICollapse.git
cd UMICollapse && mkdir lib && cd lib
curl -O -L https://repo1.maven.org/maven2/com/github/samtools/htsjdk/2.19.0/htsjdk-2.19.0.jar
curl -O -L https://repo1.maven.org/maven2/org/xerial/snappy/snappy-java/1.1.7.3/snappy-java-1.1.7.3.jar
cd .. # 配置 UMICollapse
cd splitbam && g++ -o splitbam splitbam.cpp -ICONDA_PREFIX/include -LCONDAPREFIX/include−LCONDA_PREFIX/lib -Wl,-rpath,$CONDA_PREFIX/lib -lhts # 编译 splitbam
conda install -c conda-forge openjdk=11 -y # 安装 OpenJDK 11
pip install polars scipy fastqsplitter cutseq # 安装其他依赖
cd .. && git clone https://github.com/y9c/m5C-UBSseq.git然后在 Snakefile 和 config.yaml 中指定一些关键信息后,
snakemake --cores all
二、关于多节点运行和文件系统配置
利用多节点的并行计算能力运行任务是一个重要的优化方向,对于不同的项目,可能有不同的解决方案,甚至可能需要自行实现相关需求。本次实验中,为尽可能简化配置流程,就直接使用了 GitHub 上的项目 starskyzheng/snakemake_ssh,利用 SSH 免密登录以在多节点上运行。
值得一提的是,这一项目似乎并不完善,仅按照 README 配置并未能成功运行,在 GitHub Copilot 等大模型的帮助下阅读、完善代码后才最终正常启动。
另外,要在多节点环境下运行,就需要支持的文件系统作为支撑,实现数据共享访问;本次实验先配置了 nfs 这一常用网络文件系统。关于其配置,网络上的资料非常多,其中一个重要的点是设置用户映射(配置 /etc/exports 文件),以避免出现文件权限问题。
部分过程实录
(登录节点:192.168.8.A ;目标节点:192.168.8.B)
首先,为了实现 ssh 免密登录方便后续操作,在登录节点生成 ssh key 并将公钥复制到目标节点:
ssh-keygen -t rsa
ssh-copy-id user@192.168.8.B实际节点上均已安装 nfs-utils,因此只需创建目录并改动配置文件。在目标节点上:
mkdir /home/user/nfs_snake
chmod 755 /home/user/nfs_snake
sudo vim /etc/exports向其中添加一行:
/home/user/nfs_snake 192.168.8.A(rw,sync,all_squash,no_subtree_check,anonuid=(),anongid=())然后重载配置并重启服务:
sudo exportfs -ra
sudo systemctl restart nfs-server回到登录节点,创建挂载点并挂载:
mkdir /home/user/nfs_snake
sudo mount -t nfs 192.168.8.B:/home/user/nfs_snake /home/user/nfs_snake在目标节点上也配置好环境。然后,在登录节点上安装 snakemake_ssh 以实现多节点运行:
git clone https://github.com/StarSkyZheng/snakemake_ssh.git ~/.config/snakemake/ssh
pip install snakemake-executor-plugin-cluster-generic
vim ~/.config/snakemake/ssh/server.py.configs.yaml.default并创建 Profile config:
cat > ~/.config/snakemake/ssh/config.yaml << 'EOF'
executor: cluster-generic
cluster-generic-submit-cmd: "python3 ~/.config/snakemake/ssh/SUBMIT.py"
cluster-generic-status-cmd: "python3 ~/.config/snakemake/ssh/ssh-status.py"
jobs: 10
EOF其中 SUBMIT.py 由 AI 辅助完成,内容如下:
#!/usr/bin/env python3
import os
import sys
# ssh_utils.py 中导入所有需要的函数
from ssh_utils import parse_jobscript, submit_job_cmd
jobscript_path = parse_jobscript()
# 读取该作业脚本的实际内容
# 这是我们真正需要发送到 server.py 并在远程节点执行的命令
with open(jobscript_path) as f:
command_to_execute = f.read()
# 从 Snakemake 设置的环境变量中获取核心数/线程数
threads = os.getenv("SNAKEMAKE_THREADS", 1)
# 从环境变量中获取工作目录
pwd = os.getenv("SNAKEMAKE_CWD", os.getcwd())
# 调用 ssh_utils.py 中已经存在的 submit_job_cmd 函数
# 它负责打包数据、通过 socket 发送给 server.py、并返回作业ID
try:
jobid = submit_job_cmd(cmd=command_to_execute, threads=int(threads), pwd=pwd)
# Snakemake 要求提交脚本必须在标准输出打印出作业ID
print(jobid)
except Exception as e:
# 如果提交失败,打印错误信息,Snakemake会捕获并终止
print(f"Error submitting job to server: {e}", file=sys.stderr)
sys.exit(1)完成所有修改后,将 Snakemake 工作目录放在 NFS 挂载目录下,并运行:
snakemake --profile ssh --cores all可以看到测试成功跑通。
使用 nfs 运行时,会发现文件系统延迟和性能影响了工作流效率;因此,本次还尝试了一种新的文件系统——“DAOS(分布式异步对象存储)”。然而,其配置较为复杂,相关资料并不多,只能参考其文档,
笔者按照之前的经验,使用文件模拟 NVMe 设备的形式进行了配置,但后续测试读写性能时才发现性能远不及预期。后面又尝试改用通过 InfiniBand 在节点间互联,但并没有改善,反而因权限问题使得配置进一步复杂化。与学长交流后才知道,没有独立 NVMe 设备的情况下使用文件模拟,性能不高是正常的现象;另外即使正常配置,其读取性能较 nfs 的优势也不会非常大。另外,由于在不同节点的操作用户 id 不同,为了确保所有节点都能有权限读写工作目录,采用了一种变通的方式解决:在所有相关节点上添加一个特定 id 的组,把工作目录所属组对应修改并授予组读写权限,再把用户添加到该组并将其设为此用户主组;另外在 ~/.bashrc 中指定 umask 0002 ,确保工作过程中生成的文件仍然是组可读写的。
部分过程实录
首先确定在 B 节点上安装 daos_server 用于存储数据,并在 A、B 节点上都安装 daos_admin 和 daos_client 用于管理和访问。安装软件包过程省略。
安装后配置 Server /etc/daos/daos_server.yml如下:
name: daos_server
port: 10001
access_points: ['192.168.8.B']
control_metadata:
path: /home/for_daos_server/daos_meta
transport_config:
allow_insecure: true
provider: ofi+tcp;ofi_rxm
engines:
-
targets: 16
first_core: 0
nr_xs_helpers: 4
fabric_iface: eno4
fabric_iface_port: 20000
storage:
-
scm_mount: /var/daos/daos_scm
class: ram
-
class: file
bdev_list: [/home/for_daos_server/nvme_sim.img]
bdev_size: 90
bdev_roles:
- data
- meta
- wal接着配置 Admin /etc/daos/daos_control.yml如下:
name: daos_server
port: 10001
hostlist: ['192.168.8.B']
transport_config:
allow_insecure: true然后配置 Client /etc/daos/daos_agent.yml如下:
name: daos_server
access_points: ['192.168.8.B']
port: 10001
transport_config:
allow_insecure: true关于挂载后访问权限的问题,采用了将目录所有者更改为新建的用户组 daos_share 并授予读写权限、再将两个节点的工作账号主组修改的方法来解决。具体来说就是分别执行:
sudo groupadd -g 6000 daos_shared
sudo usermod -a -G daos_shared user
sudo usermod -g 6000 user在两个节点上都执行上述命令后,重新登录。然后在节点 B 上执行:
dmg pool create rna_pool --size=80GB --user=user --group=daos_shared
daos container create rna_pool --type=POSIX rna_cont
dfuse --multi-user --pool=rna_pool --container=rna_cont --mountpoint=/home/user/daos_snake
sudo chgrp 6000 /home/user/daos_snake
sudo chmod 2775 /home/user/daos_snake同样在节点 A 上挂载,就可以正常读写目录了。
另外,DAOS 挂载文件系统似乎不便设置继承父目录权限设置,为了确保工作流中途生成的中间文件不会因为权限问题无法写入/删除,修改了 ~/.bashrc 文件,向其中添加了 umask 0002,使得新创建的文件和目录都具有组写权限。
尽管做了这些配置,实际运行工作流仍然出现了错误,又受低读写性能所累,每次测试需要较长时间才能看到结果,于是后面也没有再在此方面取得进展,略有遗憾。
三、关于性能、功耗分析
首先对于本实验,Snakemake 本身提供了生成 DAG 图的功能,可用于分析工作流整体的结构:
snakemake --dag | dot -Tpdf > test_dag.pdf为了能实时检查工作运行时的功耗,在 AI 帮助下写了一个包装脚本,利用 Intel RAPL 接口,每秒读取功耗数据并展示;但对部分读取到的信息处理不正确,还需要进一步完善。另外,现在读取的功耗是不准确的,利用独立的功耗计硬件进行读取会是更好的解决方案。
本文(https://www.cgtsoft.com/archives/82/)来源于 CGT Software,使用 CC BY-NC-SA 4.0 许可发布。