systemd-nspawn (简体中文) - ArchWiki

Jump to content

# systemd-nspawn (简体中文)

From ArchWiki

翻译状态：本文是 Systemd-nspawn 的翻译。上次翻译日期：2021-04-08。如果英文版本有所更改，则您可以帮助同步翻译。

相关文章

- Linux Containers
- systemd (简体中文)
- systemd-networkd (简体中文)
- Docker (简体中文)

systemd-nspawn 就像是 chroot 命令, 但是是 吃了类固醇的chroot（chroot on steroids）.

systemd-nspawn 可用于在一个轻量命名空间容器中运行命令或操作系统。它比 chroot 更强大在于它完全虚拟化了文件系统层次结构、进程树、各种 IPC 子系统以及主机和域名。

systemd-nspawn 将容器中各种内核接口的访问限制为只读，像是 `/sys`, `/proc/sys` 和 `/sys/fs/selinux`。 网络接口和系统时钟或许不能从容器内更改，不能创建设备节点。主机系统也无法重新启动，并且可能无法从容器内加载内核模块。

这种机制不同于 LXC 或 Libvirt，它是一种更容易配置的工具。

## 安装

systemd-nspawn 是被打包进 systemd 包的一部分。

## 用例

### 在容器中创建和启动最小 Arch Linux 发行版

首先安装 arch-install-scripts.

然后，创建一个目录来保存容器。在这个用例中我们将使用 `~/MyContainer`。

接下来，我们使用 pacstrap 在容器中安装一个基本的arch系统。我们至少需要安装base 包组.

    # pacstrap -i -c ~/MyContainer base [additional pkgs/groups]

提示： base 包已经不再依赖 linux 内核，它已准备为容器化.

一旦安装完成后，chroot至容器中并设置root密码：

    # systemd-nspawn -D ~/MyContainer
    # passwd
    # logout

最后, 启动容器:

    # systemd-nspawn -b -D ~/MyContainer

参数 `-b` 将会启动这个容器（比如：以 PID=1 运行 `systemd`）, 而不是仅仅启动一个 shell, 而参数 `-D` 指定成为容器根目录的目录。

容器启动后，输入密码以"root"身份登录。

注意： 如果登陆失败显示 "Login incorrect", 问题可能是 `securetty` TTY 设备白名单导致的。 请查看#Root登录失败。

可以在容器内运行 `poweroff` 来关闭容器。在主机端，容器可以通过 machinectl 工具进行控制。

注意： 要从容器内终止 session，请按住 `Ctrl` 并快速地按 `]` 三下。非美国键盘用户应该使用 `%` 而不是 `]`。

### 创建 Debian 或 Ubuntu 环境

安装 debootstrap 和 debian-archive-keyring 与 ubuntu-keyring 中的一个或两个（当然要安装你想要的发行版的keyrings）。

注意： systemd-nspawn 要求容器中的操作系统以 PID 1 运行systemd，并且systemd-nspawn 要安装在容器中。这意味着 15.04 之前的 Ubuntu 并不能开箱即用，并且需要额外的配置才能从upstart转换成systemd。还需要确保在容器中安装 `systemd-container` 包。

在这里很容易设置 Debian 或 Ubuntu 环境：

    # cd /var/lib/machines
    # debootstrap --include=systemd-container --components=main,universe codename container-name repository-url

对于 Debian，有效的`codename`要么是滚动式名称像是 "stable" 或 "testing"，要么是发行名称如 "stretch" 或 "sid"， 对于 Ubuntu，应使用"xenial"或者"zesty"等`codename`。 代码名称的完整列表位于 `/usr/share/debootstrap/scripts` 中。对于 Debian 镜像，"repository-url" 可以是 https://deb.debian.org/debian/ 。 对于 Ubuntu 镜像, "repository-url" 可以是 http://archive.ubuntu.com/ubuntu/ 。"repository-url" 不 应该包含尾随斜线。

