LoginSignup
18
11

More than 5 years have passed since last update.

JupyterHub + dockerspawnerでユーザごとにマウントするボリュームを変える

Last updated at Posted at 2019-07-20

JupyterHub + dockerspawnerでユーザごとにマウントするボリュームを変える

はじめに

Jupyter Notebook便利ですよね。
Jupyter Hubを使うと簡単にマルチユーザ環境も構築できて、ちょっとやって見る用途だと簡単にいい感じの環境が作れます。
そしてdockerspawnerを使えばユーザ単位で空間を分離できるし、あれやこれやできて大変便利な環境は作れます。

しかし、ユーザ単位で設定を切り替える、例えばプロジェクトAに所属するユーザAとユーザBだけ特定のディレクトリを見せる等を考え出すととたんに標準的に用意されている機能だけではうまくいく気がしなくなってきます。

ここでは、どのようにしてユーザ単位での処理を制御するのかを記載します。

前提条件

デザイン

dockerdspawnerを利用して個々のユーザの環境を分離します。
また、ユーザ認証はPAM認証(Linuxローカルユーザ)を利用して、ユーザの区別はユーザグループにて行うことにしました。
今回はproject-で始まるグループを持っているユーザのみはそのグループ名に該当するボリュームを割り当てることにします。
例えばproject-Aで始まるユーザはproject-Aという名前のボリュームをマウントします。

また、コンテナ内のディレクトリ設計は以下の通りとします。

# Directory 説明
1 /root/workspaces/ notebookのルートディレクトリ
2 /root/workspaces/personal 個人用ディレクトリ(volumeをマウント)
3 /root/workspaces/project プロジェクト単位の共有ディレクトリを設置

プロジェクト単位の共有ディレクトリは、例えばプロジェクト名がproject-Xだった場合、/root/workspaces/project/Xのような形でマウントされることにします。

構築

Dockerコンテナの用意

上記に従って、Dockerコンテナを用意します。
ベースイメージとして、jupyter/scipy-notebookを利用しますが、jovyanユーザだといろいろ面倒なのでrootユーザに切り替えちゃってます。

以下、同等のものをgistにおいてあります。

FROM jupyter/scipy-notebook

USER root
RUN mkdir -p /root/workspace/personal /root/workspace/project
WORKDIR /root/workspace
CMD ["jupyterhub-singleuser", "--allow-root"]

jupyterhub(のdockerspawner)から利用する場合、最後のCMDはほぼ固定的なことに注意してください。

buildは以下のように実施してください。

docker build -t TAGNAME .

ここで指定したTAGNAMEをjupyterhubのconfigで指定します。

Linuxユーザ&グループの用意

今回は試しに以下設定とします。

# User Group(デフォルト以外)
1 user-a project-a
2 user-b project-a,project-b
3 user-c -

ユーザ数もグループ数も大したことがないので、ここは淡々と作成します。

ユーザ作成
adduser user-a
adduser user-b
adduser user-c
グループ作成
groupadd project-a
groupadd project-b
ユーザをグループへ追加
usermod -aG project-a user-a
usermod -aG project-a user-b
usermod -aG project-b user-b

dockerの前準備

ネットワークの準備

jupyterhub + dockerspawnerを使うとき、docker networkのどこに所属させるかを指定する必要があります。
ここでは「jupyter-network」という名前のネットワークを作成しています。

ネットワークを作成
docker network create jupyter-network

私はここにこだわりはなかったのでデフォルトのままですが、もっと細かい条件を設定しても大丈夫なはずです。

ボリュームの用意

グループに対応するボリュームを作成します。

docker volume create project-a
docker volume create project-b

ちなみに、ボリュームを作らないとデフォルトの場所に勝手に作ってくれるので、実はこの手順は飛ばしてしまっても大丈夫です。
もし、デフォルトではない場所、例えばNFSなどの共有ディスク上に作りたい場合は個々で作成しておいたほうが良いでしょう。

関連するパッケージのインストール

pip3 install jupyter_client dockerspawner

Jupyterのコンフィグ作成

ここがキモです。
ふつうにやったらできないことなので、DockerSpawnerの継承したMyDockerSpawnerというクラスを作成し、startメソッドをoverrideして、そこに処理を書き込みます。
その処理が終わったら元のstartメソッドを呼び出す必要があるため、最後に「return super().start()」を呼び出します。

jupyterhub_config.py(抜粋)
from dockerspawner import DockerSpawner
import pwd, os, grp
class MyDockerSpawner(DockerSpawner):
    def start(self):
        # ユーザごとにマウントするボリューム処理ここから
        name = self.user.name
        user_data = pwd.getpwnam(name)
        gid_list = os.getgrouplist(name, user_data.pw_gid)

        self.volumes['jupyterhub-user-{username}'] = notebook_dir + '/personal'

        for gid in gid_list:
            gname = grp.getgrgid(gid).gr_name
            if gname.startswith("project-"):
                dirname = gname.replace("project-", "")
                self.volumes[gname] = notebook_dir + "/project/" + dirname
        # ここまで
        return super().start()

c.JupyterHub.spawner_class = MyDockerSpawner

処理自体はなんてことないですね。
単純にユーザ名からグループ名のリストを取得し、その中からproject-ではじまるものがあったらself.volumesにマウントするボリュームを追加しています。
作成したクラスを「c.JupyterHub.spawner_class」に指定します。

フルのコンフィグはここにおいてあります。

起動とテスト

起動

起動は通常のJupyterHubと同じです。以下のようにしてください。

jupyterhub --config jupyterhub_config.py

テスト

各ユーザでログインして、project配下を確認してください。

  • user-a

    • aディレクトリのみ見えている user-a.png
  • user-b

    • a, b両方が見えている user-b.png
  • user-c

    • aもbも見えていない user-c.png

最後に

いかがでしたでしょうか。
少しだけpythonのコードを書かなくてはいけないですが、Jupyter使う方からすれば特に問題ないレベルの操作なのかなと思っており、非常に簡単にユーザ単位の制御ができることがわかると思います。

今回はグループごとにディレクトリをつけるという話でしたが、それ以外にも色々な使い方ができると思うので是非参考にご活用ください。

18
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
11