14.5. 更新多个 jail

Contributed by Daniel Gerzo.
Based upon an idea presented by Simon L. B. Nielsen.
And an article written by Ken Tom.

多个 jail 的管理可能会成为问题,因为每个监狱必须从零开始构建,每当它被升级。如果创建并手动更新大量 jail,这可能既耗时又繁琐。

本节演示了一种解决这个问题的方法,即使用只读的 mount_nullfs(8)挂载,在 jails 之间尽可能安全地共享,这样更新就更简单了。这使得将单个服务(如HTTPDNSSMTP)放到单独的jail中更有吸引力。此外,它还提供了一个简单的方法来添加、删除和升级 jail。

注意:

有更简单的解决方案, 例如 ezjail, 它提供了一种更简单的管理 FreeBSD jails 的方法, 但没有这种设置那么灵活。ezjail第 14.6 节 “使用 ezjail 管理 Jail”/>中有更详细的介绍。

这节介绍的配置的目的包括:

这个设计极大程度上依赖于将一份只读的主模板 (known as nullfs) 挂接到每一个 jail 中, 并为每个 jail 配置一个可读写的设备。 这种设备可以是物理磁盘、 分区, 或以 vnode 为后端的内存盘(md(4))设备。 在这个例子中, 我们将使用可读写的 nullfs 挂接。

下面的表中描述了文件系统格局:

14.5.1. 建立模板

这一节将介绍创建 jail 所需的只读主模板所需的步骤。

一般来说, 您应将系统升级到最新的 RELEASE 分支, 具体做法请参见本手册的第 23.5 节 “从源码更新FreeBSD”.另外, 您还需要sysutils/cpdup。 我们将使用 portsnap 工具来下载 FreeBSD Ports 套件。

  1. 首先, 需要为将要存放只读的 FreeBSD 执行文件的文件系统建立一个目录, 接着进入 FreeBSD 源代码的目录, 并在其中安装 jail 模板:

    # mkdir /home/j /home/j/mroot
    # cd /usr/src
    # make installworld DESTDIR=/home/j/mroot
  2. 接着, 准备一份 FreeBSD Ports 套件, 以及用于执行 mergemaster 的 FreeBSD 源代码:

    # cd /home/j/mroot
    # mkdir usr/ports
    # portsnap -p /home/j/mroot/usr/ports fetch extract
    # cpdup /usr/src /home/j/mroot/usr/src
  3. 创建系统中可读写部分的骨架:

    # mkdir /home/j/skel /home/j/skel/home /home/j/skel/usr-X11R6 /home/j/skel/distfiles
    # mv etc /home/j/skel
    # mv usr/local /home/j/skel/usr-local
    # mv tmp /home/j/skel
    # mv var /home/j/skel
    # mv root /home/j/skel
  4. 使用 mergemaster 安装缺失的配置文件。 接下来, 删除 mergemaster 创建的多余目录:

    # mergemaster -t /home/j/skel/var/tmp/temproot -D /home/j/skel -i
    # cd /home/j/skel
    # rm -R bin boot lib libexec mnt proc rescue sbin sys usr dev
  5. 现在, 将可读写文件系统连接到只读文件系统中。 请确保您在 s/ 目录中建立了适当的符号连接。 如果没有建立目录或建立的位置不正确, 可能会导致安装失败。

    # cd /home/j/mroot
    # mkdir s
    # ln -s s/etc etc
    # ln -s s/home home
    # ln -s s/root root
    # ln -s ../s/usr-local usr/local
    # ln -s ../s/usr-X11R6 usr/X11R6
    # ln -s ../../s/distfiles usr/ports/distfiles
    # ln -s s/tmp tmp
    # ln -s s/var var
  6. 最后, 创建一个默认的包含下列配置的 /home/j/skel/etc/make.conf

    WRKDIRPREFIX?=  /s/portbuild

    配置 WRKDIRPREFIX 使得在每个 jail 中分别编译 FreeBSD 成为可能。 请注意 ports 目录是只读系统的一部分。 而自订的 WRKDIRPREFIX 则使得联编过程得以在 jail 中的可读写部分完成。

14.5.2. 建立 Jail

