Docker on WSL2 環境構築メモ (6/x) - Jupyter Notebook への .NET Interactive 追加導入

前提

導入対象とするマシンは下記の通り。

  • Hardware : CPU = i10900, GPU = GeForce RTX 2080Ti, Mem = 32GB, SSD 1 TB + HDD 2TB
  • ベース OS : Windows 10 Pro バージョン 20H2 (ビルド 19042.685)
  • 仮想 OS : Ubuntu 20.04 LTS on WSL2
  • Docker on WSL2 環境構築メモ (4/x) の手順を終えている

Jupyter コンテナへの .NET Interactive (C# / F# / PowerShell) および Jupyter Lab 導入

Jupyter 上で C# 9.0 と Python を同時に利用したいため、Docker on WSL2 環境構築メモ (4/x) - Tensorflow (CPU 版) 導入 - Crayon's Monologue で作成した Tensorflow + Python3 + Jupyter のコンテナへ .NET Interactive を追加導入する。Jupyter で複数言語を扱うなら Notebook ではなく Lab にしておいた方が便利であるため、Lab も追加導入する。

起動ユーザ (UID) 指定がなければ docker は既定では root 権限でコンテナを起動する。ユーザを切り替えてインストールするものが一部あるが、該当箇所までの一連の手順は root 権限で行う。

実施前確認

追加導入するもの

jupyter labpip install
nodejsapt installバージョン 12.0 以上を求められる
wget
dpkg
apt install
apt-transport-httpsapt install
dotnet-sdk-5.0apt installバージョン 5.0 が利用可能
dotnet-interactivedotnet tool installバージョン 1.0.210803 が利用可能
dotnet-interactive Jupyter Kerneldotnet-interactive install

オリジナルの docker-compose.yml
version: '2'
services:
  tensorflow:
    image: 'tensorflow/tensorflow:latest-py3-jupyter'
    restart: always
    ports:
      - 'XXXX:8888'
    volumes:
      - /mnt/... XXXXX .../JupyterNotebooks:/tf
    command: /bin/bash -c "source /etc/bash.bashrc && jupyter notebook --notebook-dir=/tf --ip 0.0.0.0 --no-browser --allow-root --NotebookApp.token='XXXXXXXXXXXXXX'

Jupyter Lab インストール手順

Jupyter Lab インストール手順

ベースとしている tensorflow/tensorflow:latest-py3-jupyter イメージが pip ベースであるため pip をアップデートをしてから Lab をインストールする。

#install
pip install --upgrade pip
pip install jupyterlab

# comfirm
jupyter lab --help

引数を同じにして jupyter notebook の代わりに jupyter lab をすれば Jupyter Lab が起動する。(後ほど、コンテナのスナップショットをとって) docker-compose.yml の command: を書き換える。

node.js インストール手順

Jupyter Lab を快適に使うために Jupyter Lab Extension を入れたくなる。その際に node.js が必要になるため、あらかじめインストールしておく。

cd ~
curl -sL https://deb.nodesource.com/setup_12.x | bash -
apt update && apt upgrade
apt install nodejs

.NET 5.0 インストール手順

作業準備として .NET 5.0 パッケージをインストールする。

# install wget & dpkg 
apt update && apt upgrade
apt install wget && apt install dpkg

# download package management file
cd ~
wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
dpkg -i packages-microsoft-prod.deb

# install apt-transport-https
apt update && apt upgrade
apt install apt-transport-https -y

# install dotnet sdk 5.0 & dotnet-interactive
apt update && apt upgrade
apt install -y dotnet-sdk-5.0

ユーザ設定手順

ここから先はユーザ依存の環境構築になる。root でも構築できるが、Jupyter Lab はターミナルコンソールも起動可能であり docker コンテナ内を root で何でもできてしまうようになるため、Jupyter 起動用のユーザを作成し、そちらで docker を起動するようにする。

# 1) change root password for security
passwd

# 2) make a new user
adduser {user}
cat /etc/passwd       # check the new user's UID and GID for later use

# 3) add root user to the new user's group 
adduser root {user}
id root               # check whether root belongs to the new group

# 4) delegate jupyter lab extensions directory to the new group
chgrp {user} /usr/local/share/jupyter/lab -R
chmod 775 /usr/local/share/jupyter/lab -R

# 5) change user (to continue installation)
su {user}
echo ''     >> $HOME/.bashrc
echo 'cd ~' >> $HOME/.bashrc
source ~/.bashrc

Jupyter Lab 環境ができ上がると Jupyter のページタブから便利拡張機能を追加できるようになるが、そのままでは Jupyter 起動用ユーザが /usr/local/share/jupyter/lab に書き込み権限を持たないため、上記 3) 4) にて当該ディレクトリを (root も既存権限を保持したまま) 権限移譲する。

以上の設定で Jupyter ページタブから extension 自体は追加・削除できるようになるが、ページタブからは (おそらく上述のディレクトリ配下に extension 関連のサブ) ディレクトリを作成するのに失敗するため、初回だけコマンドラインから機能追加しておく。サンプルは目次作成機能。

jupyter labextension install @jupyterlab/toc

