为 WSL 生成自定义 Linux 分发版

本指南将演练创建和分发 WSL 分发(文件 .wsl )的步骤。

WSL 分发有两个部分:

  1. 根文件系统(以 tar 文件的形式分发)
  2. 清单条目(其中包含分发元数据)

注意

本指南仅适用于 WSL 版本 2.4.4 及更高版本。

注意

有关上一个基于 appx 的分发打包说明,请参阅 此存储库

什么是 WSL 根文件系统 tar 文件?

WSL 分发版由具有 Windows 文件扩展名的 .wsl tar 文件定义。

TAR 文件(磁带存档简称)是一种存档文件,用于将多个文件存储在单个文件中,以便更轻松地分发或备份。 TAR 文件包含 Linux 分发版(所有分发文件)以及 WSL 配置文件的根文件系统。 WSL 配置文件告知 WIndows 操作系统如何安装和启动分发版。

拥有想要进入 WSL 分发版的 Linux 系统后,请按照以下步骤开始操作。

创建 WSL 配置文件

分发应包括两个配置文件:

  1. /etc/wsl-distribution.conf:由分发维护者创建的文件,负责控制首次使用 WSL 启动时应如何配置 Linux 分发版。
  2. /etc/wsl.conf:包含特定于用户的全局系统设置的文件,并控制分发的启动方式。 详细了解 WSL 配置文件。

添加 WSL 分发配置文件

分发配置文件 /etc/wsl-distribution.conf定义在用户首次启动时应如何配置 Linux 分发版。 此文件可用于以交互方式创建用户帐户、显示许可协议等。

下面是一个示例 /etc/wsl-distribution.conf 文件:

# /etc/wsl-distribution.conf

[oobe]
command = /etc/oobe.sh
defaultUid = 1000
defaultName = my-distro

[shortcut]
icon = /usr/lib/wsl/my-icon.ico

[windowsterminal]
ProfileTemplate = /usr/lib/wsl/terminal-profile.json

WSL 分发文件配置选项:

key value default 说明
oobe.command string <none> OOBE 代表现装体验。 此命令在用户首次在分发版中打开交互式 shell 时运行。 如果该命令返回非零,则被视为失败,并且用户将无法打开 shell。
oobe.defaultUid integer <none> 分发以默认 UID 开头。 当脚本创建新用户时 oobe.command ,这非常有用。
oobe.defaultName string <none> 分发所注册的默认名称。 此默认名称可以替换为命令: wsl.exe --install <distro> --name <name>
shortcut.icon string 默认 WSL 图标 分发的“开始”菜单快捷方式中的图标。 必须 .ico 采用最大大小为 的格式 10MB
“windowsterminal.profileTemplate” string 终端模板文件的路径 用于为此分发生成Windows 终端配置文件的 JSON 模板。

你需要为分发创建现装体验(OOBE) 首次运行体验。 下面是可以使用的示例 bash 脚本。 此脚本假定设置为oobe.defaultUid1000

#! /bin/bash

set -ue

DEFAULT_GROUPS='adm,cdrom,sudo,dip,plugdev'
DEFAULT_UID='1000'

echo 'Please create a default UNIX user account. The username does not need to match your Windows username.'
echo 'For more information visit: https://aka.ms/wslusers'

if getent passwd "$DEFAULT_UID" > /dev/null ; then
  echo 'User account already exists, skipping creation'
  exit 0
fi

while true; do

  # Prompt from the username
  read -p 'Enter new UNIX username: ' username

  # Create the user
  if /usr/sbin/adduser --uid "$DEFAULT_UID" --quiet --gecos ''  "$username"; then

    if /usr/sbin/usermod "$username" -aG "$DEFAULT_GROUPS"; then
      break
    else
      /usr/bin/deluser "$username"
    fi
  fi
done

生成Windows 终端配置文件

安装分发版时,WSL 自动生成Windows 终端配置文件。 分发维护人员可以通过在 WSL 配置文件/etc/wsl-distribution.conf中设置 windowsterminal.profileTemplate来自定义生成的配置文件。

json 文件遵循 终端配置文件 json 格式。 下面是一个示例配置文件:


