Create your own Ubuntu Core 18 image

Key Value
Summary Create your own Ubuntu Core image for a particular model, by assembling snaps available in the store. Make your own device image with some snap preinstalled or additional functionalities!
Categories iot
Difficulty 3
Author Canonical Web Team webteam@canonical.com

Overview

Duration: 1:00

Building a bespoke image for a supported platform enables an Ubuntu Core device to be customised at the point of deployment. Customisation options include configuration for both hardware and software, specific kernels, and which snap packages to pre-install.

We are going to create an image, first by generating our own authority keys, then making the snap store aware of them, then creating and signing a model assertion before building the image.

This document will walk you through all the steps to build an image for an x86 device, but the same instructions will work for other platforms.

What you’ll learn

  • Different fundamental snap notions on the board, like gadget, kernel and core snap
  • Assembling a kernel and gadget snaps
  • Creating and using your authority keys
  • Create a model assertion for your target device
  • Compose and build a custom image using the ubuntu-image command

What you’ll need

  • Ubuntu 20.04 LTS desktop. You can’t do this on an Ubuntu Core device directly as creating your image can take quite some disk spaces. A VM can work as well.
  • A Snap Store account to register your authority keys
  • Some very basic knowledge of command line use, know how to edit files.

How will you use this tutorial?

  • Only read through it
  • Read it and complete the exercises

0 voters

What is your current level of experience?

  • Novice
  • Intermediate
  • Proficient

0 voters

Getting started

Duration: 1:00

To build a custom image, first use the snapcraft command to login to the Snap Store:

$ snapcraft login

Snapcraft can be installed with sudo snap install snapcraft --classic, see Snapcraft overview for further details, and visit Create a developer account if you don’t yet have an account.

Before creating a custom model assertion, you will need to retrieve your developer ID and generate a properly formatted timestamp. The snapcraft command can be used to retrieve your developer id:

$ snapcraft whoami
email: <email>
developer-id: bJzr2XzZ56Qv6Z51HIeziXvxtn1XItIq

Use the following date command to output the correctly formatted timestamp for the model assertion:

date -Iseconds --utc
2021-01-25T10:40:41+00:00

Custom model assertion

Duration: 4:00

The following is a tweaked JSON-formatted custom model assertion based on
ubuntu-core-18-amd64:

{
  "type": "model",
  "series": "16",
  "model": "ubuntu-core-18-amd64",
  "display-name":"Ubuntu Core 18 (amd64)",
  "architecture": "amd64",
  "kernel": "pc-kernel=18",
  "gadget": "pc=18",
  "base": "core18",
  "required-snaps": ["core", "hello", "hello-world"],
  "authority-id": "bJzr2XzZ56Qv6Z51HIeziXvxtn1XItIq",
  "brand-id": "bJzr2XzZ56Qv6Z51HIeziXvxtn1XItIq",
  "timestamp": "2021-01-25T10:40:41+00:00"
}

We’ve saved the above example in a file called my-model.json , and it contains the following modified properties:

  • base: provides the run-time environment core18 is the current standard base and as is built from Ubuntu 18.04 LTS. See Base snaps for more details.
  • authority-id, brand-id: defines the authority signing the assertion reference assertions are signed by canonical. Non-reference assertions are signed by their brand store. For a custom model assertion, this needs to be the developer ID.
  • timestamp: UTC formatted time and date used to denote the assertion’s creation time.
  • required-snaps: one or more snaps to be pre-installed for deployment any snap can be listed here. If there’s a dependency on a different base, such as core, it’s best to specify that too, although it will be installed automatically.

For a complete list of model assertion keywords, see Model assertion.

Signing a model assertion

Duration: 5:00

The difference between building an image from a reference model assertion and
building from a modified model assertion is that the modified model assertion needs to be digitally signed. This is accomplished in four stages:

  1. create a key
  2. export/register the key
  3. sign the model assertion
  4. build the image

First, sign in to the Snap Store (snap login) and check whether there is already a published key available. You can list any published snaps with the snap keys command:

$ snap login
[...]
Login successful

$ snap keys
No keys registered, see `snapcraft create-key`

If you have no registered keys, create one as follows:

$ snap create-key my-key-name
Passphrase:
Confirm passphrase: <passphrase>

