10. Singularityの使い方

Singularityは、ソフトウェアを動作させるための仮想的な環境であるコンテナと呼ばれる仕組みを利用するためのプラットフォームです。コンテナを活用することで、異なるマシンやOSにおいても同じ環境でソフトウェアを動かすことができるため、ユーザーにとっては依存関係などを気にせずソフトウェアを使うことができ、また開発者にとっては可搬性や再現性を担保することができるといった利点があります。

ヒント

類似のツールにDockerがありますが、コンテナを動作させるのにroot権限が必要になるため、スパコン上など権限を簡単に付与できない環境ではSingularityが利用されています。

各種ソフトウェアはイメージと呼ばれるパッケージの形態からコンテナを起動することで使用できます。イメージはDocker用に配布されているものをSingularityでも利用することができます。ToMMoスパコンにはSingularityが既にインストールされているため、配布されているイメージを使用するだけならば、Singularity自体を個々人でインストールする必要はありません。

10.1. Docker Hub からイメージを作る

注釈

この手順はインターネットへの接続が必要なため、UnitAのみの対応となります。UnitAにて次のコマンドを実行してから手順を進めてください。

$ export https_proxy=http://epx3x01:8080
$ export http_proxy=http://epx3x01:8080

Docker用にDocker Hubで配布されているイメージを、Singurlarity用に変換して使うことができます。

Docker Hubで使用したいイメージを探し、名前を確認します。

../../_images/docker_hub.png

イメージのダウンロードとSingularity用イメージへの変換を行なうには、

singularity build (作成したいイメージの名前.sif) docker://(Dockerイメージの名前)

とします。

$ singularity build lolcow.sif docker://sylabsio/lolcow

正常に動作することを確認します。

$ singularity run lolcow.sif
 ______________________________
< Fri Jun 10 12:38:45 JST 2022 >
 ------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

10.2. 使用例

作成したイメージからコンテナを起動しコマンドを実行するには、

singularity exec (イメージの名前.sif) (実行したいコマンド) (引数)

とします。

$ singularity exec lolcow.sif cowsay hello
 _______
< hello >
 -------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

パイプでデータを渡すこともできます。

$ echo pipe | singularity exec lolcow.sif cowsay
 ______
< pipe >
 ------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

singularty shellを使うと、対話型の環境を実行することができます。

$ singularity shell lolcow.sif
Singularity> cowsay interactive
 _____________
< interactive >
 -------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

ヒント

対話型環境を終了するにはexitと入力するか、control+Dを押します。

10.2.1. スパコン上の共有データ等を使用する際の注意

/share1/public以下の共有データなどをコンテナから利用したい場合、そのままではアクセスすることができません。

$ singularity exec lolcow.sif ls /share1/public
/usr/bin/ls: cannot access '/share1/public': No such file or directory

これを可能にするには、コンテナ起動時に--bindオプションでアクセスしたいパスを指定 (バインド) します。

$ singularity exec --bind /share1/public lolcow.sif ls /share1/public
database  genome

singularity shellの場合も同様に、singularity shell --bindに続いてアクセスしたいパスを指定します。

ヒント

ホームディレクトリ$HOMEや カレントディレクトリ$PWDなどは、 (デフォルトの設定では) 自動的にバインドされます。(参考)

10.3. 自分でイメージを作る

自分でSingularity用のイメージを構築します。権限の関係上、スパコンではなくローカルのマシンで、Docker用に構築したイメージをSingularity用に変換するという手順で行ないます。

お手元のマシンにDockerがインストールされていない場合、公式の手順に従ってインストールしてください。

ヒント

手元にSingularityをインストールし直接Singularity用のイメージを作成することも可能ですが、Dockerの方が導入が簡単なため、この説明ではDockerを用いた手順を採用しています。

おおまかな手順としては、

  1. Dockerfileを記述する

  2. Docker用にイメージをビルド (構築) する

  3. Singularity用にイメージを変換する

という流れです。まずは簡単な例で手元で動作することを確認してみましょう。

10.3.1. 簡単な例

Dockerfileは、イメージをビルドするための命令を列挙したテキストファイルです。(詳しい書き方はBest practicesもご参照ください)

適当な作業用ディレクトリに以下のディレクトリを作成して、以降の工程を進めます。

$ mkdir my_docker_dir

最低限の例として、my_docker_dir/Dockerfileを次のように記述します (一行のみ) 。

1FROM ubuntu:20.04
2ENTRYPOINT ["/bin/sh", "-c", "echo Hello"]

これからイメージをビルドしコンテナを起動するには、次のコマンドを使用します。

$ docker build -t user_name/my_image:0.1 my_docker_dir/  # -t (ユーザー名)/(イメージ名):(バージョン) の形式
...  # ログが表示される

$ docker run --rm user_name/my_image:0.1
Hello

docker buildでイメージを構築し、docker runでそこからコンテナを起動し指定されたコマンドを実行しています(--rmオプションをつけることで動作終了後コンテナを削除 ) 。うまくいっていればHelloと表示されます。

10.3.2. Singularity用のイメージに変換

Docker用のイメージからSingularity用のイメージへの変換は、docker2singularityで行なえます。

まず出力用のディレクトリを作成します。

