okayurisotto.net

私が好きでやったことが他の人のためにもなったらお得かも!

WSLのディストリビューションを自作してみる

公開日:
最終更新日:

TL;DR

DOCKER_IMAGE_NAME="busybox:latest"
CONTAINER_NAME="wsl_export"
DISTRIBUTION_NAME="BusyBox"

docker container create --name $CONTAINER_NAME $DOCKER_IMAGE_NAME
docker container export $CONTAINER_NAME > /mnt/c/path/to/rootfs.tar
docker container rm $CONTAINER_NAME

wsl.exe --import $DISTRIBUTION_NAME C:/path/to/install-location C:/path/to/rootfs.tar

wsl.exe --distribution $DISTRIBUTION_NAME

はじめに

私は普段、Windows 11なデスクトップパソコンにインストールしたWSL(Ubuntu)上で諸々の趣味開発をしています。しかしLinuxディストリビューションとしてはUbuntuよりArch Linuxの方が使い慣れていて、できることならUbuntuではなくArch Linuxを使いたいと以前から感じていました。幸いなことに、WSLは標準で様々なLinuxディストリビューションをサポートしていて、その中にはArch Linuxもあります。

$ wsl.exe --list --online
インストールできる有効なディストリビューションの一覧を次に示します。
'wsl.exe --install <Distro>' を使用してインストールします。

NAME                            FRIENDLY NAME
AlmaLinux-8                     AlmaLinux OS 8
AlmaLinux-9                     AlmaLinux OS 9
AlmaLinux-Kitten-10             AlmaLinux OS Kitten 10
AlmaLinux-10                    AlmaLinux OS 10
Debian                          Debian GNU/Linux
FedoraLinux-42                  Fedora Linux 42
SUSE-Linux-Enterprise-15-SP6    SUSE Linux Enterprise 15 SP6
SUSE-Linux-Enterprise-15-SP7    SUSE Linux Enterprise 15 SP7
Ubuntu                          Ubuntu
Ubuntu-24.04                    Ubuntu 24.04 LTS
archlinux                       Arch Linux
kali-linux                      Kali Linux Rolling
openSUSE-Tumbleweed             openSUSE Tumbleweed
openSUSE-Leap-16.0              openSUSE Leap 16.0
Ubuntu-20.04                    Ubuntu 20.04 LTS
Ubuntu-22.04                    Ubuntu 22.04 LTS
OracleLinux_7_9                 Oracle Linux 7.9
OracleLinux_8_10                Oracle Linux 8.10
OracleLinux_9_5                 Oracle Linux 9.5
openSUSE-Leap-15.6              openSUSE Leap 15.6

しかしそもそもの話として、私はWSLにおける「ディストリビューション」がどういったもので、どう配布されているものであるのかを知りません。そこで今回は、適当なディストリビューションを自作する過程を通して、いろいろと勉強していこうと思います。

用語説明

まず、今日において、WSLとは一般的にWSL 2のことを指します。この記事でもWSL 2のことをWSLと呼称します。

WSLは、Windows Subsystem for Linuxの略であり、WindowsにLinuxディストリビューションをインストールする技術です。

WSLは仮想化技術(Hyper-V)を使用して、軽量ユーティリティ仮想マシン(VM)内でLinuxカーネルを実行します。このVMと関連したプロセスが、「VmmemWSL」です。

そして、各Linuxディストリビューションは、WSLで管理されるVM内で分離されたコンテナとして実行されます。コンテナとして実行されたそれぞれは一般的にインスタンスと呼ばれるようです。各インスタンスは、ネットワーク名前空間、デバイスツリー、CPU/カーネル/メモリ/スワップ、/initバイナリを共有しつつ、独自のPID名前空間、マウント名前空間、ユーザー名前空間、Cgroup名前空間、およびinitプロセスを持つとされています。

ここでは便宜上、以下のようにします。

  • Linuxディストリビューション
    • 実際にLinuxをホストOSとしてインストールする際に使われるもの
  • Dockerイメージ
    • Docker上にLinuxシステムを構築する際に使われるテンプレート
  • Dockerコンテナ
    • Docker上に構築されたLinuxシステム
  • WSLディストリビューション
    • WSL上にLinuxシステムを構築する際に使われるテンプレート
  • WSLインスタンス
    • WSL VM上に構築されたLinuxシステム
  • WSL VM
    • WSLインスタンスをつかさどる存在
    • WSLインスタンスの自動シャットダウンなどを行う

tarやvhdxからWSLインスタンスを作成する

ありがたいことにMicrosoftは公式ドキュメントにて、DockerイメージからWSLインスタンスを作成する方法を順を追って説明してくれています。例として挙げられているのはCentOSですが、簡単に異なるDockerイメージに応用できます。

