在UFS文件系统上构建Thin Jail

我因为 ZFS 非常方便管理和使用,所以大多数FreeBSD和Linux部署时都会采用ZFS作为数据存储文件系统。在 FreeBSD Thin(薄) Jail 甚至 VNET + Thin Jail 也采用ZFS来实践构建。

不过,也有一些环境采用了传统的UFS文件系统,例如,我在阿里云租用的VM,阿里云默认提供的FreeBSD镜像就是采用UFS文件系统。我需要为 FreeBSD系统更新 构建一个反向代理缓存服务器,考虑在租用的VM中构建一个隔离的Thin Jail来运行 Nginx反向代理 。这里记录准备工作,构建一个UFS上的Thin Jail。

主机激活 jail

  • 执行以下命令配置在系统启动时启动 Jail :

激活jail
# 配置在系统启动时激活Jail功能
sysrc jail_enable="YES"

# 配置所有jails在后台启动
sysrc jail_parallel_start="YES"

Jail目录树

  • 准备环境变量(方便后续操作);

准备jail相关环境变量
export jail_dir="data/jails"
export bsd_ver="14.3"
# 在FreeBSD中root用户的shell默认是sh,所以调整 ~/.shrc
echo 'jail_dir="data/jails"' >> ~/.shrc
echo 'bsd_ver="14.3"' >> ~/.shrc
  • 创建RELEASE-base模版目录:

在UFS中创建模板数据集
mkdir -p $jail_dir/templates/$bsd_ver-RELEASE-base
下载用户空间
fetch https://download.freebsd.org/ftp/releases/amd64/amd64/$bsd_ver-RELEASE/base.txz -o /$jail_dir/media/$bsd_ver-RELEASE-base.txz
  • 将下载内容解压缩到模版目录:

解压缩
tar -xf /$jail_dir/media/$bsd_ver-RELEASE-base.txz -C /$jail_dir/templates/$bsd_ver-RELEASE-base --unlink
  • 将时区和DNS配置复制到模板目录:

将时区和DNS配置复制到模板目录
cp /etc/resolv.conf /$jail_dir/templates/$bsd_ver-RELEASE-base/etc/resolv.conf
cp /etc/localtime   /$jail_dir/templates/$bsd_ver-RELEASE-base/etc/localtime
  • 更新模板补丁:

更新模板补丁
freebsd-update -b /$jail_dir/templates/$bsd_ver-RELEASE-base/ fetch install
  • 创建一个 skeleton (骨骼),这里是UFS,所以直接创建目录

创建 skeleton (骨骼)
mkdir /$jail_dir/templates/$bsd_ver-RELEASE-skeleton
  • 执行以下命令,将特定目录移入 skeleton 数据集,并构建 baseskeleton 必要目录的软连接关系

特定目录移入 skeleton 数据集
mkdir -p /$jail_dir/templates/$bsd_ver-RELEASE-skeleton/home
mkdir -p /$jail_dir/templates/$bsd_ver-RELEASE-skeleton/usr
# 我单独创建一个docs目录用于后续将host主机的docs映射到jail中
mkdir -p /$jail_dir/templates/$bsd_ver-RELEASE-skeleton/docs

# etc目录包含发行版提供的配置文件
mv /$jail_dir/templates/$bsd_ver-RELEASE-base/etc /$jail_dir/templates/$bsd_ver-RELEASE-skeleton/etc
# local目录是空的
mv /$jail_dir/templates/$bsd_ver-RELEASE-base/usr/local /$jail_dir/templates/$bsd_ver-RELEASE-skeleton/usr/local
# tmp 目录是空的
mv /$jail_dir/templates/$bsd_ver-RELEASE-base/tmp /$jail_dir/templates/$bsd_ver-RELEASE-skeleton/tmp
# var 目录有很多预存目录,但是直接mv移动时有报错显示 var/empty 目录没有权限,这会导致目标目录破坏,所以改为rsync同步
rsync -avz /$jail_dir/templates/$bsd_ver-RELEASE-base/var /$jail_dir/templates/$bsd_ver-RELEASE-skeleton/var
mv /$jail_dir/templates/$bsd_ver-RELEASE-base/var /$jail_dir/templates/$bsd_ver-RELEASE-base/var.bak
# root 目录是管理员目录,有基本profile文件
mv /$jail_dir/templates/$bsd_ver-RELEASE-base/root /$jail_dir/templates/$bsd_ver-RELEASE-skeleton/root
  • 执行以下命令创建软连接:

创建软连接
cd /$jail_dir/templates/$bsd_ver-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
# 这里增加一个docs目录用于存放数据(从Host主机映射到jail中)
ln -s skeleton/docs docs
  • 在host上执行 修复 /etc/ssl/certs 目录下证书文件软链接