与 Arch 相同，Debian 和 Ubuntu 不会让您在首次登录时无密码登录。为了设置root密码登录，要不使用"-b"参数并设置密码：

    # cd /var/lib/machines
    # systemd-nspawn -D ./container-name
    # passwd
    # logout

### 编译与测试包

请参阅 Creating packages for other distributions 寻找更多用例.

## 管理

位于 `/var/lib/machines/` 的容器可以被 machinectl 命令所控制，它可以内部控制 `systemd-nspawn@.service` 单元的实例。 `/var/lib/machines/` 下的子目录对应着容器的名字， 比如 `/var/lib/machines/container-name/`。

注意： 如果因为某些原因容器不能移进 `/var/lib/machines/` ， 可以使用符号链接。 详情看 machinectl(1) § FILES AND DIRECTORIES 。

### 默认 systemd-nspawn 选项

要明白非常重要的一点是通过 machinectl 与 `systemd-nspawn@.service` 启动的容器所使用的默认选项与通过 systemd-nspawn 命令手动启动的有所不同。 通过服务启动所使用的额外选项有：

- `-b`/`--boot` – 管理的容器会自动搜索一个init程序，并以PID 1的形式调用它。
- `--network-veth` 关联于 `--private-network` – 管理的容器获得一个虚拟网络接口，并与主机网络断开连接。 详情看 #网络 。
- `-U` – 如果内核支持，管理的容器默认使用 user_namespaces(7) 特性。 解释请看 #无特权容器 。
- `--link-journal=try-guest`

这些行为可以在每个容器配置文件中被覆盖， 详情看 #配置 。

### machinectl

注意： machinectl 工具要求在容器中安装 systemd 和 dbus 。有关详细讨论，请参阅 [1]。

容器可以被 `machinectl subcommand container-name` 命令管理， 比如说，启动容器：

    $ machinectl start container-name

相似的， 还有其他的子命令 `poweroff`， `reboot`， `status` 和 `show`. 详细解释请看 machinectl(1) § Machine Commands 。

提示： 关机与重启操作可以在容器内通过 `poweroff` and `reboot` 命令执行。

其他常见命令：

- `machinectl list` – 显示现在正在运行的容器
- `machinectl login container-name` – 在一个容器中打开一个交互式登录会话
- `machinectl shell [username@]container-name` – 在容器中打开一个交互式shell会话（这将立即调用一个用户进程，而不需要通过容器中的登录进程）
- `machinectl enable container-name` 与 `machinectl disable container-name` – 启用或禁用容器的随开机启动，详见 #启用容器的随开机启动

machinectl 也有管理容器（或虚拟机）镜像和镜像传输的子命令。 详情见 machinectl(1) § Image Commands 与 machinectl(1) § Image Transfer Commands 。

This article or section needs expansion.

Reason: 增加一些明确的例子，如何使用镜像传输命令。最重要的是，在哪里可以找到合适的镜像。 (Discuss in Talk:Systemd-nspawn (简体中文))

### systemd 工具链

大部分核心的 systemd 工具链已更新为对容器有效。工具们通常提供了 `-M, --machine=` 选项并把容器名称作为参数。 用例：

查看特定容器的 journal 日志：

    # journalctl -M MyContainer

显示控制组的内容：

    $ systemd-cgls -M MyContainer

查看容器的启动时间：

    $ systemd-analyze -M MyContainer

查看资源使用情况的概况：

    $ systemd-cgtop

## 配置

### 容器前设置

要指定容器前设置而不是全局覆盖，可以使用.nspawn文件。 详情见 systemd.nspawn(5) 。

注意：

- 当执行`machinectl remove` 的时候，.nspawn 文件可能会从 `/etc/systemd/nspawn/` 被意外移除。 [2]
- 当有`--settings=override`（在`systemd-nspawn@.service`文件内被指定）时，.nspawn内与命令行中所指定的为网络选项的交互不能正常工作。[3] 作为一个变通方法，你需要引入`VirtualEthernet=on`选项，即使服务指定了`--network-veth`。

