KNOWLEDGE - COLUMN ナレッジ - コラム

DockerでJupyterLabの環境を作ろう

先端技術部
テクニカルスペシャリスト 黒住 好忠

こんにちは。テクニカルスペシャリストの黒住です。
昔は知っている人も少なかったJupyter Notebookですが、Pythonを使ったデータ分析や機械学習の広がりもあり、今では一般的なツールとして認識されるようになりましたね。Python関連の書籍には「Jupyter Notebook対応」という言葉が、当たり前のように書かれていたりします。

JupyterLab

認知度の高いJupyter Notebookですが、Jupyter Notebookを更に使いやすくした「Jupyter Notebookの後継版」とも言える、JupyterLabというアプリケーションが存在しています。
私は2018年頃からJupyterLabを使っていますが、これまでに何度もバージョンアップを繰り返し、今では「JupyterLab 3.xシリーズ」として更に使いやすく進化を遂げています。また、以前は拡張機能を利用するためにNode.jsが必須でしたが、現在ではNode.js無しでも動作するようになっています。(ただし、古い拡張機能を使う場合は、依然としてNode.jsが必要になるケースもあります)
JupyterLabでは、複数ファイルのタブ表示やコードのデバッグ、拡張機能による様々な機能の追加が可能になります。もちろん、Jupyter Notebookで作成したipynb形式のファイルも、そのままJupyterLabで開くことができます。
下の図は、私が利用しているJupyterLabの画面イメージです。
JupyterLabの画面イメージ

JupyterLabを使って気になるところ

JupyterLabの導入自体は、 pip install jupyterlab というコマンド1つで簡単にできてしまいますが、実際に使っていると気になる点がいくつか出てきます。
Pythonの開発では、プロジェクトごとに専用のPython環境を作ることが多いのですが、この時、JupyterLabも各環境ごとに導入する必要があります。JupyterLabをデフォルト設定まま利用するのであれば問題ありませんが、実際は拡張機能の設定や、フォント等のカスタマイズ、ショートカットキーの設定など、いくつか個別にカスタマイズしたい設定が出てきます。
新しい環境を作る度にJupyterLabのカスタマイズを行うのも面倒なので、今回は「DockerとJupyterLabを組み合わせて、カスタマイズ済みのJupyterLab環境を簡単に展開する方法」をご紹介します。

バージョン情報

利用するソフトウェアのバージョンによっては、仕様が異なる可能性もあるので、今回使用した主要なソフトウェアのバージョンを記載しておきます。
これらのバージョンは、この記事を執筆した2021年10月時点での内容になります。
  • Windows 10 21H2 (Build 19043.1237)
  • Docker Desktop Ver 4.0.1 (68347)
    • Engine: 20.10.8
    • Compose: v2.0.0-rc3
  • Python 3.9.7 (python:3.9.7-buster)
    • jupyterlab 3.1.14
    • black 21.9b0
    • jupyterlab-code-formatter 1.4.10
    • jupyterlab-git 0.32.4
    • ipywidgets 7.6.5
    • jupyterlab-widgets 1.0.2
    • lckr-jupyterlab-variableinspector 3.0.9

構成

今回は「1つのPython環境を、1つのDockerコンテナ」で扱う構成にします。
コンテナが1つだけの場合でも、Docker単体で利用するよりdocker-composeを利用した方が簡単に取り扱えるので、今回はdocker-composeを採用しています。

Dockerコンテナの構成

Dockerコンテナの構成概要は下図のようになります。左側が実際に利用しているWindowsの構成、右側がコンテナの構成です。
JupyterLab上で作成するファイルとJupyterLabの設定を永続化しておくために、Windows側のフォルダとコンテナ上のディレクトリをマッピングしています。
また、JupyterLabへのアクセスはブラウザ経由で行います。(今回は8888ポートで接続していますが、ポート番号は設定ファイルで自由に変更できます)
JupyterLabへのアクセスイメージ

フォルダ構成

