SingularityコンテナをBuildする方法まとめ(Singularity v3.0.3)

# はじめに
前回，[CnetOS7にSingularityをインストールしてみる記事](https://qiita.com/HoriThe3rd/items/492afd7600ce714dd240)を書きました．この記事でコンテナエンジンは手に入ったので，今回は自分の好きな環境を構築したコンテナイメージを作ってみたいと思います．

なお，この記事で扱うのはDockerではなく，Singularityと呼ばれるHPC向けのコンテナエンジンです．この記事で扱う環境は以下のとおりです．

- Singularity version: v3.0.3
- サーバOS: CentOS 7.6
- 計算をさせるサーバにはSSHなどでログインできるがroot権限はない

# Buildとは
自分の必要とするソフトウェアや機能などを組み込んだコンテナを作ることです．作業フローは次のようになります．

1. コンテナの定義ファイル（？）である，Singularity Definition File(hoge.def)を書く，Dockerfileみたいなもの
2. Singularity buildコマンドでhope.defからイメージファイル(SIF)を生成する
3. 生成した.sifファイルを計算機などに配置し，使う

.defファイルの書き方は，公式ドキュメント[^1]に書いてあります．

# 複数のBuild方法
Singularityのコンテナイメージを作成する方法として以下の３つが挙げられます．

1. DockerHubやSingularityHubなどコンテナレジストリにあるイメージをダウンロードしてきて使う(そもそも自分でイメージを作らない)
2. 自分でdefファイルを書いて，自分のPCでビルドする
3. Sylab社が提供しているRemote Builderを用いてビルドする

2番目と3番目の方法では，ビルドして生成されたコンテナイメージを何らかの方法(scpとか？)でサーバに送って，利用することになります．１番目の方法は，サーバにそのままダウンロードしてくれば大丈夫です．この流れを図にすると次のようになります．

<img width="500" src="https://qiita-image-store.s3.amazonaws.com/0/357417/1d4d3f44-5939-8617-37c8-943cf162cca2.png">

1の方法については前回の記事でChainerやJuliaのコンテナを動かして紹介済みなので，ここでは2と3について紹介します．

# ローカルPCでビルドする
ビルドするためにはSingularityがインストールされ，root権限を行使できるPCが必要です．Linuxならインストールできますが，MacやWindowsの場合は，対応していないので，公開されているVagrant boxを使ってVMを立ち上げます．

## SingularityがインストールされたPCでビルドする
以下のコマンドでhoge.defからburi.sifをビルドできます．
Singularityコンテナの実行はユーザの権限でできますが，defファイルからのbuildはroot権限が必要になります．

```shell
sudo singularity build buri.sif hoge.def
```
修正(2019/03/18)：.sifと.defが逆だったので修正しました．

## MacやWindowsでビルドする
Singularityの[Vagrant boxが公開されている](https://app.vagrantup.com/sylabs)ので仮想マシン上でビルドできます．ただし，VirtualBoxとVagrantのインストールが必要です．

まずVagrantでVMを起動します．適当なディレクトリで，

```shell
vagrant init sylabs/singularity-3.0-ubuntu-bionic64
vagrant up
vagrant ssh
```

VMにhoge.defを共有フォルダか何かで配置します．続いて，hoge.defを置いたディレクトリで以下を実行します．

```shell
sudo singularity build buri.sif hoge.def
```
修正(2019/03/18)：.sifと.defが逆だったので修正しました．

なお，.sifファイルは読み込み専用ファイルですので，中に入ってapt installなどはできません．その時は-sオプションで書込み可能な形式でビルドする必要があります．

# Remote Builderを使う
Sylab社が[Remote Builderを公開しています](https://cloud.sylabs.io/builder)．これを使えば，自前でSingularityをインストールした環境を用意しなくてもビルドできます．これには二通りの使い方があります．

1. Remote Builderのサイトに.defファイルをアップロードしてビルドする
2. ビルドコマンドの --remoteオプションを使ってコマンド経由でビルドする(今回失敗)

## Remote Builderのサイトでビルドする方法
サイトにアクセスし，Build Recipe Fileでdefファイルを選択するか，コピペして貼り付けたのち，Buildボタンを押してしばらく待つとビルドされます．ちょっと時間がかかるようです．
<img width="400" alt="remote_builder.png" src="https://qiita-image-store.s3.amazonaws.com/0/357417/b9d9b346-78d5-56f0-57d0-1d0d502c6725.png">

## --remoteオプションを使う (上手く出来なかった・・・)
root権限のないPCや実際のサーバ上でビルドするときに便利です．

まず，アクセストークンを作り("get one here"をクリックして作成し，ダウンロード)，コマンドを走らせるコンピュータの~/.singularityフォルダに保存します．

<img width="400" alt="remoteOption.png" src="https://qiita-image-store.s3.amazonaws.com/0/357417/6340007a-01a4-0a1d-b929-98a63e4e511a.png">

その後，以下のコマンドでビルドします．

```shell
singularity build --remote buri.sif hoge.def
```
修正(2019/03/18)：.sifと.defが逆だったので修正しました．

本来ならこれでビルドされたsifファイルがダウンロードされてくると思うのですが，私の環境ではいくらやてもうまくいきませんでした・・・

なぜか途中でエラーが出てしまいました．

## remote buildがうまくいかないその後(追記)
Remote buildができたというコメントをいただき（感謝），私の方でもうまくいくかどうか検証してみました．

結局詳しいことはわかりませんでしたが，以下のような傾向をつかむことができました．

- イメージファイルが512MBを超えると，ビルド後のダウンロードで’out of memory’例外を投げ，終了する
- 上記サイズより小さければおそらく成功する（実際にDockerhubのUbuntu:latestイメージにいくつかパッケージを入れたものは200MB程度でビルドできた）

また，GitHubに気になるissueがありました．
[Out of memory exception when remote building #4394](https://github.com/sylabs/singularity/issues/4394)
ここの議論を見ると，どうやらクラウドのシステム側の問題の可能性もあるようです．

以下、実際に手元で生じた症状です．
Vagrantでsingularity 3.3.0-1の環境を立ち上げ，以下のdefファイルをリモートビルドしました．

```text:hope.def
Bootstrap: docker
From: chainer/chainer:v6.1.0-python3


%post
    export DEBIAN_FRONTEND=noninteractive
    apt update

%labels
    Version v2.0.0
```

以下のように例外を投げて終了してしまいます．

```text
$ singularity build —-remote hoge.sif hoge.def

（略）

INFO:    Adding labels
INFO:    Creating SIF file...
INFO:    Build complete: /tmp/image-648649911
WARNING: Skipping container verifying
512.00 MiB / 2.08 GiB   24.08% 351.70 MiB/s 00m03sfatal error: runtime: out of memory


runtime stack:
runtime.throw(0x131262e, 0x16)
        /usr/local/go/src/runtime/panic.go:617 +0x72
runtime.sysMap(0xc084000000, 0x80000000, 0x1db1eb8)
（略）
FATAL:   While performing build: build image size <= 0
```

結局，詳しいことは私にはよくわかりませんでした．
現状，リモートビルドは軽めのイメージ生成に用いるくらいが良いのかもしれません．

# matplotlib入りchainerコンテナを作ってみた
DockerHubで公開されているchainerコンテナはmatplotlibやOpenCVが入っていなかったので，これらを入れたコンテナを作成しました．まだちゃんと動くかは検証しきれていませんが・・・

```txt:chainer.def
# Chainer container for singularity

Bootstrap: docker
From: chainer/chainer:latest-python3

%post
    apt update
    apt install -y libpng-dev libjpeg-dev libtiff-dev python3-tk libglib2.0-0 libsm6
    export LC_ALL=C
    pip3 install matplotlib pandas scipy scikit-learn tqdm opencv-python

%label
    Author horithe3rd
    Version v1.0.2
```

[GitHub](https://github.com/HoriThe3rd/singularity_chainer/tree/fcd5500e8fa2427ef7079bb1d9b19b8d2cb621bb)に公開しました．

# コンテナ内でのファイル書き換えについて (2019年4月29日追記)
Singularityのコンテナファイル"hoge.sif"は基本的には読み込み専用として扱われます．しかし，コンテナ内でパッケージのインストールなどを行いたいことがあります(試行錯誤しながら環境構築するときなど)．そんな時はsandboxとしてコンテナをビルドします．

なお，こちらの項目は公式ドキュメント[^1]の"Build images from scratch"の項目を参考にしています．詳細についてはそちらを参照してください．

## 書き込み可能コンテナのビルド
書き込み可能コンテナを作る場合は，sandboxオプションをつけてbuildします．例えば，上で紹介したchainerコンテナの場合を考えます．

```shell
$ sudo singularity build --sandbox ch_sandbox docker://chainer/chainer:latest-python3 # --sandboxオプションは-sと省略可
```

とすると.sifファイルの代わりにch_sandbox(これはコマンドで指定した名前)のディレクトリが生成されます．ディレクトリとして中に潜ることも可能ですが，.sifファイルと同じようにch_sandboxをコマンドに渡すことでshellに入れます．ここで，"--writable"オプションをつけると書き換え可能な形でコンテナにアクセスできます．オプションをつけなければ読み込み専用です．

```shell
$ singularity shell ch_sandbox #.sifファイルと同じ扱い
$ sudo singularity shell --writable ch_sandbox #書き換え可能
```

このようにしてコンテナに入れば，中でapt installも可能ですので，色々とパッケージをインストールしてみたりしてカスタマイズできます．ただし，root権限の必要な作業をする場合はsudoをつける必要があります．

## sandboxからsifへの変換
一通り，コンテナをカスタマイズし終わって，一つのイメージファイルに固めたくなったら，buildコマンドで変換できます．

```shell
$ singularity build my_chainer.sif ch_sandbox
```

# 終わりに
今回は自作のコンテナをdefファイルからビルドしてみる方法をまとめました．
自分用コンテナを作って，ローカルでもハイパワーなサーバでも同じ環境がすぐ手に入ります，

本記事では，defファイルの書き方についてはまとめませんでしたが，どこかの機会でまとめるかもしれません（今まさに勉強中なのでわからないことだらけです・・・）．

また，サーバ管理者としては，ユーザが自分で自由に環境を作ってくれるので，色々とインストールを頼まれることが減ると嬉しいなぁと思います．

[^1]: [公式ドキュメント](https://www.sylabs.io/guides/3.0/user-guide/quick_start.html#build-images-from-scratch)