(追記)
この記事はobsoleteです(使われなくなりました)。systemd を使用して WSL で Linux サービスを管理する | Microsoft Learnに従ってください。
(追記ここまで)
initについて
Linuxを含むUnix系OSの起動に使われるシステム。カーネルから最初に起動され、プロセスID 1が付与され、ほかのすべてのプロセスの先祖として動作し、さまざま初期処理を行うプログラム。
Linuxでは、SysVInit → Upstart → Systemd のように置き換えれらてきた。
WSLのinitについて
WSLはMSによるカスタムのinitを使っている。Windowsとの相互運用に必要な重要な処理を行っており、置き換えられない。一方で、Systemdのような高度なことは行わない。
Systemdもインストールはされるが、無効化されている。有効にしようとしても、PID 1で動作しないので、systemctl
によるサービス管理がエラーになる。
systemd
を PID 1 で動かす方法(基本方針編)
Ubuntuのコミュニティサイトに Using snapd in WSL2 という投稿があったので解説してみる。
unshare
コマンドで簡易コンテナーを作る
最初に、以下のコマンドを実行する。
sudo daemonize /usr/bin/unshare --fork --pid --mount-proc /lib/systemd/systemd --system-unit=basic.target
unshare
コマンドは、実行コンテキストに名前空間を割り当てて、ほかのプロセスから分離する。
オプションは以下のものを指定している。
-
--fork
: 指定したプログラム(/lib/systemd/systemd --system-unit=basic.target
)をunshare
の子プロセスとしてforkさせる -
--pid
: PID 名前空間を分離する -
--mount-proc
: マウント名前空間を分離し、/proc
を再マウントする
systemd
コマンドは、unshare
によって、隔離された名前空間のPID 1として起動する。なお、マルチユーザーの基本サービスが定義された basic.target
がターゲットとして指定されている。
そして、それら全体を現在のシェルから切り離してデーモン化するため、demonize
コマンドを使用している。
この時点で systemd
はPID 1として動き出すものの、あくまで隔離された簡易コンテナー環境の中だけの話である。簡易コンテナーの外でサービスを管理しようとすると、簡易コンテナーの中の systemd
と通信できずに失敗する。
nsenter
コマンドで簡易コンテナーの中に入る
というわけで、サービスを管理する場合は簡易コンテナーに入らないといけない。それには、以下のコマンドを実行する。
exec sudo nsenter --target $(pidof systemd) --all su - $LOGNAME
nsenter
コマンドは、隔離された名前空間を持つ簡易コンテナ―に入ってプログラムを実行する。オプションは以下のものを指定している。
-
--target $(pidof systemd)
: 簡易コンテナ―として隔離実行されているsystemd
の実際の PID をターゲットとして指定 -
--all
: ターゲットプロセスのすべての名前空間をマウントしなおす。 -
su - $LOGNAME
: 現在のログインユーザーとしてログインしなおす。
そして、現在のプロセスを置き換えるため、 exec
コマンドを使用している。
ここまで成功すれば、systemctl
でも snap
でも動かせるようになっているはず。
systemd
を PID 1 で動かす方法(派生編)
この方法で起動した systemd
や nsenter
は、永続的ではないので、毎回実行しないといけない。
systemd
は、Windowsを再起動して最初にWSL2を実行するときに実行しておく必要がある。
nsenter
は、WSL2を実行するたびに毎回最初に実行しておく必要がある。
これらを毎回実行するのが面倒な人には、次の3つの方法を紹介する。
genie
コマンド
genie
コマンドは、 unshare
と nsenter
のラッパー的なコマンド。コマンド名は、瓶(コンテナー)に封じられた魔法の精1から。
インストール方法と使い方は上記githubのreadmeを参照のこと。
(追記) subsystemctl
コマンド
@sora_hさん作のコマンドもあります。上記のgenieに影響を受けたとのこと。
/etc/bash.bashrc
で自動実行させる
方法は下記のリンク先に書いてある通り。
一応、手順を簡単に解説しておく。
-
/usr/sbin/start-systemd-namespace
と/usr/sbin/enter-systemd-namespace
という2つのスクリプトファイルを作成しておく -
/usr/sbin/start-systemd-namespace
の中で実行するsudo /usr/sbin/enter-systemd-namespace
コマンドにはパスワードが不要なように設定しておく -
/etc/bash.bashrc
でsource /usr/sbin/start-systemd-namespace
を実行するようにしておく - 環境変数
BASH_ENV
に/etc/bash.bashrc
を設定しておく(Windows側に%WSLENV%
を設定して引き継ぐ)
sudo
コマンドの設定は sudo visudo
コマンドを使って /etc/sudoers
ファイルを編集するのが基本だが、今回の /usr/sbin/enter-systemd-namespace
のための設定は /etc/sudoers.d/
ディレクトリに切り出しておくのもよい。その方法を紹介しているページはこちら。
ログインシェルを置き換える
方法は下記のリンク先に書いてある通り。
一応、手順を簡単に解説しておく。
-
/usr/bin/bash
スクリプトファイルを作成しておく -
/etc/passwd
ファイルを編集して、rootユーザーのログインシェルを/usr/bin/bash
に設定する - ログインユーザーをrootにする
- Linuxディストリビューションをストアアプリとしてインストールしている場合は
[ディストリビューション名].exe --config --default-user root
を実行する - そうでない場合は
wsl.exe [ディストリビューション名]
コマンドで起動する際にユーザーを指定しなければrootユーザーで起動するはず
- Linuxディストリビューションをストアアプリとしてインストールしている場合は
一応紹介はしたものの、2番目の bash.bashrc
で自動実行させる方法の方がおすすめ。
-
「アラジンと魔法のランプ」の方が有名だけど、あの話、原典の千夜一夜物語には収録されていないらしいよ。 ↩