images.linuxcontainers.orgを利用して手軽に色々なWSL2ディストリビューションを作る
(追記 2024-11-22)cloud-initを使ったプロビジョニング方法について追記
WSL2ディストリビューションに関して、
Microsoft Storeからインストールできるものや、あるいは
wsl --list --online
で表示されるリスト以外のディストリビューションをインストールしたくなったり、
あるいは同じ種類のディストリビューションを複数個インストールしたくなったりすることがある(目的別にUbuntu24.04を2つ並用する、とか)。
というときの選択肢を探ってみる。
方法
に記載されているように、wsl --import
コマンドでLinuxのコンテナの中身を含むtarファイルを取り込むことで任意のLinuxディストリビューションをWSL2に登録することができる。
上記のリンク先の解説ではDockerコンテナを使っているが、
linuxcontainers.org
で公開されているイメージ:
をそのままwsl --import
コマンドで取り込むことができ、即座に対応するWSL2ディストリビューションを作ることができる。
公開されているイメージは
- ubuntu
- debian
- fedora
- archlinux
- alpine
- almalinux
- rockylinux
- amazonlinux
- (その他数え切れないくらい多数)
といった感じで、かなり多くの選択肢がある。
(もちろん無いものもあるにはあるが)
所望のイメージのページに行き、rootfs.tar.xz
をダウンロードし、
wsl --import <登録するディストリビューション名> <仮想ディスクなどの保存場所のパス> <rootfs.tar.xzの保存パス>
のようにして、wsl --import
の第3引数でダウンロードしたrootfs.tar.xz
を指定して取り込むと、所望のWSL2ディストリビューションをインポート出来る。
手元ではとりあえずubuntu-24.10(oracular), fedora41, amazonlinux2023, archlinuxあたりをインポートしてみたが、どれも問題なく起動した。
(発展)cloud-initによるディストリビューションのセットアップ・プロビジョニング
全部のイメージがそうかまでは確認できていないが、見た範囲ではどのディストリビューションでもユーザーがrootしか存在しないため、
実用的にはユーザーの作成などを行う必要がある。
また、パッケージ・ライブラリの追加や設定ファイルの記入なども一般には必要になる。
ここで、WSL2ではcloud-init:
が使えるため、cloud-initを使うことでWSL2の初期設定を自動化し、再現性の高いものにできる。
user-dataファイルの記入と配置
WSL2でcloud-initを実行するにあたり、事前に<WSL2ディストリビューション名>.user-data
という名前のyamlファイルを作成し、
Windows上の%USERPROFILE%\.cloud-init
(%USERPROFILE%
はWindowsにおけるホームディレクトリのようなもの)に格納しておく必要がある。
記入にあたり、詳細にはリファレンス:
および、設定例:
などを参考に作っていけば良い。
手元では↓のようなuser-dataを作成して動作確認した:
#cloud-config
# タイムゾーンとlocaleを設定↓
timezone: Asia/Tokyo
locale: en_US.utf8
# `wsl-usser`というUID=1000のユーザーを作成し、パスワード無しでsudoを使える設定にしておく
users:
- name: wsl-user
groups: [adm, sudo, wheel]
sudo:
- ALL=(ALL) NOPASSWD:ALL
uid: 1000
# ↓深い意味は無いが、例としてデフォルトのシェルを後でインストールするfishに設定する
shell: /usr/bin/fish
# ↓aptやdnf, pacmanなどでインストールするパッケージ名を指定
packages:
- tmux
- fish
- htop
- fzf
- ripgrep
# ↓自動でファイルを作成したり追記したりできる
write_files:
# /etc/wsl.confを記述することにより、systemdの有効化と、ログイン時のデフォルトユーザーをcloud-initで作成するものに設定している
- path: /etc/wsl.conf
append: true
content: |
[boot]
systemd=true
[user]
default=wsl-user
# ↓この例では意味のあることはしていないが、shellで実行できる操作を順次記述していける
runcmd:
- echo "cloud-init" >> /tmp/sample.txt
上記の内容で、執筆時点でのUbuntu-24.10, Fedora41, archlinuxで動作することを確認している。
※package名は同じものでもディストリビューションによって名称が違う、あるいはパッケージによって存在したりしなかったりする、ということがありうるので、常に異なるディストリビューションで使い回せるとは限らない。(が、差分を小さくできるのも事実だと思われる)
wsl2でのcloud-initの実行
記事の方法で記載したようなやり方でwsl.exe --import
により、適当なWSL2ディストリビューションを作成する。
ここで、インポートするイメージは"cloud"とついているものだと(全てではないようだが)多くのものが初めからcloud-initがインストール済みの状態になっており、オススメ。
もし入っていなければ、後で(詳細は未検証だが)おそらくはaptとかdnfなど適当なパッケージマネージャーでインストールしておけば良いと思われる。
次に作成したディストリビューションを起動する。例えば、
wsl.exe -d <WSL2ディストリビューション名> --cd ~
などとすると作成したディストリビューションに入ることができる。
その後、以下のようなスクリプトを作成し、実行する:
#!/bin/bash
cloud-init init --local
cloud-init init
cloud-init modules --mode=config
cloud-init modules --mode=final
# wsl内のrootユーザーで実行
chmod +x /path/to/cloud-init.sh
/path/to/cloud-init.sh
こうすると、Windows側の%USERPROFILE%\.cloud-init\<ディストリビューション名>.user-data
の内容を元にプロビジョニングが実行される。
(ターミナル上でcloud-initの処理が実行されるのが確認できる)
最後に、wsl2をシャットダウン・再起動すればOK。
(なお、wsl --import
で作成したディストリビューションではデフォルトではsystemdが有効でないため、systemctl enable cloud-init-***
などをせずに手動でスクリプト実行をすることにしている。)
(WSL公式のUbuntu-24.04の場合)
にWSL公式版のUbuntu-24.04版での実行例が記述されているが、ここではwsl2でのcloud-initの実行で記載されている手順は書かれておらず、実際、%USERPROFILE%\.cloud-init\<ディストリビューション名>.user-data
を置いておきさえすれば、初回起動時に勝手にcloud-initが実行される。
すなわち、手順が減ってより楽にプロビジョニングの実行ができて便利である。
WSL公式版のUbuntu-24.04、というのはMicrosoft Storeからインストールしたもの(おそらく、cliでwsl --install
でいれたUbuntuでも良いはず)、
および、Ubuntu image server:
のnoble
以下に置いてあるubuntu-noble-wsl-amd64-ubuntu24.04lts.rootfs.tar.gz
を使って、
wsl.exe --import <wsl2ディストリビューション名> <インストールパス> <↑でダウンロードしたrootfs.tar.gzファイル>
のようにして作成したもの、のいずれかが該当する。
そのため、24.04以降のUbuntu LTS版を用途ごとに複数立ち上げたい、といった場合であれば、↑のUbuntu image serverで公開されているイメージを使ってwsl --import
する方が便利かもしれない。
(補足)deprecatedなコマンドの移行
によると、cloud-init init
およびcloud-init modules
の両コマンドはdeprecated扱いとなっており、将来的に動かなくなる可能性がある。
そのため、将来的にはこの記事で紹介した内容はアップデートを行う必要がでてくると思われる。
cloud-init single
コマンド:
を使うことで、cloud-init init
コマンドおよびcloud-init modules
コマンドを置き換えることができる。
使用例は
cloud-init single --file /path/to/user-data --name <モジュール名>
のような感じである。
手元で試した感じ、cloud-init single
を使う場合、Windows側の%USERPROFILE%\.cloud-init\<WSL2ディストリビューション名>.user-data
を自動認識するとは限らないようなので、user-dataのyamlファイルは適当な場所に置いて--file
オプションで明示的に指定する必要がある。(逆に、%USERPROFILE%\.cloud-init
下に置かなくても良い)
<モジュール名>
と書いた部分に具体的に何を入れるのかが分からず難しかったが、
の各項目のうち、Internal name: cc_***
となっている部分の***
が相当する。
すなわち、cc_runcmd
であればruncmd
、cc_package_update_upgrade_install
であればpackage_update_upgrade_install
となる。
なお、各モジュールはinit
, config
, final
のいずれかのフェーズで実行されるのが本来正しいため、今回のように手動で実行する場合に間違ったタイミングで行わないように注意する必要がある。
(参考)
全ディストリビューションで共通かは不明だが、/etc/cloud/cloud.cfg
を参照するとどのモジュールが存在するか、どのタイミングなのかが分かるので参考にすると良い:
/etc/cloud/cloud.cfg(抜粋)
# The modules that run in the 'init' stage
cloud_init_modules:
- seed_random
- bootcmd
- write_files
- growpart
- resizefs
- disk_setup
- mounts
- set_hostname
- update_hostname
- update_etc_hosts
- ca_certs
- rsyslog
- users_groups
- ssh
- set_passwords
# The modules that run in the 'config' stage
cloud_config_modules:
- ssh_import_id
- keyboard
- locale
- spacewalk
- yum_add_repo
- ntp
- timezone
- disable_ec2_metadata
- runcmd
# The modules that run in the 'final' stage
cloud_final_modules:
- package_update_upgrade_install
- write_files_deferred
- puppet
- chef
- ansible
- mcollective
- salt_minion
- reset_rmc
- scripts_vendor
- scripts_per_once
- scripts_per_boot
- scripts_per_instance
- scripts_user
- ssh_authkey_fingerprints
- keys_to_console
- install_hotplug
- phone_home
- final_message
- power_state_change
上記の各モジュールの中から、必要そうなものを選んで適切な順序で実行すれば良い。
今回の例だと、
#!/bin/bash
# ↓user-dataのyamlファイルのパス。適宜編集するか、引数などで与えるようにする
USER_DATA_PATH=/path/to/user-data
# 必要なモジュールを順次実行(足りていないものがあれば適宜追記すること)
# init phase
cloud-init single --file "$USER_DATA_PATH" --name users_groups
cloud-init single --file "$USER_DATA_PATH" --name write_files
# config phase
cloud-init single --file "$USER_DATA_PATH" --name locale
cloud-init single --file "$USER_DATA_PATH" --name timezone
# final phase
cloud-init single --file "$USER_DATA_PATH" --name package_update_upgrade_install
# cloud-init single --file "$USER_DATA_PATH" --name runcmd
のような感じのスクリプト: cloud-init.sh
を作成し、bash /path/to/cloud-init.sh
のようにして実行すれば良い。
ただし、手元で試している感じだと何故かruncmd
だけはうまく動いていないようだった。
(全モジュールについて試したわけでもないので、他にもこのままでは動かないものがあるかもしれない)
という感じでまだ動作面で課題が残っている手法になるので、こちらの方法は補足としておき、
deprecatedになっているcloud-init init
およびcloud-init modules
を使った方法は掲載したままにしておく。
参考
(検証環境)
- Windows11(x86_64)
- バージョン23H2 (OSビルド22631.4460)
- wslバージョン:
wsl --version
WSL バージョン: 2.3.26.0
カーネル バージョン: 5.15.167.4-1
WSLg バージョン: 1.0.65
MSRDC バージョン: 1.2.5620
Direct3D バージョン: 1.611.1-81528511
DXCore バージョン: 10.0.26100.1-240331-1435.ge-release
Windows バージョン: 10.0.22631.4460
参考1 昔書いた記事(Dockerfileを使うやり方)
Dockerfileを使ってイメージ内容を自由にカスタマイズできる、という記事。
極めて高い自由度がメリットだが、一方で、Dockerで使われるベースイメージは一般にごく最低限のパッケージしか入っていないため、WSL2のような開発や普段使い?するためには色々と不足や差分があり、カスタマイズ(Dockerfileの記入)が大変なところがあった。
(あと、docker export
などを行うのが面倒)
images.linuxcontainers.org で公開されているイメージはそのままwsl --import
することができ、
dockerイメージよりは最低限使いそうなパッケージが入っていることが多そう?なので、より手軽に色々なディストリビューションを導入できると思われる。
参考2 distod
WSL2でsystemdを使えるようにする他、
WSL2にlinuxcontainers.orgで入手できるあらゆるイメージをWSL2にインストールできるツールらしい。
(まだwsl2で満足にsystemdが使えなかったときに、systemdを使う手段として使われていた気がする。)
前者の機能のためにこのツールを存在を知ったが、今回の記事の内容は後者の方の機能に着想を得ている。
(その他参考リンク)
Discussion