これはQiitaアドベントカレンダー2019 Jupyter Notebookの8日目の記事です。
Jupyer Kernelって一つのDockerイメージに何個まで詰め込めるのでしょうか?
これってトリビアになりませんか?
今日のトリビア
Jupyter Kernelは一つのDockerイメージに??個まで詰め込める。
実験環境
実験は次のような環境で行なった。
項目 | 項目 |
---|---|
ホストOS | Ubuntu 18.04 |
ホストCPU | 少し前のIntel Core i7 8コア |
ホストメモリ | 16GB |
コンテナ環境 | Docker 19.03 (Rootless) |
ベースイメージ | Fedora |
実験方法
Dockerでカーネルを沢山繋ぐにはいくつの方法がある。
-
/.local/share/jupyter/kernels
配下にカーネルの設定ファイルを書く - remote_ikernelを使ってssh経由でカーネルを起動し接続する
1番目の方法の利点は、Jupyterがデフォルトで提供している設定方法であり安定しているというところ。しかし、カーネルを沢山詰め込むとなるとカーネル同士の干渉が発生することがある。例えばAnaconda環境下であるカーネルをインストールすると、他のAnacondaでインストールしていないカーネルもAnaconda(正確にはConda-forge)が提供するリンカを利用して実行されることになり、Cのランタイムライブラリのバージョンが一致しないと実行に失敗することがある。これを避けるにはAnacondaをインストールしないことが解決策になるが、カーネルのインストールが若干煩雑になる。
2番目の方法の利点は、SSHで経由でカーネルを実行できるのでDocker-compose等でJupyter-kernelのクラスタを構成することができるという点がある。つまり、Jupyterはremote-ikernelが提供するカーネルを呼びremote-ikernel自身はプロキシーとなりsshで接続したJupyter Kernelに入力を渡すという形である。しかし、この方法はいくつかのカーネルでうまく動作しない。これは、古いJupyter KernelのAPIを参照していたりlocalhostを前提に設計されたKernelでKernelのシャットダウンがうまく行かないなどの問題がある。
そこで今回は1番目の方法を採用した。Anacondaは使わずFedoraが提供するパッケージやプログラミング言語のコミュニティが提供するパッケージマネージャ(npm, pip等)のみを使ってJupyter環境を構成した。
Jupyter Kernelが大量になるにつれてDockerイメージのビルド時間が長くなっていったのでこれに対処するためにマルチステージビルドを導入しカーネルごとにビルドを行い、最終的にカーネルの実行に必要な部分をメインのDockerイメージにコピーするという方針にした。結果的に初回ビルド時のコンパイル時間は変わらないが、2回目以降のビルドでビルド済みのカーネルはDockerfileの記述に独立してキャッシュされるようになり、大幅な時間短縮となった。
また、Jupyter Kernelとは関係ないがJupyter LabにはLaTeXを編集する拡張機能があるので、ついでにTeXLiveも追加してみた。
実験結果
最終的な成果物はこちら jupyter-metropolis
結果として37のプログラミング言語のJupyterKernelを追加することに成功した。LaTeXの編集機能と加えると実に38のプログラミング言語を編集することが可能となっている。対応言語は以下の通りである。
[jovyan@1ca444c5adb0 ~]$ jupyter kernelspec list
Available kernels:
kotlin /home/jovyan/.ipython/kernels/kotlin
.net-csharp /home/jovyan/.local/share/jupyter/kernels/.net-csharp
.net-fsharp /home/jovyan/.local/share/jupyter/kernels/.net-fsharp
agda /home/jovyan/.local/share/jupyter/kernels/agda
bash /home/jovyan/.local/share/jupyter/kernels/bash
c /home/jovyan/.local/share/jupyter/kernels/c
clojupyter-0.2.3 /home/jovyan/.local/share/jupyter/kernels/clojupyter-0.2.3
common-lisp /home/jovyan/.local/share/jupyter/kernels/common-lisp
coq /home/jovyan/.local/share/jupyter/kernels/coq
fortran /home/jovyan/.local/share/jupyter/kernels/fortran
go /home/jovyan/.local/share/jupyter/kernels/go
groovy /home/jovyan/.local/share/jupyter/kernels/groovy
haskell /home/jovyan/.local/share/jupyter/kernels/haskell
ielixir /home/jovyan/.local/share/jupyter/kernels/ielixir
iperl /home/jovyan/.local/share/jupyter/kernels/iperl
java /home/jovyan/.local/share/jupyter/kernels/java
jslab /home/jovyan/.local/share/jupyter/kernels/jslab
julia-1.2 /home/jovyan/.local/share/jupyter/kernels/julia-1.2
lua /home/jovyan/.local/share/jupyter/kernels/lua
ocaml /home/jovyan/.local/share/jupyter/kernels/ocaml
octave /home/jovyan/.local/share/jupyter/kernels/octave
pari_jupyter /home/jovyan/.local/share/jupyter/kernels/pari_jupyter
perl6 /home/jovyan/.local/share/jupyter/kernels/perl6
powershell /home/jovyan/.local/share/jupyter/kernels/powershell
prolog /home/jovyan/.local/share/jupyter/kernels/prolog
racket /home/jovyan/.local/share/jupyter/kernels/racket
ruby /home/jovyan/.local/share/jupyter/kernels/ruby
rust /home/jovyan/.local/share/jupyter/kernels/rust
scala /home/jovyan/.local/share/jupyter/kernels/scala
singular /home/jovyan/.local/share/jupyter/kernels/singular
tslab /home/jovyan/.local/share/jupyter/kernels/tslab
vim_kernel /home/jovyan/.local/share/jupyter/kernels/vim_kernel
gap-4 /usr/share/jupyter/kernels/gap-4
ir /usr/share/jupyter/kernels/ir
python3 /usr/share/jupyter/kernels/python3
python3-jupyroot /usr/share/jupyter/kernels/python3-jupyroot
sagemath /usr/share/jupyter/kernels/sagemath
上記はカーネル名なので、改めてプログラミング言語を列挙すると、
Kotlin, C#, F#, Agda Bash, C, C++(jupyroot), Clojure, Common Lisp, Coq, Fortran, Go, Groovy, Haskell, Elixir, Perl, Java, JavaScript, Julia, Lua, OCaml, Octave, PARI, Raku(旧Perl6), PowerShell, Prolog, Racket, Ruby, Rust, Scala, Singular, TypeScript, VimScript, GAP, R, Python3, Sage, (LaTeX)
である。ほかにGaucheやGuileといったScheme系等のカーネルもあったが今回はビルド環境(特にGuileカーネルの方)を整えるのが億劫だったこと、厳密には同一ではないが下記のようにRacketの互換ライブラリで十分だったことから取り入れていない。
(import (rnrs lists (6))
(rnrs base (6))
(rnrs io simple (6))
合計で20GBほどの巨大なDockerイメージが完成した。自分のプライベートレジストリにあげるのが精一杯でGithub Package RegistryやDocker Hubにはアップロードすることができなかったので、各自でイメージをビルドしてローカルネットワーク上で共有するなどして使用するのが良いと思われる。
まとめ
こうしてこの世界にまた一つ新たなトリビアが生まれた。
Jupyterカーネルは一つのDockerイメージに37個まで詰め込める。