GPU付きWindows上のTensorflowをMacから開発する
概要と目標
WindowsにGPU入れたのでTensorflowを触ってみようと思ったのですが、ショートカットが違うのが嫌なのでMacから触れるようにしようと思います。色々方法があってどうするのが良いのか迷うのですが、まずtensorflowの環境はdockerのコンテナを使うのが手っ取り早いと思います。でMacからはSSHで繋ぎます。SSHがつながればVSCodeのDev Containersを使って開発ができます。
また、Windowsはポチッと電源ボタンをいれてWindowsのキーボードやマウスは触らずにMacから操作できるようにしたいのと、PowerShellは得意じゃないので慣れたLinux系のシェルでSSHログインを目指します。
で、それぞれdockerとSSHは下記のパターンがあると思います。
まずSSH接続。これには2種類方法があります。
- WindowsにOpenSSHサーバーをインストールする。デフォルトのシェルはPowerShellですが変更可能です。
- WSLにSSHをいれてそこに繋げる。
そしてdockerも2パターン(厳密には3種類?)あると思います。
- WindowsのDockerDecktopを使う。
- WSLにDockerを入れて使う。
3種類と言ったのはDockerDecktopはWSLと共有されてWSL内から操作可能です。なので、Windowsに直接SSH接続してDockerDecktopを操作することも、WSLにSSH接続して操作することも可能で、なんとなくニュアンスが違う気がしたので3種類かなと。
結論(暫定)
うまく動かないところがいくつかって、結局下記のパターンを採用しました。
- SSHはWSLでSSHサーバーを立てる。
- dockerはDockerDesktopは使わずにWSLにインストールする。
他の方法では目標を達成できなかったのでこの方法を採用しましたが、結果、全てがWSLの中に収まって綺麗な開発環境ができたような気がします。
試したことはすべて手順を載せます。最初記事を分けようかと思ったけど、設定やエラーが微妙に被っていってどう分けるのか悩んだのでまとめちゃました。長いし見づらいと思いますがご容赦ください。
環境
Windows
エディション Windows 11 Pro
バージョン 22H2
OS ビルド 22621.2134
WSL
WSL バージョン: 1.2.5.0
カーネル バージョン: 5.15.90.1
Ubuntu-22.04
WSLはバージョンが複雑でややこしいですが1.2.5.0
だけどWSL2ですね。なぜ1がインストールされるんだ??としばらくハマりました。
Mac
Apple M1 Max(挙動と関係あるかもしれないので一応)
13.4 (22F66)
設定方法と解説
SSH接続
ではまずSSH接続から行きます。
WindowsのOpenSSHを使う
スタートメニュー>設定>アプリ>オプション機能>オプション機能を追加する[機能を表示]
をクリック。リストにあるOpenSSHサーバーをクリックしてインストールします。sshd_configはC:\ProgramData\ssh\sshd_config
にあります。ProgramDataは不可視になってるので注意してください。
下記のような設定があります。
# Match Group administrators
AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys
これがONになっているとadministratorsグループに登録されてるユーザーのauthorized_keys
がC:\ProgramData\ssh\administrators_authorized_keys
になるので注意が必要です。Linux系のと同じように~/.ssh/authorized_keys
に公開鍵を置きたい場合はOFFにしてください。
ターミナル(PowerShell)を起動し下記のコマンドを実行してください。
# 自動で起動するように設定
Set-Service -Name sshd -StartupType Automatic
# 起動する
Start-Service -Name sshd
この方法でインストールするとファイアウォールは勝手に開くようですが、だめだったら自分で22を開けてください。
これでMacから接続できるのですが、Windows11だとMicrosoftアカウントでログインしてて、ユーザー名ってなんだっけ?となりませんか?そんな時はターミナルでwhoami
とコマンドを打つとmachinename\username
のような形で表示されるので確認してください。なんかメールの@マーク前を変なところで切って勝手に名前になってますよね。ユーザーディレクトリも同じです。気持ち悪いなあ。ユーザー名は簡単に変更できるけど、ディレクトリ名は変更大変なのでやめました。気持ち悪いわあ。
WindowsのIPは固定しとくと便利だと思います。うちのルーターはスマホアプリから設定可能で固定する機能があったので簡単でしたが、そういうのがないとDHCPの振り分け範囲を狭くして、空けたところをWindows側に直指定し固定する等工夫が必要かも。
デフォルトのシェルについて
MacからSSHで接続した時のデフォルトのシェルは変更可能です。WindowsのコマンドやPowerShellに慣れてるなら特に問題ないですが、Macユーザーなら慣れたlinux系のコマンド使いたいですよね。
使用可能なシェルを指定するコマンドを列挙しておきます。
# Windowsのコマンド。これがデフォルト(だと思う確認する前に変えてしまったので)。
New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell -Value "C:\Windows\system32\cmd.exe" -PropertyType String -Force
# PowerShell。Windows11のデフォルトはバージョン5系。最新の7は別途インストール。
New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell -Value "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -PropertyType String -Force
# PowerShell7。5起動時に表示されるリンクの通り入れるとここに入ります。
New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell -Value "C:\Program Files\PowerShell\7\pwsh.exe" -PropertyType String -Force
# GitBash。gitをインストールしていればあるはす。
New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell -Value "C:\Program Files\Git\usr\bin\bash.exe" -PropertyType String -Force
# WSLのbash。これが一番おすすめだと思われる。
New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell -Value "C:\Windows\System32\bash.exe" -PropertyType String -Force
ところがここでハマってしまいました。WSLのbashが一番良さそうなのでそれを指定しましたが「ファイルにアクセスできません。」と接続が切断されます。詳しいエラー内容はsuperuserに質問投げてるのでそちらを参照ください。解決方法をご存知の方がいたら教えてください。
それ以外のShellは起動できたのですが、どのシェルでもwsl
を実行すると「ファイルにアクセスできません。」と表示され実行できません。これも根は同じっぽいですね。ユーザーの権限関連かとも思いましたが、C:\Windows\System32\bash.exe
をユーザーディレクトリにコピーし全権限を与えてみたけどダメでした。
WSLのSSHを使う
まずWSLをインストールします。インストールは簡単でした(Windows 11だからか?)。コマンドプロンプトを開き
wsl --install
とタイプするだけです。Ubuntuのインストールまで勝手にやってくれます。ただ、ここで入れるのは20.04です。LTSは22.04がリリースされていますのでそちらを入れました。インストールはMicorosoftStoreからUbuntuを検索してインストールできます(後述しますがこれをしたせいでトラブった可能性があります)。
WSLにSSHをインストールする。
sudo apt -y update
sudo apt -y install openssh-server
設定はお好みで・・・下記で編集、再起動が可能です。
sudo vi /etc/ssh/sshd_config
sudo systemctl restart sshd
公開鍵を~/.ssh/authorized_keys
に登録します。(たしか)ディレクトリ自体ないので作っておきます。
mkdir ~/.ssh
touch ~/.ssh/authorized_keys
chmod 0700 ~/.ssh
chmod 0600 ~/.ssh/authorized_keys
ポートフォワード設定とWSLの自動起動
Macから10022ポートで接続してきたSSHをWSLの22にフォワードします。WSL2からはネットワークが別なのでIPアドレスが変わってしまう可能性があるようです。
こちらが詳しいですが、変わってしまうのにlocalhostで繋げるという情報がありますが、実はこれがとても不安定で頻繁に解決を失敗します。
そこでWindows起動時にWSLを起動してIPアドレスを取得し、ポートフォワードの設定を自動で登録します。
下記のファイルを任意の場所に作成します。ファイル名はwsl_ssh.batとしました。
wsl -d Ubuntu-22.04 -u root -- service ssh restart
netsh interface portproxy delete v4tov4 listenport=10022
for /F %%i in ('wsl -d Ubuntu-22.04 exec hostname -I') do set ip=%%i
netsh interface portproxy add v4tov4 listenport=10022 connectaddress=%ip% connectport=22
これをタスクスケジューラーに登録すれば自動で起動するはずだったのですが上手くいきませんでした。
ダメだった方法
タスクスケジューラーに登録する方法はこちらに詳しく載っていますがダメでした。ログにはエラーも残らず起動しません。IPアドレス自体の取得も失敗し、空が登録されてます。
WSLのISSUEを見つけました。タイトル的にこちらの方が分かりやすいので先に紹介しましたが、そちらは重複で閉じてます。本家はこちらです。どうやらタスクスケジューラからwsl
コマンドを実行できないようです。MicrosoftStoreからインストールしたものは起動できない(WSLインストール時に自動で入るやつはOK)というコメントも見ましたが試してません。
ここにサービスとして登録すると起動するという話も出てますがこれもダメでした。ISSUEを覗くと他にも「これで上手くいきました」という書き込みがあります。全部は試してませんがいくつか試してダメでした。
上手くいった方法
先ほどのwsl_ssh.batのショートカットをスタートアップフォルダに登録します。ショートカットのプロパティを表示しショートカット>詳細設定
から管理者として実行
にチェックを入れます。先ほどのサイトによればwslコマンドでサービスを起動すると自動終了しない
という事なのでこれで上手くいくかと思いましたがダメでした。STATEはちょっとの間Running
になり、その間ならMacからSSH接続できました、ちょっと経つとStopped
に変わり切断されてしまいます。そこで、スタートメニュー>すべてのアプリ
からUbuntu 22.04.2 LTS
を探してスタートアップフォルダにドロップし登録します。これで起動するようになりました。
Windows起動時に自動でログイン
タスクスケジューラーに登録する方法がうまくいけばユーザーにログインしなくても起動するようですが、スタートアップフォルダに入れたのでログインする必要があります。なのでWindowsに自動でログインするよう設定しました。
2つの設定変更が必要なようです。
スタートメニュー>設定>アカウント>サインインオプション
で「セキュリティー向上のため、このデバイスではMicrosoftアカウント用にWindowsHelloログインのみを許可する(推奨)」をOFFにします。
その後Windows+Rを押して「ファイル名を指定して実行」からnetplwiz
を実行。リストから対象のユーザーを選択状態にして「ユーザーがこのコンピューターを使うには、ユーザー名とパスワードの入力が必要」のチェックを外します。パスワードを求められるので入力すれば完了です。
これで自動起動まで辿り着きました。
docker
dockerの準備です。
DockerDesktopを使う
ここからインストーラーがダウンロードできます。Windowsに入れるのでWindows版を選びます。
インストール後起動し、設定(右上のギアーアイコン)からStart Docker Desktop when you log in
を入れておくと勝手に起動します。
WSLからDockerDesktopを使う
WSLにdockerを入れなくてもDockerDesktopのdockerが使えます。設定がうまくいってないと使えません。コマンドを打ってみてください。下記のように表示されたら設定変更が必要です。
$ docker -v
The command 'docker' could not be found in this WSL 2 distro.
We recommend to activate the WSL integration in Docker Desktop settings.
For details about using Docker Desktop with WSL 2, visit:
https://docs.docker.com/go/wsl2/
コマンドを使うにはWSLのバージョンが2である必要があります。Windows11なので2になってるはずですが、wsl -l -v
で確認ができます。1になっていたらwsl --set-version <distro name> 2
で変更可能です。
また、DockerDesctopの設定>General
のUse the WSL 2 based engine
にチェックが入ってること、そしてRecources>WSL integration
で対象のWSL distroがONになっている必要があります。
WSLのDockerを使う
Ubuntuへのインストール方法がそのまま使えます。試してないですがDockerDesktopがWSLで有効になってると良くないと思うので先ほどの設定でintegrationをOFFにしてください。
nvidia-smiが無い
WSLのコマンドプロンプトでnvidia-smi
コマンドを打つとGPUの情報がでますが、MacからSSH接続した時Command 'nvidia-smi' not found
になります。
$ nvidia-smi
Command 'nvidia-smi' not found, but can be installed with:
sudo apt install nvidia-utils-390 # version 390.157-0ubuntu0.22.04.2, or
sudo apt install nvidia-utils-418-server # version 418.226.00-0ubuntu5~0.22.04.1
sudo apt install nvidia-utils-450-server # version 450.248.02-0ubuntu0.22.04.1
sudo apt install nvidia-utils-470 # version 470.199.02-0ubuntu0.22.04.1
sudo apt install nvidia-utils-470-server # version 470.199.02-0ubuntu0.22.04.1
sudo apt install nvidia-utils-525 # version 525.125.06-0ubuntu0.22.04.1
sudo apt install nvidia-utils-525-server # version 525.125.06-0ubuntu0.22.04.1
sudo apt install nvidia-utils-535 # version 535.86.05-0ubuntu0.22.04.1
sudo apt install nvidia-utils-535-server # version 535.54.03-0ubuntu0.22.04.1
sudo apt install nvidia-utils-510 # version 510.60.02-0ubuntu1
sudo apt install nvidia-utils-510-server # version 510.47.03-0ubuntu3
これはMacからSSHで接続した時にパスが通ってないからですね。色々方法はあると思いますが~/.bashrc
で通しました。nvidia-smi
は/usr/lib/wsl/lib
にありますが、Windowsで開いた時のPATHと見比べて足りないもの全部足しました(必要ないかも)。Windowで開いた時重複しないようにこちらの記事からコードを拝借してます。
# ~/.bashrc
PATH="$PATH:/usr/lib/wsl/lib:/mnt/c/Windows/system32:/mnt/c/Windows:/mnt/c/Windows/System32/Wbem:/mnt/c/Windows/System32/WindowsPowerShell/v1.0/:/mnt/c/Windows/System32/OpenSSH/:/mnt/c/Program Files/Git/cmd:/mnt/c/Program Files/PowerShell/7/:/mnt/c/Program Files/Docker/Docker/resources/bin:/mnt/c/Program Files (x86)/NVIDIA Corporation/PhysX/Common:/mnt/c/Program Files/NVIDIA Corporation/NVIDIA NvDLISR:/mnt/c/Users/miyat/AppData/Local/Programs/Python/Python310/Scripts/:/mnt/c/Users/miyat/AppData/Local/Programs/Python/Python310/:/mnt/c/Users/miyat/AppData/Local/Microsoft/WindowsApps:/mnt/c/Users/miyat/AppData/Local/Programs/Microsoft VS Code/bin"
# 重複してるパスを取り除く
_path=""
for _p in $(echo $PATH | tr ':' ' '); do
case ":${_path}:" in
*:"${_p}":* )
;;
* )
if [ "$_path" ]; then
_path="$_path:$_p"
else
_path=$_p
fi
;;
esac
done
export PATH=$_path
unset _p
unset _path
source ~/.bashrc
で読み込んでやればnvidia-smi
が実行できるはず。
$ nvidia-smi
Mon Sep 4 19:32:33 2023
+---------------------------------------------------------------------------------------+
| NVIDIA-SMI 535.86.05 Driver Version: 537.13 CUDA Version: 12.2 |
|-----------------------------------------+----------------------+----------------------+
| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|=========================================+======================+======================|
| 0 NVIDIA GeForce RTX 4070 Ti On | 00000000:01:00.0 On | N/A |
| 0% 33C P8 11W / 285W | 934MiB / 12282MiB | 12% Default |
| | | N/A |
+-----------------------------------------+----------------------+----------------------+
+---------------------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=======================================================================================|
| No running processes found |
+---------------------------------------------------------------------------------------+
nvidia-container-toolkitをインストール
WSLにインストールしたdockerのコンテナでGPUを使うためにはnvidia-container-toolkitをインストールします。これをインストールせずにGPUをONにしたコンテナを起動すると下記のエラーが出ます。
docker run --name tensorflow --gpus all -p 11022:11022 -itd tensorflow/latest-gpu-ssh
df7227396bab11.....
docker: Error response from daemon: could not select device driver "" with capabilities: [[gpu]].
インストールする時に下記エラーが出るはずです。
/sbin/ldconfig.real: /usr/lib/wsl/lib/libcuda.so.1 is not a symbolic link
これはこの辺で議論されてますが、インストーラーがlibcuda.so.1
とlibcuda.so
をシンボリックリンクを想定しているけどWindowsはコピーしているので出るエラーです。シンボリックリンクにすればエラーは消えますが、そのままシンボリックリンクにしておくと、TensorflowがGPUを認識しなくなり、演算に使ってくれないので元に戻す必要があります。
# 一応現状を確認。
ls -al /usr/lib/wsl/lib/
# バックアップをとりつつシンボリックリンクに変更
cd /usr/lib/wsl/lib/
sudo mv libcuda.so libcuda.so.bak
sudo mv libcuda.so.1 libcuda.so.1.bak
sudo ln -s libcuda.so.1.1 libcuda.so
sudo ln -s libcuda.so.1.1 libcuda.so.1
インストール方法はこちらにあります。
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
&& curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list \
&& \
sudo apt-get update
sudo apt-get install -y nvidia-container-runtime
sudo service docker restart
先ほどのシンボリックリンクをファイルに戻します。
cd /usr/lib/wsl/lib/
sudo rm libcuda.so
sudo rm libcuda.so.1
sudo mv libcuda.so.bak libcuda.so
sudo mv libcuda.so.1.bak libcuda.so.1
ポートフォワードの追加
コンテナにもSSH接続するのでWSLにSSH接続するのとは別のポートでSSHを繋げるようにします。今回は11022を使いました。先ほどのポートフォワード設定のbatファイルを以下のように変更。管理者権限で実行してください。
wsl -d Ubuntu-22.04 -u root -- service ssh restart
netsh interface portproxy delete v4tov4 listenport=10022
for /F %%i in ('wsl -d Ubuntu-22.04 exec hostname -I') do set ip=%%i
netsh interface portproxy add v4tov4 listenport=10022 connectaddress=%ip% connectport=22
netsh interface portproxy add v4tov4 listenport=11022 connectaddress=%ip% connectport=11022
11022はファイアウォールも開けてください。
コンテナを立ち上げる
どっちのSSHでも基本変わらないので章はまとめましたが、微妙に起きるエラーが違ってややこしくなっていまいました。ご了承ください。
Dockerfile
Tensorflowの開発環境のdocker containerを立ち上げてSSHでMacから接続するところまでです(きっとgitとかも入れなきゃならんでしょうな)。
FROM tensorflow/tensorflow:latest-gpu
RUN apt-get update && apt-get install -y openssh-server
RUN mkdir /var/run/sshd
RUN echo 'root:root' | chpasswd
RUN sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config
RUN sed -i 's/#Port 22/Port 11022/' /etc/ssh/sshd_config
RUN mkdir /root/.ssh && chmod 0700 /root/.ssh && touch /root/.ssh/authorized_keys && chmod 0600 /root/.ssh/authorized_keys
RUN echo "ssh-rsa ..." > /root/.ssh/authorized_keys
RUN mkdir /root/souces
EXPOSE 11022
CMD ["/usr/sbin/sshd", "-D"]
echo "ssh-rsa ..."
の所は公開鍵を直書きしてます。まあ、公開鍵だし、ローカルのdockerなので問題ない気もするけど、githubなどに保存するならファイルにしてignoreした方がいいのかな?お好みで。鍵の作成方法は巷に溢れてるので省略します。
下記コマンドでビルド。コンテナを立ち上げます。名前は適当です。
docker build -t tensorflow/latest-gpu-ssh .
docker run --name tensorflow --gpus all -p 11022:11022 -itd tensorflow/latest-gpu-ssh
sshの接続はこんな感じ(コマンド引数ではなくconfigに書いちゃいました)。
Host windows-pc-tensorflow
HostName 192.168.68.74
User root
Port 11022
IdentityFile "~/.ssh/windows"
docker login
docker build
するためにtensorflow/tensorflow:latest-gpu
のimageをPULLするのでコマンドラインでのログインが必要ですが、これが、MacからSSHで接続するごログインできませんでした。
WindowsにインストールしたSSHでもWSLに起動したSSHでも同様に失敗しました。エラーメッセージを見失いましたが、こちらのサイトの方法で解決できます。
~/.docker/config.json
を開き
{
"credsStore": "desktop.exe"
}
を
{
"credStore": "desktop.exe"
}
に編集します。他にもこのファイルを削除すればいいとか、_credsStore
に変えるといけるとか色々ありましたが試してません。要するにこの設定ファイルにcredsStore
のキーがなければOKなんだと思います。
ただしWindowsにインストールしたSSHでは別のエラーが出ました。
Error saving credentials: error storing credentials - err: exit status 1, out: `A specified logon session does not exist. It may already have been terminated.`
これで検索するとこんなdockerのISSUEにたどり着きます。こんなPRが進行中なのでそのうち治ると思います。
ただ、変なんですけど、ログインできる時もあるんですよね。これ書いてる時にエラーメッセージをもう一回出そうと思って試したら入れました。Windows側のターミナルで一回docker login
するとできるのかもしれませんが、それでも出来なかった時もあります。
sudoなしでdockerコマンドを実行する
sudoなしでdocker ps
のような何かdockerのコマンドを打つと下記のようなエラーが出ます。
ERROR: permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get "http://%2Fvar%2Frun%2Fdocker.sock/_ping": dial unix /var/run/docker.sock: connect: permission denied
dockerグループにユーザーを追加することで解決します。
```bash
sudo gpasswd -a $(whoami) docker
docker再起動で反映できるという情報もありますが、WSLの場合はWSL自体の再起動をしないとログインするたびにdockerグループから外れてしまうようです。WSLの再起動はwsl --shutdown
で終了後起動し直してください。
感想
こういうやり方の選択肢がいくつかあるのは、どれを採用するか迷いますね。他にもリモートデスクトップを使うとか、UbuntuをWindowsマシンにインストールしてデュアルブートにする方法なんかもありますね。
ちなみにリモートデスクトップはショートカットがWindowsになるので不採用。デュアルブートはRAIDを認識しなかったので試しませんでした。UbuntuServerだと認識するという噂もチラッと見ましたが、まあ、目的の環境はできたのでここで燃料切れです。
結構時間使っちゃったけど、WSLもさわれて楽しかったです。
とにかく色々試したので、細かいところで記憶違いとかあったらごめんなさい。
Discussion