.. _thin_jail:
===========================
FreeBSD Thin(薄) Jail
===========================
FreeBSD Thin Jail是基于 :ref:`zfs` (OpenZFS) 快照 或模板 和 NullFS 来创建的 **瘦** Jail
设置环境变量
================
为方便调整,我设置了环境变量来方便后续操作
.. literalinclude:: vnet_thin_jail/env
:caption: 设置 jail目录和release版本环境变量
.. _thin_jail_using_zfs_snapshot:
OpenZFS快照Thin Jail
=========================
- 为模板创建发行版,这个是只读的:
.. literalinclude:: thin_jail/zfs
:caption: 在ZFS中创建模板数据集
- 和 :ref:`thick_jail` 一样下载用户空间:
.. literalinclude:: vnet_thin_jail/fetch
:caption: 下载用户空间
- 将下载内容解压缩到模板目录:
.. literalinclude:: thin_jail/template
:caption: 内容解压缩到模板目录
- 将时区和DNS配置复制到模板目录:
.. literalinclude:: thin_jail/template_conf
:caption: 时区和DNS配置复制到模板目录
- 更新模板补丁:
.. literalinclude:: thin_jail/template_update
:caption: 更新补丁
- 从模板创建 :ref:`zfs` 快照:
.. literalinclude:: thin_jail/template_snapshot
:caption: 模板创建 :ref:`zfs` 快照
一旦创建了 OpenZFS 快照,就可以使用 OpenZFS 克隆功能创建无限个 jail
.. literalinclude:: thin_jail/clone
:caption: 从快照中clone出2个Thin Jail
- 准备 ``dev-1`` Thin Jail配置 ``/etc/jail.conf.d/dev-1.conf`` ( ``dev-2.conf`` 类似,只有主机名和IP不同):
.. literalinclude:: thin_jail/dev-1.conf
:caption: ``dev-1.conf``
- 启动Thin Jail容器:
.. literalinclude:: thin_jail/start
:caption: 启动2个Thin Jail
.. warning::
OpenZFS快照Thin Jail实际上有一个 **致命限制** : ``ZFS snapshot是不可更改的``
虽然 ``snapshot => clone`` 极大地节约了磁盘空间,但是如果需要更新OpenZFS snapshot Thin Jails,需要:
- destroy 所有的 clone,也就是销毁Thin Jails
- 更新ZFS卷,也就是上文案例中的 ``zroot/jails/templates/14.3-RELEASE`` ,当然可以是再建立一个卷,例如 ``zroot/jails/templates/14.3-RELEASE``
- 重新走一遍 ZFS snapshot => ZFS clone ,来构建Thin Jails
也就是说,这是一个重建的过程,和 :ref:`docker` 的镜像发布非常相似: 不存在更新snapshot从而达到所有clone自动更新的效果
好处是和 :ref:`docker` 容器镜像发布是一个原理,如果你熟悉容器管理,其实就是一样的技术。在大规模部署时,假设类似 :ref:`kubernetes` ,可以采用滚动销毁旧Jail,然后从新的snapshot上clone出新的Jail来实现升级。
相关讨论可以参考 `Upgrade thin jail with ZFS snapshot `_
.. _thin_jail_using_nullfs:
NullFS thin Jail
=========================
.. note::
目前我主要使用 ``NullFS thin Jail`` ,在 :ref:`vnet_thin_jail` 中就是采用这种方式。此外,我在 :ref:`freebsd_desktop` 环境也采用 ``NullFS thin Jail`` ,但没有结合 :ref:`vnet_jail` (因为桌面没有多个IP地址可以分配),具体案例见 :ref:`thin_jail_desktop`
通过结合 Thin Jail 和 ``NullFS`` 技术也可以创建节约系统文件存储开销(类似于 ``ZFS snapshot`` clone出来的卷完全不消耗空间),并且能够将Host主机的我呢见目录共享给 **多个** Jail。
.. note::
实际上直接使用 ZFS snapshot 创建 thin jail 和 使用 NullFS 创建 thin jail 的区别在于:
- ``ZFS snapshot 是只读的,不可更改的`` ,这意味着 **没有办法简单通过更新共享的ZFS snapshot来实现Thin Jail操作系统**
- ``NullFS直接使用ZFS dataset是可读写的`` ,也就是 **直接更新NullFS底座共享的ZFS dataset可以瞬间更新所有Thin Jail**
所以,两者区别仅在于创建Thin Jail的开始步骤:
- 将FreeBSD Release base存放在 **只读** 的 ``14.3-RELEASE@base`` **快照** - OpenZFS snapshot Thin Jail
- 将FreeBSD Relaase base存放在 **读写** 的 ``14.3-RELEASE-base`` **数据集** - NullFS Thin Jail
.. note::
这里创建的 ``Thin Jail Using NullFS`` 是基于 :ref:`zfs` 完成的实践。实际上,基于NullFS的Thin Jail也可以使用传统的UFS构成。由于我在阿里云租用的VM默认文件系统是UFS,所以我独立再记录到 :ref:`thin_jail_ufs` 中。
- 创建 **读写模式** 的 ``14.3-RELEASE-base`` (注意,大家约定俗成 ``@base`` 表示只读快照, ``-base`` 表示可读写数据集)
.. literalinclude:: vnet_thin_jail/templates_base
:caption: 创建 **读写模式** 的 ``14.3-RELEASE-base`` 数据集
- 和 :ref:`thick_jail` 一样下载用户空间:
.. literalinclude:: vnet_thin_jail/fetch
:caption: 下载用户空间
- 将下载内容解压缩到模板目录:
.. literalinclude:: vnet_thin_jail/tar
:caption: 内容解压缩到模板目录( ``14.3-RELEASE-base`` 后续不需要创建快照,直接使用)
- 将时区和DNS配置复制到模板目录:
.. literalinclude:: vnet_thin_jail/cp
:caption: 时区和DNS配置复制到模板目录
- 更新模板补丁:
.. literalinclude:: vnet_thin_jail/update
:caption: 更新补丁
``关键部分来了,以下是NullFS特别部分``
- 创建一个特定数据集 ``skeleton`` (骨骼) ,这个 "骨骼" ``skeleton`` 命名非常形象,用意就是构建特殊的支持大量thin jial的框架底座
.. literalinclude:: vnet_thin_jail/zfs_create
:caption: 创建特定数据集 ``skeleton`` (骨骼)
- 执行以下命令,将特定目录移入 ``skeleton`` 数据集,并构建 ``base`` 和 ``skeleton`` 必要目录的软连接关系(注意:刚开始步骤有报错,我修改了方法)
.. literalinclude:: thin_jail/create_directories_nullfs
:caption: 创建目录(以前的方法,实际有报错,修正见后文)
这里有一个报错,在 ``mv var`` 到 ``skeleton`` ZFS数据集下时会报错,显示其中 ``var/empty`` 目录没有权限删除: ``mv var/empty: Operation not permitted`` ,我后来是通过将上一级目录重命名来解决的 ``mv var var.bak`` ,具体方法按照 :ref:`vnet_thin_jail` :
.. literalinclude:: vnet_thin_jail/skeleton_link
:caption: 特定目录移入 ``skeleton`` 数据集
- 执行以下命令创建软连接
.. literalinclude:: vnet_thin_jail/link
:caption: 创建 ``skeleton`` 软连接
- **补充步骤** (实践发现需要修正certs软连接,见 :ref:`vnet_thin_jail` ) 修复 ``/etc/ssl/certs`` 目录下证书文件软链接
.. literalinclude:: vnet_thin_jail/fix_link.sh
:caption: 修复软链接
- 在 ``skeleton`` 就绪之后,需要将数据复制到 jail 目录(如果是UFS文件系统),对于ZFS则非常方便使用快照:
.. literalinclude:: vnet_thin_jail/snapshot
:caption: 将刚才建立的 ``skeleton`` 创建快照,也就是用于jail
完成后可以看到相关的ZFS数据集如下:
.. literalinclude:: vnet_thin_jail/df_jail
:caption: 完成后可以看到ZFS挂载(df)
- 创建一个 base template的目录,这个目录是 ``skeleton`` 挂载所使用的根目录:
.. literalinclude:: vnet_thin_jail/nullfs-base
:caption: 创建一个jail所使用的模板目录
- 之前构建 ``d2l`` ZFS snapshot Thin Jail时,共用 ``/etc/jail.conf`` :
.. literalinclude:: thin_jail/jail.conf
:caption: 共用的 ``/etc/jail.conf``
以及 ``/etc/jail.conf.d/dev.conf`` (其中网络部分是 :ref:`vnet_jail` 配置,可忽略):
.. literalinclude:: thin_jail/dev.conf
:caption: ``/etc/jail.conf.d/dev.conf`` 配置
:emphasize-lines: 7,30
- 注意,这里配置引用了一个针对nullfs的fstab配置,所以还需要创建一个 ``/usr/local/jails/dev-nullfs-base.fstab``
.. literalinclude:: thin_jail/fstab
:caption: ``/usr/local/jails/dev-nullfs-base.fstab`` 配置 ``dev`` Jail的nullfs挂载
- 启动 ``dev`` :
.. literalinclude:: thin_jail/start_dev
:caption: 启动 ``dev`` jail
然后通过 ``rexec dev`` 进入jail进行观察,就会明白 ``NullFS`` 构建的 :ref:`thin_jail` 巧妙之处:
.. literalinclude:: thin_jail/exec_dev
:caption: 进入 ``dev`` 检查
:emphasize-lines: 13,14,22,26,28
NullFS简析
--------------
在 ``NullFS`` Jail中,只有特定的从数据集 ``14.3-RELEASE-base`` 移出到 ``14.3-RELEASE-skeleton`` 并被快照clone成 ``skeleton`` 的数据才是可读写的。这部分通过软连接和 **只读** 挂载的 ``14.3-RELEASE-base`` 关联。
``通过数据集 14.3-RELEASE-base 只读挂载确保Jail不会修改基础软件,同时由于是数据集,所以可以在Host进行滚动更新``
这里有一个非常巧妙的构思: ``13.2-RELEASE-skeleton@base`` 将部分需要根据每个jail变化的部分单独摘除出来( ``/etc`` / ``/usr/local`` / ``root`` / ``/tmp`` / ``/var`` ),这样后续clone出来的这部分每个jail都可以各自读写没有障碍。
如果需要为所有Jail部署相同的软件和配置,例如 ``sudo`` 和 ``ssh`` ,则可以在 ``chroot`` ``14.3-RELEASE-base`` 目录下进行安装更新,完成后只要对 ``13.2-RELEASE-skeleton`` 做依次快照 ``@base`` ,那么这部分增加的软件配置会被所有NullFS Jail使用。唯一的缺点是,这部分非 ``base`` 的软件包安装是使用ZFS snapshot,是不能自动为每个Jail更新的,这部分定制需要在每个Jail中对 ``clone`` 内容进行更新( ``snapshot`` 是死的,更新需要重建所有Jail,这也是没有办法的办法,相当于底座替换)
参考
======
- `FreeBSD Handbook: Chapter 17. Jails and Containers > 17.5. Thin Jails `_
- `Issue while following the handbook to create a Thin Jail using NullFS `_