(追記)
この記事は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 で自動実行させる方法の方がおすすめ。
-
「アラジンと魔法のランプ」の方が有名だけど、あの話、原典の千夜一夜物語には収録されていないらしいよ。 ↩