现在我们已经有了完整的 FreeBSD jail 模板, 可以在 /etc/rc.conf 中安装并配置它们了。 这个例子中演示了建立 3 个 jail: NSMAILWWW

  1. /etc/fstab 文件中加入下列配置, 以便让系统自动挂接 jail 的只读模板和读写空间:

    /home/j/mroot   /home/j/ns     nullfs  ro  0   0
    /home/j/mroot   /home/j/mail   nullfs  ro  0   0
    /home/j/mroot   /home/j/www    nullfs  ro  0   0
    /home/js/ns     /home/j/ns/s   nullfs  rw  0   0
    /home/js/mail   /home/j/mail/s nullfs  rw  0   0
    /home/js/www    /home/j/www/s  nullfs  rw  0   0

    扫描批次号 (pass number) 为 0 的分区不会在启动时使用 fsck(8) 进行检查, 而转存批次号 (dump number) 为 0 的分区则不会在 dump(8) 时备份。 我们不希望 fsck 检查 nullfs 挂接, 或让 dump 备份 jail 中的只读 nullfs 挂接。 这就是为什么在每个 fstab 条目的最后两列是 0 0 的原因。

  2. /etc/rc.conf 中配置 jail:

    jail_enable="YES"
    jail_set_hostname_allow="NO"
    jail_list="ns mail www"
    jail_ns_hostname="ns.example.org"
    jail_ns_ip="192.168.3.17"
    jail_ns_rootdir="/usr/home/j/ns"
    jail_ns_devfs_enable="YES"
    jail_mail_hostname="mail.example.org"
    jail_mail_ip="192.168.3.18"
    jail_mail_rootdir="/usr/home/j/mail"
    jail_mail_devfs_enable="YES"
    jail_www_hostname="www.example.org"
    jail_www_ip="62.123.43.14"
    jail_www_rootdir="/usr/home/j/www"
    jail_www_devfs_enable="YES"

    应把jail_name_rootdir 变量设置成 /usr/home 而不是 /home 的原因是 FreeBSD上的/home目录时指向/usr/home 的一个符号连接。而jail_name_rootdir变量必须是一个 包含符号连接的路径,否则 jail 将拒绝启动。

  3. 为每个 jail 创建所需的只读文件系统挂接点:

    # mkdir /home/j/ns /home/j/mail /home/j/www
  4. 在 jail 中安装可读写的模板。 注意您需要使用 sysutils/cpdup, 它能够帮助您确保每个目录都是正确地复制的:

    # mkdir /home/js
    # cpdup /home/j/skel /home/js/ns
    # cpdup /home/j/skel /home/js/mail
    # cpdup /home/j/skel /home/js/www
  5. 在这个阶段,已经创建好并准备运行。首先,为每个 Jail 挂载所需的文件系统,然后启动它们:

    # mount -a
    # service jail start

现在 jail 应该就启动起来了。 要检查它们是否运行正常,可以使用 jls(8) 命令。 它的输出应该类似这样:

# jls
   JID  IP Address      Hostname                      Path
     3  192.168.3.17    ns.example.org                /home/j/ns
     2  192.168.3.18    mail.example.org              /home/j/mail
     1  62.123.43.14    www.example.org               /home/j/www

这时, 就可以登入 jail 并增加用户和配置服务了。 JID 列给出了正在运行的 jail 的标识编号。 您可以使用下面的命令来在 JID 编号为 3 的 jail 中执行管理任务:

# jexec 3 tcsh

14.5.3. 升级

有时, 由于安全问题, 或新增功能有用, 会希望将系统升级到一个新版本的 FreeBSD。 这种安装方式的设计使得升级现有 jail 变得很容易。 另外, 它也能最大限度地减小停机时间, 因为 jail 只在最后时刻才需要关闭。 另外, 它也提供了简单的回退到先前版本的方法。

  1. 第一步是按通常的方法升级主机的系统。 接着, 在 /home/j/mroot2 中建立一个新的临时模板。

    # mkdir /home/j/mroot2
    # cd /usr/src
    # make installworld DESTDIR=/home/j/mroot2
    # cd /home/j/mroot2
    # cpdup /usr/src usr/src
    # mkdir s

    在运行 installworld 时会创建一些不需要的目录, 应将它们删除:

    # chflags -R 0 var
    # rm -R etc var root usr/local tmp
  2. 重建到主系统中的可读写符号连接:

    # ln -s s/etc etc
    # ln -s s/root root
    # ln -s s/home home
    # ln -s ../s/usr-local usr/local
    # ln -s ../s/usr-X11R6 usr/X11R6
    # ln -s s/tmp tmp
    # ln -s s/var var
  3. 现在是时候关闭 jail 了:

    # service jail stop
  4. 将文件系统从读写状模式换为只读模式(/s):

    # umount /home/j/ns/s
    # umount /home/j/ns
    # umount /home/j/mail/s
    # umount /home/j/mail
    # umount /home/j/www/s
    # umount /home/j/www
  5. 将先前的只读文件系统挪走, 换成新的系统。 这样做也同时保留了先前系统的备份, 从而可以在出现问题时从中恢复。 这里我们根据新系统的创建时间来命名。 此外我们把先前的 FreeBSD Ports 套件直接移动到新的文件系统中, 以节省磁盘空间和 inode:

    # cd /home/j
    # mv mroot mroot.20060601
    # mv mroot2 mroot
    # mv mroot.20060601/usr/ports mroot/usr
  6. 现在新的只读模板就可以用了, 剩下的事情是重新挂接文件系统并启动 jails:

    # mount -a
    # service jail start

最后用 jls(8) 检查 jail 启动是否正常。 不要忘记在 jail 中运行 mergemaster。 配置文件和 rc.d 脚本在升级时应进行更新。

本文档和其它文档可从这里下载: ftp://ftp.FreeBSD.org/pub/FreeBSD/doc/.

如果对于FreeBSD有问题,请先阅读 文档,如不能解决再联系 <questions@FreeBSD.org>.

关于本文档的问题请发信联系 <doc@FreeBSD.org>.