在 WSL 2 中启用 Systemd

jjx*_*tra 16 c# windows-subsystem-for-linux .net-5

我正在尝试在 Windows 上使用 Ubuntu 调试 WSL 2 中的一些 C# / .NET 5 代码。我在 Windows 10 上安装了 WSL 2,并且想要测试创建 Systemd 服务。不幸的是,默认情况下 Systemd 似乎并未随 WSL 2 一起启用,尽管标准 Ubuntu 安装默认情况下已启用它。有什么方法可以在 WSL 2 中启用 Systemd 吗?

Not*_*1ds 18

注意:有关此社区 Wiki 的背景信息,请参阅此答案底部的脚注。

在 WSL2(但不是 WSL1)上启用 Systemd 有多种可能的途径。这里总结了这些内容,下面提供了更多详细信息。

  • 选项 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 功能

Microsoft 现在已在 WSL2应用程序版本中集成了 Systemd 支持(而不是较旧的“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 应用程序:

  • 通过 Microsoft Store(作为“Linux 的 Windows 子系统”)。

  • 或者从Github 存储库中的发布页面。要手动安装版本:

    1. 重新启动(以确保 WSL 根本未在使用)。简单的wsl --shutdown 可能会起作用,但通常不会。

    2. 从上面的链接下载 1.0.0(或更高版本)版本。

    3. 启动管理员 PowerShell 并:

      Add-AppxPackage <path.to>/Microsoft.WSL_1.0.0.0_x64_ARM64.msixbundle
      wsl --version # to confirm
      
      Run Code Online (Sandbox Code Playgroud)

要启用此功能,请在 WSL 下启动 Ubuntu(或其他 Systemd)发行版(通常就wsl ~可以工作)。

sudo -e /etc/wsl.conf
Run Code Online (Sandbox Code Playgroud)

添加以下内容:

[boot]
systemd=true
Run Code Online (Sandbox Code Playgroud)

退出 Ubuntu 并再次:

wsl --shutdown
Run Code Online (Sandbox Code Playgroud)

然后重新启动Ubuntu。

sudo systemctl status
Run Code Online (Sandbox Code Playgroud)

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


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

有许多可从各种来源获得的 Systemd 启用脚本。鉴于在 WSL 下运行 Systemd 所涉及的复杂性,建议您:

  • 使用一个积极维护的

  • 尝试尽可能多地了解它们的运作方式,以及它们如何影响您的 WSL 发行版中的其他功能和应用程序

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

在 WSL2 下启用 Systemd 的几个比较流行的项目是:

  • 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. 跑步:

    sudo -b unshare --pid --fork --mount-proc /lib/systemd/systemd --system-unit=basic.target
    
    Run Code Online (Sandbox Code Playgroud)

    这将在一个具有自己的 PID 映射的新命名空间中启动 Systemd。在该命名空间内,Systemd 将是 PID1(因为它必须运行)并拥有所有其他进程。然而,“真正的”PID 映射仍然存在于该命名空间之外。

    请注意,这是启动 Systemd 的“最低限度”命令行。它至少不会支持:

    • Windows Interop(运行 Windows 的能力.exe
    • Windows 路径(无论如何,如果没有 Windows Interop,则不需要)
    • WSLg

    上面列出的脚本和项目需要做额外的工作才能让这些东西正常工作。

  2. 等待几秒钟 Systemd 启动,然后:

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

    这将进入命名空间,您现在可以使用ps -efH它来查看它systemd在该命名空间中作为 PID 1 运行。

    此时,您应该可以运行了systemctl

  3. 并且在向自己证明这是可能的之后,建议您完全退出所有 WSL 实例,然后执行wsl --shutdown. 否则,有些东西会被“破坏”直到你这样做为止。它们可能会被“修复”,但这超出了这个答案的范围。如果您有兴趣,请参考上面列出的项目,看看他们是如何处理这些情况的。


替代方案 1: SysVInit 脚本(例如sudo service <service_name> start)(如果可用)

在 Ubuntu、Debian 和 WSL 上的其他一些发行版中,许多常见的系统服务仍然具有init.d可用于代替systemctlSystemd 单元的“旧”脚本。您可以使用 来查看这些 ls /etc/init.d/

因此,例如,您可以以 开头sshsudo service ssh start它将/etc/init.d/ssh使用start参数运行脚本。

即使某些非默认软件包(例如 MySql/MariaDB)也会安装 Systemd 单元文件init.d脚本,因此您仍然可以对service它们使用该命令。

另一方面,某些软件包(例如 Elasticsearch)安装 Systemd 单元。某些发行版仅为其存储库中的大多数(如果不是全部)包提供 Systemd 单元。


替代方案 2:手动配置和运行服务

对于那些没有等效初始化脚本的服务,可以“手动”运行它们。

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

在这种情况下,“答案”是找出 Systemd 单元文件正在做什么,并尝试手动复制它。这在复杂性上可能有很大差异。但我首先查看您尝试运行的 Systemd 单元文件:

less /lib/systemd/system/ssh.service
Run Code Online (Sandbox Code Playgroud)
# Trimmed
[Service]
EnvironmentFile=-/etc/default/ssh
ExecStartPre=/usr/sbin/sshd -t
ExecStart=/usr/sbin/sshd -D $SSHD_OPTS
RuntimeDirectory=sshd
RuntimeDirectoryMode=0755
Run Code Online (Sandbox Code Playgroud)

一些不太相关的行已被修剪,以使其更易于解析,但您可以man systemd.execman systemd.service和其他选项来查看大多数选项的作用。

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

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

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

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

...如果没有 Systemd,您也可以手动完成同样的事情。

同样,这可能是最简单的例子。您可能需要完成更多更复杂的任务。


替代方案 3: Docker

许多包/服务都可以作为 Docker 镜像提供。Docker 通常在 WSL2 上的 Ubuntu 下运行得很好(特别是 WSL2;它不能在 WSL1 上运行)。如果您尝试启动的服务没有 SysVinit“服务”脚本,则很可能有一个在容器化环境中运行的可用 Docker 映像。

示例:Elasticsearch,如本问题所示。

  • 好处#1:不会干扰已安装的其他软件包(没有依赖性问题)。
  • 好处#2:Docker 镜像本身几乎从不使用 Systemd,因此您可以经常检查Dockerfile以了解服务是如何在没有 Systemd 的情况下启动的。有关详细信息,请参阅下一个选项 - “手动方式”。

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


脚注该答案作为社区 Wiki 发布,因为它可以适用于多个 Stack Overflow 问题。它最初是基于询问 Ubuntu 问题的答案。然而,随着 Systemd 在 WSL 上的发展,社区希望这个 wiki 答案能够不断更新。

选择这个问题的原因是:

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

  • 这是切题的,因为*创建 Systemd 服务对于编程来说是(或者至少可以是)独特的