How to create a Real-time Ubuntu Core image¶
Note
This guide assumes access to features currently available exclusively to dedicated Snap Store users.
Canonical builds and publishes generic and silicon-optimized Ubuntu Core images for a range of supported platforms. The generic Ubuntu Core images include the generic Ubuntu kernel by default. In order to run Ubuntu Core with the real-time kernel, we need to build it ourselves.
This guide shows how to build an Ubuntu Core image with the real-time kernel. We will first build a vanilla image - which is excellent for testing and tuning - then proceed to making a custom image with production-ready kernel configurations.
Create an image with the real-time kernel¶
To do this we need to describe the image content and then use a tool to build it.
Create the model assertion¶
The model assertion is a digitally signed document that describes the content of the Ubuntu Core image. Read the model assertion documentation before continuing.
Below are example model assertions, describing the Ubuntu Core image content for recent releases:
{
"type": "model",
"series": "16",
"model": "ubuntu-core-24-amd64",
"architecture": "amd64",
"base": "core24",
"grade": "dangerous",
"authority-id": "<developer-id>",
"brand-id": "<developer-id>",
"timestamp": "<timestamp>",
"store": "<brand-store-id>",
"snaps": [
{
"name": "pc",
"type": "gadget",
"default-channel": "24/stable",
"id": "UqFziVZDHLSyO3TqSWgNBoAdHbLI4dAH"
},
{
"name": "realtime-kernel",
"type": "kernel",
"default-channel": "24/stable",
"id": "qOnOYvhgxU2fQhw3TgnjaSuOWhO2jDdm"
},
{
"name": "core24",
"type": "base",
"default-channel": "latest/stable",
"id": "dwTAh7MZZ01zyriOZErqd1JynQLiOGvM"
},
{
"name": "snapd",
"type": "snapd",
"default-channel": "latest/stable",
"id": "PMrrV4ml8uWuEUDBT8dSGnKUYbevVhc4"
},
{
"name": "console-conf",
"type": "app",
"default-channel": "24/stable",
"id": "ASctKBEHzVt3f1pbZLoekCvcigRjtuqw"
}
]
}
The console-conf
snap added here is only to allow interactive user and network configuration.
An image created for deployment at scale should not include that.
{
"type": "model",
"series": "16",
"model": "ubuntu-core-22-amd64",
"architecture": "amd64",
"base": "core22",
"grade": "dangerous",
"authority-id": "<developer-id>",
"brand-id": "<developer-id>",
"timestamp": "<timestamp>",
"store": "<brand-store-id>",
"snaps": [
{
"name": "pc",
"type": "gadget",
"default-channel": "22/stable",
"id": "UqFziVZDHLSyO3TqSWgNBoAdHbLI4dAH"
},
{
"name": "realtime-kernel",
"type": "kernel",
"default-channel": "22/stable",
"id": "qOnOYvhgxU2fQhw3TgnjaSuOWhO2jDdm"
},
{
"name": "core22",
"type": "base",
"default-channel": "latest/stable",
"id": "amcUKQILKXHHTlmSa7NMdnXSx02dNeeT"
},
{
"name": "snapd",
"type": "snapd",
"default-channel": "latest/stable",
"id": "PMrrV4ml8uWuEUDBT8dSGnKUYbevVhc4"
}
]
}
Inside an empty directory, create a file named model.json
with the above content.
Change the following:
model
to a name that accurately represents your device(s).authority-id
,brand-id
to your developer ID, since this is custom model. Usesnapcraft whoami
command to get your developer ID.timestamp
to an RFC3339 formatted time set after the registration of your signing key. If you already have a registered key, usedate -Iseconds --utc
command to generate the current time. If not, do this in the next steps after registering your key.store
to your dedicated Snap Store ID.
The snaps
array is a list of snaps that get included in the image.
In that list, the realtime-kernel
snap contains the realtime Linux kernel.
Here you can add any other snaps, including for example your real-time applications.
Sign the model assertion¶
Next, we need to sign the model assertion. Refer to the guide on signing model assertion for details on how to sign the model assertion. Below are the needed steps:
Create and register a key
Use
snapcraft list-keys
to check your existing keys. If you don’t already have a key, create one locally and register it with your account. Choose a meaningful name for the key, as it can be used to sign multiple models. In this guide we usertu-model
to sign all our real-time Ubuntu models.snapcraft create-key rtu-model snapcraft register-key rtu-model
Remember to update the model assertion’s
timestamp
, if you created a new key and plan to use it next.Sign the model assertion
snap sign -k rtu-model model.json > model.signed.yaml
The
snap sign
command takes JSON as input and produces YAML as output.Tip
You need to repeat the signing every time you change the input model, because the signature is calculated based on the model.
Build the image¶
First, get familiar with the tooling by referring to the guide on building Ubuntu Core images.
We use ubuntu-image
and need to set the paths to the following as input:
Exported store credentials
Signed model assertion YAML file
Export the store credentials to a file:
snapcraft export-login credentials.txt
Then build the image:
$ UBUNTU_STORE_AUTH_DATA_FILENAME=credentials.txt \
ubuntu-image snap model.signed.yaml --verbose --validation=enforce
[0] prepare_image
Fetching snapd (21759)
Fetching realtime-kernel (153)
Fetching core24 (490)
Fetching pc (178)
Fetching console-conf (40)
WARNING: the kernel for the specified UC20+ model does not carry assertion max formats information, assuming possibly incorrectly the kernel revision can use the same formats as snapd
[1] load_gadget_yaml
[2] set_artifact_names
[3] populate_rootfs_contents
[4] generate_disk_info
[5] calculate_rootfs_size
[6] populate_bootfs_contents
[7] populate_prepare_partitions
[8] make_disk
[9] generate_snap_manifest
Build successful
The warning about assertion max formats can be safely ignored; see ubuntu-image assertion warning.
$ UBUNTU_STORE_AUTH_DATA_FILENAME=credentials.txt \
ubuntu-image snap model.signed.yaml --verbose --validation=enforce
[0] prepare_image
Fetching snapd (21759)
Fetching realtime-kernel (149)
Fetching core22 (1586)
Fetching pc (146)
[1] load_gadget_yaml
[2] set_artifact_names
[3] populate_rootfs_contents
[4] generate_disk_info
[5] calculate_rootfs_size
[6] populate_bootfs_contents
[7] populate_prepare_partitions
[8] make_disk
[9] generate_snap_manifest
Build successful
This downloads all the snaps specified in the model assertion and builds an image file called pc.img
.
Hint
To fetch the realtime-kernel
snap for this image build, it should be included explicitly in your dedicated Snap Store.
$ file pc.img
pc.img: DOS/MBR boot sector; partition 1 : ID=0xee, start-CHS (0x0,0,0), end-CHS (0x0,0,0), startsector 1, 6195199 sectors, extended partition table (last)
✅ The image file is now ready. Refer to Ubuntu Core guide on flashing the image to a storage medium.
After installing this image on your device, you can continue by tuning your system for real-time processing. The Modify boot parameters on Ubuntu Core guide describes the method for dynamically configuring the kernel command line parameters. The configuration is an iterative process that is best done together with the expected workload.
Once satisfied with the configurations, continue below to learn how those configurations can be set statically during the image build.
Create a custom real-time Ubuntu Core image¶
This section shows how to statically set the desired Kernel command-line parameters for the Ubuntu Core system. To do this, we need to create a custom gadget snap, create a model assertion, and then build the OS image.
Project directory
Start in an empty directory. We refer to this in different parts of the document as our project directory.
Create the gadget snap¶
The gadget snap documentation is a recommended read before starting.
This is best done by forking an existing reference gadget, then changing it for our purpose. For example, there is the pc gadget which is suitable for most AMD64 platforms, and the pi gadget which is meant for Raspberry Pis.
Inside the project directory, clone the specific branch of the pc-gadget repository and enter the repository:
git clone https://github.com/canonical/pc-gadget.git --branch=24 --depth=1
cd pc-gadget
git clone https://github.com/canonical/pc-gadget.git --branch=22 --depth=1
cd pc-gadget
Add the desired kernel command line in an array to kernel-cmdline.append
in gadget/gadget-amd64.yaml
.
For example:
kernel-cmdline:
append:
- nohz=on
- nohz_full=2-N
- irqaffinity=0-1
Refer to Kernel boot parameters for the list of supported parameters.
Modify snapcraft.yaml
to fit your application.
At minimum, make sure to change the name and version to something distinct, for example, to realtime-pc
and example
respectively.
Now, build the gadget snap:
$ snapcraft --verbose
...
Created snap package realtime-pc_example_amd64.snap
Tip
You need to rebuild the snap every time you change the snapcraft.yaml file.
Create the model assertion¶
Create the model assertion inside the project directory.
Follow the same steps in Create the model assertion section but replace the pc
snap entry with the following:
{
"name": "realtime-pc",
"type": "gadget"
},
Unlike the original pc
snap definition, this entry has no listed channel
and id
, because it isn’t published in a Store.
The locally built gadget snap will be passed directly to the image builder.
For production use, the gadget snap should be uploaded to a Store and then listed in the model assertion along with its channel and id.
Uploading to the store makes it possible to use a signed snap that receives updates.
Sign the model assertion which has our custom realtime-pc
gadget, using the same key which was created in the previous section of this guide:
snap sign -k rtu-model model.json > model.signed.yaml
Before we continue, let’s have an overview of the files inside our project directory:
$ tree -L 1
.
├── model.json
├── model.signed.yaml
└── pc-gadget
2 directories, 2 files
The project directory should contain the model assertion, the signed model assertion, and the pc-gadget directory.
Build the Ubuntu Core image¶
Similar to before, we use ubuntu-image
to build the image.
This time we also need to provide the path to the custom gadget snap file.
We therefore need:
Exported store credentials
Signed model assertion YAML file
Locally built gadget snap
Build with the following command:
$ UBUNTU_STORE_AUTH_DATA_FILENAME=credentials.txt \
ubuntu-image snap model.signed.yaml --verbose --validation=enforce \
--snap pc-gadget/realtime-pc_example_amd64.snap
[0] prepare_image
Fetching snapd (21759)
Fetching realtime-kernel (153)
Fetching core24 (490)
Fetching console-conf (40)
WARNING: the kernel for the specified UC20+ model does not carry assertion max formats information, assuming possibly incorrectly the kernel revision can use the same formats as snapd
WARNING: "realtime-pc" installed from local snaps disconnected from a store cannot be refreshed subsequently!
Copying "pc-gadget/realtime-pc_example_amd64.snap" (realtime-pc)
[1] load_gadget_yaml
[2] set_artifact_names
[3] populate_rootfs_contents
[4] generate_disk_info
[5] calculate_rootfs_size
[6] populate_bootfs_contents
[7] populate_prepare_partitions
[8] make_disk
[9] generate_snap_manifest
Build successful
The warning about assertion max formats can be safely ignored; see ubuntu-image assertion warning.
$ UBUNTU_STORE_AUTH_DATA_FILENAME=credentials.txt \
ubuntu-image snap model.signed.yaml --verbose --validation=enforce \
--snap pc-gadget/realtime-pc_example_amd64.snap
[0] prepare_image
Fetching snapd (21759)
Fetching realtime-kernel (134)
Fetching core22 (1380)
WARNING: "realtime-pc" installed from local snaps disconnected from a store cannot be refreshed subsequently!
Copying "pc-gadget/realtime-pc_example_amd64.snap" (realtime-pc)
[1] load_gadget_yaml
[2] set_artifact_names
[3] populate_rootfs_contents
[4] generate_disk_info
[5] calculate_rootfs_size
[6] populate_bootfs_contents
[7] populate_prepare_partitions
[8] make_disk
[9] generate_snap_manifest
Build successful
This adds all the snaps specified in the model assertion and builds an image file called pc.img
.
There is a warning for realtime-pc
gadget snap because this is being side-loaded, rather than fetched from the store.
✅ The image file with the custom configurations is ready! Refer to Ubuntu Core guide on flashing the image to a storage medium.
After installing and running a device with this image, the kernel parameters can be verified by looking into /proc/cmdline
:
$ cat /proc/cmdline
snapd_recovery_mode=run console=ttyS0,115200n8 console=tty1 panic=-1 nohz=on nohz_full=2-N irqaffinity=0-1
This guide provides a very basic setup to configure Ubuntu Core for real-time processing and create a bootable OS image for it. For production, the operating system configuration involves many more steps, such as network configuration and full disk encryption. The device will also need a serial assertion to authenticate itself and receive for example updates to the real-time kernel snap from a dedicated Snap Store.
The Ubuntu Core documentation is the best place to continue to learn about the various aspects.