最強の WSL 環境を作る
最強の WSL 環境を作る
まあ、何が最強なのかよくわからないのですが。
WSLg は GUI が動いて音もなるので大変便利なのですが、systemd が動作していない弱点があります。LXD を多用している身としては結構しんどいのですね。
snapd が使えないので、JetBrains の開発ツールをインストールするのもちょっと面倒。まあ、Ubuntu Make を使えばいいのでそれほど気にはしていないのですが...
というわけで、定期的にいろいろ試している今日この頃、ようやく自分が常用しているものが全部動く環境が作れました。
セットアップ
(2022/01/11 追記)
手順は、入力するコマンドを PowerShell だったり、WSL だったりといりみだれます。次のルールで書いていますので、間違えないようにしてください。
プロンプトが >
のときは PowerShell に入力してください。
> echo hello
プロンプトが $
のときは WSL の Linux 環境で入力してください。
$ echo hello
WSLg 環境の構築
何はなくとも WSLg 環境の構築です。
WSL カーネルが古いと WSLg が動かないので念のためアップデートします。
> wsl --update
WSL 環境を作ります。
> wsl --install -d Ubuntu
ユーザー名とかパスワードをいつものように設定してください。apt
コマンドでアップグレードもしておきましょう。
$ sudo apt update
$ sudo apt full-upgrade
ターミナルとかをインストールします。
$ sudo apt install xfce4-terminal x11-apps
WSLg の Wayland コンポジターで表示されるはずです。
$ xfce4-terminal &
xeyes
を起動して楽しみましょう。初回起動は 10 秒くらい待たされるかも。
$ xeyes &
後で詳しく解説しますが、xfce4-terminal
は Wayland ネイティブアプリで、xeyes
は X11 アプリです。
genie で systemd 化する
genie はコンテナーとして systemd を起動するみたいです。(嘘ついてたらごめんなさい)
genie は .NET 5 Runtime が必要なのでインストールします。セットアップ方法はUbuntu に .NET SDK または .NET ランタイムをインストールするを参考にしてください。
$ sudo apt install dotnet-runtime-5.0
続けて genie をインストールします。セットアップ方法は公式サイトを参考にしてください。
$ sudo apt install systemd-genie
genie は起動時にすべてのサービスの起動を待つのですが、実際にはいくつか起動できないサービスがあります。標準では待機時間の最大が 240 秒となっていてかなり長いので、30 秒くらいに変更します。
/etc/genie.ini
ファイルを開いて、systemd-timeout
の値を 30 位にしてください。
[genie]
...
systemd-timeout=30
...
いったん、WSL 環境を停止します。
> wsl -t Ubuntu
WSL 環境で genie を起動します。(-s
はシェルを起動します)
余談ですが、ホスト名に -wsl
というサフィックスがつきます。
> wsl -d Ubuntu genie -s
Waiting for systemd....!!!!!
Timed out waiting for systemd to enter running state.
This may indicate a systemd configuration error.
Attempting to continue.
Failed units will now be displayed (systemctl list-units --failed):
UNIT LOAD ACTIVE SUB DESCRIPTION
● systemd-remount-fs.service loaded failed failed Remount Root and Kernel File Systems
● multipathd.socket loaded failed failed multipathd control socket
LOAD = Reflects whether the unit definition was properly loaded.
ACTIVE = The high-level unit activation state, i.e. generalization of SUB.
SUB = The low-level unit activation state, values depend on unit type.
2 loaded units listed.
最初は起動しないサービスのせいで 30 秒ほど待たされるので気長に待ちましょう。
メッセージにある通り、systemd-remount-fs.service と multipathd.socket の二つのサービスが起動していません。問題が出るサービスなどは Systemd units known to be problematic under WSLにあります。
systemd-remount-fs.service はパーティションにラベル cloudimg-rootfs がないのが原因のようです。
$ cat /etc/fstab
LABEL=cloudimg-rootfs / ext4 defaults 0 1
ルートパーティションのデバイスを調べます。ドキュメントでは /dev/sdb なことが多いよと書かれていましたが、自分の手元では /dev/sdc でした。
$ mount | grep '\s/\s'
/dev/sdc on / type ext4 (rw,relatime,discard,errors=remount-ro,data=ordered)
ラベルをつけます。
$ sudo e2label /dev/sdc cloudimg-rootfs
multipathd.socket は WSL そのものが非対応らしいので、無効化します。
$ sudo systemctl disable multipathd.socket
これで、次回からは 5 秒くらいで起動するようになります。
LXD をセットアップする
すんなり動きそうなのですが、動きませんでした...
lxd
グループに自分を追加します。
$ sudo usermod -aG lxd $USER
いったんシェルを抜けて、再度 genie を起動します。
$ exit
> wsl -d Ubuntu genie -s
そのままだと lxd init
に失敗するので、https://github.com/lxc/lxd/issues/8724#issuecomment-828800317 に書かれているおまじないをします。
$ sudo iptables -A INPUT
$ sudo systemctl reload snap.lxd.daemon
(2022/01/11 sudo systemctl reload snap.lxd.daemon
を追加しました)
あとはいつも通り、lxd init
で初期化します。質問されますが、すべてデフォルトで構いません。
$ sudo lxd init
...
コンテナーを作成して起動します。
$ lxc launch ubuntu:20.04 ubuntu
```shell
$ lxc exec ubuntu bash
#
無事使えました!
(2022/01/11 追記)
残念ながら、再起動すると LXD コンテナーの起動に失敗します。lxd init
前のおまじないを、WSL 環境の起動のたびに実行してください。
$ sudo iptables -A INPUT
$ sudo systemctl reload snap.lxd.daemon
LXD デーモンの起動前に INPUT
ルールが追加されるように設定すればいいだけですけど、どうすればいいんだろう?
X11 アプリケーションが動くようにする
最近の genie は xfce4-terminal のような Wayland ネイティブアプリはそのまま WSLg で表示できます。(うまく動かない時は、wsl -t Ubuntu
と環境を一度停止してから再度試してみてください)
しかし、X11 アプリケーションはそのままでは表示できませんでした。
WSL その222 - Linux GUIアプリを動かすWSLgのアーキテクチャーと仕組みを見るとわかりやすいです。
上記ブログから引用した図ですが、WSLg では通常の WSL 環境の Ubuntu のコンテナーとは別に Wayland 環境用のシステムコンテナーが別途起動しています。双方のコンテナーの通信はソケットで行われています。
X11 アプリケーションは XWayland で、Wayland アプリケーションは Wayland と別々のソケットで通信しています。genie は Wayland のソケットはよしなにしてくれているようですが、X11 のソケットは genie 側からはアクセスできず、X11 アプリは起動しません。
https://github.com/arkane-systems/genie/issues/175#issuecomment-922526126 にこのソケットを genie 側からアクセスできるようにするハックがあるのでそれを使います。(少しいじっています)
次の内容を ~/.bashrc の最後に追加してください。
wsl_mount_root=/mnt
wsl_windir="${wsl_mount_root}/c/WINDOWS/System32"
wsl_distro=$WSL_DISTRO_NAME
wsl_wslg_display=0
# check if in-built wslg socket exists at all
if test -S $wsl_mount_root/wslg/.X11-unix/X0; then
# fix is needed in case system socket in /tmp doesn't exist or it's true, canonical path isn't the wslg socket's one
if ! test -e /tmp/.X11-unix/X${wsl_wslg_display} || ! test "$(readlink -f /tmp/.X11-unix/X${wsl_wslg_display})" = "$wsl_mount_root/wslg/.X11-unix/X0"; then
echo -e "* WSL: restoring WSLG X11 server socket on /tmp/.X11-unix/X${wsl_wslg_display}"
# recreate /tmp/.X11-unix directory if needed
if test ! -d /tmp/.X11-unix; then
$wsl_windir/wsl.exe -d $wsl_distro -u root install -dm 1777 -o root /tmp/.X11-unix
fi
# delete existing socket if needed. this must obviously break something that created that socket
# and you'd rather define display not colliding with other sockets
if test -e /tmp/.X11-unix/X${wsl_wslg_display}; then
$wsl_windir/wsl.exe -d $wsl_distro -u root rm -Rf /tmp/.X11-unix/X${wsl_wslg_display} /tmp/.X${wsl_wslg_display}-lock
fi
# if desired display is different than wslg's default 0, check if WSLG socket doesn't already exist on X0
# if yes, delete it aswell to avoid having the same wslg socket bound to two separate display numbers
if test $wsl_wslg_display -ne 0; then
if test -e /tmp/.X11-unix/X0 && test "$(readlink -f /tmp/.X11-unix/X0)" = "$wsl_mount_root/wslg/.X11-unix/X0"; then
$wsl_windir/wsl.exe -d $wsl_distro -u root rm -Rf /tmp/.X11-unix/X0 /tmp/.X0-lock
fi
fi
# create symlink to wslg socket
$wsl_windir/wsl.exe -d $wsl_distro -u root ln -s $wsl_mount_root/wslg/.X11-unix/X0 /tmp/.X11-unix/X${wsl_wslg_display}
# export updated display number for user's convenience
export DISPLAY=:${wsl_wslg_display}
fi
fi
念のため、WSL 環境を停止して再度起動しなおします。
> wsl -t Ubuntu
> wsl -d Ubuntu genie -s
Waiting for systemd....!!!!!!!
* WSL: restoring WSLG X11 server socket on /tmp/.X11-unix/X0
テストで xeyes
を起動します。
$ xeyes &
少し待たされますが表示されるはずです。
余談ですが、起動時のスイッチで Wayland で動作するアプリケーションもあるようです。Firefox - ArchWiki などがそうです。
$ MOZ_ENABLE_WAYLAND=1 firefox
Wayland か X11 かは xeyes
の目玉が動くかどうかで区別できるそうです。Xwayland アプリケーションであることを視認する
日本語化と日本語入力
WSLg でそのまま Windows の IME が使えればいいんですけど、そうではありません。
WSL2にFcitx+Mozcを入れて日本語入力するを参考に、Mozc をセットアップします。
$ sudo apt install language-pack-ja fcitx-mozc dbus-x11 fonts-takao-* fonts-noto-*
$ sudo update-locale LANG=ja_JP.UTF8
$ sudo sh -c "dbus-uuidgen > /var/lib/dbus/machine-id"
$ cat << 'EOS' | tee -a ~/.profile
#Added by https://astherier.com/blog/2020/08/install-fcitx-mozc-on-wsl2-ubuntu2004/
export GTK_IM_MODULE=fcitx
export QT_IM_MODULE=fcitx
export XMODIFIERS=@im=fcitx
export DefaultIMModule=fcitx
if [ $SHLVL = 1 ] ; then
(fcitx-autostart > /dev/null 2>&1 &)
xset -r 49 > /dev/null 2>&1
fi
#end
EOS
WSL 環境を一旦停止して再度起動します。ただし、genie -s
だと日本語入力時の変換候補のウインドウが出ませんでした。genie -l
だと問題がないようです。(環境変数かなにかの問題だと思うのですが...)
> wsl -t Ubuntu
> wsl -d Ubuntu genie -l
Waiting for systemd....!!!!!!!!!!!
Connected to the local host. Press ^] three times within 1s to exit session.
Ubuntu 20.04.3 LTS shuttle-wsl pts/1
shuttle-wsl login:
ログインユーザーとパスワードを入力してログインします。
このままだと X11 アプリケーションが英語キーボード配列になってしまいます。fcitx の設定でキーボードを変更します。
$ fcitx-config-gtk3
スクリーンショットのようになるように、英語キーボードを削除して、日本語キーボードを追加して一番上に持ってきてください。
これで日本語入力も問題ないはずです。
好みに応じて
そのままだと文字が小さすぎたので... 150% 拡大にします。
$ gsettings set org.gnome.desktop.interface text-scaling-factor 1.5
JetBrains Rider をインストールします。
$ sudo snap install rider --classic
GNU EMacs をインストールします。
$ sudo apt install emacs
最後に
めんどくせー!
あ、「普通にネイティブの Ubuntu 使えばいいじゃん」とか突っ込まないでください...
Discussion
https://github.com/nullpo-head/wsl-distrod が良いと思っています。
こういうのもあるのですね! ありがとうございます。
今は力尽きたのであれですが、次またトライするときに試してみます!