はじめに
コンピュータビジョンの研究に取り組む大学院生です。
研究に関連する実験は研究室のサーバーや産総研のサーバーABCIに接続して行っています。この時、Singularityを使って仮想環境を構築しています。Singularityとは?というお話はこちらの記事が詳しいのでこちらを参照していただければと思うのですが、簡単に言えば「管理者権限が無くても実行できるDockerのようなもの」というように私は認識しています。管理者権限を持っているローカルのマシンでSingularityイメージを作成して、これを管理者権限を持っていないサーバーにアップロードすることで、同じ仮想環境をサーバー上に用意することができるという感じです。
Singularityでは、DockerコンテナをSingularityイメージに変換することも可能で、今まではNVIDIA GPU Cloud (NGC)で提供されているPyTorch用のコンテナを利用していたのですが、このコンテナではOpenCVが上手くインポートできない(依存しているパッケージがインストールされておらず、インストールには管理者権限が必要)ということが私の中で問題になり、このコンテナをベースにしてカスタマイズしたSingularityイメージを作成しようということになりました。
NGCのユーザーガイドでは、Dockerfileを書くなどしてNGCコンテナをカスタマイズするやり方は載っていたのですが、Singularityで使うという今回の目的を満たすには、カスタマイズしたコンテナをDocker Hubにアップロードして、それをSingularityイメージに変換するというやや遠回り感のある工程になりそうだったため、Singularity定義ファイルを作ることでNGCコンテナをカスタマイズしたSIngularityイメージを作成するということを試みました。本記事はこの作業の備忘録になります。
本記事の流れ
- 1. VirtualboxとVagrantのインストール
- 2. 仮想マシンの立ち上げとSingularityのインストール
- 3. 仮想マシンに割り当てるディスクを増やす
- 4. Singularity定義ファイルの作成
- 5. Singularity Image Fileの作成
環境
- OS: macOS BigSur 11.2.1
- Homebrew 3.0.1
Homebrewのインストール
Homebrewのインストールでは以前こちらの記事で紹介しているのでこちらをご参照ください。
VirtualboxとVagrantのインストール
MacでSingularityを動かすためにまずVirtualboxとVagrantを導入します。VirtualboxやVagrantとは?みたいな話はこちらの記事が詳しいです。簡単に言うとこの2つで仮想マシンを構築します。
以下でしばらくhomebrew-caskの導入について書いてますが、homebrew-caskを新たに導入する必要がないことが判明したので、しばらく読み飛ばしてください。
Homebrewは導入済みだったのですが、CUIベースでないアプリケーションをインストールするにはHomebrew-caskを導入する必要があったので、まずHomebrew-caskを以下のコマンドで入れます。
$ brew install cask
するとCLT(CommandLineTools)が最新版じゃないと怒られてしまいました。
なので指示通りにCLTを最新版にします。
$ sudo rm -rf /Library/Developer/CommandLineTools
パスワードが聞かれるので入力します。その後、次のコマンド。
$ sudo xcode-select --install
これを実行すると突然GUIウィンドウが出てきて「インストールしますか?」みたいなことを聞かれるので「インストールする」を選ぶとインストールが始まります。
Homebrew-caskのリポジトリによるとHomebrew-caskはHomebrewに統合されたみたいで新たにインストールする必要ないみたいです。
次のコマンドでbrewのバージョンを確認するとhomebrew-caskもちゃんと入っていることが確認できます。
$ brew -v
なので、brew caskでインストールしたい場合もbrew cask install
と明示的に書く必要はなく、普通にbrew install
でvitualboxやvagrantをインストールできます。
$ brew install virtualbox && \
brew install vagrant && \
brew install vagrant-manager
以下のコマンドを実行することで、これらがhomebrew-caskでインストールされていることを確認できます。
$ brew list --cask
このコマンドは、以前はbrew cask list
というコマンドだったようで、実行することでhomebrew-caskでインストールしたソフトウェアの一覧が表示されます。
virtulaboxとvagrantのインストールが終わったら、システム環境設定でこれらの使用を許可する必要があります。(参照:Vagrant upでエラーが出て躓いた時の解決方法)
仮想マシンの立ち上げとSingularityのインストール
今回はSingularity 3.5をインストールします(これはABCIで使えるSingularityのバージョンが3.5なので、これに合わせています)ので、Singularity 3.5のドキュメントに従って仮想マシンにSingularityをインストールします。
Vagrantfileという、仮想マシンの設定ファイルを保存するディレクトリを作ります。
~$ mkdir vm-singularity && \
cd vm-singularity
次に、仮想マシンにSingularity 3.5をインストールします。
~/vm-singularity$ export VM=sylabs/singularity-3.5-ubuntu-bionic64 && \
vagrant init $VM
これでSingularityがインストールできたら仮想マシンを立ち上げます。
動作は確認していませんが、後述のように、容量がデフォルトの20GBでは足りないことが明らかな時は仮想マシンを立ち上げる前にVagrantfile
を先に書き換えておくと、後々楽かもしれません(この辺りの話は情報提供いただければ幸いです)。
~/vm-singularity$ vagrant up && \
vagrant ssh
これでターミナルの表示が
vagrant@vagrant:~$
のようになり、仮想マシンを操作することができるようになります。
次のコマンドでSingularityが正しくインストールされているか確認することができます。
vagrant@vagrant:~$ singularity version
手元では3.5.1
と表示されました。
仮想マシンに割り当てるディスクを増やす
デフォルトでは仮想マシンには20GBのディスクが割り当てられていましたが、これでは足りなかった(singularity build
を実行した際にno space left on device
というエラーが出ます)ので増設を試みます。
ディスクサイズはdf -h
コマンドで確認できます。
/
にマウントしている/dev/mapper/vagrant--vg-root
のSizeが19Gとなっています。今回はこれを増量します。
まずは先程作ったディレクトリ~/vm-singularity
の直下にあるVagrantfile
を編集します。
仮想マシンに入ってしまっている場合は一度出て、仮想マシンを停止します。
vagrant@vagrant:~$ exit
~/vm-singularity$ vagrant halt
次にVagrantfile
の編集によってディスク容量を増やせるようにする、vagrant-disksize
というプラグインをインストールします(参照:Vagrantfileに一行書くだけでVMのディスク容量を増やす方法)。
~/vm-singularity$ vagrant plugin install vagrant-disksize
ここからVagrantfile
を編集します。最初にvagrant up
をする前にVagrantfile
の編集をしておくと、Vagrantfile
の編集以降の作業は省略できる可能性があります(確認はしていません)。
~/vm-singularity$ vim Vagrantfile
次の1行を加えます。
Vagrant.configure("2") do |config|
config.vm.box = "sylabs/singularity-3.5-ubuntu-bionic64"
config.disksize.size = '30GB' # <=この行を新たに追加する。
ここでは30GBとしていますが、今回用いるNGCコンテナがそれなりに大きいファイルであり、実際にやってみると30GBでも足りず、再び増量することになってしまい二度手間だったので最初から50GBにしておくと良かったです(40GBでもやってみましたがそれでも足りず、50GBにしたら最後までできるようになりました)。
ただし、このやり方はディスクを増やすことはできても減らすことはできないようなので、n度手間でも少しずつ増やしていくのが良いのかもしれません。
次に仮想マシンを再び立ち上げ接続します。立ち上げ時のメッセージで
==> default: Resized disk: old 20480 MB, req 30720 MB new 30720 MB
==> default: You may need to resize the filesystem from within the guest.
という表示があり、Vagrantfile
の内容が反映されていることが確認できます。
しかし、このままではまだ/dev/mapper/vagrant--vg-root
に割り当てられたディスクの容量は増えていません。df -h
コマンドで確認してみると19Gとなったままでした。
そこでこちらの記事を参考に、以下の操作を行いました。
まず次のコマンドでディスクの状態を確認します。
vagrant@vagrant:~$ sudo fdisk -l
ディスク/dev/sda
には30GB割り当てられているものの/dev/mapper/vagrant--vg-root
には19GBしか割り当てられていないことが確認できます。
またDeviceは/dev/sda1
の一つだけであることも確認できます。このDeviceのId8e
は次のステップで必要になるのでデバイスの状態は予め確認しておきましょう。
次にパーティションを作成します。
vagrant@vagrant:~$ sudo fdisk /dev/sda
コマンド実行後に表示される画面の指示に従って次のようにコマンドを打っていきます。
n # パーティション作成
p # primaryを選択
2 # パーティション番号(Deviceが1つだけなのでEnterを押してもデフォルトで2が選択されます)
Enter # パーティション開始位置(デフォルトのままいきます)
Enter # パーティション終了位置(デフォルトのままいきます)
t # タイプ変更
2 # パーティション番号
8e # 拡張したいパーティションと同じになるように、先程fdiskで確認したIdの文字列
w # 保存して終了
実際にこれらを行った画面は以下のようになります。
ディスクの状態を確認します。
vagrant@vagrant:~$ sudo fdisk -l
/dev/sda2
が新たに加わっていることが確認できたら、一度仮想マシンを再起動し、再び接続します。
vagrant@vagrant:~$ exit
~/vm-singularity$ vagrant reload
~/vm-singularity$ vagrant ssh
次に先程作成したパーティション/dev/sda2
の物理ボリュームを作成します。
vagrant@vagrant:~$ sudo pvcreate /dev/sda2
物理ボリュームとは?みたいな話はこちらの記事が参考になりましたので興味のある方は覗いてみてください。
次はボリュームグループに新しいパーティションを追加します。
この際にボリュームグループの名前が必要なので次のコマンドで確認します。
vagrant@vagrant:~$ sudo vgdisplay
するとVG Nameはvagrant-vg
となっていることが確認できましたので、次のコマンドでこのボリュームグループに新しいパーティションを追加します。
vagrant@vagrant:~$ sudo vgextend vagrant-vg /dev/sda2
次はディスクを拡張します。拡張したいディスクは/dev/mapper/vagrant--vg-root
であったので次のようにコマンドします。
vagrant@vagrant:~$ sudo lvextend -l +100%FREE /dev/mapper/vagrant--vg-root
そして最後にファイルシステムを拡張します。ファイルシステムがext3もしくはext4の場合はresize2fs
コマンドでできますが、そうでない場合は別のコマンドを使う必要があるようです。ファイルシステムは次のコマンドで確認できます。
vagrant@vagrant:~$ mount
一覧がたくさん出てきますが、/dev/mapper/vagrant--vg-root
を探すと以下のように書いてありました。
/dev/mapper/vagrant--vg-root on / type ext4 (rw,relatime,errors=remount-ro,data=ordered)
ファイルシステムがext4であることが確認できたので、こちらも先程と同様に/dev/mapper/vagrant--vg-root
を指定して、次のコマンドを実行します。
vagrant@vagrant:~$ sudo resize2fs /dev/mapper/vagrant--vg-root
これで仮想マシンに割り当てるディスクの増量は完了しました。df -h
コマンドで、/dev/mapper/vagrant--vg-root
の容量が30GBに増えていることが確認できました。
Singularity定義ファイルの作成
定義ファイルを編集するためにまず仮想マシン内にvimをインストールします。
vagrant@vagrant:~$ sudo apt update
vagrant@vagrant:~$ sudo apt install vim
これで仮想マシン内でvimを使って定義ファイルを編集できるようになりました。
定義ファイルでは色々と設定することが可能ですが、ここではNGCコンテナでOpenCVを正しくインポートできるようにすることが目標なので、シンプルな定義ファイルを作成します。その他様々なオプションなどはSingularityのドキュメントを参照してください。
Singularity定義ファイルを次のように編集します。
Bootstrap: docker
From: nvcr.io/nvidia/pytorch:20.12-py3
%post
apt update
apt -y upgrade
# opencvが依存しているパッケージのインストール
apt install -y libgl1-mesa-dev
# singularity image fileのサイズをなるべく小さくするために余計なファイルを削除
apt autoremove -y
apt clean
rm -rf /usr/local/src/*
rm -rf /var/lib/apt/lists/*
# pipのパスが通っていなかったため絶対パスでpipコマンドを使う
/opt/conda/bin/pip install --no-cache-dir opencv-python
From
の部分でベースにするNGCコンテナを指定しています。ここではNGCのPyTorchコンテナで最新のバージョンであるpytorch:20.12.-py3
を選択しています。
また、余計なファイルを削除する部分はこちらの記事を参考にしています。
Singularity Image Fileの作成
ようやくここまで来ました。
先程作ったSingularity定義ファイルをビルドしてSingularity Image Fileを作成します。
vagrant@vagrant:~$ sudo singularity build my_pytorch.sif my_pytorch.def
ビルドには結構時間がかかります。実行が終わるとカレントディレクトリにmy_pytorch.sif
ができています。
仮想マシン内の/vagrant
は仮想マシンを作成したディレクトリvm-singularity
をマウントしていますので、次のようにすることで、Singularity Image Fileを仮想マシンの外に移すことができます。
vagrant@vagrant:~$ mv my_pytorch.sif /vagrant/my_pytorch.sif
すると~/vm-singularity
の直下にmy_pytorch.sif
が移動しています。
これを使用したいサーバーにscp
コマンドなどで送るとサーバーでこの仮想環境を使用できるようになります。
実際にサーバーにこのmy_pytorch.sif
を送り、ちゃんとOpenCVがインポートできるようになったことが確認できました。