$ snap keys
Name         SHA3-384
my-key-name  E-n0AOKPFjIyy4S_i9JxTT4tkuaZf7rP9D2ARCmBNXjlgTGDjL8euFSlb87U0NPl

With a key created, use the snapcraft command to upload and register it with the store:

$ snapcraft register-key
[...]
Registering key...

A custom model assertion is signed by piping the assertion through the snap sign command with the key name as its sole argument:

$ cat my-model.json | snap sign -k my-key-name > my-model.model

The resulting my-model.model file contains the signed model assertion and can now be used to build the image.

Building the image

Duration: 3:00

With a signed model assertion, the Ubuntu Core image can now be built just like a reference image, using the ubuntu-image command. First, install ubuntu-image if it isn’t already installed:

$ sudo snap install ubuntu-image --classic

You can now use ubuntu-image to build the image:

$ ubuntu-image snap my-model.model
Fetching snapd
Fetching pc-kernel
Fetching core18
Fetching pc
Fetching core
Fetching hello
Fetching hello-world

The output includes the img file itself, alongside seed.manifest and snaps.manifest files. These manifest files simply list the specific revision numbers for the snapd, pc, pc-kernel and core snaps built within the image.

Testing the image

Duration: 8:00

You can now test the resulting image. Using QEMU, for instance, the following command will boot the image inside a VM and forward (SSH) port 22 to 8022 on your machine:

$ qemu-system-x86_64 -enable-kvm -smp 2 -m 1500 -netdev \ 
user,id=mynet0,hostfwd=tcp::8022-:22,hostfwd=tcp::8090-:80 \
-device virtio-net-pci,netdev=mynet0 -drive file=pc.img,format=raw

After running through the Ubuntu Core network setup and entering your account details, you will be able to SSH to your new Ubuntu Core deployment:

$ ssh <username>@localhost -p 8022

You are now connected to the Ubuntu Core virtual machine, from where you can configure and install whatever apps you need.

From within a running session on a custom image, you can run the pre-installed
snaps:

$ hello-world
Hello World!

Use snap list to see which snaps are installed:

$ snap list
Name         Version         Rev    Tracking       Publisher   Notes
core         16-2.48.2       10583  latest/stable  canonical✓  core
core18       20201210        1944   latest/stable  canonical✓  base
hello        2.10            38     latest/stable  canonical✓  -
hello-world  6.4             29     latest/stable  canonical✓  -
pc           18-2            104    18/stable      canonical✓  gadget
pc-kernel    4.15.0-134.138  706    18/stable      canonical✓  kernel
snapd        2.48.2          10707  latest/stable  canonical✓  snapd

The snap known model command will show the read-only custom model assertion used to build the image:

$ snap known model
type: model
authority-id: bJzr2XzZ56Qv6Z51HIeziXvxtn1XItIq
series: 16
brand-id: bJzr2XzZ56Qv6Z51HIeziXvxtn1XItIq 
model: ubuntu-core-18-amd64
architecture: amd64
base: core18
display-name: Ubuntu Core 18 (amd64)
gadget: pc=18
kernel: pc-kernel=18
required-snaps:
  - core
  - hello
  - hello-world
timestamp: 2021-01-25T10:40:41+00:00
sign-key-sha3-384: 9aZR3b1UX9kqiVVxzfUrKYzYjHX-gC8jGNc4hTCpGfpPyaFdWR7K68HLoY1EH3yR
[...]

Congratulations!

Duration: 1:00

You now have your own device image file for your specific device. This image is easily flashable on any SDCard or eMMC and can be booted right away.

You should by now be familiar with the various snaps composing an Ubuntu image: Core snap, kernel, gadget. You know that snapd is using a model assertion to define all pieces composing an image and this is what is used to build the image via the ubuntu-image tool.

Finally, you know also that you can change those default snaps, and add more applications snaps as you require them. If you produce your own gadget or kernel snap, you can swap as well default ones and enable a new board that way.

Next steps

When creating an Ubuntu Core 18 image, is it possible to specify a version in the model assertion to separate different image builds?

is it possible to specify a specific channel for one of the snaps in the required-snaps set section of the model?

this is possible with UC20 onwards (where the manifest format changed), but not with UC18 …