AzureVMのDockerコンテナ内にJupyterhub環境を構築してみた

# はじめに
Azure VM上で、Dockerコンテナ内にJupyterHubを構築する手順をまとめています。　もちろんUbuntuのバージョンが古くため、需要はほぼないとは思っていますが、自身の忘備録として公開しました。

# 参考
以下のブログを参考にさせていただき、部分的にカスタマイズしています。

参考1

https://qiita.com/tomofu74/items/16c10017549d64c23d64#%E5%BF%85%E8%A6%81%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB

参考2

https://qiita.com/dyamaguc/items/db1da3084e36029f20cc

参考3

https://dev.classmethod.jp/articles/al2-jupyterhub/


# 環境
```bash
cat /etc/os-release
NAME="Ubuntu"
VERSION="20.04.6 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04.6 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal
```

```bash
$ docker --version
Docker version 26.1.3, build b72abbb
```


また、コンテナ内にはデフォルトでsudoがインストールされていないことを考慮しています。

### 1. Dockerコンテナの起動

まず、JupyterHubの公式イメージを使用してDockerコンテナを起動します。

```bash
docker run -it -p 7000:7000 --name jupyterhub-dev jupyterhub/jupyterhub bash
```

このコマンドは、`jupyterhub-dev`という名前のコンテナを起動し、ホストのポート7000をコンテナのポート7000にマッピングします。

### 2. コンテナ内での基本的なパッケージのインストール

コンテナ内で必要なパッケージをインストールします。

```bash
apt update && apt upgrade -y
apt install -y software-properties-common
add-apt-repository ppa:deadsnakes/ppa
apt install -y python3 python3-pip npm nodejs vim git iputils-ping net-tools wget curl python3-dev python3-venv python3-pycurl build-essential cmake clang libaio1 libffi-dev libfreetype-dev libpq-dev nano
apt install -y --no-install-recommends fonts-noto-cjk
apt autoremove -y && apt clean && rm -rf /var/lib/apt/lists/*
pip install configurable-http-proxy
```

これにより、Python 3やNode.js、開発ツールなどがインストールされます。
実は、筆者の仮想マシンのコンテナ内では、curlとwgetコマンドによる `configurable-http-proxy`のインストールができなかったので、pipでインストールしています。

上記コマンドによりインストールされるpythonバージョンは3.10.12です。
```bash
python -V
Python 3.10.12
```

### 3. sudoのインストール

コンテナ内で `sudo`を使用できるようにするため、`sudo`パッケージをインストールします。

```bash
su -
apt-get update
apt-get install -y sudo
exit
```

### 4. JupyterHubおよび関連パッケージのインストール

`sudo`を使用して、JupyterHub、JupyterLab、Notebook、SudoSpawnerをインストールします。

```bash
sudo python3 -m pip install jupyterhub jupyterlab notebook sudospawner
```

### 5. JupyterHubの設定

JupyterHubの設定ファイルを生成し、編集します。

```bash
jupyterhub --generate-config
nano jupyterhub_config.py
```

設定ファイル内に以下の内容を追加または編集してください。

```python
c = get_config()
c.JupyterHub.confirm_no_ssl = True  # SSLは使用しない
c.JupyterHub.base_url = '/'
c.JupyterHub.bind_url = 'http://0.0.0.0:7000'

# 管理ユーザーの指定
c.Authenticator.admin_users = {'admin'}
# ノートブックディレクトリの指定
c.Spawner.notebook_dir = '~/notebook'
# JupyterLabをデフォルトのインターフェースに設定
c.Spawner.default_url = '/lab'

# Spawnerの指定
c.JupyterHub.spawner_class = 'sudospawner.SudoSpawner'

# ログインが許可されているユーザーの指定
c.Authenticator.allowed_users = {'user01', 'user02'}
# 自動終了設定
c.NotebookApp.shutdown_no_activity_timeout = 30 * 24 * 60 * 60  # 30日間の非アクティビティでシャットダウン
c.MappingKernelManager.cull_idle_timeout = 3 * 24 * 60 * 60  # 3日間のアイドルでカーネルをクローズ
```

