Deployment Model 2.0

This page describes a design for a new on-disk layout. The core problem with the previous model was that kernel/initramfs updates were non-atomic, and in fact the code didn't even support upgrading kernels at all. This layout depends on the BootLoaderSpec.

An "OS tree"

The eponymous "OSTree" tool deploys operating system trees, or "OS/" to distinguish them from ostree itself. These are immutable, and should have the following filesystem layout:

 /boot/vmlinuz-VERSION-CHECKSUM
 /boot/initramfs-VERSION-CHECKSUM
 /usr        # contains all of the OS
 /usr/etc    # Default contents of /etc

It's fully permitted for operating systems to contain other readonly toplevel content, such as a symlink bin -> usr/bin. However note for other FHS components, because OSTree only preserves the contents of /etc on upgrades, it's strongly recommended to make the following symbolic links:

  /usr/local -> ../../var/usrlocal
  /opt -> var/opt
  /srv -> var/srv
  /mnt -> var/mnt
  /media -> run/media

Furthermore, your OS initialization process should ensure that the symlink target directories exist, because OSTree will not precreate them. If using systemd, add them to systemd-tmpfiles.

The deployment list

The core concept in the 2.0 model is the "deployment list", which manifests to the user as a list of entries in the bootloader. This list is ordered, and the first entry is the default. Something like:

 $ ostree admin status
 deploy0:   da39a538021.1  (tag:current, ref:x86_64-runtime, running)
 deploy1:   1ca9d348d67.0  (tag:previous, ref:x86_64-devel)
 deploy2:   da39a538021.0  (ref:x86_64-runtime)
 deploy3:   587e2144109.0  (ref:x86_64-runtime)
 $ 

An OS

In the OSTree model, an OS is represented by /ostree/deploy/osname. Inside each osdir is a copy of var, shared by all deployments.

A deployment

A deployment is an OS/, plus a writable copy of /etc. It has the following attributes:

  • index: Integer offset of this deployment in the global deployment list
  • ref: The originating OSTree ref (e.g. gnome-ostree/buildmaster/x86_64-runtime)
  • csum: Exact OSTree checksum, derived from ref
  • deployserial: How many times this ${csum} appears in the deployment list
  • bootcsum: Checksum of kernel+initramfs from csum
  • bootserial: An integer assigned to this tree per its bootcsum

Global variables

  • bootversion: Either 0 or 1, as expressed by /boot/loader.${bootversion}.

Implementation

We have two deployment lists: OLD and NEW. We need to preserve any entries from OLD in NEW. Let's take the case above:

  • deploy0: da39a538021.1 (tag:current, ref:x86_64-runtime, bootcsum:c90d227a60, running)
  • deploy1: 1ca9d348d67.0 (tag:previous, ref:x86_64-devel, bootcsum:648a9123b4)
  • deploy2: da39a538021.0 (ref:x86_64-runtime, bootcsum:c90d227a60)
  • deploy3: 587e2144109.0 (ref:x86_64-runtime, bootcsum:c90d227a60)

And we want to generate the new deployment list:

  • deploy0: afd96ee01b1.0 (tag:current, ref:x86_64-runtime, bootcsum:c90d227a60)
  • deploy1: da39a538021.1 (tag:current, ref:x86_64-runtime, bootcsum:c90d227a60, running)
  • deploy2: 1ca9d348d67.0 (tag:previous, ref:x86_64-devel, bootcsum:648a9123b4)
  • deploy3: da39a538021.0 (ref:x86_64-runtime, bootcsum:c90d227a60)
  • deploy4: 587e2144109.0 (ref:x86_64-runtime, bootcsum:c90d227a60)

Filesystem Layout

If we need to change kernel configuration, we do so atomically via the "swapped directory pattern". In this case, that's /boot/loader.{0,1}, and /boot/loader is a symbolic link to one or the other.

A tree deployment is represented by these directories:

 /boot/ostree-${osname}-${bootcsum}
 /boot/loader.${bootversion}/entries/ostree-${osname}-${bootcsum}-${treebootserial}.conf