修复软链接
#cd /$jail_dir/templates/$bsd_ver-RELEASE-base/etc/ssl/certs
cd /$jail_dir/templates/$bsd_ver-RELEASE-skeleton/etc/ssl/certs

# 先保存一份原始列表记录
ls -lh > /tmp/fix_link.txt

# 生成unlink命令
ls | sed "s@^@unlink @" > /tmp/unlink.sh

# 生成fix命令
# 这里不能使用绝对路径链接,否则会报错 link: ffdd40f9.0: Cross-device link
# ls -lh | awk '{print $NF, $(NF-2)}' | cut -c 9- | tail -n +2 | sed  "s@^@link @" > /tmp/fix_link.sh
ls -lh | awk '{print $NF, $(NF-2)}' | tail -n +2 | sed 's@^@ln -s ../@' > /tmp/fix_link.sh

# 检查一下 /tmp/fix_link.sh 是否满足要求
# 没有问题在执行以下2条命令

sh /tmp/unlink.sh
sh /tmp/fix_link.sh
  • skeleton 就绪之后,需要将数据复制到 jail 目录(如果是UFS文件系统):

skeleton 数据复制到 jail 目录
# 假设这里创建名为dev的jail
jail_name=web
cp -R /$jail_dir/templates/$bsd_ver-RELEASE-skeleton /$jail_dir/containers/$jail_name
  • 创建一个 base template的目录,这个目录是 skeleton 挂载所使用的根目录

创建 skeleton 挂载所使用的根目录
# 创建 dev 所使用的nullfs模板目录,这个目录就是jail的根PATH
# 和常规Jail仅命名 ${name} 不同,采用 ${name}-nullfs-base
mkdir -p /$jail_dir/$jail_name-nullfs-base

配置jail

  • 创建所有jail使用的公共配置部分 /etc/jail.conf (这里使用共享网络模式):

所有jail使用的公共配置部分 /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/PATH - NullFS
host.hostname = "${name}";
path = "/data/jails/${name}-nullfs-base";

# NETWORK
ip4 = inherit;
interface = vtnet0;

# MOUNT
mount.fstab = "/data/jails/${name}-nullfs-base.fstab";

.include "/etc/jail.conf.d/*.conf";
  • /etc/jail.conf.d/web.conf (主机名 web )独立配置部分(这里因为都是公用部分,所以实际上是一个只包含主机名的空配置):

/etc/jail.conf.d/web.conf
web {
}

数据目录(可选)

为了能够将Host主机上的数据目录映射到jail容器内部使用(方便在Host主机上统一管理和备份),执行以下命令:

创建将要映射到jail web/docs 目录
mkdir -p /data/docs/web

目录挂载

  • 注意,这里配置引用了一个针对nullfs的fstab配置(主机名 web ),所以还需要创建一个 /data/jails/web-nullfs-base.fstab :

/data/jails/web-nullfs-base.fstab
/data/jails/templates/14.3-RELEASE-base  /data/jails/web-nullfs-base/         nullfs  ro  0 0
/data/jails/containers/web               /data/jails/web-nullfs-base/skeleton nullfs  rw  0 0
/data/docs/web                           /data/jails/web-nullfs-base/skeleton/docs nullfs rw,noatime,nosuid,nocache 0 0

备注

这里将Host主机的 /data/docs/web 目录映射到jail web 中的 /docs 目录,方法和 在多个Jail间共享文件目录 一样

  • 最后启动 web :

启动 web
service jail start web

通过 jexec web 进入jail

此时在jail web 中检查目录 df -h 显示如下:

在jail web 中检查目录 df -h
Filesystem                                 Size    Used   Avail Capacity  Mounted on
/data/jails/templates/14.3-RELEASE-base     29G     23G    3.7G    86%    /
/data/jails/containers/web                  29G     23G    3.7G    86%    /skeleton
/data/docs/web                              29G     23G    3.7G    86%    /skeleton/docs
devfs                                      1.0K      0B    1.0K     0%    /dev

其中 /skeleton/docs 对应Host主机的数据目录 /data/docs/web ,就可以在Host主机操作容器数据,方便维护

  • 设置Jail web 在操作系统启动时启动,修改 /etc/rc.conf :

/etc/rc.conf
jail_enable="YES"
jail_parallel_start="YES"
jail_list="web"
jail_reverse_stop="YES"

备注

这里完成了阿里云FreeBSD虚拟机的 web jail 运行,下一步来构建一个 Nginx反向代理 实现WEB服务器:

参考