JupyterLab CMS化計画

私が管理するこのサイトや python.jpMiyadaikuというSSGを使って構築しています。

記事の執筆は普通にエディタを立ち上げてMarkdownやreStructuredTextで書いていましたが、最近、JupyterLab を使うようになってきました。MiyadaikuはJupyter NotebookからHTMLを作成できるようになっており、私が書くブログ記事の執筆には最適なPythonの実行・ドキュメント環境です。

Notebook以外にも、Preview付きMarkdownエディタはあるし、画像ファイルなどもドラッグアンドドロップでアップロードできるし、git連携もできるしで、プログラマが使うCMSとしてその辺のブログサービスよりも使いやすい環境だと思います。

image.png

これまでは手元のMacでJupyterLabを起動してメモ書き環境として使っていましたが、メインのマシン以外からもアクセスしたいことも多く、雑用サーバとして使っていたGoogle Compute EngineのVMに常駐させてもうちょっと本格的に使うことにしました。

VMスペック

GCEのe2タイプで、vCPU*2 メモリ2GBのインスタンスでUbuntuを使っています。JupyterLabとは言っても、このマシンでややこしい計算をする予定はないのでスペックは控えめ。

とはいえ、JupyterLabにextensionを追加するときにnodeでフロントエンドをビルドするので、あんまりメモリが少ないとOOMキラーにプロセスを殺されます。とりあえず、メモリ2Gにスワップを2GBを追加していれば大丈夫そうです。

プロセス構成

インターネットにJupyterLabを直接さらすのもどうかと思うので、フロントにnginxをおいています。nginxの設定はこんな感じでWebSocketも問題なく使えます。

参考URL

# JupyterLabをポート8888で実行している場合

server {

  server_name xxx.example.com;
  listen 443 ssl;

  location / {
    proxy_set_header        Host $host;
    proxy_set_header        X-Real-IP $remote_addr;
    proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header        X-Forwarded-Proto $scheme;
    proxy_pass              http://127.0.0.1:8888;
    proxy_read_timeout      120;
  }

  location ~* /(api/kernels/[^/]+/(channels|iopub|shell|stdin)|terminals/websocket)/? {
    proxy_pass http://localhost:8888;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    # WebSocket support
    proxy_http_version 1.1;
    proxy_set_header      Upgrade "websocket";
    proxy_set_header      Connection "Upgrade";
    proxy_read_timeout    86400;
  }
}

JupyterLabに外部のホストから接続する場合は、~/.jupyter/jupyter_notebook_config.pyに次の設定が必要です。

# ~/.jupyter/jupyter_notebook_config.py
c.NotebookApp.allow_remote_access = True

セキュリティ

次のコマンドでJupyterLabにパスワードを設定し、ログインするように設定しています。

jupyter notebook password

これだけだとBrute-forceな攻撃をくらってもうざいので、fail2banを導入しています。JupyterLabに間違えたパスワードを入力すると401エラーになるので、これを検出するフィルタを定義します。

/etc/fail2ban/filter.d/nginx-http-login-401.conf

[Definition]
failregex = ^<HOST> - - \[[^]]*\] "\w+[^"]+" 401

/etc/fail2ban/jail.localを作成して、このフィルタを有効にします。

/etc/fail2ban/jail.local

[nginx-http-login-401]

enabled = true
port    = http,https
logpath = %(nginx_access_log)s

findtime  = 5m
maxretry = 20
bantime  = 10m

この指定では、findtime(=5)分以内にmaxretry(=20)回ログインに失敗すると、bantime(=10)分のあいだ接続を禁止します。