The first contains kernel+initramfs files shared by that ${osname}-${bootcsum}, and the second contains kernel configuration for booting a specific OS/ with that ${bootcsum}.

 /ostree/boot.${bootversion}.${subbootversion}/${osname}/${bootcsum}/${treebootserial}

Where ${treebootserial} is a symbolic link 0..N, pointing to ../../../deploy/${osname}/${treecsum}.${treeserial}. This extra layer of indirection allows upgrades that don't affect the kernel to avoid touching /boot. To implement this, /ostree/boot.${bootversion} is a symbolic link pointing to the current ${subbootversion}.

And these directories:

 /ostree/deploy/${osname}/${treecsum}.${serial}

This is the core directory for a deployment; contains "OS/", plus a copy of /etc. It's used as a chroot target.

Booting

The kernel commandline contains:

 ostree=/ostree/boot.${bootversion}/${osname}/${bootcsum}/${treeserial}

Inside the initramfs (dracut), the ostree-prepare-root tool parses this, and sets up /sysroot. Note that here we're following the symlink /ostree/boot.${bootversion} to /ostree/boot.${bootversion}.${subbootversion}.

Redeploying

We have two deployment lists, call them CURRENT and NEW. First, determine if we need to generate a new bootloader configuration; this will be true if NEW adds or removes "bootcsum" entries.

New bootloader configuration

Let's assume the current bootversion is 0; the new bootversion will therefore be 1. Our new subbootversion will be 1.0.

For each deployment (osname, treecsum, bootcsum, serial=0):

  • Check out /ostree/deploy/${osname}/deploy/${treecsum}.${serial}
  • Rename usr/etc from that tree to /etc, and merge changes from the current /etc into it.
  • Look in its boot/ directory, and copy the kernel/initramfs to /boot/${osname}-${bootcsum} (unless the directory already exists).
  • Allocate an unused treebootserial; and create /ostree/boot.1.0/${osname}/${bootcsum}/${treebootserial} pointing to the deploydir.
  • Create /boot/loader.1/entries/ostree-${osname}-${treecsum}.conf

fsync() all the above, all files and directories

Atomically swap /boot/loader to point to /boot/loader.1

Cleanup

Read value of /boot/loader. If it's 1, then the cleanup value is 0, and vice versa. Assume here that it's 1. Read the value of /ostree/boot.1; assume this is 0; then the cleanup value is 1.

rm -rf /boot/loader.0 /ostree/boot.0.[01] /ostree/boot.1.1

Generate set of deployments following bootloader config and symbolic links in /ostree/boot.1; this is CURRENTSET Generate set of all deployments from /ostree/deploy and /boot/ostree-*; this is DISKSET Delete all full or partial deployment data in DISKSET - CURRENTSET

Reusing existing bootloader configuration

This only applies when we changing the deployment list only for a given ${bootcsum}, AND the new and old number of entries is the same. However it's worth optimizing for this case because the default OSTree configuration is to keep only "current" and "previous", and most updates are not going to change the kernel.

Read the value of /boot/loader; this is ${bootversion}.

Compute KEEPSET, which is the set of deployments in both OLD and NEW. Each of these will have a symbolic link number, referenced by the bootloader entry, that needs to be preserved. for each (treecsum, bootsum, serial, boottag) in KEEPSET:

  • ln -s ../deploy.${bootversion}.0/${treecsum} /ostree/deploy/${osname}/deploy/${treecsum}.${treeserial}

Compute NEWSET, which is the set of deployments in only in NEW. Assign each entry in NEWSET a boottag that has not yet been used. The normal case is removing previous, so that would be boottag 1.

for each deployment (treecsum, bootsum, serial) in NEWSET:

  • Check out /ostree/deploy/${osname}/deploy/${treecsum}.0, then create its /etc directory, merging from the running tree.

Projects/OSTree/DeploymentModel2 (last edited 2013-11-22 20:27:22 by walters)