Docker / Singularity の実用面での違い
Singularity と Docker の互換性の問題
Singularity は、 Docker image を変換してSingularity image として実行することができる機能を有している。Singularity が導入されているマシンでは、no-root でDocker image を実行することができる。このほかにも、Singularity は、明示的にマウントをしなくとも、カレントディレクトリが、Singularity コンテナ内のワークディレクトリとなるように、Docker に比べてファイルシステムの isolation 制約が一部緩和されているなど、特定の用途にとっては便利なことが多い。しかしながら、Singularity と Docker を同一視して扱ってしまうと色々な落とし穴にはまる。ここでは、そのいくつかを紹介したい。
Docker run vs Singularity run
まず、docker run と singularity run は異なるインターフェースになってるので注意。例えば、dockerhub 上にある同一のイメージに対して、同じ引数でを実行する際でも、以下のようになる。
- Docker 上で、 DockerfileでEntrypoint に指定しているコマンドに対して引数を与えて実行したいとき
$ docker run --rm <image_name> 1 2 3 4
- Singularity 上で、 DockerfileでEntrypoint に指定しているコマンドに対して引数を与えて実行したいとき
$ singularity -s run docker://<image_name_on_docker_hub> 1 2 3 4
- Docker 上で、 DockerfileでEntrypoint に指定しているコマンドを無視して別のコマンドを実行したいとき
$ docker run --entrypoint <another_command> --rm <image_name> 1 2 3 4
- Singularity 上で、DockerfileでEntrypoint に指定しているコマンドを無視して別のコマンドを実行したいとき
$ singularity -s exec docker://<image_name_on_docker_hub> <another_command> 1 2 3 4
このように、実用的な面からは、 Singularity では、run
と exec
の間でコマンドの役割が分離しているとみることができるだろう。
次に、Dockerfile で latest と指定していても、あるいはdocker run で latest と指定していても、 dockerhub を毎回みにいっているわけではなく、あくまでローカルにある image の最新の docker image を利用しているに過ぎないので、dockerhub 上の更新が反映されない。これに比べて、Singularity は、 latest とタグを指定していると毎回 dockerhub に問い合わせに行くので、最新の docker image をもとに singularity image が構築される。
Docker image をそのままSingularity に持っていくと、image によってはエラーを出力することがある。例えば、neo4j のdocker を動かそうとすると、
$ singularity -s run docker://neo4j:latest [WARN tini (15358)] Tini is not running as PID 1 and isn't registered as a child subreaper. Zombie processes will not be re-parented to Tini, so zombie reaping won't work. To fix the problem, use the -s option or set the environment variable TINI_SUBREAPER to register Tini as a child subreaper, or run Tini as PID 1. /docker-entrypoint.sh: line 3: $1: unbound variable
上記のようなエラーが出る。個人的な印象だが、Singiularity で緩められた isolation の制約のために、isolation されていることを前提に構築された Docker image はそのままでは動かないようにみえる。これは、こういうDocker container はエラーを吐くというように、原因を一意に特定するのは難しく、試しに動作させてみてうまくいかなかったらやり直す、ということを繰り返すしてデバッグすることになりがちである。
また、Entrypoint で動作する、Docker 内のスクリプトを記述するときも、 Singularity であれば pwd
がマウントされているという想定だが、 Docker であればそうではないので、両方に対応したスクリプトを書くときに、どのディレクトリに自分が動かしたいデータが入っているかに留意して記述する必要がある。例えば、Docker image は WORKDIR には元から置いてあるデータが入っていて、マウントするディレクトリはWORKDIR とは別になるときに、Singularity ではWORKDIR がマウントされてしまっているので、WORKDIR に元から入っているファイルは使えなくなることがある、など。
Miscellaneous
Dockerfile を書くときに気になっていること
以下は未解決の問題である。
- build するときに、任意の途中の step の intermediate container をもとに再実行する方法はないのだろうか?
- どのbuild image を使えば良いのか分からない。公式がサポートしているようなライブラリでは、slim 版と alpine 版もしばしば提供されているが、どのbuild image を使うと良いか分からない。
実際には、そのライブラリ内部で依存しているライブラリやパッケージに基づいて、インストールがしやすいものを選ぶ、ということになりがちな気がする。片方から片方に移行しようとする際には、Debian と Alpine では、公式リポジトリから提供されているパッケージ名が異なることがあることから注意が必要である。
Docker で、Docker image の保存領域を変更しようと思った際に叩くコマンド
/app2# grep ExecStart /lib/systemd/system/docker.service ExecStart=/usr/bin/dockerd -H fd:// -g /app2/docker