Torizon: First Steps and Impressions
By Urs Fässler
In this article, I share my initial impressions gathered from my first steps with Torizon, a Linux-based containerized embedded OS developed by Toradex. Torizon aims to simplify the user experience and customization process compared to alternatives like Yocto. It achieves this by running user applications within custom containers on a base OS, supported by an array of tools. While Torizon simplifies the setup and lowers the entry barrier for newcomers, I found it occasionally challenging as an experienced Embedded Linux and Yocto user.
Initial Setup
Each new System on Module (SoM) comes with the Toradex Easy Installer, a graphical tool for selecting and installing an OS. Initially, I struggled to connect to the board via VNC due to my unfamiliarity with the system. The process of connecting a monitor to an embedded Linux device for graphical OS installation felt unusual. I would have preferred using a serial connection to select the OS. However, I later learned that while a serial connection is straightforward, it doesn’t allow control of the Toradex Easy Installer. Once I understood the correct network name for connection, VNC worked seamlessly.
Connecting over SSH worked smoothly as expected. As I became more familiar with the system, I came to appreciate the convenience of connecting to the device using a static name rather than an IP address. The connection command is straightforward:
ssh torizon@verdin-imx8mp-<serial-number>.local
Worry(-free) Package: Torizon IDE Extension
A tutorial involves setting up and using the Torizon IDE Extension for Visual Studio Code, which promises an easy way to develop applications and images. However, I eventually gave up on getting it to work. The IDE Extension simplifies development by abstracting many steps. This isn’t ideal for my goal of finally integrating it in an automated CI/CD environment. For this, I need to perform and understand all steps from the console.
The issues I encountered most likely arose because I was using Debian testing instead of the recommended Ubuntu 22.04. While the extension likely works well on Ubuntu and serves as a good entry point for newcomers, it wasn’t suitable for my needs.
I don’t require a fast compilation-packaging-deployment cycle because I practice Test Driven Development (TDD). This allows me to develop the application with tests on the host system. I abstract away the hardware to enable running and testing the application on the host system. Running applications or other code on the device is only necessary to verify correct integration or debug interface issues.
Developing and Testing Container Images
There are various ways to manage containers and several base images to choose from. Ultimately, I opted for the plain Torizon OS image. While it runs Docker, the challenge is determining how to deploy our image during development. The two feasible methods are using an online registry or a local registry.
The work was done with the "application" showtime.sh:
#!/bin/sh
while true; do
echo "Hello, it is $(date --iso-8601=seconds)"
sleep 1
done
The Dockerfile:
ARG CT_TAG_DEBIAN
FROM torizon/debian:$CT_TAG_DEBIAN
COPY showtime.sh /usr/local/bin/
CMD ["/usr/local/bin/showtime.sh"]
Since the target system runs on a different processor architecture than the host system, we need to "cross-compile" the application. Specifically, we need to build an image for arm64 while running on amd64. This can be easily achieved using the platform argument in Docker.
Deploying Images Using an Online Registry
To use an online registry like Docker Hub, an account is needed. Then the steps on the host system are:
docker build \
--platform=linux/arm64 \
--build-arg CT_TAG_DEBIAN="3-bookworm" \
--tag iqilio/showtime \
.
docker login
docker push iqilio/showtime
The container image can also be run on the host with QEMU:
sudo apt install qemu binfmt-support qemu-user-static
docker run \
--platform=linux/arm64 \
--interactive --tty --rm \
iqilio/showtime
Download and run the container image on the target:
docker login
docker run \
--pull=always \
--interactive --tty --rm \
iqilio/showtime
Deploying Images Using a Local Registry
A local registry is available with the docker-registry Debian package. There are setup guides available, which I won’t detail here. Ensure you allow unsafe connections on both the host and target by adding the following to /etc/docker/daemon.json:
{
"insecure-registries" : ["<host-ip>:5000"]
}
This setup is necessary to allow HTTP connections, as we connect via IP and want to avoid the hassle of dealing with certificates. Note that this configuration should only be used for internal development purposes.
Steps on the host:
docker build \
--platform=linux/arm64 \
--build-arg CT_TAG_DEBIAN="3-bookworm" \
--tag <host-ip>:5000/iqilio/showtime .
docker login http://<host-ip>:5000
docker push <host-ip>:5000/iqilio/showtime
Steps on the target:
docker login http://<host-ip>:5000
docker run \
--pull=always \
--interactive --tty --rm \
<host-ip>:5000/iqilio/showtime
Remarks
You can specify the platform (linux/arm64) inside the Dockerfile, but I prefer to set it during the build process. This flexibility allows the image to be built for different architectures. It was particularly useful when I developed a cross-architecture container for an OnWay router. Compiling the application and creating the image for the native host architecture is significantly faster, shortening the feedback cycle time.
Customizing and Building a Linux Image
For a production system, numerous customizations are required. In addition to running custom container images, extending the device tree is often necessary. The System on Module (SoM) will be on a custom board that requires configuration. Moreover, our upcoming project will likely involve integrating a custom Linux driver.
All these tasks can be accomplished with the TorizonCore Builder. A configuration file outlines the base image, all customizations, and the output image. Although it takes some time to become familiar with it, the tool works effectively from the command line.
After creating the configuration file according to the tutorial and adjusting it, I use the following commands to build and deploy the Linux image:
torizoncore-builder build \
--force
torizoncore-builder images \
unpack <output-directory>
# press y when asked to delete current image
torizoncore-builder deploy \
--remote-host verdin-imx8mp-<serial>.local \
--remote-username torizon \
--remote-password **** \
--reboot
Remarks on the commands:
-
build: --force is needed to overwrite <output-directory>, useful when developing incrementally.
-
images: I don’t understand why it is needed and if there is an easy way to avoid interacting with the script.
-
deploy: A security issue arises as the password needs to be provided as an argument. Both the username and password have the same default values as a vanilla Torizon image. Unfortunately, for security reasons, the password must be changed on the target, so we can’t use the default values here.
All commands take time to complete, making fast feedback unattainable. However, since I plan to develop the individual parts separately, this shouldn’t be a significant obstacle.
Further Work
The interaction between an app in a container and the rest of the system is not addressed here. Running an app/container in isolation, without access to other containers, the system, or hardware, does not provide the necessary value.
One use case is manual network configuration. For this, the application needs to access NetworkManager from within a container, likely over D-Bus. Users must be able to connect to the app in the container, probably over a network.
Furthermore, the entire deployment process with Torizon Cloud might be of interest in the future.
Conclusion
Torizon offers a comprehensive software ecosystem for customizing and deploying containerized and Linux images. While it looks great for newcomers, experienced system developers can also find their preferred methods.
There is good introductory information and much additional help. However, finding the right information within the extensive knowledge base can sometimes be challenging, but it is available.
Using a prebuilt image is beneficial for two reasons. First, on Toradex hardware, the image is expected to work as it is Toradex’s responsibility to ensure its functionality. Second, it helps comply with the Cyber Resilience Act. With a clear separation between the base Linux image and custom container images, responsibilities are distinctly assigned.
Learning to create and customize an embedded Linux system with Torizon, apart from using Yocto, is an interesting experience. We will see if we can work with TorizonCore Builder or if we need to revert to building our Linux image with Yocto based on the Torizon layer.