### 启用容器的随开机启动

当频繁使用一个容器时，你可能希望在系统启动时启动它。

首先确保 `machines.target` 已经 enabled。

容器被 machinectl 的可发现可以 enabled 或者 disabled。

    $ machinectl enable container-name

注意：

- 这样做的效果是启用 `systemd-nspawn@container-name.service` systemd单元。
- 如#默认 systemd-nspawn 选项中提到的，由machinectl启动的容器会获得一个虚拟的以太网接口。要禁用私有网络，请参见#使用主机网络。

### 资源控制

您可以使用控制组来使用 `systemctl set-property` 实现对容器的限制和资源管理，请参阅 systemd.resource-control(5)。例如，您可能希望限制内存量或 CPU 使用率。要将容器的内存消耗限制为 2 GiB：

    # systemctl set-property systemd-nspawn@myContainer.service MemoryMax=2G

或者将 CPU 时间使用率限制为大约相当于 2 个内核：

    # systemctl set-property systemd-nspawn@myContainer.service CPUQuota=200%

这将在 `/etc/systemd/system.control/systemd-nspawn@myContainer.service.d/` 中创建常驻文件}。

根据文档，`MemoryHigh` 是控制内存消耗的首选方法，但也不会像 `MemoryMax` 那样进行强制限制。您可以使用这两个选项将 `MemoryMax` 作为您最后的一道防线。还要考虑到如果您不会限制容器可以“看到”的 CPU 数量，但通过限制容器相对于总 CPU 时间的最大时间，也可以获得类似的结果。

提示： 如果不希望在重新启动后保留此更改，则可以传递选项 `--runtime` 使更改成为临时性的。您可以使用 `systemd-cgtop` 检查其结果。

### 网络

systemd-nspawn 容器可以使用 主机网络 或者 私有网络:

- 在主机网络模式下，容器可以完全访问主机网络。这意味着容器将能够访问主机上的所有网络服务，来自容器的数据包将在外部网络中显示为来自主机（即共享同一IP地址）。
- 在私有网络模式下，容器与主机的网络断开连接，这使得容器无法使用所有网络接口，但环回设备和明确分配给容器的接口除外。为容器设置网络接口有多种不同的方法：
  - 可以将现有接口分配给容器（例如，如果您有多个以太网设备）。
  - 可以创建一个与现有接口（即VLAN接口）相关联的虚拟网络接口，并将其分配给容器。
  - 可以创建主机和容器之间的虚拟以太网链接。

  在后一种情况下，容器的网络是完全隔离的（与外部网络以及其他容器），由管理员来配置主机和容器之间的网络。这通常涉及创建一个网桥network bridge来连接多个（物理或虚拟）接口，或者在多个接口之间设置一个NATNetwork Address Translation。

主机网络模式适用于应用程序容器，它不运行任何网络软件来配置分配给容器的接口。当你从shell运行systemd-nspawn时，主机联网是默认模式。

另一方面，私有网络模式适用于应与主机系统隔离的 "系统容器"。创建虚拟以太网链路是一个非常灵活的工具，可以创建复杂的虚拟网络。这是由machinectl或`systemd-nspawn@.service`启动的容器的默认模式。

下面的小节描述了常见的情况。关于可用的systemd-nspawn选项，请参见systemd-nspawn(1) § Networking Options。

#### 使用主机网络

要禁用私有网络和创建由machinectl启动的容器使用的虚拟以太网链接，请添加.nspawn文件，其中包含以下选项：

    /etc/systemd/nspawn/container-name.nspawn

    [Network]
    VirtualEthernet=no

这将覆盖在`systemd-nspawn@.service`中使用的`-n`/`--network-veth`选项，新启动的容器将使用主机网络模式。

#### 使用虚拟以太网链接