{
  "profiles": [
    {
      "antialiasingMode": "aliased",
      "fontWeight": "bold",
      "colorScheme": "Postmodern Tango Light"
    }
  ],
  "schemes": [
    {
      "name": "Postmodern Tango Light",
      "black": "#0C0C0C",
      "red": "#C50F1F",
      "green": "#13A10E",
      "yellow": "#C19C00",
      "blue": "#0037DA",
      "purple": "#881798",
      "cyan": "#3A96DD",
      "white": "#CCCCCC",
      "brightBlack": "#767676",
      "brightRed": "#E74856",
      "brightGreen": "#16C60C",
      "brightYellow": "#F9F1A5",
      "brightBlue": "#3B78FF",
      "brightPurple": "#B4009E",
      "brightCyan": "#61D6D6",
      "brightWhite": "#F2F2F2"
    }
  ]
}

此文件不需要指定配置文件 name,也 commandLine不需要指定配置文件。 生成终端配置文件时,WSL 会自动添加这些配置文件。

为每个分布区添加本地设置的 WSL 配置

在分发根文件系统的上下文中,我们建议你配置系统设置,包括默认是否以按分布方式在本地设置中/etc/wsl.conf启动系统设置。 请参阅以下示例。

# /etc/wsl.conf

[boot]
systemd=true|false

分发作者通过将值设置为 boot.systemd true (enabled)或 false (未启用)来确定系统是默认启用的。

如果选择默认启用系统,请参阅最佳做法部分

有关所有受支持的设置,/etc/wsl.conf请参阅 WSL 中的高级设置配置。

创建 tar 文件

分发和配置文件到位后,可以在 tar 中捕获根文件系统。

下面是创建 tar 的建议方法:

$ cd /path/to/rootfs
$ tar --numeric-owner --absolute-names -c  * | gzip --best > ../install.tar.gz

tar 的根目录应该是文件系统的根(而不是包含根文件系统的目录)。

建议的压缩格式为 gzip。 其他压缩格式可能会破坏与旧版 WSL 版本的兼容性。

请参阅应或不应包含的文件的最佳做法部分

若要获取现有 Linux 分发版的 TAR 文件,请查找有关如何在导入任何 Linux 分发版中 导出 Docker 容器以用于 WSL 的指导。

tar 文件存档准备就绪后,请参阅 替代分发清单 以在本地试用。

创建 .wsl 文件扩展名

最后一步是创建一个 TAR 文件来表示自定义 Linux 分发版,方法是通过重命名文件扩展名将 .tar 文件扩展名 .wsl 更改为文件扩展名。 重命名此文件扩展名会将它标记为 WSL 分发版。 将 TAR 重命名为 <.wsla0/> 后,在文件资源管理器中打开(双击)时,该文件将在 Windows 上正确安装。 此/etc/wsl-distribution.conf双击体验在文件中需要一个oobe.defaultName条目才能正常工作

分发 WSL 分发

WSL 用户可以通过运行 wsl --list --online 查看可用的分发版,并可以直接 wsl --install <distroName> 安装它们(替换为 Linux 分发版的实际名称)。 此过程由分发清单文件控制。 可以将此清单文件添加到客户 Linux 分发版,以便将其包含在命令选项中 wsl --install

但是,可以使用文件扩展名创建和重命名 .wsl 的自定义 Linux 分发 TAR。 下载后,用户可以直接从命令行 wsl --install --from-file <fileLocation> 安装它(替换为文件的实际位置)。 或者, .wsl 可以通过双击它打开自定义 WSL 分发的文件。

分发清单详细信息

分发 清单 包含有关可通过 wsl --install <distribution>安装分发版的元数据。

基于 TAR 的分发版按以下格式列出 ModernDistribution

