mirror of
https://github.com/ostreedev/ostree.git
synced 2024-12-22 17:35:55 +03:00
130 lines
5.5 KiB
Markdown
130 lines
5.5 KiB
Markdown
---
|
|
nav_order: 5
|
|
---
|
|
|
|
# Atomic Upgrades
|
|
{: .no_toc }
|
|
|
|
1. TOC
|
|
{:toc}
|
|
|
|
## You can turn off the power anytime you want...
|
|
|
|
OSTree is designed to implement fully atomic and safe upgrades;
|
|
more generally, atomic transitions between lists of bootable
|
|
deployments. If the system crashes or you pull the power, you
|
|
will have either the old system, or the new one.
|
|
|
|
## Simple upgrades via HTTP
|
|
|
|
First, the most basic model OSTree supports is one where it replicates
|
|
pre-generated filesystem trees from a server over HTTP, tracking
|
|
exactly one ref, which is stored in the `.origin` file for the
|
|
deployment. The command `ostree admin upgrade`
|
|
implements this.
|
|
|
|
To begin a simple upgrade, OSTree fetches the contents of the ref from
|
|
the remote server. Suppose we're tracking a ref named
|
|
`exampleos/buildmaster/x86_64-runtime`. OSTree fetches the URL
|
|
`http://example.com/repo/refs/heads/exampleos/buildmaster/x86_64-runtime`,
|
|
which contains a SHA256 checksum. This determines the tree to deploy,
|
|
and `/etc` will be merged from currently booted tree.
|
|
|
|
If we do not have this commit, then, then we perform a pull process.
|
|
At present (without static deltas), this involves quite simply just
|
|
fetching each individual object that we do not have, asynchronously.
|
|
Put in other words, we only download changed files (zlib-compressed).
|
|
Each object has its checksum validated and is stored in `/ostree/repo/objects/`.
|
|
|
|
Once the pull is complete, we have all the objects locally
|
|
we need to perform a deployment.
|
|
|
|
## Upgrades via external tools (e.g. package managers)
|
|
|
|
As mentioned in the introduction, OSTree is also designed to allow a
|
|
model where filesystem trees are computed on the client. It is
|
|
completely agnostic as to how those trees are generated; they could be
|
|
computed with traditional packages, packages with post-deployment
|
|
scripts on top, or built by developers directly from revision control
|
|
locally, etc.
|
|
|
|
At a practical level, most package managers today (`dpkg` and `rpm`)
|
|
operate "live" on the currently booted filesystem. The way they could
|
|
work with OSTree is instead to take the list of installed packages in
|
|
the currently booted tree, and compute a new filesystem from that. A
|
|
later chapter describes in more details how this could work:
|
|
[Adapting Existing Systems](adapting-existing.md).
|
|
|
|
For the purposes of this section, let's assume that we have a
|
|
newly generated filesystem tree stored in the repo (which shares
|
|
storage with the existing booted tree). We can then move on to
|
|
checking it back out of the repo into a deployment.
|
|
|
|
## Assembling a new deployment directory
|
|
|
|
Given a commit to deploy, OSTree first allocates a directory for
|
|
it. This is of the form `/boot/loader/entries/ostree-$stateroot-$checksum.$serial.conf`.
|
|
The `$serial` is normally `0`, but if a
|
|
given commit is deployed more than once, it will be incremented.
|
|
This is supported because the previous deployment may have
|
|
configuration in `/etc` that we do not want to use or overwrite.
|
|
|
|
Now that we have a deployment directory, a 3-way merge is
|
|
performed between the (by default) currently booted deployment's
|
|
`/etc`, its default
|
|
configuration, and the new deployment (based on its `/usr/etc`).
|
|
|
|
## Atomically swapping boot configuration
|
|
|
|
At this point, a new deployment directory has been created as a
|
|
hardlink farm; the running system is untouched, and the bootloader
|
|
configuration is untouched. We want to add this deployment to the
|
|
"deployment list".
|
|
|
|
To support a more general case, OSTree supports atomic transitioning
|
|
between arbitrary sets of deployments, with the restriction that the
|
|
currently booted deployment must always be in the new set. In the
|
|
normal case, we have exactly one deployment, which is the booted one,
|
|
and we want to add the new deployment to the list. A more complex
|
|
command might allow creating 100 deployments as part of one atomic
|
|
transaction, so that one can set up an automated system to bisect
|
|
across them.
|
|
|
|
## The bootversion
|
|
|
|
OSTree allows swapping between boot configurations by implementing the
|
|
"swapped directory pattern" in `/boot`. This means it is a symbolic
|
|
link to one of two directories `/ostree/boot.[0|1]`. To swap the
|
|
contents atomically, if the current version is `0`, we create
|
|
`/ostree/boot.1`, populate it with the new contents, then atomically
|
|
swap the symbolic link. Finally, the old contents can be garbage
|
|
collected at any point.
|
|
|
|
## The /ostree/boot directory
|
|
|
|
However, we want to optimize for the case where the set of
|
|
kernel/initramfs/devicetree sets is the same between both the old and new
|
|
deployment lists. This happens when doing an upgrade that does not
|
|
include the kernel; think of a simple translation update. OSTree
|
|
optimizes for this case because on some systems `/boot` may be on a
|
|
separate medium such as flash storage not optimized for significant
|
|
amounts of write traffic. Related to this, modern OSTree has support
|
|
for having `/boot` be a read-only mount by default - it will
|
|
automatically remount read-write just for the portion of time
|
|
necessary to update the bootloader configuration.
|
|
|
|
To implement this, OSTree also maintains the directory
|
|
`/ostree/boot.$bootversion`, which is a set
|
|
of symbolic links to the deployment directories. The
|
|
`$bootversion` here must match the version of
|
|
`/boot`. However, in order to allow atomic transitions of
|
|
*this* directory, this is also a swapped directory,
|
|
so just like `/boot`, it has a version of `0` or `1` appended.
|
|
|
|
Each bootloader entry has a special `ostree=` argument which refers to
|
|
one of these symbolic links. This is parsed at runtime in the
|
|
initramfs.
|
|
|
|
###### Licensing for this document:
|
|
`SPDX-License-Identifier: (CC-BY-SA-3.0 OR GFDL-1.3-or-later)`
|