如果使用`-n`/`--network-veth`选项启动容器，systemd-nspawn将在主机和容器之间创建一个虚拟的以太网链接。链接的主机侧将作为名为`ve-容器名称`的网络接口提供。链路的容器侧将被命名为`host0`。请注意，这个选项意味着 `--private-network`。

注意：

- 如果容器名称太长，接口名称将被缩短（例如`ve-long-conKQGh`而不是`ve-long-container-name`），以适应15个字符的限制。全名将被设置为接口的`altname`属性（见ip-link(8)），并且仍然可以用来引用接口。
- 当检查带有`ip link`的接口时，接口名会显示一个后缀，例如`ve-容器名@if2`和`host0@if9`。`@ifN`实际上并不是接口名称的一部分，相反，`ip link`附加了这个信息，以指示虚拟以太网电缆连接到另一端的哪个 "槽"。

  例如，显示为`ve-foo@if2`的主机虚拟以太网接口与容器`foo`相连，在容器内部与第二个网络接口--在容器内部运行`ip link`时，显示索引为2的接口。同理，容器内名为`host0@if9`的接口连接到主机上的第9个网络接口。

当您启动容器时，必须为两个接口（主机上和容器中）分配一个 IP 地址。如果您在主机上和容器中都使用systemd-networkd，这就是开箱即用：

- 主机上的`/usr/lib/systemd/network/80-container-ve.network`文件与`ve-container-name`接口相匹配，并启动一个DHCP服务器，该服务器为主机接口和容器分配IP地址，
- 容器中的`/usr/lib/systemd/network/80-container-host0.network`文件与`host0`接口相匹配，并启动一个DHCP客户端，该客户端从主机接收一个IP地址。

如果不使用 systemd-networkd，可以配置静态 IP 地址或在主机接口上启动 DHCP 服务器，在容器中启动 DHCP 客户端。详情请参见Network configuration。

要让容器访问外部网络，您可以按照 Internet sharing#Enable NAT 中的描述配置 NAT。如果您使用 systemd-networkd，这将通过 `/usr/lib/systemd/network/80-container-ve.network` 中的 `IPMasquerade=yes` 选项自动（部分）完成。然而，这只会发出一个iptables规则，比如说：

    -t nat -A POSTROUTING -s 192.168.163.192/28 -j MASQUERADE

`filter`表必须手动配置，如Internet sharing#Enable NAT所示。您可以使用通配符来匹配所有以`ve-`开头的接口：

    # iptables -A FORWARD -i ve-+ -o internet0 -j ACCEPT

注意： systemd-networkd使用libiptc库与iptables交互。如果您使用nftables，请安装 iptables-nft 转译层。参见 systemd issue 13307。

The factual accuracy of this article or section is disputed.

Reason: 调查是否有必要采取以下措施/原因： (Discuss in Talk:Systemd-nspawn (简体中文))

补充，`-A FORWARD -i ve-+ -o internet0-j ACCEPT`规则可能会像Internet sharing#Enable NAT中描述的那样不起作用。如果是这种情况，请尝试`-A FORWARD -i ve-+ -j ACCEPT`。

#### 使用网络桥接

如果您已在主机系统上配置了网桥network bridge，则可以为容器创建一个虚拟以太网链路，并将其主机侧添加到网桥中。这可以通过`--network-bridge=bridge-name`选项来完成。请注意，`--network-bridge`意味着`--network-veth`，即虚拟以太网链路是自动创建的。然而，链路的主机端将使用`vb-`前缀而不是`ve-`，因此用于启动DHCP服务器和IP伪装的systemd-networkd选项将不会被应用。

网桥的管理由管理员负责。例如，网桥可以用一个物理接口连接虚拟接口，也可以只连接几个容器的虚拟接口。参见systemd-networkd#Network bridge with DHCP和systemd-networkd#Network bridge with static IP addresses，了解使用systemd-networkd进行配置的例子。