"ModernDistributions": {

"<flavor>": [
    
    {
    "Name": "<version name>",
    "FriendlyName": "<friendly name>",
    "Default": true | false,
    "Amd64Url": {
        "Url": "<tar url>",
        "Sha256": "<tar sha265 hash>"
        },
    "Arm64Url": {
        "Url": "<tar url>",
        "Sha256": "<tar sha265 hash>"
        }
    }
}

每个 flavor 条目都包含一个可安装分发版的列表。 可以通过风格名称(即安装默认条目)或版本名称来安装分发版。

查看命令如何使用 wsl --install 以下清单:

{
    "ModernDistributions": {
        "my-distro": [
            {
                "Name": "my-distro-v3", 
                "Default": true,
                "FriendlyName": "My distribution version 3 (latest)"
                 [...]
            },
            {
                "Name": "my-distro-v2", 
                "Default": false,
                "FriendlyName": "My distribution version 2"
                 [...]
            }
    }        
}

示例安装命令:

$ wsl --install my-distro # Installs 'my-distro-v3' since it's the default for 'my-distro' flavor
$ wsl --install my-distro-v3 # Installs 'my-distro-v3' explicitly
$ wsl --install my-distro-v2 # Installs 'my-distro-v2' explicitly

为所有 WSL 用户添加发行版wsl --install

若要使 WSL 发行版可供所有用户使用,请在 WSL GitHub 存储库打开拉取请求,修改DistributionInfo.json文件以包含发行版信息。

WSL 团队将审查此拉取请求。

将发行版添加到 wsl --install 企业或组

还可以通过在所选计算机上编辑注册表项,使发行版仅可用于 wsl --install 选择组。

可以通过在其中 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss创建注册表值来重写 WSL 分发清单。

  • DistributionListUrl:重写分发清单 URL
  • DistributionListUrlAppend:将清单 URL 中的分发版添加到可安装分发版列表

这两个注册表值都是字符串(REG_SZ),应采用 URL 格式。 从 WSL 2.4.4 开始, file:// 支持协议以简化本地测试。 预期格式为: file:///C:/path/to/file

测试本地分发

若要测试分发 tar,可以使用以下示例 powershell 脚本通过新的分发替代分发清单。 首先将以下脚本保存为 override-manifest.ps1

#Requires -RunAsAdministrator

[cmdletbinding(PositionalBinding = $false)]
param (
    [Parameter(Mandatory = $true)][string]$TarPath,
    [string]$Flavor = "test-distro",
    [string]$Version = "test-distro-v1",
    [string]$FriendlyName = "Test distribution version 1")

Set-StrictMode -Version latest

$TarPath = Resolve-Path $TarPath
$hash = (Get-Filehash $TarPath -Algorithm SHA256).Hash


$manifest= @{
    ModernDistributions=@{
        "$Flavor" = @(
            @{
                "Name" = "$Version"
                Default = $true
                FriendlyName = "$FriendlyName"
                Amd64Url = @{
                    Url = "file://$TarPath"
                    Sha256 = "0x$hash"
                }
            })
        }
    }

$manifestFile = "$PSScriptRoot/manifest.json"
$manifest | ConvertTo-Json -Depth 5 | Out-File -encoding ascii $manifestFile


Set-ItemProperty -Path "HKLM:SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss" -Name DistributionListUrl -Value "file://$manifestFile" -Type String -Force 

然后,通过在提升的 powershell 中运行以下命令来配置本地清单:

.\override-manifest.ps1 -TarPath /path/to/tar

完成后,应会看到以下输出 wsl.exe --list --online

$ wsl --list --online
The following is a list of valid distributions that can be installed.
Install using 'wsl.exe --install <Distro>'.

NAME              FRIENDLY NAME
test-distro-v1    Test distribution version 1

然后,可以运行 wsl.exe --install test-distro-v1 以尝试安装新分发版。

完成后,可以删除 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss\DistributionListUrl 以还原到官方清单。

最佳做法

配置文件

  • /etc/wsl.conf/etc/wsl-distribution.conf 应包含。 它们应归其 root:root 所有,其权限应为 0644
  • 如果 oobe.command 用于创建新用户,则其 uid 应 1000设置为该值,应 oobe.defaultUid 设置为该值。
  • oobe.defaultNameshortcut.icon 应在 /etc/wsl-distribution.conf
  • /etc/resolv.conf不应包含在根文件系统
  • 应有根用户 /etc/passwd ,并且其 uid 应为 0
  • 中不应有密码哈希 /etc/shadow
  • 存档不应包含内核或 initramfs

Systemd

如果启用了 systemd,则应禁用或屏蔽可能导致 WSL 问题的单元。 以下已知会导致 WSL 分发中问题的单位(适用于系统和用户单位):

  • systemd-resolved.service
  • systemd-networkd.service
  • NetworkManager.service
  • systemd-tmpfiles-setup.service
  • systemd-tmpfiles-clean.service
  • systemd-tmpfiles-clean.timer
  • systemd-tmpfiles-setup-dev-early.service
  • systemd-tmpfiles-setup-dev.service
  • tmp.mount