LoginSignup
81
76

More than 3 years have passed since last update.

WSL2でSystemdを使うハック

Last updated at Posted at 2019-12-31

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 で動かす方法(派生編)

この方法で起動した systemdnsenter は、永続的ではないので、毎回実行しないといけない。

systemd は、Windowsを再起動して最初にWSL2を実行するときに実行しておく必要がある。

nsenter は、WSL2を実行するたびに毎回最初に実行しておく必要がある。

これらを毎回実行するのが面倒な人には、次の3つの方法を紹介する。

genie コマンド

genie コマンドは、 unsharensenter のラッパー的なコマンド。コマンド名は、瓶(コンテナー)に封じられた魔法の精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.bashrcsource /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ユーザーで起動するはず

一応紹介はしたものの、2番目の bash.bashrc で自動実行させる方法の方がおすすめ。


  1. 「アラジンと魔法のランプ」の方が有名だけど、あの話、原典の千夜一夜物語には収録されていないらしいよ。 

81
76
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
81
76