snakemakeでEnvironment Modulesをつかう
バイオインフォマティクスの解析を実行する際、ソフトウェアやパッケージのバージョンなどのコンフリクトを避けたり、簡単に切り替えたりするためにEnvironment Modulesを利用して環境を動的に変更することがあるかと思います。
また、バイオインフォマティクスの解析パイプラインをsnakemakeで定義して実行することもあるかと思います。
snakemakeからEnvironment Modulesで管理されているソフトウェアをロードするには、単にshell:の部分にmodule load <module name>を追加すればよいですが、もう少し洗練された方法にenvmodulesディレクティブを使う方法があります。
具体的には、
rule rule_with_envmodules:
envmodules:
"some_module_01/0.0.0",
"some_module_02/1.1.1",
といった感じで指定してから、snakemake --use-envmodulesというオプション付きで実行すると、当該ruleが実行される前に、指定されたモジュールが上から順にmodule loadされるようです。
利用方法はかなり簡便ですが、使っているうちにいくつか注意点があるなと思ったので備忘録的に記しておきます。
1. 再現性について
公式のドキュメントにも記載の通り、Environment Modulesの実装はプラットフォーム特異的な部分も多く、再現性という点でcondaやcontainerを指定する場合より劣る可能性があります。
2. module: command not foundエラーについて
いくつかの条件ではmodule: command not foundというエラーとともに、モジュールが適切にロードされない場合があります。私の場合は特に、zshでsnakemakeコマンドを実行することでこの現象が起きました。zsh上ではmodule loadがちゃんと機能していたのにもかかわらすです。
いろいろ迷いましたが、これはおそらく、zsh環境で定義されていたmoduleコマンドがsnakemake側で各タスクの実行のために立ち上がるbash上に継承されないからだろうという結論に行きつきました。
moduleコマンドは実際にはシェル関数として定義されており、各種シェルスクリプト用に定義されたファイルが存在します。moduleコマンドを利用するためにはそのファイルを事前に読み込む必要があるのですが、多くの場合は.bash_profileや.bashrc、.zshrcなどのようなファイルによってログインの際に自動的に定義されるかと思います。このあたりの内容はこちらの記事が参考になります。
bashでログインし、そのままsnakemakeを起動すると、現在のシェルで定義されているmodule関数がうまいことsnakemake側で起動するbashに継承され、moduleコマンドが見つかるのだと思います。一方でzshから立ち上げるとその継承は働かず、snakemake側で起動するbashは素のbashとなり、module: command not foundエラーが発生するものと思われます。
snakemakeが起動するbashはlogin/interactive shellではないことが原因かと思います。module loadが実行される前に明示的にmoduleの初期化スクリプトをsourceする(. /etc/profile.d/modules.shといったコードを実行する)方法も考えましたが、それを実現する手段は見つかりませんでした。
簡単な解決策はsnakemakeでuse-envmodulesを利用するときはbashを使うということかと思います。(ちなみに、同じようなワークフロー言語のnextflowでも似た事象にあったことがあるのですがこちらはまた別記事で書こうと思います。)
参考
- https://modules.readthedocs.io/en/latest/
- https://snakemake.github.io/
- https://snakemake.readthedocs.io/en/stable/snakefiles/deployment.html#using-environment-modules
- https://modules.readthedocs.io/en/latest/INSTALL.html#enable-modules-in-shells
- https://github.com/snakemake/snakemake-executor-plugin-slurm/issues/63 (
module loadが実行される前に初期化スクリプト読み込みコマンドを挿入できるような機能追加をする動きに関するGithub Issues)