Wednesday, April 24, 2013

The /export file system

Beyond historical facts around /export, I'm concerned with ZFS issues here.
I'll start this post referring to the global zone, later I hope to touch non-global zones.
By default, ZFS creates /export below rpool as rpool/export.
rpool/export in turn has the following hierarchy:
  • rpool/export/home
  • rpool/export/home/<user>

The problem is:

I don't like this default, because I prefer a maximal decoupling from rpool.
It's typically a good idea for NFS servers to set /export on a larger device.

NOTE
I haven't heard about any issues by deviating from the default.
Don't forget to update the automounter info as well, as needed.
That may typically include /etc/auto_home, NIS or LDAP.
Accomplishing the mission:

Enter single-user state (SINGLE USER MODE) by booting with the -s option.
Note that init S or s isn't enough to umount eventually busy file systems.
Enter single-user credentials (usually root or preferably a RABC configured login).















This will precisely let us play with rpool/export without any hassles.
rpool/VARSHARE is a new Solaris 11.1 feature to save space in boot environments.
<user> is an user typically configured with access to the root role.

# zfs list -r -d 3 -o name,mountpoint,mounted rpool
NAME                         MOUNTPOINT         MOUNTED
rpool                        /rpool                  no
rpool/ROOT                   legacy                  no
rpool/ROOT/solaris           /                      yes
rpool/ROOT/solaris-bk        /                       no
rpool/ROOT/solaris-bk/var    /var                    no
rpool/ROOT/solaris/var       /var                   yes
rpool/VARSHARE               /var/share             yes
rpool/dump                   -                        -

