systemd 教程 / 容器管理(systemd-nspawn)
容器管理(systemd-nspawn)
systemd-nspawn 是 systemd 内置的轻量级容器工具,名称源自 “chroot 的命名空间增强版”。它利用 Linux 内核的命名空间(namespaces)和 cgroups 实现进程隔离,是测试、开发和构建环境的理想选择。
1. systemd-nspawn 概述
什么是 systemd-nspawn?
systemd-nspawn 是一个容器运行时工具,功能介于 chroot 和完整容器(如 Docker)之间。
| 特性 | chroot | systemd-nspawn | Docker |
|---|---|---|---|
| 文件系统隔离 | ✅ | ✅ | ✅ |
| PID 命名空间 | ❌ | ✅ | ✅ |
| 网络隔离 | ❌ | ✅ | ✅ |
| 用户命名空间 | ❌ | ✅ | ✅ |
| cgroup 资源限制 | ❌ | ✅ | ✅ |
| 镜像管理 | ❌ | ✅ (machinectl) | ✅ |
| 依赖最小化 | ✅ | ✅ | ❌ |
| systemd 作为 PID 1 | ❌ | ✅ | 可选 |
典型应用场景
- 开发环境隔离:不同项目使用不同的系统环境
- 构建打包:在干净的环境中编译软件
- 测试环境:测试 systemd 服务和配置
- 最小化部署:无需完整虚拟机的场景
2. 启动容器
2.1 基本启动
# 使用目录作为容器根文件系统
sudo systemd-nspawn -D /var/lib/machines/mycontainer
# 指定容器名称
sudo systemd-nspawn -D /var/lib/machines/mycontainer --machine=myapp
# 以只读模式启动
sudo systemd-nspawn -D /var/lib/machines/mycontainer --read-only
2.2 使用 boot 参数
# 启动完整系统(容器内运行 systemd)
sudo systemd-nspawn -D /var/lib/machines/mycontainer --boot
# 分配伪终端
sudo systemd-nspawn -D /var/lib/machines/mycontainer --boot --console=interactive
2.3 常用启动参数
| 参数 | 说明 |
|---|---|
-D, --directory= | 指定容器根目录 |
-b, --boot | 以完整系统模式启动 |
--machine= | 设置容器名称 |
--uuid= | 指定容器 UUID |
--read-only | 只读挂载根文件系统 |
--bind= | 绑定挂载宿主目录 |
--bind-ro= | 只读绑定挂载 |
--network-interface= | 分配网络接口 |
--private-network | 隔离网络 |
--private-users= | 启用用户命名空间 |
--drop-capability= | 移除 capabilities |
3. 容器内 systemctl
当容器以 --boot 模式启动时,容器内会运行 systemd 作为 PID 1:
# 进入容器
sudo machinectl login mycontainer
# 在容器内使用 systemctl
systemctl list-units
systemctl start nginx
systemctl enable redis
journalctl -u nginx
⚠️ 注意:容器内的 systemd 与宿主 systemd 完全独立,拥有自己的 unit 文件和服务状态。
4. 镜像管理(machinectl)
machinectl 是管理 systemd 容器和虚拟机的命令行工具。
4.1 基本操作
# 列出所有容器/虚拟机
machinectl list-images
# 查看容器状态
machinectl status mycontainer
# 登录容器
machinectl login mycontainer
# 启动/停止/重启容器
machinectl start mycontainer
machinectl stop mycontainer
machinectl reboot mycontainer
# 查看容器日志
machinectl shell mycontainer
4.2 镜像操作
# 从模板下载镜像
machinectl pull-tar https://example.com/image.tar.gz myimage
machinectl pull-raw https://example.com/image.raw.xz myimage
# 导出镜像
machinectl export-tar mycontainer /tmp/mycontainer.tar.gz
# 删除镜像
machinectl remove mycontainer
4.3 创建容器文件系统
# 方法 1:使用 dnf/yum 创建
sudo dnf --releasever=38 --installroot=/var/lib/machines/fedora38 \
install -y systemd passwd dnf
# 方法 2:使用 debootstrap 创建 Debian 容器
sudo debootstrap stable /var/lib/machines/debian http://deb.debian.org/debian
# 方法 3:使用 dnf 标准安装
sudo dnf -y --releasever=40 --installroot=/var/lib/machines/mycontainer \
--repo=fedora --repo=updates install systemd passwd dnf
5. 容器网络配置
5.1 使用虚拟网络接口
# 自动创建虚拟以太网对(veth)
sudo systemd-nspawn -D /var/lib/machines/mycontainer \
--network-veth \
--boot
5.2 使用现有网络接口
# 直接使用宿主网络接口
sudo systemd-nspawn -D /var/lib/machines/mycontainer \
--network-interface=eth0 \
--boot
# 使用 MACVLAN
sudo systemd-nspawn -D /var/lib/machines/mycontainer \
--network-macvlan=eth0 \
--boot
5.3 网络配置文件
为容器创建专用网络配置:
# /etc/systemd/nspawn/mycontainer.nspawn
[Exec]
Boot=yes
[Network]
VirtualEthernet=yes
Bridge=br0
网络类型对比:
| 类型 | 参数 | 说明 |
|---|---|---|
| 虚拟以太网 | --network-veth | 创建 veth pair,适合桥接 |
| 接口绑定 | --network-interface= | 直接使用宿主接口 |
| MACVLAN | --network-macvlan= | 创建 MACVLAN 子接口 |
| IPVLAN | --network-ipvlan= | 创建 IPVLAN 子接口 |
| 隔离 | --private-network | 完全无网络 |
6. 容器资源限制(cgroups)
6.1 通过 nspawn 参数限制
sudo systemd-nspawn -D /var/lib/machines/mycontainer \
--cpu-quota=50% \
--memory=512M \
--boot
6.2 通过 machinectl 限制
# 设置 CPU 配额
sudo machinectl set-property mycontainer CPUQuota=50%
# 设置内存限制
sudo machinectl set-property mycontainer MemoryMax=512M
# 设置 I/O 权重
sudo machinectl set-property mycontainer IOWeight=500
6.3 通过 slice 管理
# /etc/systemd/system/machine-mycontainer.slice.d/limits.conf
[Slice]
CPUQuota=50%
MemoryMax=512M
IOWeight=500
TasksMax=100
💡 提示:使用 systemd-cgls 查看 cgroup 树结构,确认限制是否生效。
7. 容器自动启动
7.1 使用 [email protected] 模板
systemd 提供了 [email protected] 模板用于自动启动容器:
# 启用容器自动启动
sudo systemctl enable [email protected]
# 启动容器
sudo systemctl start [email protected]
# 查看容器状态
sudo systemctl status [email protected]
7.2 容器配置文件
# /etc/systemd/nspawn/mycontainer.nspawn
[Exec]
Boot=yes
Parameters=--boot --console=interactive
[Files]
ReadOnly=no
[Network]
VirtualEthernet=yes
VirtualEthernetExtra=host0:ve-mycontainer
7.3 自定义服务模板
# /etc/systemd/system/[email protected]
[Unit]
Description=Custom Container %i
After=network.target
[Service]
ExecStart=/usr/bin/systemd-nspawn \
-D /var/lib/machines/%i \
--boot \
--network-veth \
--machine=%i
ExecStop=/usr/bin/machinectl poweroff %i
Restart=on-failure
KillMode=mixed
[Install]
WantedBy=multi-user.target
8. machinectl 命令大全
8.1 容器生命周期
# 列出运行中的容器
machinectl list
# 列出所有镜像
machinectl list-images
# 查看容器详细信息
machinectl show mycontainer
# 登录容器(需要容器内有可登录用户)
machinectl login mycontainer
# 在容器内执行命令
machinectl shell mycontainer /bin/bash
# 关闭容器
machinectl poweroff mycontainer
# 重启容器
machinectl reboot mycontainer
# 终止容器(强制)
machinectl terminate mycontainer
8.2 镜像管理
# 重命名镜像
machinectl rename oldname newname
# 导出镜像
machinectl export-raw mycontainer /tmp/mycontainer.raw
# 从 URL 导入
machinectl pull-tar https://example.com/rootfs.tar.xz myimage
9. systemd-nspawn vs Docker 对比
| 维度 | systemd-nspawn | Docker |
|---|---|---|
| 设计目标 | 系统容器 | 应用容器 |
| PID 1 | systemd | 用户进程 |
| 镜像格式 | 目录/tar/raw | OCI/Docker 镜像 |
| 生态系统 | systemd 原生 | 完整生态系统 |
| 编排 | machinectl/systemd | Docker Compose/K8s |
| 安全性 | systemd 沙箱 | Seccomp/AppArmor/SELinux |
| 适用场景 | 开发/测试/构建 | 生产部署 |
| 学习曲线 | 低(已有 systemd 知识) | 中 |
| 资源开销 | 极低 | 低 |
选择建议:
- 需要快速搭建开发/测试环境 → systemd-nspawn
- 需要完整的 CI/CD 流程 → Docker
- 需要最小化隔离环境 → systemd-nspawn
- 需要生产级编排和调度 → Docker + Kubernetes
10. 构建容器镜像(mkosi)
mkosi 是 systemd 官方的镜像构建工具。
10.1 安装 mkosi
# Fedora
sudo dnf install mkosi
# Ubuntu/Debian
sudo apt install mkosi
# pip 安装
pip install mkosi
10.2 创建镜像配置
# mkosi.conf
[Distribution]
Distribution=fedora
Release=40
[Output]
Format=directory
Output=mycontainer
[Content]
Packages=systemd passwd dnf vim curl wget
Password=root
[Partitions]
RootSize=2G
10.3 构建并使用
# 构建镜像
sudo mkosi
# 使用构建的目录启动
sudo systemd-nspawn -D mycontainer --boot
# 导入到 machines 目录
sudo mv mycontainer /var/lib/machines/
生产场景:构建自动化容器环境
以下是一个完整的自动化脚本,用于创建、配置和启动生产级容器:
#!/bin/bash
# create-container.sh
CONTAINER_NAME="webapp"
CONTAINER_DIR="/var/lib/machines/${CONTAINER_NAME}"
# 1. 创建容器根文件系统
sudo dnf --releasever=40 --installroot="${CONTAINER_DIR}" \
--repo=fedora --repo=updates \
install -y systemd systemd-networkd nginx redis
# 2. 配置容器
sudo systemd-nspawn -D "${CONTAINER_DIR}" \
systemctl enable nginx redis
# 3. 创建 nspawn 配置
sudo tee "/etc/systemd/nspawn/${CONTAINER_NAME}.nspawn" <<EOF
[Exec]
Boot=yes
[Network]
VirtualEthernet=yes
Bridge=br0
[Files]
BindReadOnly=/srv/webapp:/var/www/html
EOF
# 4. 启用自动启动
sudo systemctl enable systemd-nspawn@${CONTAINER_NAME}.service
sudo systemctl start systemd-nspawn@${CONTAINER_NAME}.service
echo "Container ${CONTAINER_NAME} is running."
⚠️ 注意事项
- 文件系统兼容性:容器根文件系统必须与宿主架构一致(目前不支持跨架构)
- 内核共享:systemd-nspawn 与宿主共享内核,无法运行不同内核版本的系统
- 存储空间:每个容器都是完整根文件系统,注意磁盘空间管理
- 安全隔离:相比 Docker,systemd-nspawn 的安全隔离较弱,不建议用于多租户场景
- 网络配置:桥接网络需要宿主配置网桥设备
💡 提示
- 使用
machinectl shell比machinectl login更方便执行单次命令 - 容器配置文件
.nspawn放在/etc/systemd/nspawn/目录下可被 machinectl 自动识别 - 使用
systemd-nspawn --ephemeral启动临时容器,关闭后自动丢弃修改 systemd-nspawn --pipe可用于非交互式命令执行,适合脚本自动化