$ mkdir out_sif

次のコマンドでdocker2singularityを実行します。

$ docker run -v /var/run/docker.sock:/var/run/docker.sock \
-v $PWD/out_sif:/output \
--privileged -t --rm \
quay.io/singularity/docker2singularity \
--name my_singularity_image-0.1 \  # 変換後のイメージ名を指定
user_name/my_image:0.1

出力されたout_sif/my_singularity_image-0.1.sifscpコマンドなどで転送すれば、スパコン上のSingularityでも同様に動作させることができます。詳しい転送方法は、Unit Aとインターネットにつながった手元の端末でデータを転送する方法もご参照ください。

10.3.3. 実践的な例

現実的なプログラム動作環境のイメージ化を想定した手順を、PythonとRの二通りの例で説明します。Dockerfileなどを適宜記述しイメージ化することで、動作環境ごとプログラムを配布することができます。

10.3.3.1. Python動作環境のイメージを構築する

特定のバージョンのPythonやモジュールの下で動くことを期待したプログラムのための環境を、イメージとして作成します。(参考:Build your Python image)

適当な作業用ディレクトリに以下のディレクトリを作成して、以降の工程を進めます。

$ mkdir my_docker_dir_py

my_docker_dir_py/Dockerfileを以下のように記述します。

1FROM python:3.10-slim-buster
2COPY requirements.txt requirements.txt
3RUN pip3 install -r requirements.txt
4COPY . .
5ENTRYPOINT ["python3", "my_app.py"]

Pythonのバージョンを指定 (今回は3.10系) し、requirements.txtに記述されたモジュールをインストールしたのち、my_app.pyを実行するという流れです。

モジュールとして、今回はNumPyのみを使用することとし、my_docker_dir_py/requirements.txtを以下のように記述します (一行のみ) 。

1numpy==1.22.4

動作確認用のプログラムとして、my_docker_dir_py/my_app.pyを以下のように記述します。

 1#!/usr/bin/env python3
 2
 3import sys
 4
 5import numpy as np
 6
 7
 8def _main():
 9    py_expected = '3.10'
10    py_running = ('.').join([str(x) for x in sys.version_info[:2]])
11    print('Expected python version:', py_expected)
12    print('Running python version :', py_running)
13    print()
14
15    numpy_expected = '1.22.4'
16    numpy_running = np.__version__
17    print('Expected numpy version:', numpy_expected)
18    print('Running numpy version :', numpy_running)
19
20
21if __name__ == '__main__':
22    _main()

PythonおよびNumPyのバージョンをチェックし、想定した通りのバージョンが動作していることを確認しています。

ビルドとコンテナの起動を以下のコマンドで行ないます。

$ docker build -t user_name/my_image_py:0.1 my_docker_dir_py/
...

$ docker run --rm user_name/my_image_py:0.1
Expected python version: 3.10
Running python version : 3.10

Expected numpy version: 1.22.4
Running numpy version : 1.22.4

あとは上記の例と同様にSingularity用のイメージに変換し、my_app.pyとともにスパコン上に転送すれば、ローカル環境と同様に動作します。

10.3.3.2. R動作環境のイメージを構築する

Rの場合もPythonと同様に、特定のバージョンのRやパッケージの下で動くことを期待したプログラムのための環境を、イメージとして作成します。

適当な作業用ディレクトリに以下のディレクトリを作成して、以降の工程を進めます。

$ mkdir my_docker_dir_r

my_docker_dir_r/Dockerfileを以下のように記述します。

1FROM r-base:4.2.0
2RUN R -e "install.packages('remotes', repos='https://cran.ism.ac.jp/')"
3RUN R -e "remotes::install_github('cran/class@7.3-20')"
4COPY . .
5ENTRYPOINT ["Rscript", "my_app.R"]

Rのバージョンを指定 (今回は4.2.0) し、必要なパッケージをインストールしたのち、my_app.Rを実行するという流れです。パッケージのバージョンを指定するためremotesをはじめにインストールし、それを使って今回はclassをGitHubからインストールしています。

動作確認用のプログラムとして、my_docker_dir_r/my_app.Rを以下のように記述します。

 1#!/usr/bin/env Rscript
 2
 3library(class)
 4
 5
 6r_expected = '4.2.0'
 7r_running = strsplit(version[['version.string']], ' ')[[1]][3]
 8print(paste('Expected R version:', r_expected))
 9print(paste('Running R version :', r_running))
10print('')
11
12class_expected = '7.3.20'
13class_running = packageVersion('class')
14print(paste('Expected class version:', class_expected))
15print(paste('Running class version :', class_running))

Rおよびclassのバージョンをチェックし、想定した通りのバージョンが動作していることを確認しています。

ビルドとコンテナの起動を以下のコマンドで行ないます。

$ docker build -t user_name/my_image_r:0.1 my_docker_dir_r/
...

$ docker run --rm user_name/my_image_r:0.1
[1] "Expected R version: 4.2.0"
[1] "Running R version : 4.2.0"
[1] ""
[1] "Expected class version: 7.3.20"
[1] "Running class version : 7.3.20"

これをSingularity用のイメージに変換し、my_app.Rとともにスパコン上に転送すれば、ローカル環境と同様に動作します。