FreeBSD Jail访问ZFS文件系统

ZFS数据集可以委托给Jail使用:

  • zfs set jailed=on <volume name> 将ZFS卷集设置给jail使用

  • zfs jail <jailid|jailname> filesystem 命令可以将ZFS文件系统attach到FreeBSD jail上

  • jexec <jailname> zfs mount filesystem 命令在jail内部挂载上文件系统就可以开始使用

整个过程有一些技巧,特别适合在jail中运行数据库这种独占卷集的方案: 在FreeBSD Jail中运行PostgreSQL

实践案例

目标: host主机的 zdata/docs 分配给 dev jail使用

  • 创建 zdata/docs (可选设置挂载目录)

创建卷集 zdata/docs
# 创建挂载到 /docs 目录的卷集 zdata/docs
#zfs create -o mountpoint=/docs zdata/docs

zfs create zdata/docs

此时在host主机上执行 df -h 可以看到一个被挂载到 /docs 目录上的ZFS卷集:

创建挂载到 /docs 目录的卷集 zdata/docs 的挂载情况
Filesystem                                     Size    Used   Avail Capacity  Mounted on
zroot/ROOT/default                             192G    1.4G    191G     1%    /
devfs                                          1.0K      0B    1.0K     0%    /dev
zdata                                          6.1T     96K    6.1T     0%    /zdata
...
zdata/docs                                     6.1T     96K    6.1T     0%    /docs
  • 设置卷集 jailed=on 属性:

设置卷集 jailed=on 属性
zfs set jailed=on zdata/docs

备注

此时再执行 df -h 就会看到 zdata/docs 卷集挂载从host主机上消失了,因为这个卷集已经设置为用于jail,对于host主机将不可用

  • 执行 zfs jail 命令将设置了 jailed=on 的ZFS卷集分配给jail dev :

jailed=on 属性的ZFS卷集分配给jail dev
zfs jail dev zdata/docs

备注

只有执行了 zfs jail dev zdata/docs 命令之后,才能在 dev jail 中看到这个ZFS卷集

另外,如果host主机重启,则 dev jail中就看不到这个ZFS卷集,需要重新执行这个命令。

要持久化配置,需要调整 dev jail 配置,加入这个jail启动时自动执行命令,见下文。

  • 现在在 dev jail中可以看到卷集:

dev jail中可以看到卷集
# 在jail中检查
zfs list
  • 可以在 dev jail 中执行挂载:

dev jail 中执行挂载
zfs mount zdata/docs

这里遇到一个报错:

dev jail 中执行挂载报错
cannot mount '/docs': failed to create mountpoint: Read-only file system

这个报错是因为我部署的是 VNET + Thin Jail 的NullFS jail,其中根文件目录是只读的snapshot挂载。不过,NullFS模式有部分目录是读写模式的,例如 /home 。所以调整 zdata/docs 的挂载点来重试:

  • 修订挂载点(host)

调整挂载(host主机上执行)
# 首先要将jailed属性关闭才能在host主机上修改zfs
# 否则修改mountpoint会报错: cannot set property for 'zdata/docs': 'mountpoint' cannot be set on dataset in a non-global zone

zfs set jailed=off zdata/docs
zfs set mountpoint=/home/docs zdata/docs

zfs set jailed=on zdata/docs

# 因为之前已经执行过 zfs jail 命令,我发现不需要再执行
# zfs jail dev zdata/docs
  • 此时在 dev jail 中执行 zfs list 可以看到ZFS卷集 zdata/docs 已经自动反映出挂载点修改了

dev jail 可以看到挂载点修订
NAME         USED  AVAIL  REFER  MOUNTPOINT
zdata       2.32G  6.12T    96K  /zdata
zdata/docs    96K  6.12T    96K  /home/docs
  • 再次执行挂载 zfs mount zdata/docs ,可以看到另外一个报从显示jail没有权限:

在jail中挂载zfs卷集没有权限
cannot mount 'zdata/docs': Insufficient privileges
  • 调整 dev` jail的权限,先停止 dev jail,然后修订 /etc/jail.conf (公共设置) 或 /etc/jail.conf.d/dev.conf (单独设置)

配置允许挂载ZFS
# PERMISSIONS
allow.raw_sockets;
exec.clean;
mount.devfs;

allow.mount;
allow.mount.devfs;
allow.mount.zfs;

enforce_statfs = 1;

备注

这里同时需要配置:

需要同时配置2个参数才能有权限挂载ZFS
allow.mount.zfs;
enforce_statfs = 1;

否则挂载时候都会报同样的错误:

在jail中挂载zfs卷集没有权限
cannot mount 'zdata/docs': Insufficient privileges
  • 重新启动 dev jail之后,就可以在该 jail 内部执行ZFS挂载: zfs mount zdata/docs

自动化ZFS挂载

上述执行步骤完整展示了如何在jail内部使用一个ZFS数据集,但是整个过程是纯手工命令完成的。如何在jail启动时自动完成ZFS卷集的挂载呢?

  • 修改 /dev/jail.conf.d/dev.conf :

配置 dev jail 启动时自动挂载分配的ZFS dataset
dev {
  # PERMISSIONS
  allow.mount.zfs;

  # NETWORKS/INTERFACES
  $id = "253";
  $ip = "192.168.7.${id}/24";

  # auto mount ZFS datasets
  $dataset = "zdata/docs";
  exec.created += "/sbin/zfs jail ${name} ${dataset}";
  exec.created += "jexec ${name} zfs mount ${dataset}";
  exec.release += "/sbin/zfs unjail ${name} ${dataset}";
  exec.release += "umount -f ${dataset}";
}

这样启动 dev jail后,通过 jexec dev 进入jail,检查 df -h 就可以看到这个 zdata/docs 被自动挂载到指定的 /skeleton/home/docs 目录:

自动挂载ZFS dataset之后的 df -h 状态
Filesystem                                  Size    Used   Avail Capacity  Mounted on
/zdata/jails/templates/14.3-RELEASE-base    6.1T    457M    6.1T     0%    /
/zdata/jails/containers/dev                 6.1T    860M    6.1T     0%    /skeleton
devfs                                       1.0K      0B    1.0K     0%    /dev
zdata/docs                                  6.1T     96K    6.1T     0%    /skeleton/home/docs

参考