rpool/export                 /export                 no
rpool/export/home            /export/home            no
rpool/export/home/<user>     /export/home/<user>     no
rpool/swap                   -                        -
 
 
Create and configure a new pool and name it export.
You may decide to enable compression, encryption, deduplication, and so on...
I'll omit many details on this step and assume the existence of such a new pool.
(# zpool create -O compression=on -O dedup=on export <device>)
 
For each hierarchy within the current /export subtree (for instance home):

Recursively snapshot it: 

# zfs snapshot -r rpool/export/home@migrate

Replicate it via send/receive to the new pool:

# zfs send -R rpool/export/home@migrate \
| zfs recv -d -x mountpoint export

Delete it or at least disable it from automatically mounting:

# zfs set -r canmount=noauto rpool/export

Clean up snapshots used in the migration:

# zfs destroy -r rpool/export/home@migrate
# zfs destroy -r export/home@migrate
   
NOTE
The above snapshot-releated commands could be simplified:
(the -F option overwrites anything in the new export pool, "empty" anyway)

# zfs snapshot -r rpool/export@migrate
# zfs send -R rpol/export@migrate |zfs recv -F export
# zfs destroy -r rpool/export
# zfs destroy -r export@migrate
Finally, our result is as follows:

# zfs list -r -o name,mountpoint,mounted export
NAME                         MOUNTPOINT         MOUNTED
export                       /export                 no
export/home                  /export/home            no
export/home/<user>           /export/home/<user>     no

 



For the non-global zones things vary depending on the system version.
The /export is only present by default from Solaris 11 upwards.
In fact on newer systems, the default file systems are:

# zfs list
NAME                      USED AVAIL REFER MOUNTPOINT
rpool                     446M  252G   31K /rpool
rpool/ROOT                446M  252G   31K legacy
rpool/ROOT/solaris        446M  252G  414M /
rpool/ROOT/solaris/var   29.7M  252G 29.0M /var
rpool/VARSHARE             39K  252G   39K /var/share
rpool/export             96.5K  252G   32K /export
rpool/export/home        64.5K  252G   32K /export/home
rpool/export/home/<user> 32.5K  252G 32.5K /export/home/
<user>

 
This is the view from within a non-global zone <ngz>.
That's quite cool as it closely resembles a physical system.
As such, rpool/swap and rpool/dump aren't present, of course.

Assume a dedicated pool zone for zones.
From the global zone perspective, the non-global zone file system hierarchy is:

# zfs list -r -o name,mountpoint zone
NAME                                  MOUNTPOINT
zone                                  /zone
zone/<ngz>                            /zone/<ngz>
zone/<ngz>/rpool                      /rpool
zone/<ngz>/rpool/ROOT                 legacy
zone/<ngz>/rpool/ROOT/solaris         
/zone/<ngz>/root
zone/<ngz>/rpool/ROOT/solaris/var     /
zone/<ngz>/root/var
zone/<ngz>/rpool/VARSHARE             
/zone/<ngz>/root/var/share

zone/<ngz>/rpool/export               /export
zone/<ngz>/rpool/export/home         
/export/home
zone/<ngz>/rpool/export/home/<user>  
/export/home/<user>


Decoupling /export as was proposed for the global zone is just slightly different.

NOTE
It's crucial that the new dataset be independent of the zone's datasets hierarchy. I mean that it shouldn't be a descendand of the zone/<ngz> pool as per the above example, otherwise non-global zone machinery may get broken.
There are many possibilities where to locate the non-global zone's export dataset.
For instance, among others, we could choose one of the following options:
  • Use new individual pools for each non-global zones' export dataset
  • Use a new single pool for all non-global zones' export dataset
  • Use an already existent export pool for the global zone
  • Use an already existent zone pool for the zones 
 
Which approach to follow, depends on a variety of reasons.
If a zone makes heavy usage of /export, then a dedicated pool might be better.
Otherwise, it could certainly share a pool with other such zones.
But note that too many pools may lead to inefficient storage utilization.

For Solaris 11 onwards things are even easier due to dataset aliasing.
Let's see each of these possibilities, in turn, starting by Solaris 11.

SOLARIS 11

Just for example, assume that <ngz> is in zone, that is, zone/<ngz>
The scenario is excatly the one described above for a non-global zone.
Here's an strategy:
  • Create an appropriate dataset to host the new /export hierarchy
  • Assign it to the zone configuration, aliasing it to export.
  • Boot in single-user mode and migrate data
 
# zfs create -o zoned=on -o mountpoint=/export zone/<ngz>-export

# zonecfg -z <ngz>
zonecfg:<ngz>add dataset
zonecfg:<ngz>:dataset> set name=zone/<ngz>-export
zonecfg:<ngz>:dataset> set alias=export
zonecfg:<ngz>:dataset> end
zonecfg:<ngz>> verify
zonecfg:<ngz>> commit
zonecfg:<ngz>> exit

# zoneadm -z <ngz> boot -s

# zlogin -C <ngz>
[NOTICE: Zone booting up with arguments: -s]

SunOS Release 5.11 Version 11.1 64-bit
Copyright (c) 1983, 2012, Oracle ... All rights reserved.
Booting to milestone "milestone/single-user:default".
Hostname: <ngz>
Requesting System Maintenance Mode
SINGLE USER MODE


Enter user name for system maintenance (...): root
Enter root password (...): ************

Apr 29 13:05:07 su: 'su root' succeed for root on /dev/console
root@<ngz>:~# zfs list -r -o name,mountpoint,mounted,canmount
NAME                      MOUNTPOINT          MOUNTED  CANMOUNT
export                    /export                  no        on
rpool                     /rpool                   no        on
rpool/ROOT                legacy                   no       off
rpool/ROOT/solaris        /                       yes    noauto
rpool/ROOT/solaris/var    /var                    yes    noauto
rpool/VARSHARE            /var/share              yes    noauto
rpool/export              /export                  no        on
rpool/export/home         /export/home             no        on
rpool/export/home/<user>  /export/home/<user>      no        on


root@<ngz>:~# zfs snapshot -r rpool/export/home@migrate
 
root@<ngz>:~# zfs send -R rpool/export/home@migrate |
                    zfs recv -e -x mountpoint export

root@<ngz>:~# zfs list -r -o name,mountpoint,mounted,canmount
NAME                      MOUNTPOINT          MOUNTED  CANMOUNT
export                    /export                 yes        on

export/home               /export/home            yes        on
export/home/<user>        /export/home/<user>     yes        on
rpool                     /rpool                   no        on
rpool/ROOT                legacy                   no       off
rpool/ROOT/solaris        /                       yes    noauto
rpool/ROOT/solaris/var    /var                    yes    noauto
rpool/VARSHARE            /var/share              yes    noauto
rpool/export              /export                  no        on
rpool/export/home         /export/home             no        on
rpool/export/home/<user>  /export/home/<user>      no        on

 
root@<ngz>:~# zfs set -r canmount=noauto rpool/export
 
root@<ngz>:~# zfs destroy -r rpool/export/home@migrate
root@<ngz>:~# zfs destroy -r export/home@migrate
 
root@<ngz>:~# reboot
 
LEGACY

In legacy systems, such as Solaris 11 Express, dataset aliasing isn't available.
In this case, I'd propose using an export pool for a more consistent naming.
Most steps are the same so I won't repeat them, showing only the outline.
From the global zone, it would be seen as in the following arrangement:

# zfs list -r -o name,mountpoint export
NAME                       MOUNTPOINT
export                     /export
export/home                /export/home
export/home/<user>         /export/home/<user>
export/<ngz>               /export/<ngz>
export/<ngz>/home          /export/<ngz>/home
export/<ngz>/home/<user>   /export/<ngz>/home/
<user>


# zonecfg -z <ngz> info dataset
dataset:
        name: export/<ngz>

 
From a legacy non-global zone (without aliasing) the arrangement would be seen as:

zlogin -l <user> <ngz>
[Connected to zone '<ngz>' pts/2]


<user>@<ngz>:~# zfs list -o name,mountpoint
NAME                             MOUNTPOINT
export                           /export
export/<ngz>                     /export/<ngz>
export/<ngz>/home                /export/<ngz>/home
export/<ngz>/home/<user>         /export/<ngz>/home/<user>
zone                             /zone
zone/<ngz>                       /zone/<ngz>
zone/<ngz>/ROOT                  legacy
zone/<ngz>/ROOT/zbe              legacy


<user>@<ngz>:~# tail /etc/auto_home
...
#
+auto_home
#
*       localhost:/export/<ngz>/home/&

   
Naturally, this will imply that the user info must be updated accordingly.

# usermod -d /home/<user> <user>