「TL;DR」として記事の冒頭に書いたのが、BusyBoxなWSLインスタンスを作成するスクリプトです。主な手順としては、以下のようになっています。

  1. DockerイメージからDockerコンテナを作成
  2. Dockerコンテナのファイルシステムをtar形式でエクスポート
  3. tarファイルをWSLディストリビューションとしてインポート
  4. WSLインスタンスが作られる

まずはWSL固有の事情について説明します。

wsl.exeでは、--importオプションを使って、ファイルからディストリビューションをインストールする(WSLインスタンスを作成する)方法を提供しています。対応しているファイル形式はtarもしくはvhdxです。(ファイル名に制限はありません。)

wsl.exe --import $DistroName C:/path/to/install-location C:/path/to/rootfs.tar

vhdxというのは、仮想ハードディスク(Virtual Hard Disks)のためのフォーマットで、Hyper-V仮想マシンで使われているものです(公式ドキュメント)。WSLはHyper-Vの上に成り立っているもので、インストールしたLinuxが使うファイルシステムもvhdx形式でWindowsのファイルシステム上に保存されています。

私の環境では、Ubuntuのファイルシステムを保存したvhdxは、~\AppData\Local\wsl\{0ae58ad6-369c-424e-8f74-3f17610e65f0}\ext4.vhdxに保存されているようでした。また、以下のキーでレジストリを検索することで、WSLインスタンスについてのメタデータを確認することができます。

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Lxss

wsl.exe --importを実行したときには、このレジストリに新たにキーが登録されると同時に、C:/path/to/rootfs.tarを元にC:/path/to/install-location/ext4.vhdxが作成される形です。

ちなみに、wsl.exe --install--from-fileオプション付きで実行することでも、インストールは可能なようです。この場合、インストール先ディレクトリは自動的に決定されるようです。

wsl.exe --install --name $DISTRIBUTION_NAME --from-file C:/path/to/rootfs.tar

wsl.exeには--exportオプションもあり、WSL環境のバックアップを取りたいなどの一般的なユースケースではこちらが便利でしょう。)

Dockerイメージからtarを作成する

さて、tarさえ用意できれば、WSLインスタンス構築に使えるWSLディストリビューションを作ることができることがわかりましたが、ではそのtarはどのような形式をしているのでしょうか? これは、ファイルシステムをそのままアーカイブとしてまとめたものなようでした。

そのようなtarアーカイブとしては、Alpine Linuxが提供するMini root filesystemや、Dockerでコンテナをエクスポートする際に作成するアーカイブが例として挙げられます。

$ docker container export --help
Usage:  docker container export [OPTIONS] CONTAINER

Export a container's filesystem as a tar archive

Aliases:
  docker container export, docker export

Options:
  -o, --output string   Write to a file, instead of STDOUT

しかしDockerコンテナをエクスポートしたものが常に問題なくWSLにインポートでき使用できるとは限らないようです。例えばbusybox:latestを元に作成したものでは、/etc/fstabが存在しないことから、wsl.exeからの起動時にエラーが出力されてしまいました。(起動自体はしました。)

wsl: Processing /etc/fstab with mount -a failed.

あらかじめtarファイルに空の/etc/fstabを追加しておくことで対処可能なようでした。

mkdir ./etc/
touch ./etc/fstab
tar --append ./etc/fstab --file rootfs.tar

また、archlinux:latestを元に作成したものでは、/etc/machine-idが存在しないことから、bashが次のようなエラーを出力してしまいました。

-bash: /etc/machine-id: No such file or directory

そのパスに空ファイルを作成することでエラー自体はなくなりますが……対処としてこれで十分であるとは思えません。どうすればいいのでしょうねぇ……。

アプリケーションが動くWSLを作ることはできるのか?

Dockerコンテナのように1つのアプリケーションを動かし続けるようなWSLインスタンスを作ることも不可能ではありません。例えばnginxイメージから作ったnginxディストリビューションについて、以下のようにするとhttp://localhost:80/でnginxが立ち上がります。

wsl.exe -d nginx --exec /usr/sbin/nginx

そしてwsl.exe--execオプションなしで実行した際に呼び出されるものは、/etc/passwdから変更可能なため、以下のように変更することで、wsl.exe -d nginxだけでnginxが立ち上がるようになります。

-root:x:0:0:root:/root:/bin/bash
+root:x:0:0:root:/root:/usr/sbin/nginx

しかしどちらの場合も、15秒が経過すると接続できなくなってしまいました。これは、一定時間アイドル状態が続いた場合に、リソースの節約のため、WSLインスタンスがVMによって自動でシャットダウンされる挙動によるものです。WSLのインスタンスはサーバとして動かし続けるものとしては設計されていないのです。

おわりに

WSL用のディストリビューションの自作を通して、WSLについて少し詳しくなれたのでよかったです。あとでUbuntuからArch Linuxへデータの移行などをしようと思います。