ostree/doc/adapting-existing.xml
2013-08-25 16:12:14 -04:00

261 lines
11 KiB
XML

<?xml version="1.0"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
"http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
<!ENTITY version SYSTEM "../version.xml">
]>
<part id="adapting-existing">
<title>Adapting existing mainstream distributions</title>
<chapter id="layout">
<title>System layout</title>
<para>
First, OSTree encourages systems to implement <ulink
url="http://www.freedesktop.org/wiki/Software/systemd/TheCaseForTheUsrMerge/">UsrMove</ulink>.
This is simply to avoid the need for more bind mounts. By
default OSTree's dracut hook creates a read-only bind mount over
<filename class='directory'>/usr</filename>; you can of course
generate individual bind-mounts for <filename
class='directory'>/bin</filename>, all the <filename
class='directory'>/lib</filename> variants, etc. So it is not
intended to be a hard requirement.
</para>
<para>
Remember, because by default the system is booted into a
<literal>chroot</literal> equivalent, there has to be some way
to refer to the actual physical root filesystem. Therefore,
your operating system tree should contain an empty <filename
class='directory'>/sysroot</filename> directory; at boot time,
OSTree will make this a bind mount to the physical / root
directory. There is precedent for this name in the initramfs
context. You should furthermore make a toplevel symbolic link
<filename class='directory'>/ostree</filename> which points to
<filename class='directory'>/sysroot/ostree</filename>, so that
the OSTree tool at runtime can consistently find the system data
regardless of whether it's operating on a physical root or
inside a deployment.
</para>
<para>
Because OSTree only preserves <filename
class='directory'>/var</filename> across upgrades (each
deployment's chroot directory will be garbage collected
eventually), you will need to choose how to handle other
toplevel writable directories specified by the <ulink
url="http://www.pathname.com/fhs/">Filesystem Hierarchy
Standard</ulink>. Your operating system may of course choose
not to support some of these such as <filename
class='directory'>/usr/local</filename>, but following is the
recommended set:
<itemizedlist>
<listitem>
<para>
<filename class='directory'>/home</filename> to <filename class='directory'>/var/home</filename>
</para>
</listitem>
<listitem>
<para>
<filename class='directory'>/opt</filename> to <filename class='directory'>/var/opt</filename>
</para>
</listitem>
<listitem>
<para>
<filename class='directory'>/srv</filename> to <filename class='directory'>/var/srv</filename>
</para>
</listitem>
<listitem>
<para>
<filename class='directory'>/root</filename> to <filename class='directory'>/var/roothome</filename>
</para>
</listitem>
<listitem>
<para>
<filename class='directory'>/usr/local</filename> to <filename class='directory'>/var/local</filename>
</para>
</listitem>
<listitem>
<para>
<filename class='directory'>/mnt</filename> to <filename class='directory'>/var/mnt</filename>
</para>
</listitem>
<listitem>
<para>
<filename class='directory'>/tmp</filename> to <filename class='directory'>/sysroot/tmp</filename>
</para>
</listitem>
</itemizedlist>
</para>
<para>
Furthermore, since <filename class='directory'>/var</filename>
is empty by default, your operating system will need to
dynamically create the <emphasis>targets</emphasis> of these at
boot. A good way to do this is using
<command>systemd-tmpfiles</command>, if your OS uses systemd.
For example:
</para>
<programlisting>
<![CDATA[
d /var/log/journal 0755 root root -
L /var/home - - - - ../sysroot/home
d /var/opt 0755 root root -
d /var/srv 0755 root root -
d /var/roothome 0700 root root -
d /var/usrlocal 0755 root root -
d /var/usrlocal/bin 0755 root root -
d /var/usrlocal/etc 0755 root root -
d /var/usrlocal/games 0755 root root -
d /var/usrlocal/include 0755 root root -
d /var/usrlocal/lib 0755 root root -
d /var/usrlocal/man 0755 root root -
d /var/usrlocal/sbin 0755 root root -
d /var/usrlocal/share 0755 root root -
d /var/usrlocal/src 0755 root root -
d /var/mnt 0755 root root -
d /run/media 0755 root root -
]]>
</programlisting>
<para>
Particularly note here the double indirection of <filename
class='directory'>/home</filename>. By default, each
deployment will share the global toplevel <filename
class='directory'>/home</filename> directory on the physical
root filesystem. It is then up to higher levels of management
tools to keep <filename>/etc/passwd</filename> or equivalent
synchronized between operating systems.
</para>
<para>
Each deployment can easily be reconfigured to have its own home
directory set simply by making <filename
class='directory'>/var/home</filename> a real directory.
</para>
</chapter>
<chapter id="booting">
<title>Booting and initramfs technology</title>
<para>
OSTree comes with optional dracut+systemd integration code that
parses the <literal>ostree=</literal> kernel command line
argument in the initramfs, and then sets up the read-only bind
mount on <filename class='directory'>/usr</filename>, a bind
mount on the deployment's <filename
class='directory'>/sysroot</filename> to the physical <filename
class='directory'>/</filename>, and then finally uses
<literal>mount(MS_MOVE)</literal> to make the deployment root appear to be the
root filesystem before telling systemd to switch root.
</para>
<para>
If you are not using dracut or systemd, using OSTree should still
be possible, but you will have to write the integration code. Patches
to support other initramfs technologies and init systems, if sufficiently
clean, will likely be accepted upstream.
</para>
<para>
A further specific note regarding <command>sysvinit</command>:
OSTree used to support recording device files such the
<filename>/dev/initctl</filename> FIFO, but no longer does.
It's recommended to just patch your initramfs to create this at
boot.
</para>
</chapter>
<chapter id="lib-passwd">
<title>/lib/passwd</title>
<para>
In order to ship an OS that contains both system users and users
dynamically created on client machines, you will need to choose
a solution for <filename>/etc/passwd</filename>. The core
problem is that if you add a user to the system for a daemon,
the OSTree upgrade process for <filename
class='directory'>/etc</filename> will simply notice that
because <filename>/etc/passwd</filename> differs from the
previous default, it will keep the modified config file, and
your new OS user will not be visible.
</para>
<para>
The solution chosen for the <ulink
url="https://wiki.gnome.org/GnomeOSTree">gnome-ostree</ulink>
operating system is to create <filename>/lib/passwd</filename>,
and to include a NSS module <ulink
url="https://github.com/aperezdc/nss-altfiles">nss-altfiles</ulink>
which instructs glibc to read from it. Then, the build system places
all system users there, freeing up <filename>/etc/passwd</filename>
to be purely a database of local users.
</para>
</chapter>
<chapter id="adapting-package-manager">
<title>Adapting existing package managers</title>
<para>
The largest endeavor is likely to be redesigning your
distribution's package manager to be on top of OSTree,
particularly if you want to keep compatibility with the "old
way" of installing into the physical <filename
class='directory'>/</filename>. This section will use examples
from both <command>dpkg</command> and <command>rpm</command> as
the author has familiarity with both; but the abstract concepts
should apply to most traditional package managers.
</para>
<para>
There are many levels of possible integration; initially, we
will describe the most naive implementation which is the
simplest but also the least efficient. We will assume here that
the admin is booted into an OSTree-enabled system, and wants to
add a set of packages.
</para>
<para>
Many package managers store their state in <filename
class='directory'>/var</filename>; but since in the OSTree model
that directory is shared between independent versions, the
package database must first be found in the per-deployment
<filename class='directory'>/usr</filename> directory. It
becomes read-only; remember, all upgrades involve constructing a
new filesystem tree, so your package manager will also need to
create a copy of its database. Most likely, if you want to
continue supporting non-OSTree deployments, simply have your
package manager fall back to the legacy <filename
class='directory'>/var</filename> location if the one in
<filename class='directory'>/usr</filename> is not found.
</para>
<para>
To install a set of new packages (without removing any existing
ones), enumerate the set of packages in the currently booted
deployment, and perform dependency resolution to compute the
complete set of new packages. Download and unpack these new
packages to a temporary directory.
</para>
<para>
Now, because we are merely installing new packages and not
removing anything, we can make the major optimization of reusing
our existing filesystem tree, and merely
<emphasis>layering</emphasis> the composed filesystem tree of
these new packages on top. A command lke this: <command>ostree
commit -b osname/releasename/description
--tree=ref=<replaceable>osname/releasenamename/description</replaceable>
--tree=dir=/var/tmp/newpackages.13A8D0/</command> will create a
new commit in the
<replaceable>osname/releasename/description</replaceable>
branch. The OSTree SHA256 checksum of all the files in
/var/tmp/newpackages.13A8D0/ will be computed, but we will not
re-checksum the present existing tree. In this layering model,
earlier directories will take precedence, but files in later
layers will silently override earlier layers.
</para>
<para>
Then to actually deploy this tree for the next boot:
<command>ostree admin deploy
<replaceable>osname/releasenamename/description</replaceable></command>
</para>
</chapter>
</part>