実際のフォルダ構成は以下のようになります。rootフォルダ(名前は何でも良い)配下に必要なファイルを集約する形にしているので、このrootフォルダをコピーすれば、新しいJupyterLabの環境を「必要な初期設定が完了した状態で」作成できるようになります。
Dockerコンテナの起動や終了は、コマンドを手で入力しても良いのですが、今回は簡単に操作できるように、専用のバッチファイルを用意します。
フォルダ構成イメージ

Docker用の各種ファイル

それでは、各ファイルを実際に作成していきましょう。
重要なファイルは、Dockerコンテナのメインとなる Dockerfile と、全体の構成を取りまとめている、docker-compose用の docker-compose.yml の2ファイルです。
up-down.cmd はdocker-composeを起動/終了するための簡単なバッチファイルです。

Dockerfile

ベースイメージは python:3.9.7-buster を使用しています。極端に古いバージョンでなければ、これ以外のイメージを選択しても問題ありません。
大きな流れとして、apt-getで必要なパッケージを導入し、その後JupyterLab関連のパッケージ、共通的に利用するPythonの基本的なパッケージ、そして追加パッケージを導入する形にしています。
Dockerは RUN でコマンドを実行する度に新しいレイヤーが作成される仕様なので、関連する処理は「1つのRUNでまとめて記述」しておくことをお勧めします。
Pythonの基本パッケージや追加パッケージは、私の好みでピックアップしたもの(主にデータ分析で利用するパッケージ群)なので、実際に利用する際は、各プロジェクトに合わせて書き換えてくださいね。
FROM python:3.9.7-buster
ARG DEBIAN_FRONTEND=noninteractive

# パッケージの追加とタイムゾーンの設定
# 必要に応じてインストールするパッケージを追加してください
RUN apt-get update && apt-get install -y \
    tzdata \
