虽然瘦 jail 使用与厚 jail 相同的技术,但创建过程是不同的。瘦 jail 可以通过 OpenZFS 快照或使用模板和 NullFS 创建。使用 OpenZFS 快照和模板结合 NullFS 相比经典的 jail 有一些优势,例如可以更快速地从快照中创建,或者能够通过 NullFS 更新多个 jail。
17.5.1. 使用 OpenZFS 快照创建瘦 jail
由于 FreeBSD 和 OpenZFS 的良好集成,通过 OpenZFS 快照创建新的瘦 jail 非常容易。
要通过 OpenZFS 快照创建瘦 jail,第一步是创建 jail 目录树,按照设置 Jail 目录树中的说明进行操作。
接下来,创建一个模板。模板仅用于创建新的 jail。因此,它们以“只读”模式创建,以便从不变的基础创建 jail。
要为模板创建数据集,执行以下命令:
# zfs create -p zroot/jails/templates/14.2-RELEASE
然后执行以下命令下载用户空间:
# fetch https://download.freebsd.org/ftp/releases/amd64/amd64/14.2-RELEASE/base.txz -o /usr/local/jails/media/14.2-RELEASE-base.txz
下载完成后,需要通过以下命令将内容解压到模板目录中:
# tar -xf /usr/local/jails/media/14.2-RELEASE-base.txz -C /usr/local/jails/templates/14.2-RELEASE --unlink
在模板目录中提取用户空间后,需要通过以下命令将时区和 DNS 服务器文件复制到模板目录中:
# cp /etc/resolv.conf /usr/local/jails/templates/14.2-RELEASE/etc/resolv.conf
# cp /etc/localtime /usr/local/jails/templates/14.2-RELEASE/etc/localtime
接下来的步骤是通过执行以下命令更新到最新的补丁级别:
# freebsd-update -b /usr/local/jails/templates/14.2-RELEASE/ fetch install
更新完成后,模板就准备好了。
要从模板创建 OpenZFS 快照,执行以下命令:
# zfs snapshot zroot/jails/templates/14.2-RELEASE@base
待创建了 OpenZFS 快照,就可以使用 OpenZFS 克隆功能创建无限多个 jail。
要创建一个名为 thinjail
的瘦 jail,执行以下命令:
# zfs clone zroot/jails/templates/14.2-RELEASE@base zroot/jails/containers/thinjail
最后一步是配置 jail。需要在 /etc/jail.conf 或 jail.conf.d 配置文件中添加该 jail 的条目。
例如,配置可能如下所示:
thinjail {
# 启动/日志记录
exec.start = "/bin/sh /etc/rc";
exec.stop = "/bin/sh /etc/rc.shutdown";
exec.consolelog = "/var/log/jail_console_${name}.log";
# 权限
allow.raw_sockets;
exec.clean;
mount.devfs;
# 主机名/路径
host.hostname = "${name}";
path = "/usr/local/jails/containers/${name}";
# 网络
ip4 = inherit;
interface = em0;
}
执行以下命令启动 jail:
# service jail start thinjail
有关如何管理 jail 的更多信息,请参阅Jail 管理章节。
17.5.2. 使用 NullFS 创建瘦 jail
通过使用瘦 jail 技术并利用 NullFS 有选择性地共享主机系统中的特定目录,可以减少系统文件的重复,从而创建一个瘦 jail。
第一步是创建用于保存模板的数据集。如果使用 OpenZFS,请执行以下命令:
# zfs create -p zroot/jails/templates/14.2-RELEASE-base
如果使用 UFS,请执行以下命令:
# mkdir /usr/local/jails/templates/14.2-RELEASE-base
然后执行以下命令下载用户空间:
# fetch https://download.freebsd.org/ftp/releases/amd64/amd64/14.2-RELEASE/base.txz -o /usr/local/jails/media/14.2-RELEASE-base.txz
下载完成后,需要通过以下命令将内容解压到模板目录中:
# tar -xf /usr/local/jails/media/14.2-RELEASE-base.txz -C /usr/local/jails/templates/14.2-RELEASE-base --unlink
待用户空间提取到模板目录中,就需要通过以下命令将时区和 DNS 服务器文件复制到模板目录中:
# cp /etc/resolv.conf /usr/local/jails/templates/14.2-RELEASE-base/etc/resolv.conf
# cp /etc/localtime /usr/local/jails/templates/14.2-RELEASE-base/etc/localtime
将文件移动到模板中后,接下来的步骤是通过以下命令更新到最新的补丁级别:
# freebsd-update -b /usr/local/jails/templates/14.2-RELEASE-base/ fetch install
除了基础模板外,还需要创建一个目录来存放 skeleton
。一些目录将从模板复制到 skeleton
中。
如果使用 OpenZFS,请执行以下命令来创建 skeleton
的数据集:
# zfs create -p zroot/jails/templates/14.2-RELEASE-skeleton
如果使用 UFS,请执行以下命令:
# mkdir /usr/local/jails/templates/14.2-RELEASE-skeleton
然后创建 skeleton
目录。skeleton
目录将存放 jail 的本地目录。
执行以下命令创建这些目录:
# mkdir -p /usr/local/jails/templates/14.2-RELEASE-skeleton/home
# mkdir -p /usr/local/jails/templates/14.2-RELEASE-skeleton/usr
# mv /usr/local/jails/templates/14.2-RELEASE-base/etc /usr/local/jails/templates/14.2-RELEASE-skeleton/etc
# mv /usr/local/jails/templates/14.2-RELEASE-base/usr/local /usr/local/jails/templates/14.2-RELEASE-skeleton/usr/local
# mv /usr/local/jails/templates/14.2-RELEASE-base/tmp /usr/local/jails/templates/14.2-RELEASE-skeleton/tmp
# mv /usr/local/jails/templates/14.2-RELEASE-base/var /usr/local/jails/templates/14.2-RELEASE-skeleton/var
# mv /usr/local/jails/templates/14.2-RELEASE-base/root /usr/local/jails/templates/14.2-RELEASE-skeleton/root
接下来的步骤是通过执行以下命令创建指向 skeleton
的符号链接:
# cd /usr/local/jails/templates/14.2-RELEASE-base/
# mkdir skeleton
# ln -s skeleton/etc etc
# ln -s skeleton/home home
# ln -s skeleton/root root
# ln -s ../skeleton/usr/local usr/local
# ln -s skeleton/tmp tmp
# ln -s skeleton/var var
skeleton
准备好后,需要将数据复制到 jail 目录中。
如果使用 OpenZFS,可以使用 OpenZFS 快照轻松创建所需数量的 jail,执行以下命令:
# zfs snapshot zroot/jails/templates/14.2-RELEASE-skeleton@base
# zfs clone zroot/jails/templates/14.2-RELEASE-skeleton@base zroot/jails/containers/thinjail
如果使用 UFS,可以使用 cp(1) 程序,执行以下命令:
# cp -R /usr/local/jails/templates/14.2-RELEASE-skeleton /usr/local/jails/containers/thinjail
然后创建一个目录,用于挂载基础模板和 skeleton:
# mkdir -p /usr/local/jails/thinjail-nullfs-base
在 /etc/jail.conf 或 jail.conf.d 文件中添加 jail 条目,如下所示:
thinjail {
# 启动/日志记录
exec.start = "/bin/sh /etc/rc";
exec.stop = "/bin/sh /etc/rc.shutdown";
exec.consolelog = "/var/log/jail_console_${name}.log";
# 权限
allow.raw_sockets;
exec.clean;
mount.devfs;
# 主机名/路径
host.hostname = "${name}";
path = "/usr/local/jails/${name}-nullfs-base";
# 网络
ip4.addr = 192.168.1.153;
interface = em0;
# 挂载
mount.fstab = "/usr/local/jails/${name}-nullfs-base.fstab";
}
然后创建 /usr/local/jails/thinjail-nullfs-base.fstab 文件,内容如下:
/usr/local/jails/templates/14.2-RELEASE-base /usr/local/jails/thinjail-nullfs-base/ nullfs ro 0 0
/usr/local/jails/containers/thinjail /usr/local/jails/thinjail-nullfs-base/skeleton nullfs rw 0 0
执行以下命令启动 jail:
# service jail start thinjail
17.5.3. 创建 VNET Jail
FreeBSD VNET Jail 拥有独立的网络栈,包括接口、IP 地址、路由表和防火墙规则。
创建 VNET jail 的第一步是创建 bridge(4),执行以下命令:
# ifconfig bridge create
输出应该类似于以下内容:
创建好 bridge
后,接下来需要将其附加到 em0
接口,并启动它们,执行以下命令:
# ifconfig bridge0 addm em0 up
# ifconfig em0 up
为了使此设置在重启后生效,将以下行添加到 /etc/rc.conf 中:
defaultrouter="192.168.1.1"
cloned_interfaces="bridge0"
ifconfig_bridge0="inet 192.168.1.150/24 addm em0 up"
ifconfig_em0="up"
有关桥接的更多信息,请参阅 Network Bridging。
接下来的步骤是根据上述内容创建 jail。
可以使用 经典 Jail(厚 Jail) 或 瘦 Jail 的方法。唯一不同的是 /etc/jail.conf 文件中的配置。
在这个示例中,将使用路径 /usr/local/jails/containers/vnet 来表示创建的 jail。
以下是 VNET jail 的示例配置:
vnet {
# 启动/日志记录
exec.consolelog = "/var/log/jail_console_${name}.log";
# 权限
allow.raw_sockets;
exec.clean;
mount.devfs;
devfs_ruleset = 5;
# 路径/主机名
path = "/usr/local/jails/containers/${name}";
host.hostname = "${name}";
# VNET/VIMAGE
vnet;
vnet.interface = "${epair}b";
# 网络/接口
$id = "154"; ①
$ip = "192.168.1.${id}/24";
$gateway = "192.168.1.1";
$bridge = "bridge0"; ②
$epair = "epair${id}";
# 添加到 bridge 接口
exec.prestart = "/sbin/ifconfig ${epair} create up";
exec.prestart += "/sbin/ifconfig ${epair}a up descr jail:${name}";
exec.prestart += "/sbin/ifconfig ${bridge} addm ${epair}a up";
exec.start += "/sbin/ifconfig ${epair}b ${ip} up";
exec.start += "/sbin/route add default ${gateway}";
exec.start += "/bin/sh /etc/rc";
exec.stop = "/bin/sh /etc/rc.shutdown";
exec.poststop = "/sbin/ifconfig ${bridge} deletem ${epair}a";
exec.poststop += "/sbin/ifconfig ${epair}a destroy";
}
17.5.4. 创建 Linux Jail
FreeBSD 可以在 jail 内运行 Linux,利用 Linux 二进制兼容性 和 debootstrap(8)。jail 无内核,它们运行在宿主机的内核上。因此,需要在宿主系统中启用 Linux 二进制兼容性。
要在启动时启用 Linux ABI,执行以下命令:
# sysrc linux_enable="YES"
启用后,可以通过执行以下命令在不重启的情况下启动它:
接下来的步骤是按照上述方法创建 jail,例如在 使用 OpenZFS 快照创建瘦 Jail 中所示,但 不进行 配置。FreeBSD Linux jails 需要特定的配置,接下来将详细说明。
待按照上述说明创建了 jail,执行以下命令进行所需配置并启动它:
# jail -cm \
name=ubuntu \
host.hostname="ubuntu.example.com" \
path="/usr/local/jails/ubuntu" \
interface="em0" \
ip4.addr="192.168.1.150" \
exec.start="/bin/sh /etc/rc" \
exec.stop="/bin/sh /etc/rc.shutdown" \
mount.devfs \
devfs_ruleset=4 \
allow.mount \
allow.mount.devfs \
allow.mount.fdescfs \
allow.mount.procfs \
allow.mount.linprocfs \
allow.mount.linsysfs \
allow.mount.tmpfs \
enforce_statfs=1
要访问 jail,首先需要安装 sysutils/debootstrap。
执行以下命令访问 FreeBSD Linux jail:
在 jail 内,执行以下命令安装 sysutils/debootstrap 并准备 Ubuntu 环境:
# pkg install debootstrap
# debootstrap jammy /compat/ubuntu
当进程完成并在控制台上显示 Base system installed successfully
信息时,需要从宿主系统停止 jail,执行以下命令:
# service jail onestop ubuntu
然后,在 /etc/jail.conf 中添加 Linux jail 的条目:
ubuntu {
# 启动/日志记录
exec.start = "/bin/sh /etc/rc";
exec.stop = "/bin/sh /etc/rc.shutdown";
exec.consolelog = "/var/log/jail_console_${name}.log";
# 权限
allow.raw_sockets;
exec.clean;
mount.devfs;
devfs_ruleset = 4;
# 主机名/路径
host.hostname = "${name}";
path = "/usr/local/jails/containers/${name}";
# 网络
ip4.addr = 192.168.1.155;
interface = em0;
# 挂载
mount += "devfs $path/compat/ubuntu/dev devfs rw 0 0";
mount += "tmpfs $path/compat/ubuntu/dev/shm tmpfs rw,size=1g,mode=1777 0 0";
mount += "fdescfs $path/compat/ubuntu/dev/fd fdescfs rw,linrdlnk 0 0";
mount += "linprocfs $path/compat/ubuntu/proc linprocfs rw 0 0";
mount += "linsysfs $path/compat/ubuntu/sys linsysfs rw 0 0";
mount += "/tmp $path/compat/ubuntu/tmp nullfs rw 0 0";
mount += "/home $path/compat/ubuntu/home nullfs rw 0 0";
}
然后,使用以下命令像往常一样启动 jail:
# service jail start ubuntu
可以使用以下命令访问 Ubuntu 环境:
# jexec ubuntu chroot /compat/ubuntu /bin/bash
更多信息,请参阅 Linux 二进制兼容性章节。
17.5.5. 配置服务 Jail
服务 jail 完全通过 /etc/rc.conf 或 sysrc(8) 配置。基本系统服务已经准备好作为服务 jail。它们包含一行配置,启用网络连接或解除其他 jail 限制。不适合在 jail 中运行的基本系统服务,即使在 /etc/rc.conf 中启用,也被配置为不作为服务 jail 启动。例如,某些服务会在启动或停止方法中挂载或卸载某些内容,或者仅配置诸如路由、防火墙等内容。
第三方服务可能已经准备好作为服务 jail,或者可能没有。要检查某个服务是否已经准备好作为服务 jail,可以使用以下命令:
# grep _svcj_options /path/to/rc.d/servicename
如果没有输出,表示该服务没有准备好作为服务 jail,或者不需要任何额外的权限(例如,网络访问)。
如果服务没有准备好作为服务 jail,并且需要网络访问,可以通过向 /etc/rc.conf 添加必要的配置来使其准备好:
# sysrc servicename_svcj_options=net_basic
有关所有可能的 _svcj_options
,请参阅 rc.conf(5) 手册页。
要为某个服务启用服务 jail,需要先停止该服务,然后将 servicename_svcj
变量设置为 YES。以将 syslogd(8) 放入服务 jail 为例,执行以下命令:
# service syslogd stop
# sysrc syslogd_svcj=YES
# service syslogd start
如果更改了 servicename_svcj
变量,必须在更改之前停止该服务。如果服务没有停止,rc 框架将无法检测到服务的正确状态,并且无法执行所请求的操作。
服务 jails 仅通过 rc.conf(5)/sysrc(8) 和 service(8) 命令进行管理。可以使用 jail 工具(如 jls(8),在 Jail 管理 中有描述)来检查服务的运行情况,但不建议使用 jail(8) 命令来管理它们。