WSL2 に外部から直接アクセスできるようにしてサーバー化する方法など、
ベアメタル相応に扱う方法について。
Contents
背景とやりたいこと
私は macOS, Windows, Linux(Ubuntu) 3つの OS を用途に応じて使い分けている。
- macOS: ブラウジング, メール, スライド, 写真, 動画 ... などほぼ全ての直接操作する作業
- Windows: Windows でしか動かない専門的なソフトウェアなどを主にRDP経由で
- Linux: 各種サーバー, 負荷の高い計算, 処理などを主にCLI/SSH 経由で
このうち Windows, Linux はそれぞれそれなりに負荷の高い処理をするので、
それぞれに相応の計算資源を用意していたが、
WSL2 で動いている Linux(Ubuntu) をベアメタル相応にサーバー化して、
Windows と Linux の用途を一つにまとめてしまいたい。
主に以下の3つについて記述する。
- 外部から WLS へのアクセス (ポートの公開)
- Windowsホストの raw ストレージを直接 WSL に渡す
- WSL で ZFS扱う
外部から WSLへのアクセス
Windows ホストに届いた、特定のポートへの外部からのリクエストを WSL に導けば良い。
例として、SSH (port: 22) の場合は Power Shell から以下のようにする。
1 2 3 |
$IP = (wsl -d NAME exec hostname -I).Trim() netsh.exe interface portproxy delete v4tov4 listenport=22 netsh.exe interface portproxy add v4tov4 listenport=22 connectaddress=$IP |
NAME
はディストリビューション名 (Ubuntu) など。
1行目で外部からのリクエストを渡したい対象の WSL上での IP を取得。
2行目で当該ポートへの既存の設定を (あれば) 削除して、
最終行でポート: 22 へのアクセスを WSL のターゲットに接続。
私は ssh(22) に加えて http(80) を作業場のネットワークに公開している。
これらのコマンドをスクリプト化してタスクスケジューラでログイン時に自動実行する(後述)。
Windows Defender ファイアウォールなどで、当該ポートへの通信許可を追加することを忘れずに。
これで Windowsホストへの ssh や http リクエストが WSLへ渡される。
WSL に raw ストレージを直接扱わせる
比較的負荷の高い計算などは基本的に Linux(WSL) で行うこと、
私自身が Windows 上よりも Linux 上の方が自由が効くこと、
仮想ディスク(VHD) や drvfs を経由するとオーバーヘッドが出そうなこと、
などから Windowsホストに付けてある rawストレージ をそのままデバイスとして WSL に渡すことにする。
まず PowerShell から対象のストレージの DeviceID を調べる。
1 2 3 4 |
$ GET-CimInstance -query "SELECT * from Win32_DiskDrive" DeviceID Caption Partitions Size Model -------- ------- ---------- ---- ----- ... |
次にその DeviceID を指定して wslコマンドから --bare
オプション付きで WSL に渡す。
[DeviceID] は私の場合は \\.\PHYSICALDRIVE1
みたいな名前だった。
1 |
$ wsl --mount [DeviceID] --bare |
上記のコマンドをスクリプト化して、やはりタスクスケジューラでログイン後に自動実行する(後述)。
WSL 側から lsblk
などとすると /dev/sdd
などとして見えていることがわかる。
WSL2 で ZFS を扱う
WSL は kernel が特殊仕様(?) なので $ sudo apt install zfsutils-linux
のようなのは通らない。
mod_zfs_on_wsl (github) のスクリプト: build_wsl_module.sh
を使わせてもらった。
ダウンロードして ZFS_VERSION
を必要に応じて変更する。
あとは基本的にはこのスクリプトを実行して、完了後に表示される設定を行うだけ。
(OS は Ubuntu 22.04)
1 2 3 4 5 6 7 8 9 |
$ bash ./build_wsl_module.sh /etc/wsl.conf [boot] ... command=modprobe zfs $ sudo dpkg -i linux-module-x.xx.xxx.1-microsoft-standard-wsl2_x.xx.xxx.1-1_amd64.deb $ sudo dpkg -i zfs_x.x.x-1_amd64.deb $ modprobe zfs $ zfs version |
kernel module が load されない (削除される)
kernel が 5.15.133.1 だった時は上記で動いていました。
ところが、いつしか気づいたら kernel が 5.15.153.1 に更新されており、
zpool がマウントされなくなったので kernel module を上記の手順で更新しようとしたところ、
ビルド直後は動いていたのに WSL を立ち上げ直すと kernel module がまた load されなくなる現象が発生。
調べてみると、kernel module を入れたディレクトリ(/usr)/lib/modules/x.x.xxx.1-microsoft-standard-WSL2
が、
丸ごと変更前の状態に戻されており、ビルドした zfs.ko
などの kernel module も当然なくなっていた。
この領域は df などでみると Filesystem: none として表示されていた。
これを変更するには overlayfs で上書きする必要があるっぽい。
1 |
$ sudo mount -t overlay overlay -o lowerdir=..., upperdir=..., workdir=... /usr/lib/modules/... |
/etc/wsl.conf
で同様のコマンドを発行してやれば良いっぽい。
色々調べ回ってこのことに辿り着いたけど、よく見たら本家の README に書いてありました。
ログオン時に各種自動設定
というわけで、これらの設定を行う PowerShell スクリプトをタスクスケジューラでログオン時に実行させる。
以下は私の例。
1 2 3 4 5 6 7 8 9 |
$IP = (wsl -d Ubuntu exec hostname -I).Trim() netsh.exe interface portproxy delete v4tov4 listenport=22 netsh.exe interface portproxy delete v4tov4 listenport=80 netsh.exe interface portproxy add v4tov4 listenport=22 connectaddress=$IP netsh.exe interface portproxy add v4tov4 listenport=80 connectaddress=$IP wsl.exe --mount \\.\PHYSICALDRIVE1 --bare wsl.exe --mount \\.\PHYSICALDRIVE2 --bare wsl.exe --mount \\.\PHYSICALDRIVE3 --bare |
これで WSL を開始した時には既にポート22, 80 が開放され、3つの rawストレージが WSL から見えるようになっている。
恐らく1行目で3つのドライブが attach されていない状態で一度 WSL が起きてしまっているので、
WSL 起動時に、一度作成した zpool がマウントされていない。
$ sudo zpool import [poolName]
で再マウントされる。