### 6. ユーザーとグループの設定

JupyterHub用のグループを作成し、ユーザーを追加します。

```bash
sudo groupadd jupyterhub
```

#### ユーザーの作成

```bash
adduser admin
adduser user01
adduser user02
```

#### ユーザーをグループに追加

```bash
sudo usermod -aG jupyterhub admin
sudo usermod -aG jupyterhub user01
sudo usermod -aG jupyterhub user02
```

#### グループの変更を適用

```bash
newgrp jupyterhub
```

各ユーザーのノートブックディレクトリを作成し、適切な権限を設定します。

```bash
sudo mkdir -p /home/admin/notebook
sudo chown admin:jupyterhub /home/admin/notebook

sudo mkdir -p /home/user01/notebook
sudo chown user01:jupyterhub /home/user01/notebook

sudo mkdir -p /home/user02/notebook
sudo chown user02:jupyterhub /home/user02/notebook

# ディレクトリのパーミッションを設定
sudo chmod 777 /home/admin/notebook
sudo chmod 777 /home/user01/notebook
sudo chmod 777 /home/user02/notebook
```
注: または、セキュリティ上の理由から、ディレクトリのパーミッションは770とし、必要なユーザーのみがアクセスできるように設定してもです。

### 7. ログディレクトリとファイルの設定

JupyterHubのログディレクトリとログファイルを作成し、適切な権限を設定します。

```bash
# ログディレクトリの作成
sudo mkdir -p /var/log/jupyterhub

# ログファイルの作成
sudo touch /var/log/jupyterhub/jupyterhub.log

# 権限の設定
sudo chown root:jupyterhub /var/log/jupyterhub/jupyterhub.log
sudo chmod 640 /var/log/jupyterhub/jupyterhub.log
```

JupyterHubのログファイルを適切に管理するために、`logrotate`を使用してログのローテーションを設定します。

### 8. `logrotate`のインストール

`logrotate`はUbuntuにデフォルトでインストールされていますが、念のため以下のコマンドでインストールを確認し、必要に応じてインストールします。

```bash
sudo apt update
sudo apt install -y logrotate
```

インストール後、バージョンを確認して正しくインストールされたことを確認します。

```bash
logrotate --version
```

### 9. `logrotate`の設定

JupyterHubのログファイル `/var/log/jupyterhub/jupyterhub.log` を対象とする `logrotate`の設定ファイルを作成します。

```bash
sudo nano /etc/logrotate.d/jupyterhub
```

以下の内容をファイルに記述します。

```conf
/var/log/jupyterhub/jupyterhub.log {
  rotate 12
  monthly
  compress
  missingok
  notifempty
  create 640 root jupyterhub
  postrotate
    systemctl restart jupyterhub
  endscript
}
```

この設定により、ログファイルは毎月ローテーションされ、12世代分保持されます。古いログはgzipで圧縮され、ログファイルが存在しない場合や空の場合はローテーションを行いません。また、ローテーション後にJupyterHubサービスを再起動します。

### 10. `logrotate`設定のテスト

設定が正しく行われているかをテストします。

```bash
sudo logrotate -d /etc/logrotate.d/jupyterhub
```

このコマンドはデバッグモードで `logrotate`を実行し、実際にはローテーションを行わずに設定を検証します。

### 11. JupyterHubの起動

設定が完了したら、JupyterHubを起動します。

```bash
jupyterhub -f jupyterhub_config.py
```

ターミナルを閉じてもプロセスが継続するように、`nohup`を使用してバックグラウンドで実行することも可能です。

```bash
nohup jupyterhub -f /srv/jupyterhub/jupyterhub_config.py >> /var/log/jupyterhub/jupyterhub.log 2>&1 &
```

これにより、JupyterHubのログは指定したログファイルに出力され、`logrotate`によって定期的に管理されます。

#### ログの確認
コンテナ内で、以下のコマンドをたたくと確認できます。
```bash
cat /var/log/jupyterhub/jupyterhub.log
```

以上で、Azure VM上のDockerコンテナ内にJupyterHubを構築し、ログ管理を設定する手順は完了です。