在WSL 2中启用Systemd

16

我正在尝试在Windows上的WSL 2和Ubuntu中调试一些C# / .NET 5代码。 我已经在Windows 10中安装了WSL 2,并想测试创建一个Systemd服务。 不幸的是,尽管标准的Ubuntu安装默认启用了Systemd,但是似乎WSL 2默认未启用。 有没有办法在WSL 2中启用Systemd?

1个回答

17

注:关于此社区Wiki的背景,请参见下面的脚注。

有几种可能的方法可以在WSL2上启用Systemd(但不适用于WSL1)。以下是这些方法的概述,更详细的信息请参见下文。

  • 选项1:将WSL升级到最新的应用程序版本(如果系统支持),并选择加入Systemd功能
  • 选项2:运行专为WSL2设计的Systemd-helper脚本
  • 选项3:手动在其自己的名称空间中运行Systemd

虽然这不是本问题的一部分,但对于那些只是想运行需要Systemd的某些应用程序,还有其他替代方案:

  • 在WSL1和WSL2上:

    • 替代方案1:SysVInit脚本(例如sudo service <service_name> start)(如果可用)
    • 替代方案2:手动配置和运行服务
  • 仅适用于WSL2:

    • 替代方案3:Docker

您应该在WSL中启用Systemd吗?

首先,请考虑您是否应该需要在WSL中启用Systemd。 启用Systemd将自动启动许多背景服务和任务,这些服务和任务实际上可能并不需要在WSL下面运行。 因此,它还将增加WSL的启动时间,尽管影响取决于系统。 请查看下面的备选方案部分,以查看是否有更适合您需求的更好的选项。例如, service 命令可能可以在不添加任何其他工作的情况下完成您的需求。


每个答案的详细信息:


选项1:将WSL升级到最新的应用程序版本(如果系统支持),并选择加入Systemd功能

微软现已将Systemd支持集成到WSL2的应用程序版本中(而不是较旧的“Windows功能”实现)。

从WSL应用程序发布1.0.0开始,此功能在Windows 10和Windows 11上都可用。 Windows 10用户需要处于UBR(更新版本修订)2311或更高版本。 UBR是完整Windows构建编号的最后4位数字(例如,对于Windows 10 22H2,版本号为10.0.19045.2311)。2311通过KB5020030进行安装,这是一个可选的预览更新,但如果您稍后阅读本文,它可能是较新的(非预览)每月维护更新。
如果您使用支持的Windows版本,则可以安装具有Systemd支持的WSL应用程序:
  • Through the Microsoft Store (as "Windows Subsystem for Linux").

  • Or from the Releases page in the Github repo. To install a release manually:

    1. Reboot (to make sure that WSL is not in use at all). A simple wsl --shutdown may work, but often will not.

    2. Download the 1.0.0 (or later) release from the link above.

    3. Start an Administrator PowerShell and:

      Add-AppxPackage <path.to>/Microsoft.WSL_1.0.0.0_x64_ARM64.msixbundle
      wsl --version # to confirm
      

要启用此功能,请在WSL下启动您的Ubuntu(或其他Systemd)发行版(通常只需运行wsl ~即可)。

sudo -e /etc/wsl.conf

添加以下内容:

[boot]
systemd=true

退出 Ubuntu 并重新进入:

wsl --shutdown

然后重新启动Ubuntu。

sudo systemctl status

... 应该显示您的Systemd服务。


选项2:运行专为WSL2设计的Systemd-helper脚本

有许多来自不同来源的Systemd启用脚本可用。鉴于在WSL下运行Systemd涉及的复杂性,建议您:

  • 使用正在积极维护的脚本

  • 尽可能地理解它们的操作方式,以及它们可能如何影响WSL下分发版和其他功能和应用程序

  • 当在此处或任何其他网站上提问时,请披露您正在使用的脚本,以便其他人可以在正确的上下文中尝试理解和/或重现您的问题

使Systemd在WSL2下工作的几个较受欢迎的项目包括:

  • Genie:1.8k颗星,最后一次提交于2022年9月

  • Distrod:1.4k颗星,最后一次提交于2022年7月

  • WSL2-Hacks:1.1k颗星,主要是教程,带有一个支持脚本示例。最后一次提交于2022年1月

从根本上讲,它们都遵循下一个选项中介绍的相同原则...

选项3:手动运行Systemd在自己的命名空间中

早期版本的WSL中运行Systemd的一个主要问题是两个init进程都需要成为PID 1。为了解决这个问题,可以创建一个新的命名空间或容器,在其中Systemd可以作为PID 1运行。

