狐の王国

人は誰でも心に王国を持っている。

バッチファイル1つで WSL をサーバ化する方法、および Windows 11 のセットアップについてのメモ

昨今 Windows 環境がたいへんデベロッパーフレンドリーになってきたので、なんか1つ WSL で開発できるような環境を持ち歩けるようにしておこうかとラップトップのパソコンを1つ調達してみたのだが、まあどうにも Windows はショートカット体系がなってなさすぎて使いにくい。

ということで WSL を常時起動しておいてサーバとして使い、Mac からリモートで使ってみることにした。sshvscode のリモート接続があればまあそんなに困ることはない、はず。

Windows 11 で意図したユーザー名のローカルアカウントを作る

最近の Windows は意図したユーザー名を付けるのが難しい。ssh のときなどに自動で使われるし、C:\Users 以下に作られるディレクトリ名にもなるのでホントやめて欲しい。

atmarkit.itmedia.co.jp

上記サイトによるとセットアップ中に Shift + F10 を押すとコマンドプロンプトが出てきて、これで C:Windows\System32\oobe\BypassNRO.cmd を実行すると再起動し、ネットワーク接続の設定で「インターネットに接続していません」という選択肢が現れるのだそうだ。

これでローカルアカウントでセットアップできるようになる。秘密の質問がうざったいがしょうがない。こんなのまったくセキュアじゃないのに。

Home Edition で Hyper-V を有効化する

Windows 11 Home Edition では Hyper-V がないのだが、手動でインストールすれば使えるらしい。ライセンス的にも問題ないと聞くのだがちょっとエビデンスが見つからない。問題があれば Pro にアップグレードしようかなあと思いつつ、

jo-sys.net

上記サイトのバッチファイルをそのまま使わせていただいた。

WSL のインストール

Winキー → cmd と入力 でコマンドプロンプトが出てくる。必要があればそのまま「管理者として実行」が選べるの便利だねこれ。

wsl --install -d Debian

これだけで Debian がインストールされるの便利。なんか文字化けエラーが出たけど、Microsoft から WSL のアップデータを入手したら治った。

WSL の常時起動

WSL すぐ死ぬ。メモリも食うしそりゃ普通ならそうだろうが、今回はサーバとして使うので常時稼働しててもらわないと困る。

wsl -u root -- service cron restart

とかやっておくと daemon が起動し続けてくれるので常時起動状態になるようだ。

最近の wsl は systemd に対応していて、設定で systemd を動かしておけるのだが、この状態だと上のコマンドを実行しても WSL すぐ死ぬ。

WSLが公式にSystemDをサポートするらしい

/etc/wsl.conf で設定できるようだ。

[boot]
systemd=false

デフォルトで false なのでこの設定は要らないのだが、将来デフォルトが変わるかもしれないのでまあメモ的に書いておく。

localhost forwarding と外部から接続するためのポート転送

WSL に Windows から localhost で接続できる仕組みがあるらしい。

ascii.jp

これは今ではデフォルトで動いてるとのこと。なるほど確かに wsl で sshd を起動しておくと、コマンドプロンプトから ssh localhost でログインできる。意図したユーザー名がつけられてなければ ssh {wslusername}@localhost とやらねばならなかったところ。

Windows 環境だけで開発するならもうこれだけで十分だね。

だが今回外部からアクセスするための設定をしないといけない。それに関しては localhost forwarding が動いてるならぜんぶ ::1フォワードしちゃえばいいんじゃね? という話がおもしろかった。

matsudamper.hatenablog.com

IPv4でアクセスしたものをIPv6に変換してしまえば良いと考えました。「192.168.0.10」を「::1」に変換するのです。

WSL2へ外部PCからアクセスする - アプリ開発備忘録

なるほどこれなら今の IP アドレスを見なくてもいいし、1回設定すれば済んでしまうな。

起動用バッチファイル

というわけで出来上がったバッチファイルは以下の通り。 まだ環境が流動的なので portproxy はリセットして設定し直すようにしている。docker は起動してない時に restart しても動かないので start にしてある。

@echo off

cd %~dp0

netsh interface portproxy reset

wsl -u root -- service cron restart
wsl -u root -- service ssh restart
wsl -u root -- service docker start

set ports=22 80 3000 8080

for %%p in (%ports%) do (
    netsh interface portproxy add v4tov6 listenport=%%p connectaddress=::1 connectport=%%p
    netsh interface portproxy add v6tov6 listenport=%%p connectaddress=::1 connectport=%%p
)

タスクスケジューラで自動起動……が動かない

さてあとは Windows のタスクスケジューラで OS 起動時に上のバッチファイルを自動実行させておけば便利……かと思いきやこれが動かない。

「ユーザーがログオンしているかどうかにかかわらず実行する(Run whether user is logged in or not)」では wsl コマンドが実行されない現象があるようだ。手元の別の Windows 10 のマシンでは動くので Windows 11 の一部バージョンでの現象かもしれない。

github.com

すでに github に issue があがってていろんな回避策が議論されているが、どれもこれもうまくいったりいかなかったりしてる様子。

Strange how everyone has a different method that works.

不思議なことに、人によってうまくいく方法が違うんですね。

Unable to start WSL via Task Scheduler · Issue #8835 · microsoft/WSL · GitHub

このコメントがおもしろかった。こんな不可思議なことが最先端のソフトウェアでも起きるんだから、本当にソフトウェア開発というのは複雑なものである。

しょうがないので上のバッチファイルへのショートカットをデスクトップに作ってしばらく手動起動することにしよう。

なんにせよバッチファイル一つでさくっとサーバ化できるのだからたいへん便利なものである。

おまけ: オーディオを転送する

# apt install sox libsox-fmt-all

これで play コマンドで mp3 などが再生できるようになる。

ssh で接続先のマシンで鳴らした音を手元の環境で再生するには pulseaudio を使えばいい。

$ brew install pulseaudio
$ brew services restart pulseaudio

これで Mac 側で pulseaudio が起動する。Venture にアップグレードしたら「バックグラウンド項目が追加されました」の通知がうざいことになったが、オンオフしたりなんだりしてると止むので我慢してる。なんとかならんのかこれ。

あ、あと IPv6 でアクセスされることもあるので、/usr/local/etc/pulse/default.pa で以下のように設定しておく。

load-module module-native-protocol-tcp auth-ip-acl=127.0.0.0/8;::1

それから ~/.ssh/config でポート転送を仕掛けておく。

Host wsl-machine
  HostName wsl-machine.local
  # PulseAudio
  RemoteForward 14713 localhost:4713

あとは環境変数PULSE_SERVER=tcp:localhost:14713 としておけば、リモートのマシンで

$ play -q path/to/audio/file.mp3

として手元のマシンから音が鳴るはず。

enjoy!

Sugano `Koshian' Yoshihisa(E) <koshian@foxking.org>