I have been using ezjail since at least 2008 (see earlier blog post). A few years ago, I started deploying iocage on new servers. About three months ago, I starting converting systems from ezjail to iocage.
When I converted my first system, I found that the existing documentation for conversion was incomplete. Specifically, symlinks were a problem.
I raised an issue and wrote a better script which I have since used on a few hosts. Today, as I convert my last ezjail host, I will outline what I’m doing in a blog post so it is easier to follow.
HEADS UP:
- This was run on a FreeBSD 11.2 system.
- The thin_to_think script requires rsync to be installed.
- To automatically start your iocage jails, run this command to add the approprate setting to /etc/rc.conf:
sudo sysrc iocage_enable="YES"
- When you are done with ezjail, remove that setting from /etc/rc.conf with this command:
sudo sysrc -X ezjail_enable
My main examples are with simple jail names, no periods, no hyphens. If you have more complex name, please refer to the Other things I had to do for other jails.
Why go thick?
I want to go to thick jails and away from thin jails for several reasons. The biggest of which is: I don’t want to upgrade all of my jails at once. I want to be able to pick and choose when some jails are upgraded. Going thick also allows me to run jails which are purposely on different versions from the host, should I want do so.
Getting iocage ready
Here is what I did before running my scripts.
$ sudo iocage fetch ******************************************************************************** fdescfs(5) is not mounted, performance may suffer. Please run: mount -t fdescfs null /dev/fd You can also permanently mount it in /etc/fstab with the following entry: fdescfs /dev/fd fdescfs rw 0 0 ******************************************************************************** Creating system/iocage Creating system/iocage/download Creating system/iocage/images Creating system/iocage/jails Creating system/iocage/log Creating system/iocage/releases Creating system/iocage/templates Default configuration missing, creating one [0] 11.2-RELEASE [1] 12.0-RELEASE Type the number of the desired RELEASE Press [Enter] to fetch the default selection: (11.2-RELEASE) Type EXIT to quit: 0
I selected 0 because this host is running 11.2-RELEASE.
Fetching: 11.2-RELEASE Downloading: MANIFEST [####################] 100% Downloading: base.txz [####################] 100% Downloading: lib32.txz [####################] 100% Downloading: src.txz [####################] 100% Extracting: base.txz... Extracting: lib32.txz... Extracting: src.txz... * Updating 11.2-RELEASE to the latest patch level... Looking up update.FreeBSD.org mirrors... 3 mirrors found. Fetching public key from update2.freebsd.org... done. Fetching metadata signature for 11.2-RELEASE from update2.freebsd.org... done. Fetching metadata index... done. Fetching 2 metadata files... done. Inspecting system... done. Preparing to download files... done. Fetching 137 patches.....10....20....30....40....50....60....70....80....90....100....110....120....130... done. Applying patches... done. Fetching 2 files... . done. The following files will be added as part of updating to 11.2-RELEASE-p9: ...
The fetch process continued until:
/usr/src/usr.sbin/bhyve/fwctl.c WARNING: FreeBSD 11.2-RELEASE is approaching its End-of-Life date. It is strongly recommended that you upgrade to a newer release within the next 2 months. Installing updates... done.
This is OK, no big deal I am moving to iocage before upgrading this host to FreeBSD 12.0 at a later date.
Here is what was created:
$ zfs list -r system/iocage system/iocage 1.27G 172G 100K /system/iocage system/iocage/download 271M 172G 88K /system/iocage/download system/iocage/download/11.2-RELEASE 270M 172G 270M /system/iocage/download/11.2-RELEASE system/iocage/images 88K 172G 88K /system/iocage/images system/iocage/jails 88K 172G 88K /system/iocage/jails system/iocage/log 88K 172G 88K /system/iocage/log system/iocage/releases 1.01G 172G 88K /system/iocage/releases system/iocage/releases/11.2-RELEASE 1.01G 172G 88K /system/iocage/releases/11.2-RELEASE system/iocage/releases/11.2-RELEASE/root 1.01G 172G 1.01G /system/iocage/releases/11.2-RELEASE/root system/iocage/templates 88K 172G 88K /system/iocage/templates
I don’t want to use that mountpoint, I want /iocage instead:
$ sudo zfs set mountpoint=/iocage system/iocage
And because I know that mountpoint is not otherwise in use:
$ sudo rmdir /system
Now we have this:
$ zfs list -r system/iocage NAME USED AVAIL REFER MOUNTPOINT system/iocage 1.27G 172G 100K /iocage system/iocage/download 271M 172G 88K /iocage/download system/iocage/download/11.2-RELEASE 270M 172G 270M /iocage/download/11.2-RELEASE system/iocage/images 88K 172G 88K /iocage/images system/iocage/jails 88K 172G 88K /iocage/jails system/iocage/log 88K 172G 88K /iocage/log system/iocage/releases 1.01G 172G 88K /iocage/releases system/iocage/releases/11.2-RELEASE 1.01G 172G 88K /iocage/releases/11.2-RELEASE system/iocage/releases/11.2-RELEASE/root 1.01G 172G 1.01G /iocage/releases/11.2-RELEASE/root system/iocage/templates 88K 172G 88K /iocage/templates
Repeat for each jail
The following steps are repeated for each jail.
Define what jail we are converting
This is the first part of the example1 script.
export JAIL=ns2
All the folowing steps depend upon ${JAIL}.
Create the new thick jail
I created each new jail like this:
sudo iocage create -r 11.2-RELEASE --thickjail --name ${JAIL}
Now we have this:
$ zfs list -r system/iocage/jails/ns2 NAME USED AVAIL REFER MOUNTPOINT system/iocage/jails/ns2 292K 172G 92K /iocage/jails/ns2 system/iocage/jails/ns2/root 200K 172G 1.01G /iocage/jails/ns2/root
Looking inside that root directory, we see what looks like an entirely vanilla FreeBSD filesystem.
$ ls -l /iocage/jails/ns2/root total 77 -r--r--r-- 1 root wheel 6197 Jun 22 2018 COPYRIGHT drwxr-xr-x 2 root wheel 47 Apr 7 14:25 bin drwxr-xr-x 9 root wheel 52 Apr 7 14:25 boot dr-xr-xr-x 2 root wheel 2 Jun 22 2018 dev drwxr-xr-x 27 root wheel 105 Apr 7 15:13 etc drwxr-xr-x 4 root wheel 56 Apr 7 14:25 lib drwxr-xr-x 3 root wheel 5 Jun 22 2018 libexec drwxr-xr-x 2 root wheel 2 Jun 22 2018 media drwxr-xr-x 2 root wheel 2 Jun 22 2018 mnt drwxr-xr-x 2 root wheel 2 Jun 22 2018 net dr-xr-xr-x 2 root wheel 2 Jun 22 2018 proc drwxr-xr-x 2 root wheel 148 Apr 7 14:25 rescue drwxr-xr-x 2 root wheel 6 Jun 22 2018 root drwxr-xr-x 2 root wheel 137 Jun 22 2018 sbin lrwxr-xr-x 1 root wheel 11 Apr 7 14:23 sys -> usr/src/sys drwxrwxrwt 2 root wheel 2 Jun 22 2018 tmp drwxr-xr-x 14 root wheel 14 Jun 22 2018 usr drwxr-xr-x 24 root wheel 24 Jun 22 2018 var
Grab information from the old jail and try assigning it into the new jail
I took the example1 script and put it into a note application, and adjusted it for my needs. All I did was set the JAIL variable. Then I copied and pasted part of it into a shell (the part down to and including grep devfs_rule.
NOTE: the example2-hypens.txt script is actually more flexible. I suggest using it instead. Set both JAIL and JAIL_FIXED to the same value until you hit trouble, then see the Other things I had to do for other jails.
Here is what I ran and I have added my comments along the way:
[dan@zuul:~] $ grep USERLAND_VERSION= /usr/jails/basejail/bin/freebsd-version USERLAND_VERSION="11.2-RELEASE-p8" [dan@zuul:~] $
Yes, that’s expected, good. Nothing to do there.
[dan@zuul:~] $ grep ip= /usr/local/etc/ezjail/${JAIL} export jail_ns2_ip="10.80.0.85,162.208.116.85,2610:1c1:0:4:e6aa:8980:e324:a3e9" [dan@zuul:~] $ grep ip= /usr/local/etc/ezjail/${JAIL} | cut -f 2 -d '"' 10.80.0.85,162.208.116.85,2610:1c1:0:4:e6aa:8980:e324:a3e9 [dan@zuul:~] $ [dan@zuul:~] $ # NOTE this is not useful for IPv6 addresses for address dynamically assigned such as em0|10.0.0.1 [dan@zuul:~] $ IPV4=`grep ip= /usr/local/etc/ezjail/${JAIL} | cut -f 2 -d '"'` [dan@zuul:~] $ [dan@zuul:~] $ sudo iocage set ip4_addr="${IPV4}" ${JAIL} Please provide a valid ip: Expected 4 octets in '2610:1c1:0:4:e6aa:8980:e324:a3e9'
Yes, I will have to manually assign IP addresses because I didn’t want to get too fancy in this script.
[dan@zuul:~] $ [dan@zuul:~] $ grep hostname /usr/local/etc/ezjail/${JAIL} export jail_ns2_hostname="ns2.unixathome.org" [dan@zuul:~] $ [dan@zuul:~] $ JAIL_HOSTNAME=`grep hostname /usr/local/etc/ezjail/${JAIL} | cut -f 2 -d '"'` [dan@zuul:~] $ [dan@zuul:~] $ sudo iocage set host_hostname=${JAIL_HOSTNAME} ${JAIL} Property: host_hostname has been updated to ns2.unixathome.org
Good, this is automated and what i want.
[dan@zuul:~] $ [dan@zuul:~] $ cat /etc/fstab.${JAIL} /usr/jails/basejail /usr/jails/ns2/basejail nullfs ro 0 0
Nothing special there, no action required.
[dan@zuul:~] $ [dan@zuul:~] $ grep devfs_rule /usr/local/etc/ezjail/${JAIL} export jail_ns2_devfs_ruleset="devfsrules_jail" [dan@zuul:~] $
Good, simple, no action required.
Action required for that jail
Based on the output of the script, I did the following steps manually:
- Set the IP4 IP address[es]:
[dan@zuul:~] $ sudo iocage set ip4_addr="10.80.0.85,162.208.116.85" ${JAIL} Property: ip4_addr has been updated to 10.80.0.85,162.208.116.85
- Set the IPv6 IP address[es]:
[dan@zuul:~] $ sudo iocage set ip6_addr="2610:1c1:0:4:e6aa:8980:e324:a3e9" ${JAIL} Property: ip6_addr has been updated to 2610:1c1:0:4:e6aa:8980:e324:a3e9
That’s it.
Other things I had to do for other jails
This section shows some of the special things I had to do for certain jails.
More complex jail names
When I went to convert svn.pgcon.org I hit this snag:
[dan@zuul:~] $ export JAIL=svn.pgcon.org [dan@zuul:~] $ grep USERLAND_VERSION= /usr/jails/basejail/bin/freebsd-version USERLAND_VERSION="11.2-RELEASE-p8" [dan@zuul:~] $ [dan@zuul:~] $ grep ip= /usr/local/etc/ezjail/${JAIL} grep: /usr/local/etc/ezjail/svn.pgcon.org: No such file or directory
The errors continued from there. It was because of special case like this that I created example2-hypens.txt.
When I used that example, I set these values:
export JAIL=svn.pgcon.org export JAIL_FIXED=svn_pgcon_org
With this I got:
[dan@zuul:~] $ grep USERLAND_VERSION= /usr/jails/basejail/bin/freebsd-version USERLAND_VERSION="11.2-RELEASE-p8" [dan@zuul:~] $ [dan@zuul:~] $ grep ip= /usr/local/etc/ezjail/${JAIL_FIXED} export jail_svn_pgcon_org_ip="10.80.0.90,162.208.116.90,2610:1c1:0:4::90" [dan@zuul:~] $ grep ip= /usr/local/etc/ezjail/${JAIL_FIXED} | cut -f 2 -d '"' 10.80.0.90,162.208.116.90,2610:1c1:0:4::90 [dan@zuul:~] $ [dan@zuul:~] $ IPV4=`grep ip= /usr/local/etc/ezjail/${JAIL_FIXED} | cut -f 2 -d '"'` [dan@zuul:~] $ [dan@zuul:~] $ sudo iocage set ip4_addr="${IPV4}" ${JAIL} Please provide a valid ip: Expected 4 octets in '2610:1c1:0:4::90' [dan@zuul:~] $ [dan@zuul:~] $ grep hostname /usr/local/etc/ezjail/${JAIL_FIXED} export jail_svn_pgcon_org_hostname="svn.pgcon.org" [dan@zuul:~] $ [dan@zuul:~] $ JAIL_HOSTNAME=`grep hostname /usr/local/etc/ezjail/${JAIL_FIXED} | cut -f 2 -d '"'` [dan@zuul:~] $ [dan@zuul:~] $ sudo iocage set host_hostname=${JAIL_HOSTNAME} ${JAIL} Property: host_hostname has been updated to svn.pgcon.org [dan@zuul:~] $ [dan@zuul:~] $ cat /etc/fstab.${JAIL_FIXED} /usr/jails/basejail /usr/jails/svn.pgcon.org/basejail nullfs ro 0 0 [dan@zuul:~] $ [dan@zuul:~] $ grep devfs_rule /usr/local/etc/ezjail/${JAIL_FIXED} export jail_svn_pgcon_org_devfs_ruleset="devfsrules_jail"
Which is much easier to deal with. Now I’m back in the same situation as before.
Simple jail name, more complex hostname
Even with this, sometimes the jail short name does not match the hostname and you get this situation:
[dan@zuul:~] $ sudo ~/thin_to_thick.sh /usr/jails/newjail /usr/jails/${JAIL}/ /iocage/jails/${JAIL}/root The destination jail ('/iocage/jails/FOO/root') must exist and must be a directory [dan@zuul:~] $ sudo ~/thin_to_thick.sh /usr/jails/newjail /usr/jails/${JAIL}/ /iocage/jails/FOO.example.org/root/
In this case, the required iocage pathname will be obvious if you check it.
Copy over the jail
This step shuts down the old jail and copies over the files.
[dan@zuul:~] $ sudo ezjail-admin stop ${JAIL} Stopping jails: ns2.unixathome.org. $ sudo ~/thin_to_thick.sh /usr/jails/newjail /usr/jails/${JAIL}/ /iocage/jails/${JAIL}/root /basejail exists, and this will interfere with the rsync process $
Oh, that stuff is there and it will interfere with the rsync stuff. The script makes use of rsync and having that /basejail there will mess with stuff. Those directories do not exist on my other hosts, but it did on this one. Simple solution:
[dan@zuul:/] $ sudo mv basejail DELETEME.basejail [dan@zuul:/] $ sudo mv basejail2 DELETEME.basejail2 [dan@zuul:/] $
Back to the copy process:
[dan@zuul:~] $ sudo ~/thin_to_thick.sh /usr/jails/newjail /usr/jails/${JAIL}/ /iocage/jails/${JAIL}/root finding symlinks in new jail: /usr/jails/newjail original NEW JAIL symlinks, full paths /usr/jails/newjail/bin /usr/jails/newjail/boot /usr/jails/newjail/etc/aliases /usr/jails/newjail/etc/rmt /usr/jails/newjail/etc/termcap /usr/jails/newjail/etc/unbound /usr/jails/newjail/lib /usr/jails/newjail/libexec /usr/jails/newjail/rescue /usr/jails/newjail/sbin /usr/jails/newjail/sys /usr/jails/newjail/usr/bin /usr/jails/newjail/usr/include /usr/jails/newjail/usr/lib /usr/jails/newjail/usr/lib32 /usr/jails/newjail/usr/libdata /usr/jails/newjail/usr/libexec /usr/jails/newjail/usr/ports /usr/jails/newjail/usr/sbin /usr/jails/newjail/usr/share /usr/jails/newjail/usr/src /usr/jails/newjail/usr/tests/lib/libc/tls/libh_tls_dynamic.so /usr/jails/newjail/usr/tests/lib/libthr/dlopen/h_pthread_dlopen.so /usr/jails/newjail/usr/tests/libexec/rtld-elf/libpythagoras.so /usr/jails/newjail/var/db/etcupdate/current/etc/aliases /usr/jails/newjail/var/db/etcupdate/current/etc/rmt /usr/jails/newjail/var/db/etcupdate/current/etc/termcap /usr/jails/newjail/var/db/etcupdate/current/etc/unbound /usr/jails/newjail/var/db/etcupdate/current/sys /usr/jails/newjail/var/db/etcupdate/current/usr/share/man/en.ISO8859-1/man1 /usr/jails/newjail/var/db/etcupdate/current/usr/share/man/en.ISO8859-1/man2 /usr/jails/newjail/var/db/etcupdate/current/usr/share/man/en.ISO8859-1/man3 /usr/jails/newjail/var/db/etcupdate/current/usr/share/man/en.ISO8859-1/man4 /usr/jails/newjail/var/db/etcupdate/current/usr/share/man/en.ISO8859-1/man5 /usr/jails/newjail/var/db/etcupdate/current/usr/share/man/en.ISO8859-1/man6 /usr/jails/newjail/var/db/etcupdate/current/usr/share/man/en.ISO8859-1/man7 /usr/jails/newjail/var/db/etcupdate/current/usr/share/man/en.ISO8859-1/man8 /usr/jails/newjail/var/db/etcupdate/current/usr/share/man/en.ISO8859-1/man9 /usr/jails/newjail/var/db/etcupdate/current/usr/share/man/en.ISO8859-15 /usr/jails/newjail/var/db/etcupdate/current/usr/share/man/en.UTF-8/man1 /usr/jails/newjail/var/db/etcupdate/current/usr/share/man/en.UTF-8/man2 /usr/jails/newjail/var/db/etcupdate/current/usr/share/man/en.UTF-8/man3 /usr/jails/newjail/var/db/etcupdate/current/usr/share/man/en.UTF-8/man4 /usr/jails/newjail/var/db/etcupdate/current/usr/share/man/en.UTF-8/man5 /usr/jails/newjail/var/db/etcupdate/current/usr/share/man/en.UTF-8/man6 /usr/jails/newjail/var/db/etcupdate/current/usr/share/man/en.UTF-8/man7 /usr/jails/newjail/var/db/etcupdate/current/usr/share/man/en.UTF-8/man8 /usr/jails/newjail/var/db/etcupdate/current/usr/share/man/en.UTF-8/man9 /usr/jails/newjail/var/db/etcupdate/current/usr/share/nls/POSIX /usr/jails/newjail/var/db/etcupdate/current/usr/share/nls/en_US.US-ASCII /usr/jails/newjail/var/db/etcupdate/current/usr/share/openssl/man/en.ISO8859-1/man1 /usr/jails/newjail/var/db/etcupdate/current/usr/share/openssl/man/en.ISO8859-1/man3 /usr/jails/newjail/var/db/etcupdate/current/usr/share/openssl/man/en.ISO8859-15 /usr/jails/newjail/var/yp/Makefile modified NEW JAIL symlinks, relative paths /bin /boot /etc/aliases /etc/rmt /etc/termcap /etc/unbound /lib /libexec /rescue /sbin /sys /usr/bin /usr/include /usr/lib /usr/lib32 /usr/libdata /usr/libexec /usr/ports /usr/sbin /usr/share /usr/src /usr/tests/lib/libc/tls/libh_tls_dynamic.so /usr/tests/lib/libthr/dlopen/h_pthread_dlopen.so /usr/tests/libexec/rtld-elf/libpythagoras.so /var/db/etcupdate/current/etc/aliases /var/db/etcupdate/current/etc/rmt /var/db/etcupdate/current/etc/termcap /var/db/etcupdate/current/etc/unbound /var/db/etcupdate/current/sys /var/db/etcupdate/current/usr/share/man/en.ISO8859-1/man1 /var/db/etcupdate/current/usr/share/man/en.ISO8859-1/man2 /var/db/etcupdate/current/usr/share/man/en.ISO8859-1/man3 /var/db/etcupdate/current/usr/share/man/en.ISO8859-1/man4 /var/db/etcupdate/current/usr/share/man/en.ISO8859-1/man5 /var/db/etcupdate/current/usr/share/man/en.ISO8859-1/man6 /var/db/etcupdate/current/usr/share/man/en.ISO8859-1/man7 /var/db/etcupdate/current/usr/share/man/en.ISO8859-1/man8 /var/db/etcupdate/current/usr/share/man/en.ISO8859-1/man9 /var/db/etcupdate/current/usr/share/man/en.ISO8859-15 /var/db/etcupdate/current/usr/share/man/en.UTF-8/man1 /var/db/etcupdate/current/usr/share/man/en.UTF-8/man2 /var/db/etcupdate/current/usr/share/man/en.UTF-8/man3 /var/db/etcupdate/current/usr/share/man/en.UTF-8/man4 /var/db/etcupdate/current/usr/share/man/en.UTF-8/man5 /var/db/etcupdate/current/usr/share/man/en.UTF-8/man6 /var/db/etcupdate/current/usr/share/man/en.UTF-8/man7 /var/db/etcupdate/current/usr/share/man/en.UTF-8/man8 /var/db/etcupdate/current/usr/share/man/en.UTF-8/man9 /var/db/etcupdate/current/usr/share/nls/POSIX /var/db/etcupdate/current/usr/share/nls/en_US.US-ASCII /var/db/etcupdate/current/usr/share/openssl/man/en.ISO8859-1/man1 /var/db/etcupdate/current/usr/share/openssl/man/en.ISO8859-1/man3 /var/db/etcupdate/current/usr/share/openssl/man/en.ISO8859-15 /var/yp/Makefile ...
Insert long pause here. This is the bulk of the copying, the time will vary according to the size of the jail.
It took about 5 minutes and then I was back at the prompt.
Start the new jail
From the example script:
$ sudo iocage start ${JAIL} * Starting ns2 + Started OK + Using devfs_ruleset: 5 + Starting services OK + Executing poststart OK
Checking all is well
At this point, I run two checks:
- ssh – if I can ssh in, that’s a good sign.
- nagios – if nagios gives the all green, I’m good to proceed.
Once everything is good, I proceed to the next step.
But what could go wrong?
Perhaps you missed a mount point, or an IP address. Check them all.
Disable the old jail, enable the new jail
This will prevent the old jail from starting and make sure that the new one starts. You don’t want both running.
$ sudo ezjail-admin config -r norun ${JAIL} $ sudo iocage set boot=on ${JAIL} Property: boot has been updated to on
Until all jails are completed
The above steps are performed for each jail.
Current status
As I type this, one jail is being copied, and I have seven jails remaining on ezjail:
- zuul-pg01 – production PostgreSQL 11.2 database server for this jail server
- bsdcan.org – I want to be particular about when I migrate this one: registration for BSDCan 2019 has just opened
- pgcon.org – same criteria as bsdcan.org
I want to choose the times for those transitions careful. With registration freshly open, I’d like to do it at a quiet time.