11. 1.x ➞ 2.x updates considerations
Lots of differences between 1.x/2.x
Update-relevant changes:
● Partition layout / size
● State partition
● File system labels
● BTRFS ➞ Ext4 file system
● Docker storage driver change
12. 1.x ➞ 2.x updates process
● Stop supervisor & user application
● Backup /data (user data)
● Move partitions
● Pull OS image
● Resize Root A / recreate Root B
● Export root and boot content
● Reformat data part, restore /data
aka. “the juggle”...
13. 1.x ➞ 2.x updates schematics
Root BRoot A
Boot
User Data
14. 1.x ➞ 2.x updates
● Updater is a shell script
○ Lightweight
○ If any issue, easy to pick up
from an intermediate state
● Update logs are transient (/tmp)
● Quite slow (`docker export | gz`)
● OS needs to do more work on reboot
● 1.8.0-1.30.1 ➞ 2.2.0-2.4.2
15. 2.x ➞ 2.x updates (v1)
● Updater is a shell script
● Logic similar to 1.x➞1.x updates
○ Stop supervisor & user app
○ Pull resinOS image
○ Export to secondary root
○ Update supervisor
○ Switch boot
○ Reboot
16. 2.x ➞ 2.x updates (v2) - hostapp-update
● Devices boot directly into a
resinOS container (mobynit)
● Root is within the container
● Two root partitions accessible:
○ sysroot/active; sysroot/inactive
● Secondary Balena(Docker):
○ balena-host (docker-host)
● OS update: ~ a “balena pull”
17. 2.x ➞ 2.x updates (v2) - hostapp-update
Image A Image B Image C
inactiveactive
Boot
User Data
Super
18. 2.x ➞ 2.x updates (v2) - hostapp-update
● No need to shut down supervisor &
user application anymore
● Balena deltas for OS updates
● Can create new OS versions by
Dockerfile and `hostapp-update`
● Update hooks do /boot or firmware
changes (with rollback on error)
● 2.5.0 ➞ current
19. 2.x ➞ 2.x updates (v1➞v2)
● Manually set up “balena-host”
● Pull resinOS image
● Export image
● Run resinOS image (update run in
hostapp system)
● Import image to be able to use
hostapp-update within
… aka “the return of the juggle”
20. From beginning to end
From 1.0.0-pre / 0.0.10 (~2015)
to 2.12.6+rev1 / 7.4.3 (~1 week ago)
● Total time ~2h
● ~4-5 reboots
21. Running a HUP
Self-service:
● Triggering through the Proxy’s
action server
● Backend logs into device
● Runs the updater script
Manual:
● Log into the device and run script
23. hostapp-updates with custom image
FROM resin/resinos:2.12.6_rev1-intel-nuc
RUN echo "Kilroy was here" > /tagged
Then
$ docker build . -t imrehg/resinos-mod:kilroy
$ docker push imrehg/resinos-mod:kilroy
On the device:
# hostapp-update -i imrehg/resinos-mod:kilroy -r
24. Device support
Aiming to support all device types
on hostapp-enabled resinOS versions.
For 1.x➞2.x updates we might might
the range of currently supported
versions (Pi, BBB, NUC)
1.x->1.x updates probably won’t be
expanded further
25. Further Goals
● Easy management of host OS
variants/modifications
● The host being able to update
itself (just as supervisor is
capable to self-update with the
`update-resin-supervisor` service
● Delta updates all around
26. ResinOS in a container
The same image that used for updates
can now be run as a full-fleded
system:
resin-os/resinos-in-container
(Github)
Host OS updates:
● Run new image w/ existing volumes
It’s indeed a lot of information, and can be quite involved. Will skip over some technical details to keep things focus, nevertheless feel free to ask questions any time!
“git push to devices” is one of our earlier motto (even though it’s changing) - everyone’s familiar with application update, but we are managing the entire device - including the host OS. The “host OS” is the minimal system that mainly gets docker up and running (plus vpn, time management,
Anyone who hasn’t done a device update on the team before? (Why?)
This is the conceptual diagram of resinOS layout that is often quoted.
OS + Balena on that + Application images within balena.
A slightly modified version for explanation.
Dual root partition (enables os updates)
Boot partition (storing config.json, config.txt, uEnv, device trees….)
Rest is pretty much bundled
Images, user data, supervisor are all currently part of the same “data” section of the OS
This is not directly mapped onto partitions, there’s a bit more of that, but this is the nutshell.
The updates are packaged as docker images, and posted on Docker Hub (currently)
They contain the root fs, but also specific locations for the files that go to the boot (kernel, firmware, device trees) and additional scripts (hooks)
This section goes through how updates were done historically from earlier versions to latest ones, to appreciate changes.
The original setup of host OS updates
The shell script coordinates the update steps, while HUP is mainly for the host-os part of the update
3-4 docker pulls for the entire process (supervisor, updater, OS image, migrator (for pre-1.10 Docker))
Of course the switch can work from Root B ➞ Root A too,, if the device was updated before already. The angled arrow means, that in the pull, the intermediate data is stored on the same partition as the user data.
Partition layout / size: increased rootA/rootB, state partition, moved data partition
File system labels: resin-rootA, resin-rootB labels for the two roots
Docker storage driver: btrfs ➞ aufs (overlay2 in some versions on resinOS, but fortunately those devices are not updated 1.x➞2.x)
A simplified version of what’s going on, but still…
Root B is recreated elsewhere on the disk, so the system has to run from Root A for this to work. If not, the partition is switched and redone.
gz is very slow on some devices (like Pi, even Pi 3)
Updates can be ~5 minutes on a NUC, to ~20-30 minutes on Pi (slower on PiZero, eg.), on fast network even
Extra OS work: the supervisor starting up after reboot will need to pull the new supervisor image, and that new supervisor will have to repull the user application
The ranges of updates vary between devices (e.g. BBB needs latest 1.x to update, while Pi can do updates)
Still have a bit below 900 devices on 1.x.
Faster, though, as 1 less pulls
Mobynit is part of balena: bootable containers (original PR is linked)
The two root partitions are there to use with the secondary, host balena: active where the currently running system’s balena storage, while the inactive is where the update would land if run, currently empty
The pull is directly to inactive, maybe using the active as delta
We are working on this sort of updates be a central part of the platform (the actual “hostapps”). In the meantime it might be handy for hacking.
Note, that when building an image, to use RUN, have to build on a compatible architecture. On incompatible ones can only use COPY (for example building a modified Raspberry Pi image on an x86 work machine).
For 2.x➞2.x updates that are not hostapp-enabled, we will add support if it’s low hanging fruit, if has any issues, some updates will be exempted.
For 1.x➞2.x updates it depends on the number of provisioned devices we have on 1.x for a given device type, that gains 2.x release. Also, if there’s no intermediate non-hostapp version, then it cannot have such update at the moment.
The variants and modifications can be tools added to the host OS (extra binaries one needs), or convenience (adding extra authorized_keys) - many of these what people would do could be later part of the platform too, this is a good way to prototype.
Supervisor updates - run once every 24h (and ~15m after device start to kick off), queries the API and pulls an update if needed.
Delta updates should reduce the amount of data moved, so in line with our theme of “getting more and more lightweight) - just like the update is using less and less data, reduced HUP container, then done away with completely, smaller and smaller supervisor….
When running resinOS in a container, the boot, state and data partitions are added volumes.
This os delivering over the air updates...
Anyone who wants to go and do an update now?