还有一个`--network-zone=zone-name`选项，它与`--network-bridge`类似，但网桥由systemd-nspawn和systemd-networkd自动管理。当第一个用`vz-zone-name`配置的容器启动时，会自动创建名为`--network-zone=zone-name`的网桥接口，当最后一个用`--network-zone=zone-name`配置的容器退出时，会自动删除。因此，这个选项可以方便地将多个相关的容器放置在一个共同的虚拟网络上。请注意，`vz-*`接口由systemd-networkd管理的方式与`ve-*`接口相同，使用`/usr/lib/systemd/network/80-container-vz.network`文件中的选项。

#### 使用 "macvlan" 或者 "ipvlan" 接口

您可以在现有的物理接口（即VLAN接口）上创建一个虚拟接口，并将其添加到容器中，而不是创建一个虚拟的以太网链路（其主机端可能被添加到桥接中，也可能没有）。该虚拟接口将与底层主机接口进行桥接，从而使容器暴露在外部网络中，从而使其能够通过DHCP从与主机相连的同一局域网中获得一个独特的IP地址。

systemd-nspawn 提供两个选项:

- `--network-macvlan=interface` – 虚拟接口的MAC地址将与底层物理`interface`不同，并被命名为`mv-interface`。
- `--network-ipvlan=interface` – 虚拟接口的MAC地址将与底层物理`interface`相同，并命名为`iv-interface`。​

所有选项都意味着 `--private-network`.

#### 使用现有接口

如果主机系统有多个物理网络接口，可以使用`--network-interface=interface`将`interface`分配给容器（并使它在容器启动时对主机不可用）。请注意，`--network-interface`意味着`--private-network`。

注意： 目前不支持将无线网络接口传递给systemd-nspawn容器。[4]

### 端口映射

当启用私有网络时，可以使用`-p`/`-port`选项或使用.nspawn文件中的`Port`设置将主机上的各个端口映射到容器上的端口。这可以通过向`nat`表发出iptables规则来完成，但需要手动配置`forward`表中的`filter`链，如#使用虚拟以太网链接所示。

例如，将主机上的TCP端口8000映射到容器中的TCP端口80：

    /etc/systemd/nspawn/container-name.nspawn

    [Network]
    Port=tcp:8000:80

注意：

- systemd-nspawn在映射端口时明确地排除了`loopback`接口。因此，在上面的例子中，`localhost:8000` 连接到主机而不是容器。只有到其他接口的连接才会受到端口映射的影响。详情请参见 [5]。
- 端口映射只对IPv4连接有效。[6]。

### 域名解析

容器中的域名解析Domain name resolution可以通过systemd-nspawn的`--resolv-conf`选项或.nspawn文件的相应选项`ResolvConf=`来配置。有很多可能的值，这些值在 systemd-nspawn(1) § Integration Options 中有描述。

默认值是`auto`，即：

- 如果启用了`--private-network`，`/etc/resolv.conf`就会保持容器中的原样。
- 否则，如果systemd-resolved在主机上运行，它的存根文件`resolv.conf`将被复制或绑定到容器中。
- 否则，`/etc/resolv.conf` 文件就会被从主机复制或绑定到容器上。

在后两种情况下，如果容器根部是可写的，则复制文件，如果是只读的，则绑定挂载。

## 提示和技巧

### 无特权容器

systemd-nspawn支持无特权的容器，不过容器需要以root身份启动。​

This article or section needs language, wiki syntax or style improvements. See Help:Style for reference.

Reason: Very little of Linux Containers#Enable support to run unprivileged containers (optional) applies to systemd-nspawn. (Discuss in Talk:Systemd-nspawn (简体中文))
注意： 此功能需要 user_namespaces(7), 更多信息请参见 Linux Containers#Enable support to run unprivileged containers (optional)。

最简单的方法是通过`-U`选项让systemd-nspawn自动选择一个未使用的UID/GID范围。

    # systemd-nspawn -bUD ~/MyContainer

