FreeBSD Linux Jail
备注
我之前曾经实践过 FreeBSD Linux Jail(归档) ,但是当时经验不足折腾了很久记录也比较杂乱。最近我准备构建一个在FreeBSD系统编译 LFS(Linux from scratch) ,也就是通过Linux Jail模拟出一个Linux环境,来访问和FreeBSD共存的Linux分区,通过这个模拟linux jail来编译LFS。
本文是重新实践的笔记,我将采用ZFS存储上构建 VNET + Thin Jail(Snapshot) 基础上部署linux jail:
我最初想采用 VNET + Thin Jail(NullFS) ,但是在挂载Linux根目录时遇到了和NullFS根目录挂载的冲突(为避免资源死锁,拒绝挂载),仔细看了官方手册发现官方案例是Snapshot类型的Thin Jail,所以改为采用 VNET + Thin Jail(Snapshot)
警告
由于本次实践是在FreeBSD 15 Alpha 2上进行,遇到了好几个坑:
不支持在ZFS存储上存放
/compat/ubuntu系统(应该是现阶段的bug,之前RELEASE版本是正常的)bootstrap不能正确创建Ubuntu工作环境( 最后通过 使用 ubuntu-base tgz 包部署Linux Jail Ubuntu 绕过问题)
不过,本文的方法步骤是正确的,相信再过几个月(2025年12月会发布FreeBSD 15 RELEASE)正式版本发布以后就可以了。
FreeBSD Linux Jail是在FreeBSD Jail中激活支持Linux二进制程序的一种实现,通过一个允许Linux系统调用和库的兼容层来实现转换和执行在FreeBSD内核上。这种特殊的Jail可以无需独立的linux虚拟机就可以运行Linux软件。
VNET + Thin Jail
主机激活 jail
执行以下命令配置在系统启动时启动
Jail:
# 配置在系统启动时激活Jail功能
sysrc jail_enable="YES"
# 配置所有jails在后台启动
sysrc jail_parallel_start="YES"
Jail目录树
使用了
jail_zfs环境变量来指定ZFS位置,为Jail创建目录树:
export jail_dir="zdata/jails"
export bsd_ver="14.3"
# 在FreeBSD中root用户的shell默认是sh,所以调整 ~/.shrc
echo 'export jail_dir="zdata/jails"' >> ~/.shrc
echo 'export bsd_ver="14.3"' >> ~/.shrc
创建jail目录结构
zfs create $jail_dir
zfs create $jail_dir/media
zfs create $jail_dir/templates
zfs create $jail_dir/containers
完成后检查
df -h可以看到磁盘如下:
Filesystem Size Used Avail Capacity Mounted on
...
zdata 6.1T 96K 6.1T 0% /zdata
zdata/jails 6.1T 104K 6.1T 0% /zdata/jails
zdata/jails/media 6.1T 96K 6.1T 0% /zdata/jails/media
zdata/jails/templates 6.1T 96K 6.1T 0% /zdata/jails/templates
zdata/jails/containers 6.1T 96K 6.1T 0% /zdata/jails/containers
快照(snapshot)型Thin Jails
为OpenZFS Snapshot Thin Jail 准备模版dataset:
14.3-RELEASE 模版zfs create -p $jail_dir/templates/$bsd_ver-RELEASE
下载用户空间:
fetch https://download.freebsd.org/ftp/releases/amd64/amd64/$bsd_ver-RELEASE/base.txz -o /$jail_dir/media/$bsd_ver-RELEASE-base.txz
将下载内容解压缩到模版目录: 内容解压缩到模板目录(后续要在14.3-RELEASE上创建快照,这就是和NullFS的区别)
tar -xf /$jail_dir/media/$bsd_ver-RELEASE-base.txz -C /$jail_dir/templates/$bsd_ver-RELEASE --unlink
将时区和DNS配置复制到模板目录:
cp /etc/resolv.conf /$jail_dir/templates/$bsd_ver-RELEASE/etc/resolv.conf
cp /etc/localtime /$jail_dir/templates/$bsd_ver-RELEASE/etc/localtime
更新模板补丁:
freebsd-update -b /$jail_dir/templates/$bsd_ver-RELEASE/ fetch install
备注
我的最近一次实践是在 15.0-ALPHA2 上运行 14.3-RELEASE Jail,由于 freebsd-update 不支持ALPHA,则更新会报错(同 FreeBSD 15 Alphas更新和升级 ):
freebsd-update 报错src component not installed, skipped
Looking up update.FreeBSD.org mirrors... 3 mirrors found.
Fetching public key from update1.freebsd.org... failed.
Fetching public key from update2.freebsd.org... failed.
Fetching public key from dualstack.aws.update.freebsd.org... failed.
No mirrors remaining, giving up.
This may be because upgrading from this platform (amd64)
or release (15.0-ALPHA1) is unsupported by freebsd-update. Only
platforms with Tier 1 support can be upgraded by freebsd-update.
See https://www.freebsd.org/platforms/ for more info.
If unsupported, FreeBSD must be upgraded by source.
为模版创建快照(完整快照):
zfs snapshot $jail_dir/templates/$bsd_ver-RELEASE@base
基于快照(snapshot)的Thin Jail 比 NullFS 类型简单很多,只要在模块快照基础上创建clone就可以生成一个新的Thin Jail:
jail_name 环境变量,方便后续调整jail命名# 假设这里创建名为ldev的jail
jail_name=ldev
ldev 的Thin Jail(后续将进一步改造为 FreeBSD Linux Jail )zfs clone $jail_dir/templates/$bsd_ver-RELEASE@base $jail_dir/containers/$jail_name
现在可以看到相关ZFS数据集如下:
Filesystem Size Used Avail Capacity Mounted on
...
zdata/jails 1.3T 116K 1.3T 0% /zdata/jails
zdata/jails/media 1.3T 201M 1.3T 0% /zdata/jails/media
zdata/jails/containers 1.3T 104K 1.3T 0% /zdata/jails/containers
zdata/jails/templates 1.3T 104K 1.3T 0% /zdata/jails/templates
...
zdata/jails/templates/14.3-RELEASE 1.3T 460M 1.3T 0% /zdata/jails/templates/14.3-RELEASE
zdata/jails/containers/ldev 1.3T 460M 1.3T 0% /zdata/jails/containers/ldev
配置Snapshot Thin Jails
备注
先Thin Jail ,运行起来以后再调整为Linux Jail
Jail的配置分为公共部分和特定部分,公共部分涵盖了所有jails共有的配置
尽可能提炼出Jails的公共部分,这样就可以简化针对每个jail的特定部分,方便编写校验维护
适合不同Jail的公共配置
/etc/jail.conf:
/etc/jail.conf# STARTUP/LOGGING
exec.start = "/bin/sh /etc/rc";
exec.stop = "/bin/sh /etc/rc.shutdown";
exec.consolelog = "/var/log/jail_console_${name}.log";
# PERMISSIONS
allow.raw_sockets;
exec.clean;
mount.devfs;
allow.mount;
allow.mount.devfs;
enforce_statfs = 1;
# HOSTNAME
host.hostname = "${name}";
# NETWORK - VNET/VIMAGE
#ip4 = inherit;
interface = igc0bridge;
vnet;
vnet.interface = "${epair}b";
# common NETWORK config
$gateway = "192.168.7.221";
$bridge = "igc0bridge";
$epair = "epair${id}";
# ADD TO bridge INTERFACE
exec.prestart += "ifconfig ${epair} create up";
exec.prestart += "ifconfig ${epair}a up descr jail:${name}";
exec.prestart += "ifconfig ${bridge} addm ${epair}a up";
exec.start += "ifconfig ${epair}b ${ip} up";
exec.start += "route add default ${gateway}";
exec.poststop = "ifconfig ${bridge} deletem ${epair}a";
exec.poststop += "ifconfig ${epair}a destroy";
.include "/etc/jail.conf.d/*.conf";
用于Snapshot类型的
ldev独立配置/etc/jail.conf.d/ldev.conf:
/etc/jail.conf.d/ldev.confldev {
# thin jail devfs_ruleset 5 和Linux Jail的4不同
devfs_ruleset=5;
# HOSTNAME/PATH - Snapshot
path = "/zdata/jails/containers/${name}";
# NETWORKS/INTERFACES
$id = "253";
$ip = "192.168.7.${id}/24";
}
备注
挂载路径和NullFS类型的Thin Jail不同
启动jail
最后启动
ldev:
ldevservice jail start ldev
Linux Jail
Linux Jail是在常规Jail上启用 Linux ABI实现
首先激活启动时Linux ABI支持:
sysrc linux_enable="YES"
一旦激活,就可以执行以下命令无需重启系统:
service linux start
启动Linux支持后,在 FreeBSD Host 上执行 df 可以看到系统挂载了Linux兼容的设备文件系统:
Filesystem Size Used Avail Capacity Mounted on
...
linprocfs 8.0K 0B 8.0K 0% /compat/linux/proc
linsysfs 8.0K 0B 8.0K 0% /compat/linux/sys
devfs 1.0K 0B 1.0K 0% /compat/linux/dev
fdescfs 1.0K 0B 1.0K 0% /compat/linux/dev/fd
tmpfs 61G 4.0K 61G 0% /compat/linux/dev/shm
...
现在进入
ldev容器:
ldev 容器jexec ldev
在jail内部执行以下命令安装
sysutils/debootstrap以便安装 Ubuntu Linux 环境:
debootstrap 部署Ubuntupkg install debootstrap
# 构建Ubuntu 22.04 jammy, 注意 /compat 目录必须是读写目录,所以使用了Snapshot类型Thin Jail
debootstrap jammy /compat/ubuntu
完成后观察Linux Jail中文件系统挂载,可以看到增加了一个 devfs 挂载到 /compat/ubuntu/dev :
# df -h
Filesystem Size Used Avail Capacity Mounted on
zdata/jails/containers/ldev 1.3T 785M 1.3T 0% /
devfs 1.0K 0B 1.0K 0% /dev
devfs 1.0K 0B 1.0K 0% /compat/ubuntu/dev
备注
所有 debootstrap 使用的脚本都存储在 /usr/local/share/debootstrap/scripts/ 目录,目前只支持 Ubuntu 22.04 (jammy)。如果要安装更高版本,请参考 使用 ubuntu-base tgz 包部署Linux Jail Ubuntu
暂时停止
ldevjail:
service jail onestop $jail_name
修订
/etc/jail.conf.d/ldev.conf添加挂载配置:
/etc/jail.conf.d/ldev.conf 添加挂载配置ldev {
# thin jail devfs_ruleset 5 和Linux Jail的4不同
devfs_ruleset=4;
# HOSTNAME/PATH - Snapshot
path = "/zdata/jails/containers/${name}";
# NETWORKS/INTERFACES
$id = "253";
$ip = "192.168.7.${id}/24";
# MOUNT
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";
}
然后启动
ldev:
service linux start
进入Linux Jail的Ubuntu环境(需要结合
chroot):
jexec ldev chroot /compat/ubuntu /bin/bash
在Linux Jail Ubuntu中执行以下
uname命令检查环境:
uname 命令检查Linux环境uname -s -r -m
输出显示如下:
uname 命令检查Linux环境的输出Linux 5.15.0 x86_64
异常排查
执行
jexec ubuntu chroot /compat/ubuntu /bin/bash进入了Linux Jail Ubuntu环境,但是提示一个错误信息,显示没有组ID,确实发现在Linux Jail中的/etc/目录下没有passwd和group文件
# jexec ldev chroot /compat/ubuntu /bin/bash
groups: cannot find name for group ID 0
groups: cannot find name for group ID 5
I have no name!@ldev:/# cat /etc/groups
cat: /etc/groups: No such file or directory
I have no name!@ldev:/# cd /etc/
I have no name!@ldev:/etc# ls -lh
ls: reading directory '.': Invalid argument
total 105K
...
解决的方法是在FreeBSD Host主机上,将Host主机的 /etc/group 和 /etc/passwd 文件复制到Linux Jail中:
/etc/groupcp /etc/group /$jail_dir/containers/$jail_name/compat/ubuntu/etc/
# 消除I have no name!
cp /etc/passwd /$jail_dir/containers/$jail_name/compat/ubuntu/etc/
另外,在
ls命令执行时总是提示Invalid argument:
ls 都提示 Invalid argumentI have no name!@ldev:/# ls -lh
ls: reading directory '.': Invalid argument
total 41K
lrwxr-xr-x 1 0 wheel 7 Sep 23 04:55 bin -> usr/bin
...
比较奇怪,虽然 debootstrap 显示安装了 apt 2.4.5 ,但是我 chroot /comapt/ubuntu /bin/bash 之后却显示找不到 apt 命令
我检查发现,在 Linux Jail Ubuntu的 chroot 目录后, /var/cache/apt/archives 目录下实际上已经下载了需要安装的软件包,但是却没有安装。我手工尝试安装:
dpkg 手工安装 apt 包dpkg -i apt_2.4.5_amd64.deb
看出了异常,连目录不能读取出现报错:
dpkg 手工安装 apt 包报错信息dpkg: error: error opening configuration directory '/etc/dpkg/dpkg.cfg.d': Invalid argument
实际上目录是存在的,但是无法读取:
root@ldev:/var/cache/apt/archives# cd /etc/dpkg/dpkg.cfg.d
root@ldev:/etc/dpkg/dpkg.cfg.d# pwd
/etc/dpkg/dpkg.cfg.d
root@ldev:/etc/dpkg/dpkg.cfg.d# ls /etc/dpkg/dpkg.cfg.d
ls: reading directory '/etc/dpkg/dpkg.cfg.d': Invalid argument
正是这个目录无法读取的报错,导致了一系列apt软件包(包括自己)都没有安装成功
我回顾了以下自己的操作步骤,发现有一步是我自作主张和手册不同的,就是我首次启动 ldev Snapshot Thin Jail使用了自己之前在 VNET + Thin Jail(Snapshot) 的配置文件,而不是手册中的命令行方式,我怀疑是这个步骤差异导致了Linux兼容层安装失败(实践验证并不是)
对比手册初次启动Thin Jail命令:
jail -cm \
name=ldev \
host.hostname="ldev.cloud-atlas.dev" \
path="/zdata/jails/containers/ldev" \
interface="igc0" \
ip4.addr="192.168.7.253" \
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
对比了配置文件,主要差别就是上述高亮的权限没有允许,我尝试修订配置文件添加上述权限允许。然而很不幸,再次启动 ldev 并没有解决问题。而且我发现实际上添加前后,进入 ldev (尚未chroot),在FreeBSD Thin Jail中看到挂载虚拟文件系统是一样的,说明这些权限添加不添加都没有关系。再看手册中配置案例也没有添加,所以我还是去除这些配置:
ldev 中检查 df -h 可以看到已经挂载了Linux需要的文件系统$ sudo jexec ldev
# df -h
Filesystem Size Used Avail Capacity Mounted on
zdata/jails/containers/ldev 1.3T 786M 1.3T 0% /
devfs 1.0K 0B 1.0K 0% /compat/ubuntu/dev
devfs 1.0K 0B 1.0K 0% /compat/ubuntu/dev
tmpfs 1.0G 4.0K 1.0G 0% /compat/ubuntu/dev/shm
fdescfs 1.0K 0B 1.0K 0% /compat/ubuntu/dev/fd
linprocfs 8.0K 0B 8.0K 0% /compat/ubuntu/proc
linsysfs 8.0K 0B 8.0K 0% /compat/ubuntu/sys
/tmp 230G 46K 230G 0% /compat/ubuntu/tmp
/usr/home 230G 55K 230G 0% /compat/ubuntu/home
devfs 1.0K 0B 1.0K 0% /dev
google gemini提示是 Linux执行程序和FreeBSD内核提供的Linux兼容层存在功能不匹配,通常是文件系统metadata相关的翻译问题,或者是 目录包含的属性Linux二进制程序不能识别 。
我忽然想到我在底层的ZFS上启用了压缩属性,会不会导致上述问题?
在 FreeBSD Host 主机上检查:
ldev 所在ZFS卷属性zfs get all zdata/jails/containers/ldev
输出显示这个ZFS dataset继承了上一级ZFS的压缩属性:
ldev 所在ZFS卷属性可以看到继承了上级ZFS的压缩属性NAME PROPERTY VALUE SOURCE
zdata/jails/containers/ldev type filesystem -
zdata/jails/containers/ldev creation Mon Sep 22 22:41 2025 -
zdata/jails/containers/ldev used 327M -
zdata/jails/containers/ldev available 1.26T -
zdata/jails/containers/ldev referenced 786M -
zdata/jails/containers/ldev compressratio 1.55x -
...
zdata/jails/containers/ldev compression lz4 inherited from zdata
...
回滚步骤,重新开始创建 ldev linux jail所使用的ZFS卷,也就是clone步骤:
ldev 的Thin Jail(后续将进一步改造为 FreeBSD Linux Jail )zfs clone $jail_dir/templates/$bsd_ver-RELEASE@base $jail_dir/containers/$jail_name
这步完成后立即执行关闭压缩:
zfs set compression=off zdata/jails/containers/ldev
然后检查关闭情况:
zfs get all zdata/jails/containers/ldev | grep compress
可以看到已经off
zdata/jails/containers/ldev compressratio 1.00x -
zdata/jails/containers/ldev compression off local
zdata/jails/containers/ldev refcompressratio 2.00x
然后重新创建 ldev
悲伤,没有解决这个问题,报错依旧
UFS文件系统上Linux Jail
警告
后续实践发现问题,看来有可能和Ubuntu系统有关:
我最初使用 使用 ubuntu-base tgz 包部署Linux Jail Ubuntu 完成最小化系统安装并成功运行Linux Jail,此时在系统中执行
ls命令是没有Invalid argument报错的但是后续我执行了
apt update && apt upgrade之后,在Linux系统中使用ls命令又重新出现了Invalid argument报错
总之,看起来并不是底层文件系统问题,我切换到UFS中可能恰好因为初次解压缩官方core系统没有问题,但是后续升级就出现问题了。之前 debootstrap 构建的系统因为是直接从互联网下载软件包解压缩,所以用的软件包最新,可能就是有问题的
待继续排查
那如果ZFS始终存在上述问题,是否改成UFS能够绕开呢?好吧,那就再搞一次 VNET + Thick(厚) Jail(UFS) ( FreeBSD Thin(薄) Jail 需要 ZFS 支持,所以改为UFS的话需要采用 Classic Jail ,也就是 FreeBSD Thick(厚) Jail )
按照上文启动
ludevJail之后,通过jexec ludev进入Jail执行debootstrap:
debootstrap 部署Ubuntupkg install debootstrap
# 构建Ubuntu 22.04 jammy, 注意 /compat 目录必须是读写目录,所以使用了Snapshot类型Thin Jail
debootstrap jammy /compat/ubuntu
注意到工具接收到安装包进行校验,然后 Extracting 软件包:
debootstrap 输出信息...
I: Retrieving vim-tiny 2:8.2.3995-1ubuntu2
I: Validating vim-tiny 2:8.2.3995-1ubuntu2
I: Retrieving whiptail 0.52.21-5ubuntu2
I: Validating whiptail 0.52.21-5ubuntu2
I: Retrieving xdg-user-dirs 0.17-2ubuntu4
I: Validating xdg-user-dirs 0.17-2ubuntu4
I: Retrieving xkb-data 2.33-1
I: Validating xkb-data 2.33-1
I: Retrieving xxd 2:8.2.3995-1ubuntu2
I: Validating xxd 2:8.2.3995-1ubuntu2
I: Retrieving zlib1g 1:1.2.11.dfsg-2ubuntu9
I: Validating zlib1g 1:1.2.11.dfsg-2ubuntu9
I: Chosen extractor for .deb packages: ar
I: Extracting base-files...
I: Extracting base-passwd...
I: Extracting bash...
I: Extracting bsdutils...
I: Extracting coreutils...
...
I: Extracting libsemanage-common...
I: Extracting libsemanage2...
I: Extracting libsepol2...
暂停
ludevJail,并修订/etc/jail.conf.d/ludev.conf添加Linux文件系统挂载部分:
ludev {
# thin jail devfs_ruleset 5 和Linux Jail的4不同
devfs_ruleset=4;
# HOSTNAME/PATH - Snapshot
path = "/udata/jails/containers/${name}";
# NETWORKS/INTERFACES
$id = "252";
$ip = "192.168.7.${id}/24";
# MOUNT
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";
}
再次启动
ludevJail,然后进入Linux Jail的Ubuntu环境:
jexec ludev chroot /compat/ubuntu /bin/bash
备注
将底层文件系统切换到UFS之后,确实解决了 Invalid argument 的报错问题,此时 chroot 进入Ubuntu系统后, ls 命令不再报错
比较奇怪的是,在这个 Ubuntu Jail环境中,默认没有安装apt工具包,但是在 /var/cache/apt/archives 目录下却有很多下载的 deb 软件包,也包括 apt-utils_2.4.5_amd64.deb apt_2.4.5_amd64.deb
我尝试在 /var/cache/apt/archives 手工先安装 apt 工具以便后面再补充软件包:
cd /var/cache/apt/archives
dpkg -i apt_2.4.5_amd64.deb gpgv_2.2.27-3ubuntu2_amd64.deb \
libapt-pkg6.0_2.4.5_amd64.deb ubuntu-keyring_2021.03.26_all.deb \
libc6_2.35-0ubuntu3_amd64.deb libgcc-s1_12-20220319-1ubuntu1_amd64.deb \
libgnutls30_3.7.3-4ubuntu1_amd64.deb libseccomp2_2.5.3-2ubuntu2_amd64.deb \
libstdc++6_12-20220319-1ubuntu1_amd64.deb libsystemd0_249.11-0ubuntu3_amd64.deb
上述安装命令依然报依赖错误,我一狠心,直接执行了 dpkg -i *.deb ,看起来很多软件包安装成功,但是也有大量软件包报告依赖安装错误,特别是 Systemd进程管理器 相关。我知道在Linux Jail中是不支持systemd的,看起来这个 debootstrap 下载了很多依赖安装包是不能直接安装的,有可能会误安装了 systemd 相关软件包导致异常。
不过, dpkg -i *.deb 确实也安装成功了 apt 软件,只不过系统误安装了 systemd 相关软件后(实际安装失败,但是残留了很多包信息),导致混乱而无法进一步更新系统。
我感觉应该安装一个类似Docker容器中运行的Ubuntu版本,这样精简过的系统可以剔除systemd干扰 ,准备尝试 使用 ubuntu-base tgz 包部署Linux Jail Ubuntu
参考
(Solved)How to have network access inside a Linux jail? 这个讨论非常详细,是解决Linux Jail普通用户网络问题的线索
Setting up a (Debian) Linux jail on FreeBSD 一篇非常详细的Linux Jail实践