&&  ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime \
&&  apt-get clean \
&&  rm -rf /var/lib/apt/lists/*

ENV TZ=Asia/Tokyo

# JupyterLab関連のパッケージ(いくつかの拡張機能を含む)
# 必要に応じて、JupyterLabの拡張機能などを追加してください
RUN python3 -m pip install --upgrade pip \
&&  pip install --no-cache-dir \
    black \
    jupyterlab \
    jupyterlab_code_formatter \
    jupyterlab-git \
    lckr-jupyterlab-variableinspector \
    jupyterlab_widgets \
    ipywidgets \
    import-ipynb

# 基本パッケージ
# Pythonでよく利用する基本的なパッケージです
# JupyterLabの動作には影響しないので、必要に応じてカスタマイズしてください
RUN pip install --no-cache-dir \
    numpy \
    pandas \
    scipy \
    scikit-learn \
    pycaret \
    matplotlib \
    japanize_matplotlib \
    mlxtend \
    seaborn \
    plotly \
    requests \
    beautifulsoup4 \
    Pillow \
    opencv-python

# 追加パッケージ(必要に応じて)
# 各環境に特化したパッケージがある場合、この部分に追加します
RUN pip install --no-cache-dir \
    pydeps \
    graphviz \
    pandas_profiling \
    shap \
    umap \
    xgboost \
    lightgbm

docker-compose.yml

docker-compose用の設定ファイルになります。 このファイルは1度作ってしまえば、再編集するケースは少ないと思います。
Windowsとコンテナ側のフォルダのマッピングやポートの設定、JupyterLabの起動設定を行っています。
最後のコメントアウトしている部分は「Windows上のWSL2 Docker環境でGPU(CUDA)を利用したい場合」にコメントを外してご利用ください。ただし、GPUを使う場合は事前の準備作業が必要になるため、詳細は私が以前に書いた記事「Windows上のDockerでGPUを使おう」をご参照ください。
なお、以前は開発者向けのInsider Programを使う必要がありましたが、2021年10月5日に公開されるWindows 11は、「最初からWSL2でGPUが利用できる」ので、導入のハードルが下がりますね!(ただし、NVIDIAドライバのインストールは必要になります)
version: "3.9"
services:
  py3:
    build:
      context: ./py3
      dockerfile: Dockerfile
    restart: always
    entrypoint: >
      jupyter-lab
      --allow-root
      --ip=0.0.0.0
      --port=8888
      --no-browser
      --NotebookApp.token=''
      --notebook-dir=/workspace
    expose:
      - "8888"
    ports:
      - "127.0.0.1:8888:8888"
    volumes:
      - ./py3/root_jupyter:/root/.jupyter
      - ./workspace:/workspace

    # GPUを使う場合の設定
    # environment:
    #   - "NVIDIA_VISIBLE_DEVICES=all"
    #   - "NVIDIA_DRIVER_CAPABILITIES=all"
    # deploy:
    #   resources:
    #     reservations:
    #       devices:
    #         - capabilities:
    #           - gpu

up-down.cmd

vdocker-composeを起動/終了させるためのバッチファイルです。
最初に起動確認を行って、サービスが起動していなければ起動、既に起動していれば終了するような構造にしています。また、Dockerfileが変更された場合のことを考えて、起動時にビルド処理も組み込んでいます。
今回はコンテナが終了までコンソール画面を表示するようにしていますが、コンテナをバックグラウンドで動作させたい場合は docker-compose up の部分を docker-compose up -d に変更してください。
@echo off
setlocal

for /f "usebackq" %%X in (`docker-compose ps -q`) do set DCPS=%%X
if ""%DCPS%%""=="""" (goto __LAUNCH__) else (goto __SHUTDOWN__)

:__LAUNCH__
echo Launch...
docker-compose build
start http://localhost:8888
docker-compose up
goto __END__

:__SHUTDOWN__
echo Shutdown...
docker-compose down
goto __END__

:__END__

Jupyter Labの設定

ファイルの準備ができたら「up-down.cmd」を実行してJupyterLabを起動しましょう!
初回はDockerイメージのビルド処理が走るため、起動に時間がかかりますが、2回目以降は高速に起動するようになります。(利用するPythonのバージョンが新しいと、新しいバージョンに対応したビルド済みのパッケージが存在しないケースがあり、その場合はソースコードからのビルドが行われるため、初回起動時は十数分の時間がかかる場合もあります)
JupyterLabが起動したら、次はJupyterLabの設定を行います。
JupyterLabの設定内容は、Dockerコンテナ側とマッピングしている root_jupyter フォルダに永続化するようにしているので、次回以降も設定内容が引き継がれるようになります。
設定内容は各自の好みで良いのですが、参考までに私のお勧め設定を紹介します。

フォントのインストールと設定

Windowsで利用する場合、デフォルトのフォントは綺麗とは言えません。コードの読みやすさの観点からもフォントは重要になるため、「コーディングに適したフォント」をインストールして設定します。
なお、フォントは人の好みが分かれる部分でもあるため、既に好みのフォントがあるのであれば、そちらを使用していただいてかまいません。

フォントのインストール

以下のフォントをダウンロードして「Windows」にインストールします。フォントによっては書体やウェイトによってファイルが分割されている場合があるので、その場合は全てのフォントファイルをインストールしてくださいね。(フォントファイルを右クリックして「インストール」を選択するとインストールできます)

※以下外部サイト

フォントの設定

JupypterLabのメニューから、Settings> Advanced Settings Editor (Ctrl+,) を選択して、Settingsページを表示します。
左側の設定一覧から「Terminal」を選択し、右側の User Preferences に以下のコードを入力し、右上の保存アイコン「Save User Settings (Ctrl+S)」をクリックして保存します。この設定で、JupyterLabのターミナル画面のフォントが変更されます。
{
    "fontFamily": "'Roboto Mono', 'Migu 1M'"
}
続いて、Settingsページの左側の一覧から「Theme」を選択し、右側のUser Preferencesで “overrides” 部分を以下のように設定します。
この設定で、JupyterLabのコードブロック、およびMarkdown部分のフォントを変更できます。基本的に、コード部分は「等幅フォント」、Markdown(コンテンツ)部分は「プロポーショナルフォント」を利用します。
フォントサイズは、利用しているディスプレイの解像度によっても最適な値が異なるので、各環境に合わせて見やすいサイズに調整してください。なお、JupyterLabは、設定を保存すると「結果がすぐに反映される」ので、実際の結果を確認しながら細かい微調整が可能です。
{
    "overrides": {
        "code-font-family": "'Roboto Mono', 'Migu 1M', Consolas",
        "code-font-size": "10.5pt",
        "content-font-family": "'Migu 1C'",
        "content-font-size1": null,
        "ui-font-family": null,
        "ui-font-size1": null
    },

    "theme": "JupyterLab Light",
    "theme-scrollbars": false
}

コードフォーマッターの設定

JupyterLabでコードフォーマッター(コードの自動整形)を利用できるようにします。
Pythonのコードフォーマッターには、Black、YAPF、Autopep8 などが存在しますが、今回は設定項目が少なくて、なおかつ良い感じにフォーマットできる「Black」というフォーマッターを利用します。また Shift+Alt+F でBlackを使ったコードフォーマットを行えるように、ショートカットキーの登録も行います。
フォーマッター自体の詳細なパラメーターは、Settingsの「Jupyterlab Code Formatter」で変更できますが、今回はデフォルト設定をそのまま利用します。もし、フォーマッターの設定を独自にカスタマイズしたい場合は、User Preferencesに上書きしたい設定を追記してください。
続いて、ショートカットキーの登録を行います。 Settingsの「Keyboard Shortcuts」を選択して、右側の User Preferences に以下の設定を追加してください。この設定を行えば「Shift+Alt+F」キーを押すだけで、Blackを使ったコードフォーマットが行えます。(ちなみに、このショートカットキーは、VSCodeで採用されているコードフォーマットのショートカットキーと同じになります)

{
    "shortcuts": [
        {
            "command": "jupyterlab_code_formatter:black",
            "keys": [ "Shift Alt F" ],
            "selector": ".jp-Notebook.jp-mod-editMode"
        }
    ]
}

自動保存の無効化

デフォルトでは「自動保存」が有効になっているため、ファイルを開いて処理を実行するだけで、ファイルが上書き保存されてしまいます。好みにもよりますが、保存するタイミングを自分で選びたい場合は、自動保存の設定を無効化します。
自動保存の設定は、これまでの設定と同様に、Settingsの「Document Manager」で設定することも可能ですが、JupyterLabのメニューから「Settings > Autosave Documents」をクリックしてAutosaveのチェックを外す方法もあります。(こちらの方が簡単)
Autosave Documentsのチェックマークが消えていれば、自動保存が「無効化」された状態になっています。

使い方

一通りの設定を終えたら、一度JupyterLabを終了して、今回のrootフォルダ配下のバックアップを取っておきましょう。このファイルが、今後新しいJupyterLab環境を作成する際のテンプレートになります。
上記で作成した「up-down.cmd」を再度実行すれば、Dockerのコンテナをシャットダウンできます。ブラウザ上ではJupyterLabの画面が残りますが、「×」ボタンで閉じてかまいません。
これまでに設定した内容は「./py3/root_jupyterlab」フォルダ配下に保存されているので、次回の起動時にも、これまでの設定が引き継がれるようになります。

新しいJupyterLab環境の作成方法

新しいJupyterLab環境を作りたい場合は「今回作成したrootフォルダを丸ごとコピー」してリネームすれば、これまでの設定を引き継いだ状態で新しい環境を作成できます。(workspaceフォルダの中身は、不要であれば削除してかまいません)
新しい環境では、利用するPythonライブラリを変更したいケースがあると思いますが、その場合は「Dockerfileの内容を一部修正」するだけで対応可能です。もちろん、現在利用している環境で、新しいPythonパッケージを追加したくなった場合も、Dockerfileを修正してコンテナを再起動するだけで対応可能です。

JupyterLabの便利な機能

ここまでは、ポータブル性の高いJupyterLab環境の作成を中心にご紹介しましたが、JupyterLabの便利な機能についてもいくつか紹介したいと思います。

コードフォーマット

上記の「ショートカットキー」の設定ができていれば、コードブロック上で「Shift+Alt+F」キーを押すだけで、ソースコードのフォーマットが可能になります。
なお、フォーマッターにBlackを使っている場合は、文字列の括りは「ダブルクオーテーション」に変換されます。(内部にダブルクオーテーションを含む文字列を表現するためにシングルクオーテーションを使っている場合は、変換されずにそのまま残ります)

パフォーマンス計測

コードの処理時間を計測したい場合、コードブロックの最初に %%time と記入しておけば、自動でコードブロックの実行時間を計測できます。
また %time%timeit などを使って、特定処理のパフォーマンスや、指定された回数分、繰り返し実行したときのパフォーマンスなども計測可能です。
サンプルソース

コマンドの実行

JupyterLabのコードブロックからシェルコマンドを実行したい場合、実行したいシェルコマンドの先頭に ! マークを付けてコマンドを実行できます。コマンドの実行結果は、JupyterLabの実行結果に出力できるので、結果を確認したい場合にも便利です。
スポットでPythonライブラリを導入したいときも、JupyterLabのコードブロックの中に !pip install xxx と記載できます。なお、スポットでインストールしたライブラリは、コンテナを終了したタイミングで消えます。(この特徴を活かして、色々なパッケージの導入テストを行えます。万が一環境が壊れてしまっても、コンテナを再起動すれば元通りになります)
サンプル
また ! で実行した結果をPythonの変数に代入して、処理に使う事も可能です。( ! マークによる処理はJupyterLab側の機能なので、Pythonの文法上は正しくありません。そのため、コードフォーマッターを適用しようとするとエラーになります)
ソースサンプル

ヘルプの表示

コーディングしていると、関数の仕様や引数を調べたいケースが多々あります。関数名は覚えていても、細かい引数までは覚えていない…ということも、よくあります。
そんなときは、クラス名や関数名の後ろに ? を付けて実行すれば、簡単な説明を表示できます。デフォルト引数の値も確認できますし、わざわざ別ページでAPIドキュメントを調べる必要もないので、とても便利ですね。
なお ?? という形で、?マークを2つ付けると、更に詳細なヘルプが表示されます。
ソースサンプル

変数値の表示

コードセルを右クリックして「Open Variable Inspector」を選択すると、定義されている変数を一覧表示できます。別タブでコードと並べて表示できるので、ちょっとした変数の確認に便利です。
変数一覧イメージ

Notebook(ipynb)のインポート

JupyterLabを使っていると、共通的に利用する関数もNotebook(ipynb)形式で作成し、それを別のNotebookから利用したいケースが出てきます。
ipynbファイルのインポートは標準機能では実現できないのですが、「import_ipynb」というライブラリを使用することで、ipynbファイルを直接インポートして利用できるようになります。
ただし、最初にimportされると、次回以降はimportされない(インポート元のNotebookを変更しても再ロードされない)のでご注意ください。インポートした後に再ロードしたい場合は del でimportしたオブジェクトを削除するか、JupyterLabのカーネルを再起動してください。
イメージ

最後に

今回は、DockerとJupyterLabを使った環境作成についてお伝えしましたが、いかがでしたでしょうか。
上記でご紹介した内容以外にも、JupyterLabには多くの機能や拡張機能が存在しています。また、追加のパッケージを導入すると、JupyterLab上で、RやJuliaなど、Python以外の言語も扱えるようになります。
JupyterLabは奥が深くて、とても便利なツールなので、ぜひ活用してみてくださいね。
それではまた、次回のコラムでお会いしましょう。

当サイトの内容、テキスト、画像等の転載・転記・使用する場合は問い合わせよりご連絡下さい。

エバンジェリストによるコラムやIDグループからのお知らせなどを
メルマガでお届けしています。

メルマガ登録ボタン

黒住 好忠

株式会社インフォメーション・ディベロプメント 先端技術部 テクニカルスペシャリスト

この執筆者の記事一覧

関連するナレッジ・コラム

どのインスタンスを選べばいいの?EC2性能比較!

AIがプログラミングする時代の到来!?

残された攻撃の痕跡を追え! ~スレットハンティングのススメ~