查看如何执行此操作(非常基本水平):

  1. Run:

    sudo -b unshare --pid --fork --mount-proc /lib/systemd/systemd --system-unit=basic.target
    

    This starts Systemd in a new namespace with its own PID mapping. Inside that namespace, Systemd will be PID1 (as it must, to function) and own all other processes. However, the "real" PID mapping still exists outside that namespace.

    Note that this is a "bare minimum" command-line for starting Systemd. It will not have support for, at least:

    • Windows Interop (the ability to run Windows .exe)
    • The Windows PATH (which isn't necessary without Windows Interop anyway)
    • WSLg

    The scripts and projects listed above do extra work to get these things working as well.

  2. Wait a few seconds for Systemd to start up, then:

    sudo -E nsenter --all -t $(pgrep -xo systemd) runuser -P -l $USER -c "exec $SHELL"
    

    This enters the namespace, and you can now use ps -efH to see that systemd is running as PID 1 in that namespace.

    At this point, you should be able to run systemctl.

  3. And after proving to yourself that it's possible, it is recommended that you exit all WSL instances completely, then doing wsl --shutdown. Otherwise, some things will be "broken" until you do. They can likely be "fixed", but that's beyond the scope this answer. If you are interested, please refer to the projects listed above to see how they handle these situations.

备选方案1: SysVInit 脚本(例如sudo service <service_name> start)可用

在 Ubuntu、Debian 和一些其他支持 WSL 的发行版中,许多常见的系统服务仍然可以使用“旧的”init.d脚本代替 Systemd 单元与 systemctl 一起使用。你可以使用 ls /etc/init.d/ 命令来查看这些脚本。

因此,例如,您可以使用 sudo service ssh start 启动 ssh,它将使用 /etc/init.d/ssh 脚本并传递 start 参数来运行。

即使非默认安装的软件包,如 MySql/MariaDB,也会同时安装 Systemd 单元文件和旧的 init.d 脚本,因此您仍然可以为它们使用 service 命令。

但是,某些软件包(例如 Elasticsearch)只安装 Systemd 单元。有些发行版只提供大部分(如果不是全部)软件包的 Systemd 单元。


备选方案2: 手动配置和运行服务

对于那些没有 init 脚本的服务,可以尝试手动运行它们。

为了简单起见,我们假设ssh init.d 脚本不可用。

在这种情况下,答案是找出 Systemd 单元文件正在做什么,并尝试手动复制。这可能因复杂性而异。但我会从查看您要运行的 Systemd 单元文件开始:

less /lib/systemd/system/ssh.service

# Trimmed
[Service]
EnvironmentFile=-/etc/default/ssh
ExecStartPre=/usr/sbin/sshd -t
ExecStart=/usr/sbin/sshd -D $SSHD_OPTS
RuntimeDirectory=sshd
RuntimeDirectoryMode=0755

一些不重要的行已被裁剪,以便更易于解析,但是您可以通过man systemd.execman systemd.service等查看大多数选项的含义。

在这种情况下,当您执行sudo systemctl start ssh时,它会:

  • /etc/default/ssh读取环境变量($SSHD_OPTS
  • 测试配置,如果失败就退出
  • 确保带有指定权限的RuntimeDirectory存在。 这将转换为/run/sshd(来自man systemd.exec)。 这还会在停止服务时删除运行时目录。
  • 使用选项运行/usr/sbin/sshd

因此,如果您没有任何基于环境的配置,您可以设置一个脚本来完成以下操作:

  • 确保运行时目录存在。 请注意,由于它在/run中,这是一个tmpfs挂载,在WSL实例每次重新启动后都会被删除。
  • 将权限设置为0755
  • 以root用户身份启动/usr/sbin/sshd

......而且你也可以手动使用Systemd完成相同的事情。

再次说明,这可能是最简单的示例。 对于更复杂的任务,您可能需要更多的工作。


备选方案3: Docker

许多软件包/服务都可用作Docker镜像。 Docker在WSL2上运行得非常好(特别是WSL2;它不会在WSL1上运行)。 如果没有针对您要启动的服务的SysVinit“service”脚本,则很可能有一个可用的Docker映像,可在容器化环境中运行。

例如:Elasticsearch,如此问题中所述。

  • 额外收获#1:不会干扰已安装的其他软件包(无依赖性问题)。
  • 额外收获#2:Docker映像本身几乎从不使用Systemd,因此您通常可以检查Dockerfile以查看如何在没有Systemd的情况下启动服务。 有关更多信息,请参见下一个选项 - “手动方式”。

Microsoft建议在WSL2下使用Docker Desktop for Windows运行Docker容器。


脚注本回答作为社区Wiki发布,因为它适用于多个Stack Overflow问题。 它最初基于这个Ask Ubuntu问题的答案。然而,希望这个维基答案可以随着Systemd在WSL上的不断发展而被社区持续更新。

选择此问题的原因是:

  • 它似乎是最规范、直接的“如何在WSL上启用Systemd?”问题。

  • 它是有关程序设计的,因为*创建Systemd服务是(或至少可以)独特的


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接