はじめに
非情報系の研究室にて、研究者・インターン・学生の個人PCや共有サーバ上でのソフトウェアの管理方法について、様々な方法を試したが、どれもしっくりこない(全員が使いこなせない)と感じていました。
失敗例:
- .bashrc->ソフトウェアの依存関係が把握できなくなり破綻
- docker->重い。Unix系に触れたことがない非情報系の研究者、学生の学習コストが高い
- spack->spackが対応していないソフトウェアの扱いが面倒
何の気なしにchatGPTに尋ねてみたところ、
✅ 推奨:モジュール管理(Environment Modules / Lmod)
と、module管理を推奨されました。特に、Environment Modulesでなく、Lmodがお勧めのようです。
Envionment ModulesとLmodの違いは、moduleコマンドの制御用のスクリプト言語がtclか、Luaの違いのようで、どちらでもよさそうな印象を受けますが、ここではchatGPTとの議論の末に落ち着いたLmodによるライブラリ管理の方法論をまとめてみます。
参考:
もう少し丁寧な導入(初心者向け)
オープンソースのライブラリを個別の環境にインストールする際、
・ライブラリのビルド時に用いたコンパイラ
・対象ライブラリのビルド時に必要な別のライブラリ
との依存関係が生じるため、インストールしたライブラリを自身のプロジェクトで用いる際、これらの欠落やバージョンの不整合は、ビルド失敗や実行不良を招く。
また、複数プロジェクトを管理する際には、使用するライブラリのパッケージが更新されておらず、以前のバージョンを敢えて使用せざるを得ない場合も生じる。そのため、あるライブラリについて、複数のバージョンを個々の環境にインストールせざるを得ない状況もよく生じる。
この解決策として、インストールしたライブラリへのパス設定を管理し、個別のプロジェクトにおいて、使用したいライブラリ(+バージョン)が生じたときのみ、ライブラリへのパスを有効化、必要ないときには無効化したいという動機が生まれる。
ライブラリの数が少ない場合には、.bashrcにパスを記載しておき、都度コメントアウトなどの方法が考えられるが、この場合、ライブラリの依存関係(コンパイラ・他ライブラリ)との情報が保存されないため、パッケージの数が増えてくると管理が破綻する。
この問題を解決するのがEnvironment ModulesやLmodが提供するmoduleコマンドである。コンパイラや他ライブラリとの依存関係をスクリプトに明記しておくことで、各ライブラリを有効化(module上ではloadと呼ぶ)する際、ライブラリの依存関係を読み取り、必要なライブラリも含めて有効化できる。依存関係を意識するのはライブラリのビルド時のみでよいため、管理上の手間がかからない。ここではLmodを前提として、ライブラリ管理の手順を整理する。
各ライブラリ、module用スクリプトのインストール構成
はじめに、各ライブラリの構成が以下のようにまとめられていることを前提とする。各ライブラリのインストールの際に使用したコンパイラやライブラリの情報は把握しているものとする。
moduleコマンドで読み取るスクリプトを格納するディレクトリ(modulefiles)、下記のように/optの下に作成し、各ライブラリの名前やバージョンに合わせてディレクトリ構成やluaファイル名を設定しておく。具体的なluaファイルの記述は後述する。
経験が浅い場合、何も意識せず、必要に応じて手あたり次第/usr下にライブラリをインストールしてしまい、管理が破綻する事例が多々ある。そうなりかけている場合は、インストール構成を改めて見直しておくといいかもしれない。
/opt
--/hogehoge (ライブラリ名)
----/x.x.xx (バージョン)
----/y.y.yy (バージョン)
--/fugafuga
----/x.x.xx
--/modulefiles
----/hogehoge
------/x.x.xx.lua
------/y.y.yy.lua
----/fugafuga
------/x.x.xx.lua
インストール
特にこだわりが無ければパッケージをそのままインストールすればいい。ただし、スクリプトの記載方法にバージョン依存性があることに注意がいる。本稿ではLmod.8以降を対象にしている。Ubuntu 22.04ではLmod.6.xがaptでインストールされるため、ソースコードから自身でビルドが必要となる。
sudo apt install lmod
その後、シェルスクリプトへのパスを.bashrcに記載。
echo "source /etc/profile.d/lmod.sh" >> ~/.bashrc
スクリプトが配置されているディレクトリ位置を.bashrcに記載。
echo "export MODULEPATH=/opt/modulefiles:$MODULEPATH" >> ~/.bashrc
moduleコマンド(最低限)
現在moduleで管理できているライブラリの一覧を表示
module avail
hogehogeをロード。スクリプトにライブラリ間の依存関係を記述していれば、依存関係にあるライブラリもまとめてロードされる。
module load hogehoge
現在ロードしているライブラリ一覧を表示
module lists
ロードしているライブラリを全て外す
module parge
スクリプト(Luaファイル)の記載項目
例:xtenor
C++にて多次元配列をnumpyと同様のフォーマットで扱うためのライブラリであるxtensorを題材とする。
xtensorはヘッダオンリーのライブラリであり、xtlに依存する。
以下に、Luaファイルの構成を示す。ほぼchatGPTで自動作成しており、install prefixなど以外ほとんど変更していないので、割り切って全て生成AIに任せても何とかなる。
xtl:
-- xtl/0.8.0 modulefile for Lmod
whatis("Name: xtl")
whatis("Version: 0.8.0")
whatis("Description: xtl - xtensor template library (header-only)")
-- C++ヘッダファイル検索パスに追加(コンパイル時に必要)
prepend_path("CPATH", "/opt/xtl/0.8.0/include")
-- CMakeから find_package(xtl) を使う場合のサポート
prepend_path("CMAKE_PREFIX_PATH", "/opt/xtl/0.8.0")
xtensor:
-- xtensor/0.26.0 modulefile for Lmod
whatis("Name: xtensor")
whatis("Version: 0.26.0")
whatis("Description: xtensor - C++ multi-dimensional arrays with NumPy-like syntax (header-only)")
whatis("URL: https://github.com/xtensor-stack/xtensor")
-- 必須依存モジュールの読み込み
depends_on("xtl/0.8.0")
-- コンパイル用ヘッダパス
prepend_path("CPATH", "/opt/xtensor/0.26.0/include")
-- CMake用パッケージ探索パス(find_packageで使用)
prepend_path("CMAKE_PREFIX_PATH", "/opt/xtensor/0.26.0")
HDF5
大規模データ管理用フォーマットを提供するライブラリHDF5について、Luaファイルは下記の通り。xtensorと異なり、binやlibの設定が含まれる。
help([[
HDF5 1.14.4 (non-MPI version, built with Intel oneAPI Compiler icx)
]])
whatis("Name: HDF5")
whatis("Version: 1.14.4")
whatis("Built with: Intel oneAPI Compiler icx")
whatis("Description: HDF5 (non-MPI) for scientific computing")
whatis("URL: https://www.hdfgroup.org/solutions/hdf5/")
-- 実体パス
local hdf5_root = "/opt/hdf5/1.14.4-2"
prepend_path("PATH", pathJoin(hdf5_root, "bin"))
prepend_path("LD_LIBRARY_PATH", pathJoin(hdf5_root, "lib"))
prepend_path("CPATH", pathJoin(hdf5_root, "include"))
prepend_path("CMAKE_PREFIX_PATH", hdf5_root)
setenv("HDF5_ROOT", hdf5_root)
setenv("HDF5_DIR", hdf5_root)
intel oneAPI
intel oneAPIおよび、oneAPIが提供するコンパイラの使用環境の設定を考える。推奨通りの手順を踏めば、/opt以下にインストールされ、oneAPIが提供するsetvars.shを有効化すれば、使用環境が一通り整う。
ただ、ライブラリの依存関係をすべて明記することを前提とし、gnu系などoneAPI以外のコンパイラの使用も考慮するなら、敢えてsetvars.shを用いず、oneAPIの必要なパッケージのみをmoduleで管理し、oneAPIを使ってインストールしたライブラリについて、oneAPIへの依存関係を明示する必要が生じる。
下記はコンパイラのみの最小設定。内部環境変数も書き換えておくと、cmakeもしくはconfigure時に楽なので記入している。
help([[
Intel oneAPI Compiler (icx, ifx)
]])
whatis("Name: Intel Compiler")
whatis("Version: 2025")
whatis("Category: compiler")
whatis("URL: https://www.intel.com/content/www/us/en/developer/tools/oneapi/overview.html")
local root = "/opt/intel/oneapi/2025.2"
prepend_path("PATH", pathJoin(root, "bin"))
prepend_path("LD_LIBRARY_PATH", pathJoin(root, "lib"))
prepend_path("LIBRARY_PATH", pathJoin(root, "lib"))
prepend_path("CPATH", pathJoin(root, "include"))
setenv("CC", "icx")
setenv("CXX", "icpx")
setenv("FC", "ifx")
mkl
help([[
Intel Math Kernel Library (MKL)
]])
whatis("Name: Intel MKL")
whatis("Version: 2025")
whatis("Category: Math Library")
whatis("URL: https://www.intel.com/content/www/us/en/developer/tools/oneapi/onemkl.html")
local root = "/opt/intel/oneapi/mkl/latest"
prepend_path("PATH", pathJoin(root, "bin"))
prepend_path("LD_LIBRARY_PATH", pathJoin(root, "lib", "intel64"))
prepend_path("LIBRARY_PATH", pathJoin(root, "lib", "intel64"))
prepend_path("CPATH", pathJoin(root, "include"))
setenv("MKLROOT", root)
MPIライブラリ。内部環境変数などの追記が必要なようで、tiral & errorで作成したため、一部不要なものも含まれる可能性がある。
help([[
Intel MPI Library
]])
whatis("Name: Intel MPI")
whatis("Version: 2025")
whatis("Category: MPI")
whatis("URL: https://www.intel.com/content/www/us/en/developer/tools/oneapi/mpi-library.html")
local root = "/opt/intel/oneapi/mpi/latest"
prepend_path("PATH", pathJoin(root, "bin"))
prepend_path("LD_LIBRARY_PATH", pathJoin(root, "lib"))
prepend_path("LD_LIBRARY_PATH", pathJoin(root, "opt/mpi/libfabric/lib"))
prepend_path("LIBRARY_PATH", pathJoin(root, "lib"))
prepend_path("CPATH", pathJoin(root, "include"))
prepend_path("PKG_CONFIG_PATH", pathJoin(root, "lib/pkgconfig"))
setenv("I_MPI_ROOT", root)
setenv("FI_PROVIDER_PATH", root .. "/opt/mpi/libfabric/lib/prov:/usr/lib/x86_64-linux-gnu/libfabric")
setenv("CLASSPATH", root .. "/share/java/mpi.jar")
setenv("I_MPI_CC", "icx")
setenv("I_MPI_CXX", "icpx")
setenv("I_MPI_FC", "ifx")
他のライブラリについては、上記をひな型にパスを修正すれば使いまわせると思われる。
終わりに
一連の検討を通じて、moduleだけでなく、ライブラリのインストール構成などを整理しました。これらは実務的な観点から重要な内容と思われますが、おそらく研究機関・研究室・個人でノウハウが閉じており、一度閉じた環境に所属すると、その後のアップデートが難しい事項でもあります。この記事が一人で苦しんでいる誰かの役に立てば幸いです。