
サーバー上のJupyterをColabのローカルランタイムで動かす(with Tailscale)
Colabに慣れてるととにかくJupyterが不便すぎる。そして、1週間ぐらい回し続けなければいけないような類のモデリングというのはColabではランタイムが続かない(そして死ぬほど高くつく)という問題がある。ということで研究室に置いてあるM3 UltraのMac Studioに手元のMacBook上のColabからアクセスして使うことに。
リモートサーバーへの接続には今回はTailscaleでトンネリングします。
1. Mac StudioでJupyterのセットアップ
今回サーバー側の環境構築が面倒臭くてAnaconda入れました(あんまり好きじゃないんだけど)。それで必要なものは大体入ってるけど、入れるべきものはpipでももちろん入る。
pip install jupyter notebook
pip install jupyter_server jupyter_http_over_ws
サーバー側でも使用時はColab使えばいいし、サーバー側でJupyterが立ち上がる必要は全くないので今回はjupyter serverでやります。
立ち上げはこんな感じ。今回は実験環境なので、jupyterのconfigは作らずにオプション刺して実行します。
jupyter server --ServerApp.allow_origin='https://colab.research.google.com' --port=8888 --NotebookApp.port_retries=0
port_retries=0を指定しているので、ポートがすでに使用されている場合にはエラーになります。特に分析サーバーみたいなものにおいて、使用ポートは明示的に管理・把握されているべきなので僕はいつもこうです。
(おそらく上のコマンドで--no-browserをオプションを入れていない限りは叩いた瞬間に勝手にブラウザが立ち上がるとは思うけど、)この状態で画面に表示されているlocalhost:8888?token=****にMac Studio側のブラウザからアクセスすると、

いい感じですね。jupyter serverなので通常のJupyterとは違い具体的な画面は出ない。
2. 双方でTailscaleのセットアップ
サーバーをグローバルネットワークに晒すのもポート解放も絶対にやりたくないので、リモートサーバーへのアクセスのためにサーバー(Mac Studio)とクライアント(MacBook Pro)の両方にTailscaleを入れる。
Tailscaleはリモートサーバーと簡単にVPN接続できるサービス。ネットワーク設定やルーティングがほとんど不要と、簡単すぎて個人的には「リモートサーバーをローカルネットワーク上のデバイスみたいに扱えるサービス」っていう表現の方が近い気すらする。とにかく入れる。そしてデバイス2台を登録する。

すると、管理画面にaddressが表示されるので、2台ともがConnectedであることを確認する。
3. サーバーにSSH+ポートフォワーディングで接続
ここからリモートサーバーにTailscale経由でSSH接続する。そもそもSSH接続自体したことないという場合には、サーバー側macOS上の設定でリモートログインをオンにする必要がある。

その際に、先方の8888番ポートをローカルの8888ポートにフォワーディングする。Mac Studio上のアカウント名がmyaccountで、tailscaleで割り当てられたIPアドレス100.97.192.122(あるいはサブドメイン付きのアドレスmymacstudio.tailxxxxx.ts.netとか)だとすれば、
ssh myaccount@mymacstudio.tailxxxxx.ts.net -L 8888:localhost:8888
って感じでサーバー側の8888ポートをローカルで受けることができる。もちろん実際にはSSHのセキュリティ維持のため公開鍵とかの設定もした方がいいけど、ベースはこんな感じ。
これが通ったら、クライアント側MacBookのブラウザから`localhost:8888?token=****`を叩いて先のJupyter Serverの画面が出てくるはず。
4. Colabからアクセスする
Colabのローカルインスタンス接続はChrome(もしくはせいぜいFirefox?)あたりのみが対応で、少なくともSafariでは相変わらず動かない。しかし、上のJupyter Serverの画面さえ立ち上がっていればあとは簡単で、Colabから「ローカルランタイムへ接続」で、接続先にトークン付きのURLを入れる。

ただし、これはトークンが入っていないだけのURL(http://localhost:8888)を入れた場合でも「接続エラー」みたいな表示しか返ってこない。つまり、認証周りがうまくいっていないのか、そもそも8888ポートにすら到達できていないのか、など問題の切り分けが難しいことに注意。
無事接続できるとConnected (Local)の緑チェックとともにコマンドが通るようになる。

Colabに慣れてると反応がめちゃくちゃ早いことに驚く。
もちろんssh後にnohupで立ち上げてもいい
さっきの立ち上げをわざわざサーバー側で操作する必要はもちろんなく、Tailscaleとsshによるリモートログインさえできていれば、
nohup jupyter server --ServerApp.allow_origin='https://colab.research.google.com' --port=8888 --NotebookApp.port_retries=0 > /dev/null 2>&1 &
って感じでnohupでバックグラウンドでjupyter serverを立ち上げつつ、
jupyter server list
で、稼働したサーバーのトークンだけ返してColabに差し出せばいい。
リモートサーバーが落ちてさえいなければ立ち上げ自体は遠隔でできるので楽でいいですよね。