如果内核支持用户命名空间，`-U`选项相当于`--private-users=pick --private-users-chown`。这意味着当容器启动时，容器中的文件和目录会被chown到选定的私有 UID/GID 范围。详情请参见systemd-nspawn(1) § User Namespacing Options。

注意： 您也可以手动指定容器的UID/GID范围，但是，这很少有用。

一旦用私有UID/GID范围启动了一个容器，就需要一直这样使用它，以避免权限错误。另外，也可以撤销`--private-users-chown`的效果。(或`-U`)在文件系统中，通过指定一个从0开始的ID范围：

    # systemd-nspawn -D ~/MyContainer --private-users=0 --private-users-chown

### 使用 X 环境

The factual accuracy of this article or section is disputed.

Reason: 本节末尾有关 systemd 版本的注释似乎已经过时。对于我（译者注：原作者）来说（systemd 版本为239） 如果 `/tmp/.X11-unix` 权限是 rw，X应用程序可以工作运行。 (Discuss in Talk:Systemd-nspawn (简体中文)#/tmp/.X11-unix contents have to be bind-mounted as read-only - still relevant?)

详情见 Xhost 和 Change root#Run graphical applications from chroot.

您需要设置您容器会话中 `DISPLAY` 以连接到外部 X 服务器。

X 在 `/tmp` 文件夹中存储一些必要的文件。为了使容器能显示任何内容，它需要访问这些文件。为此，当启动容器时，请追加 `--bind-ro=/tmp/.X11-unix` 选项。

注意： 从 systemd 版本 235 开始, `/tmp/.X11-unix` 的内容 必须以只读方式装入，否则它们将从文件系统中消失。只读挂载标志不会阻止在套接字上使用 `connect()`。如果你还绑定了`/run/user/1000` 那么你有可能希望显式绑定 `/run/user/1000/bus` 为只读，以防止 dbus 套接字被删除。

#### Avoiding xhost

`xhost` 仅提供对 X 服务器相当粗糙的访问权限。更细节的访问控制可通过`$XAUTHORITY` 文件。遗憾的是, 仅使 `$XAUTHORITY` 文件在容器中可被访问无法执行工作: 您的 `$XAUTHORITY` 文件只特定于您的主机，但是容器是另一台主机。 根据 stackoverflow 下面这个技巧可以让你的X服务器接受来自于你容器中的X应用的 `$XAUTHORITY` 文件：

    $ XAUTH=/tmp/container_xauth
    $ xauth nextract - "$DISPLAY" | sed -e 's/^..../ffff/' | xauth -f "$XAUTH" nmerge -
    # systemd-nspawn -D myContainer --bind=/tmp/.X11-unix --bind="$XAUTH" -E DISPLAY="$DISPLAY" -E XAUTHORITY="$XAUTH" --as-pid2 /usr/bin/xeyes

上面第二行将连接组设定为 "FamilyWild"，值`65535`，这会使输入匹配你的每一个显示。 更多信息请参考 Xsecurity(7) 。

### 运行 Firefox

以PID 1运行​

     # systemd-nspawn --setenv=DISPLAY=:0 \
                  --setenv=XAUTHORITY=~/.Xauthority \
                  --bind-ro=$HOME/.Xauthority:/root/.Xauthority \
                  --bind=/tmp/.X11-unix \
                  -D ~/containers/firefox \
                  firefox

或者你可以启动容器，让systemd-networkd等设置虚拟网络接口：

    # systemd-nspawn --bind-ro=$HOME/.Xauthority:/root/.Xauthority \
                  --bind=/tmp/.X11-unix \
                  -D ~/containers/firefox \
                  --network-veth -b

一旦你的容器被启动，就像这样运行Xorg二进制文件：

    # systemd-run -M firefox --setenv=DISPLAY=:0 firefox

### 访问主机文件系统

请见 `--bind` 和 `--bind-ro` 于 systemd-nspawn(1).

如果主机和容器都是 Arch Linux，则例如，可以共享 pacman 缓存：

    # systemd-nspawn --bind=/var/cache/pacman/pkg

或许你还可以使用文件来进行指定的先于容器的绑定：

    /etc/systemd/nspawn/my-container.nspawn

    [Files]
    Bind=/var/cache/pacman/pkg

详情见 #容器前设置.

要将该目录绑定到容器内的不同路径，请添加路径，并用冒号分隔。例如：

    # systemd-nspawn --bind=/path/to/host_dir:/path/to/container_dir

### 在非systemd系统上运行

详情见 Init#systemd-nspawn。

### 使用Btrfs子卷作为容器的根

要使用 Btrfs subvolume 作为容器根目录的模板，请使用 `--template` 标志。这将获取子卷的快照，并以它填充容器的根目录。

注意： 如果指定的模板路径不是子卷的根，则会复制整个树。这将非常耗时。​

例如，要使用位于`/.snapshots/403/snapshot`的快照：

    # systemd-nspawn --template=/.snapshots/403/snapshots -b -D my-container

其中`my-container`是将为容器创建的目录的名称。关机后，会保留新创建的子卷。​

### 使用容器的临时Btrfs快照

可以使用`--ephemeral`或`-x`标志来创建容器的临时btrfs快照，并将其作为容器的根。在容器中启动时所作的任何更改都将丢失。例如：

    # systemd-nspawn -D my-container -xb

其中my-container是现有容器或系统的目录。例如，如果`/`是一个btrfs子卷，我们可以通过以下操作创建一个当前运行的主机系统的短暂容器：

    # systemd-nspawn -D / -xb

关闭容器电源后，创建的btrfs子卷会立即被删除。

### 在 systemd-nspawn 中运行 docker

Docker 需要 `/sys/fs/cgroup` 的 `rw` 权限去运行容器， 由于cgroup命名空间的原因，它默认被systemd-nspawn挂载为只读。但是，通过从主机系统绑定挂载`/sys/fs/cgroup`并启用必要的能力和权限，可以在systemd-nspawn容器中运行Docker。

注意： 下面的步骤实质上是将cgroup / 用户命名空间共享给容器，赋予内核键环权限，使其成为特权容器，这很可能会增加攻击面，降低安全级别。在遵循这些步骤之前，你一定要通过这样做来评估实际的好处。

首先，通过`systemctl edit systemd-nspawn@myContainer`禁用cgroup命名空间。

    systemctl edit systemd-nspawn@myContainer

    [Service]
    Environment=SYSTEMD_NSPAWN_USE_CGNS=0

然后， 编辑（或创建） `/etc/systemd/nspawn/myContainer.nspawn` 并加入下方配置。

    /etc/systemd/nspawn/myContainer.nspawn

    [Exec]
    Capability=all
    SystemCallFilter=add_key keyctl
    PrivateUsers=no

    [Files]
    Bind=/sys/fs/cgroup

这将授予容器所有的功能，禁用用户名称间隔，将两个系统列入调用`add_key`和`keyctl`的白名单。(与内核键环有关，被Docker 所需要)，以及从主机到容器的绑定挂载`/sys/fs/cgroup`。编辑这些文件后，您需要关机并重新启动您的容器，它们才能生效。如果您的容器在此更改之前启用了用户命名空间（如果使用 `systemd-nspawn@.service` 单元，则为默认值），您还需要撤销由用户命名空间引起的权限更改以避免权限错误。详情请参见#无特权容器。

注意：

- 在systemd-nspawn里面启动Docker之前，你可能需要在主机上加载`overlay`模块，才能正确使用`overlay2`存储驱动（Docker的默认存储驱动）。如果没有加载驱动，会导致Docker选择效率低下的驱动`vfs`，该驱动会为每一层Docker容器复制一切。请参考Kernel modules#Automatic module loading with systemd，了解如何自动加载模块。
- 从2020年11月起，cgroups v2 似乎会破坏systemd-nspawn内的Docker。如果你想以这种方式使用Docker，不要设置内核参数`systemd.unified_cgroup_hierarchy=1`。

### 在无root权限的情况下使用machinectl

machined 启用了 Polkit，这就允许创建polkit规则，允许在不成为root用户的情况下执行某些操作。不同的权限在`/usr/share/polkit-1/actions/org.freedesktop.machine1.policy`中进行了描述，所有权限都在`org.freedesktop.machine1.`下。

要允许名为 "foo "的用户在没有root权限的情况下执行所有操作，请添加一个规则：

    /etc/polkit-1/rules.d/machined.rules

    polkit.addRule(
      function(action, subject) {
        if (action.id.startsWith("org.freedesktop.machine1.") && subject.user == "foo") {
          return polkit.Result.YES;
        }
      }
    );

另外，用户需要有管理`systemd-nspawn@`单元的权限，才能启动和停止nspawn容器，添加这个规则就可以了：

    /etc/polkit-1/rules.d/machined.rules

    polkit.addRule(
      function(action, subject) {
        if ((action.id.startsWith("org.freedesktop.machine1.") || (action.id == "org.freedesktop.systemd1.manage-units" && action.lookup("unit").startsWith("systemd-nspawn@"))) && subject.user == "foo") {
          return polkit.Result.YES;
        }
      }
    );

## 疑难解答

### Root登录失败

如果您在尝试登录时（即使用`machinectl login <name>`）得到以下错误:

    arch-nspawn login: root
    Login incorrect

并且 `journalctl` 显示:

    pam_securetty(login:auth): access denied: tty 'pts/0' is not secure !

删除容器文件系统中的`/etc/securetty`[7]和`/usr/share/factory/etc/securetty`。可以选择将它们添加到`/etc/pacman.conf`中的NoExtract，以防止它们被重新安装。详情请参见FS#45903。

### execv(...) failed: Permission denied

当试图通过`systemd-nspawn -bD /path/to/container`来启动容器时（或在容器中执行一些东西），出现以下错误：

    execv(/usr/lib/systemd/systemd, /lib/systemd/systemd, /sbin/init) failed: Permission denied

即使有关文件的权限（即 `/lib/systemd/systemd`）是正确的，这也可能是由于以非root用户的身份挂载容器所在的文件系统造成的。例如，如果您在fstab中手动挂载了具有`noauto,user,...`选项的磁盘，systemd-nspawn将不允许执行文件，即使它们是由root用户拥有的。

### TERM中的终端类型不正确（破损的颜色）

当通过`machinectl login`登录容器时，容器内的终端中的颜色和按键可能会被破坏。这可能是由于`TERM`环境变量中的终端类型不正确。除非明确配置，否则环境变量不会从主机上的 shell 继承，而是回到 systemd 中固定的默认值 (`vt220`)。要配置，在容器内为 `container-getty@.service` systemd 服务创建一个配置覆盖，启动 `machinectl login` 的登录 getty，并将 `TERM` 设置为与您登录的主机终端匹配的值：

    /etc/systemd/system/container-getty@.service.d/term.conf

    [Service]
    Environment=TERM=xterm-256color

或者使用`machinectl shell`。它正确地继承了终端的`TERM`环境变量。

### 在容器内挂载NFS共享

目前（2019年6月）不可能

## See also

- Automatic console login
- Creating containers with systemd-nspawn
- Presentation by Lennart Poettering on systemd-nspawn
- Running Firefox in a systemd-nspawn container
- Graphical applications in systemd-nspawn

Categories:

- Virtualization (简体中文)
- Sandboxing (简体中文)

Hidden categories:

- Pages or sections flagged with Template:Expansion
- Pages or sections flagged with Template:Accuracy
- Pages or sections flagged with Template:Style