1. 背景
大学の研究室に新しいGPUマシンを設置したので,
いつ,どこからでもGPUマシンにアクセスし,手元のクライアントPCでコーディングや実行を行える環境を整えようと思い,
セットアップを行ったついでに備忘録としてセットアップ方法をここに書き連ねておきます.
PyTorchなどで深層学習(機械学習を含む)をしたい方におすすめです.
具体的には,sshで接続することで,GPUマシンがある場所はもちろん,どんな場所からでもコーディングやGPUを用いた実行ができます.
また,1台のGPUマシンを複数人で共有し,同じタイミングでもリソースを共有することができます.
さらにDockerコンテナを活用することで,複数ユーザーが同時に様々な環境で開発を行うことができます.
今回,初めてJupyter Labを触ったのですが,ローカルで開発しているかと思えるような様々な拡張機能がとても便利ですね.
また,この記事では,GPUマシンと同じLAN内からのssh接続だけではなく,LAN外のどこからでもssh接続する方法についても記述しております.
初心者大学生の記事なので,専門用語の使い方や,セキュリティ関係が怪しいかもしれません.ご了承ください.
2. 前提
PC環境
- GPUマシン(サーバー側)
- Ubuntu 20.04 LTS
- AMD EPYC 7402P 24-Core Processor
- RTX 3090 ×2
- メモリ128GB
- openssh-serverインストール済み
- Dockerインストール済み
- ローカルPC(クライアント側)
- MacBook Pro (13-inch, 2020, Thunderbolt 3ポートx 4)
- メモリ16GB
- macOS 12.2.1
- 2.3GHzクアッドコアIntel Core i7
尚,ローカルPCとしてWindowsでも動作確認しました.
Windows10最新版のpowershellを使えば問題なく実行できそうです.
GPUマシンにはopenssh-serverとDockerをインストールしておきましょう.
3. ssh接続の準備
やりたいこと
- sshでUbuntuPCに接続
- rsa暗号を用いてパスワード入力を省略(公開鍵認証)
- host名を用いてIPアドレス入力を省略
手順
初めて接続する際はクライアント側で秘密鍵と公開鍵を発行し,公開鍵をGPUマシンに送信し,保存しておく必要があります.
鍵発行
cd #ホームディレクトリ(Users/ユーザー名/)に移動
cd .ssh #.sshフォルダがない場合はmkdirしてください
ssh-keygen [-f output_keyfile]
パスフレーズはなくてもOKです(Enterキー押すだけ).
例:output_keyfile
をid_rsa
などにすると,.ssh
フォルダ内にid_rsa
(秘密鍵)とid_rsa.pub
(公開鍵)の2種類のファイルが生成されます.複数名でGPUマシンにアクセスすることを想定しているならば,id_rsa_user1
など違いがあるとわかりやすいです(管理しやすく).
参考:ssh-keygenコマンドの使い方
ホスト名の簡略化(hostsファイルの編集)
まず,GPUマシンのIPアドレスを調べておきます.
ip a
例:
inet 10.##.#.### (わざと隠してます)
クライアント側のhostsファイルを開き,編集します.
sudo vi /etc/hosts
viの使い方はこちらを参考にしました.
「コマンドモードと入力モードの切り替え」と「保存と終了」部分が参考になります.
hostsファイルに以下を追記します.
[UbuntuPCのIPアドレス] [Tabキー] [任意のホスト名]
[UbuntuPCのIPアドレス]には先程調べたIPアドレスを入力してください.
[Tabキー]はTabスペースを空けるという意味です.
[任意のホスト名]はgpumachine
など覚えやすいホスト名で構いません.
※Windowsの場合C:\windows\system32\drivers\etc\hosts
にあります.これを編集してください(管理者権限が必要かも).
公開鍵の転送・登録
先ほど作成した公開鍵をGPUマシンに転送し,登録します.
パスワードの入力が求められた時は,GPUマシンにログインする時のパスワードを入力してください.
macの場合
ssh-copy-id -i ~/.ssh/id_rsa.pub [gpuマシンのユーザー名]@[gpuマシンのホスト名]
例:
ssh-copy-id -i ~/.ssh/id_rsa_user1.pub user@gpumachine
Windowsの場合
こちらを参考にするとssh-copy-idと同様のことができます.
リモート接続時のコマンドを簡単にする
クライアント側の「.ssh」ディレクトリの「config」ファイルにエイリアスを定義することで、リモート接続時のコマンドを省略できます.
Host [エイリアス1]
HostName [リモートのホスト名]
User [リモートユーザー]
IdentityFile /Users/[user名]/.ssh/[秘密鍵]
Port [ポート番号]
ServerAliveInterval 60
LocalForward 8888 localhost:8000
ポート番号は,あれば記述します.
ServerAliveIntervalはssh接続時に一定時間放置した時自動的に接続が切れてしまうのを防ぐために設定します.
LocalFowardはポートフォワーディングです.セットアップ時にエラーが出る場合は全て終わった後,設定してもいいかもしれません.
私はポートフォワーディングをconfigに予め書いておけることを後で知り,セットアップ後にconfigに追記しました.
※(2022年5月9日追記)
ポートフォワーディングに関して,例で示しているport番号で実行すると
クライアント側のport 8000 ↔ GPUマシンのport 8888 が紐づけされ,
後述のdocker run
時の-p オプションにより(例:docker run -it --gpus all --name user1lab -v ~/Desktop/user1:/container/user1 -p 8000:8888 condalab-cuda-torch-jp
),最終的にクライアント側のport 8000 ↔ (GPUマシン経由) ↔ dockerコンテナ内のport 8888が紐づけされるイメージになります(間違っていたらごめんなさい).
ポート番号は排他的に使用しなければいけないので(実行中の1コンテナごとに違うポート番号),configファイルでLocalForwardを例のように固定してる場合,2個目のコンテナを並列で立ち上げるためにはクライアント側で2つ目のconfigファイルを活用しないssh接続を行うなどの方法があります.
例として,2つ目のターミナルウィンドウでssh user名:ホスト名 -L 8888: localhost:8001
でGPUマシンにssh接続し,docker run -it --gpus all --name コンテナ名 -v ~~~~ -p 8001:8888 dockerimage_name
でコンテナ作成しコンテナ内でjupyterの起動をし接続するやり方でうまくいきました.
また,GPUマシンを複数人で共有する場合,利用者それぞれでconfig内LocalForwardを違うport番号で設定しておき,portが被るのを防ぐなどのやり方があると思います.
※(2022年6月1日追記)
実際に複数人でコンテナを起動し試したところ,以下のようにするとスムーズに実行できました.
1人目はconfigファイル内でLocalForward 8888 localhost:8000
としておき,コンテナrun
実行時オプションにて-p 8000:8888
,jupyterの起動時jupyter lab --ip=0.0.0.0 --allow-root --port 8888
とport番号(8888はデフォルト値)の指定をします.
2人目はconfigファイル内でLocalForward 8889 localhost:8001
としておき,コンテナrun
実行時オプションにて-p 8001:8889
,jupyterの起動時jupyter lab --ip=0.0.0.0 --allow-root --port 8889
とport番号の指定をします.
このように一人ひとりが以上の箇所で違う数値を用いることにより,同時にコンテナを実行できることを確認しました.
例:
Host gpumachine
HostName gpumachine
User user
IdentityFile /Users/user1/.ssh/id_rsa_user1
ServerAliveInterval 60
LocalForward 8888 localhost:8000
これで,sshで接続したい時は以下のコマンドのみでパスワード入力無しでssh接続できるようになります.
ssh gpumachine
4. ngrokを用いたWANからのssh接続
ここまで書いていたssh接続はLAN内のみで実行でき,外部ネットワークから接続したい場合,グローバルIPアドレスからアクセスするため,ルーターでの変換を行うための設定が必要でした.
こちらで紹介されているような設定でもWANからのssh接続が可能になるようですが,ルーターの設定をいじれない環境でもssh接続できるようにならないか調べたところ,うまくいったのがngrokを用いた接続でした.
やりたいこと
- ngrokのインストール・設定・起動
手順
こちらのサイトが非常に参考になります.
- https://ngrok.com/ でアカウントを作成してください(無料).
- GPUマシンにUbuntu(Linux)用のngrokをDL,インストールしてください.
- ngrok公式サイトで発行した自アカウントのauthtokenを,インストールしたGPUマシン内で登録してください.
-
./ngrok tcp 22 --region jp
を実行することでWANからsshする際のホスト名(IPアドレス)を発行できます.
※--region jp
のオプションでJPサーバーを利用できpingを最小限に抑えることができます.
GPUマシンのterminalの画面がngrokの情報が書かれた画面に変わった後,その中のForwardingという項目のアドレスをメモしておきましょう.
Forwardingの例:
tcp://0.tcp.jp.ngrok.io:12345 -> localhost:22
ここで,ホスト名は0.tcp.jp.ngrok.io
,ポート番号は12345
になります.
ちなみにGPUマシンはシャットダウンせずに稼働させっぱなしな前提で話を進めています.
というのも,ngrokの無料プランはngrokを再起動してしまうとホスト名の最初の数字(0など)やポート番号が変わってしまうからです.
ngrok起動中にGPUマシンを直接操作する人がいる時はngrokが終了しないようにterminalの別ウィンドウや別タブを操作してもらいましょう.
ここで,ngrok経由での接続用にhostsファイルとconfigファイルに追記を行います.
ngrok経由で接続する時用に別の秘密鍵・公開鍵の作成,GPUマシンへの転送・登録も行いました.
例:
0.tcp.jp.ngrok.io:12345 nggpumachine
Host nggpumachine
HostName 0.tcp.jp.ngrok.io
User user
IdentityFile /Users/user1/.ssh/id_rsa_user1_ngrok
Port 12345
ServerAliveInterval 60
ngrokを再起動した際にはhostsファイルとconfigファイルの内容を修正する必要があります.
これでWANからsshで接続した時は,以下のようなコマンドで接続できます.
ssh nggpumachine
5. Dockerコンテナの作成,起動,カスタマイズ,保存
やりたいこと
- クライアントPCからsshでGPUマシンに接続し,その中でDockerを用いてJupyter Labが使えるPyTorchの仮想実行環境を構築する
- Dockerの中に入れるもの
- cuda 11.3.1
- Anaconda
- Python 3.9.7
- PyTorch 1.10.2
- Jupyter Lab
- +好きなライブラリなど
※今回は,nvidia/cudaのDockerイメージをベースにAnacondaを追加し,その他のライブラリを追加していく,といったやり方の例を載せています.
※容量を抑えた軽量版Docker Image用の記事を作ったのでGPUマシンの容量を気にしている方はこちらもご覧ください.
もちろんPyTorchのDockerイメージをベースにするなど,色々やり方はあると思います.
また,GPUの型番やOSのバージョンに合わせて,GPUドライバーやcuda,Python,PyTorchのバージョンが対応しているものを選ぶ必要があります.
GPUマシンの容量削減をしたい場合は最小限の構成でコンテナを作ると良いです.
私が試した最小構成は,Docker HubからUbuntuのOfficial Imageをpullし(docker pull ubuntu:20.04
),これをベースにPython3をインストールし,下記のinstallnodejs.command
を行い,pipでjupyter labや好きなライブラリーを追加インストールする,といった手順です.
つまり,やり方はかなり自由だということですね.
手順
ここからはsshでGPUマシンに接続した状態のterminalで操作するか,
GPUマシンを直接触ってGPUマシンのterminalで操作してください.
GPUマシンにクライアントPCから接続
GPUマシンで直接操作する場合,ここは飛ばしてOKです.
ssh gpumachine
cudaのDockerイメージをpull
docker pull nvidia/cuda:11.3.1-cudnn8-devel-ubuntu20.04
新規コンテナ作成 & run
docker run -it --gpus all --name <コンテナ名> -v ~/Desktop/user1:/container/user1 nvidia/cuda:11.3.1-cudnn8-devel-ubuntu20.04
今回は,コンテナ名
をdockertest
とします.
~/Desktop/user1:/container/user1
部分に関して,
GPUマシンの~/Desktop/user1
フォルダを仮想環境内の/container/user1
フォルダと同期することで,
仮想環境外からデータを取ってきたり,仮想環境外にデータを保存することができます.
各種インストール
Anacondaをインストールして,condaコマンドを使えるようにします.
~/Desktop/user1
フォルダにcondainstall.command
ファイルを作成しておき,
仮想環境内で$ bash condainstall.commnad
を実行するか,もしくはコマンドラインで1行ずつ実行します.
途中で地域を入力する必要があるので,「Asia」,「Tokyo」を選択してください.
最後にAnacondaのinitializeをするか入力を求められますが,私は「yes」にしました.
Anacondaに関して,こちらのサイトが参考になりました.
apt update
apt upgrade
apt install curl
apt install vim
apt install wget
apt install ffmpeg
apt install imagemagick
mkdir anaconda_installation
cd anaconda_installation
curl -O https://repo.anaconda.com/archive/Anaconda3-2021.11-Linux-x86_64.sh
chmod +x Anaconda3-2021.11-Linux-x86_64.sh
./Anaconda3-2021.11-Linux-x86_64.sh
source ~/.bashrc
exec $SHELL -l
次に,PyTorchとnode.jsのインストールを行います.
node.jsがないと動かないJupyter Labの拡張機能がいくつかあるので,インストールします.
condaコマンドで何かインストールする時はコマンドラインの最初が(base)になっていないと実行できないので注意してください.
exec $SHELL -l
などでbashの再起動をすると(base)が現れると思います.
apt install nodejs npm
npm install -g n
n stable
apt purge nodejs npm
exec $SHELL -l
source ~/.bashrc
conda install pytorch torchvision torchaudio cudatoolkit=11.3 -c pytorch
source ~/.bashrc
最後にJupyter Labをより使いやすくするために拡張機能(よく使うライブラリも)をインストールします.ここはお好みで…
私のおすすめの拡張機能を以下に貼っておきます.
pip install jupyterlab-language-pack-ja-JP
pip install lckr-jupyterlab-variableinspector
pip install jupyterlab_nvdashboard
pip install ipywidgets
pip install black
pip install yapf
pip install isort
pip install jupyterlab_code_formatter
pip install jupyterlab_tabnine
pip install jupyterlab-lsp
pip install 'python-lsp-server[all]'
pip install pprintpp
pip install jupyterlab-spreadsheet-editor
pip install ipympl
Docker Imageの保存
全ての追加インストールが完了した後,コンテナから抜けます.
exit
現在のdockertestの環境を保存しておきます.
docker commit dockertest <新規Image名>
新規Image名
は好きに決めてOKです.
例:
docker commit dockertest condalab-cuda-torch-jp
commitがうまくいかない場合はdockertestがまだ動いてる可能性が多いです.
以下で終了できます.
docker stop dockertest
保存が完了しているかはDocker Imageの一覧を表示することで確認できます.
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
condalab-cuda-torch-jp latest 791d31a682c2 3 hours ago 13.3GB
nvidia/cuda 11.3.1-cudnn8-devel-ubuntu20.04 c8217e621925 2 months ago 8.26GB
6. 作業用コンテナの新規作成,Jupyter Lab起動
一度セットアップが完了していれば,
ここ以降のやり方で,新規コンテナを作成して開発スペースを増やせます(2人目以降や1人で複数コンテナ使いたい方).
やりたいこと
- コンテナ作成
- jupyter起動
- ブラウザでjupyterを開く
- jupyterの終了
手順
コンテナ作成
docker run -it --gpus all --name user1lab -v ~/Desktop/user1:/container/user1 -p 8000:8888 condalab-cuda-torch-jp
このコンテナでメインの開発をするというイメージです.
追加で色々入れても大丈夫ですし,もしもエラーや不具合が多発するようでしたら,このコンテナを消して,新たなコンテナをcondalab-cuda-torch-jp
ベースで作ることができます.
Jupyterの起動
jupyter lab --ip=0.0.0.0 --allow-root
と打つと,コマンドライン上に
To access the server, open this file in a browser:
file:///root/.local/share/jupyter/runtime/jpserver-####-open.html
Or copy and paste one of these URLs:
http://42f4cd####:8888/lab?token=###############
or http://127.0.0.1:8888/lab?token=##############
といったURLが表示されます.
一番下のhttp://127.0.0.1:8888/lab?token=##############
をコピーしておきます.
ブラウザで開く
Chromeなどのブラウザを開き,アドレス入力欄に,
http://127.0.0.1:8888/lab?token=##############
もしくは,localhost::8888/lab?token=##############
と入力,
Enterキーを押すことでJupyter Labを開くことができます.
Jupyter Labの使い方は記述しませんが,検索したら色々出てくると思います.
Jupyterの終了
Jupyter実行中のterminalで<Ctrl + C>
を押すと終了できます.
その後,exit
でコンテナを抜けられます.
7. MacのFinder上でUbuntuマシンとファイルやり取り
GPUマシンのディレクトリをローカルPCにマウントすることで,
GPUマシン内のフォルダやファイルを直感的にコピー・移動することができます.ローカルPCとGPUマシン間のフォルダ,ファイルのやり取りも可能です.
以下の記事がわかりやすくおすすめです(mac).
Macでsshfsを使ってローカルからリモートのファイルを触る
また,Windowsの場合はwin-sshfsを使うと同様のことができます.
以下の記事が非常に参考になります.
Windows10にwin-sshfsをインストールする方法
※Dokanは最新版ではなくDokan_x64-1.0.0を使わないとエラーになります.
8. よく使うDockerコマンド
私がよく使うDockerコマンドです.使っているうちに勝手に覚えてしまいます.
ローカルにあるイメージの表示
docker images
起動中のコンテナ表示
docker ps
コンテナ表示(停止中含む)
docker ps -a
Docker Imageから新規コンテナ生成&実行
docker run -it --gpus all --name <コンテナ名> -v <マウント元ディレクトリpath>:<マウント先コンテナ内path> -p <外部からアクセスされるポート番号>:<コンテナ側のポート番号> <Docker Image ファイル名>
※ --gpus all
と -v <マウント元ディレクトリpath>:<マウント先コンテナ内path>
,-p <外部からアクセスされるポート番号>:<コンテナ側のポート番号>
は必要に応じて使います.
コンテナ起動
docker start <コンテナ名 or ID>
コンテナ停止
docker stop <コンテナ名 or ID>
起動中のコンテナに入る
docker exec -it <コンテナ名 or ID> bash
コンテナから抜ける(コンテナ内の実行中の作業は停止する)
exit
コンテナから抜ける(コンテナ内の実行中の作業は中止しない)
<Ctrl+p Ctrl+q>
コンテナの削除(停止しているコンテナしか削除できない)
docker rm <コンテナ名 or ID>
保存されているDocker Images一覧
docker images
Image削除
docker rmi <イメージID or イメージファイル名>
9. まとめ
今回の手順の全体の流れをまとめると以下のようになります.
- ssh接続の設定(鍵発行など)
- cudaのDocker ImageをベースにAnaconda,PyTorch,Jupyter Lab拡張機能などを追加したコンテナを作成
- 作成したコンテナをcommitし,Imageとして保存
- Jupyter Labの起動方法の簡単な紹介
細かいところまで書いてしまっていますが,必ずしもこのようにしないとできないわけではないので勘違いしないでください.
私自身がやり方を忘れてしまったときの備忘録としても使えるように,細かく書きました.
最後に
まだまだDocker初心者なので,変な表現をしている点や間違ったやり方をしているかもしれません.
ミスなどありましたら,コメントでご指摘の程よろしくお願いします.
ここまで読んでくださり,ありがとうございました.