.NET Interactive インストール手順

続いて、前節で作成したユーザで .NET Interactive → C#, F#, PowerShell の Jupyter カーネルという順にインストールしていく。

# confirm in advance
jupyter kernelspec list

# expected result is ...
#    python3            /usr/local/share/jupyter/kernels/python3

# install dotnet-interastive
dotnet tool install --global Microsoft.dotnet-interactive --version 1.0.210803

# install .NET-related Jupyter kernels
~/.dotnet/tools/dotnet-interactive jupyter install

# reconfirm
jupyter kernelspec list

# expected result is ...
#    .net-csharp        $HOME/.local/share/jupyter/kernels/.net-csharp
#    .net-fsharp        $HOME/.local/share/jupyter/kernels/.net-fsharp
#    .net-powershell    $HOME/.local/share/jupyter/kernels/.net-powershell
#    python3            /usr/local/share/jupyter/kernels/python3

# return to root
exit

環境設定

インストールは完了なのだが、コンテナを再起動して Jupyter にアクセスすると C# / F# / PowerShell がメニューにあるものの、コンパイルしてくれず、Starting Kernel. Please Wait ... と表示される。参考記事/ブログ等にはインストールは簡単だと書いてあるだけで起動しない事例は見当たらず、ここからが悩みどころ。結論としては、機能させるために次の2つが必要となる。

  • dotnet-interactive にパスが通っていないため、環境変数設定をしてパスを通す
  • docker-compose.yml が command: で起動するプロセス (コンテナ内の pid = 1) にその環境変数設定を反映させる

Jupyter のプロセスに環境変数が効いておらず dotnet-interactive コマンドが command not found になっているのは容易に想像がつくが、この 2 点目に気付くのがなかなか難しい。

Jupyter が当該コンテナでどう起動されているか、/etc/init.d/ にシェルスクリプトをおいても自動起動しないこと、コンテナ内に入ってからサービス起動すると pid = 1 にならないこと *1、docker-compose.yml を編集するために (コンテナのスナップショットをとらずに) docker-compose down してしまうとコンテナ内で行った環境変数設定が消えてしまう、等々を理解してようやくたどりつく結論である。

結局、環境変数command: で起動される jupyter 以前に、余分な fork をしないで行うのがよく、/etc/bash.bashrc の末尾に追記するのがよいということになった。

# 環境変数の設定
echo ''                                         >> /etc/bash.bashrc
echo '# Environmental settings'                 >> /etc/bash.bashrc
echo 'export PATH=$PATH:$HOME/.dotnet/tools'    >> /etc/bash.bashrc
echo 'export DOTNET_TRY_CLI_TELEMETRY_OPTOUT=1' >> /etc/bash.bashrc

というわけで、上記のとおり環境変数設定の準備をする。2つめの環境変数 DOTNET_TRY_CLI_TELEMETRY_OPTOUTMicrosoft .NET Interactive の利用状況を情報提供しないという設定。

docker-compose restart をすれば環境変数が反映された形で Jupyter が起動する、が、この設定はいま alive であるコンテナでしか活きていないため restart (してサービスが動き出し、永続化されたものと勘違いして docker-compose down) する前にコンテナのスナップショットを取っておく。

コンテナの整備

環境変数保全してコンテナ稼働開始時に最初に起動するプロセスに反映させなければならないため、意外に大事であるコンテナ整備。その手順をここに記す。

コンテナ内部の整理

コンテナのスナップショットをとる前に、コンテナ内の不要なファイルは削除する。

apt clean
rm -rf /var/lib/apt/lists/*
コンテナ整備の手順

コンテナから外に出て、下記の作業を行う。docker-compose stopdocker-compose down を間違わないように。

# stop the running container (DO NOT make it down)
docker-compose stop

# preserve snapshot
docker commit {container id} {image:tag}

# abandon the container brefore editing docker-compose.yml
docker-compose down

# edit docker-compose.yml
editor docker-compose.yml

# start a new container derived from the preserved image
docker-compose up -d

docker-compose.yml の書き換えるべきは3か所。

  • 参照するイメージを変更する
    image: 'tensorflow/tensorflow:latest-py3-jupyter-yyyymmdd'
  • Docker 起動ユーザを変更する
    user: "{uid}:{gid}"
  • 起動コマンドを jupyter note から jupyter lab に変更し --allow-root オプションを外す
    command: /bin/bash -c "source /etc/bash.bashrc && jupyter lab --notebook-dir=/tf --ip 0.0.0.0 --no-browser --NotebookApp.token='XXXXXXXXXXXXXX'

これで Jupyter Lab + C# 9.0 + Python3 + Tensorflow の同時利用ができる。ちなみに python で次のように書くことにより JavaScript も実行できる。

from IPython.display import HTML

javascript = '''
<script type="text/javascript">
    alert("alert output");
    document.write("html output");
</script>
'''

HTML(javascript)

*1:ユーザが docker exec -it /bin/bashインタラクティブに実行した場合はもちろん、command: 起動のプロセスがコンテナ内部でシェルスクリプトをキックしても fork になるせいかダメらしい。