Build and boot your own Linux image

Build a Linux image for the open Linux nodes on top of the IoT-LAB image, push it to Docker Hub and boot it on IoT-LAB A8-M3 nodes.

The open Linux nodes boot an OCI image as their root filesystem (see Linux). In this tutorial you build your own image, with the tools of your experiment already installed, and boot it on two IoT-LAB A8-M3 nodes of Grenoble.

You need, on your computer:

  • Docker with buildx and the emulation of the ARM platforms (Docker Desktop has both; on Linux: docker run --privileged --rm tonistiigi/binfmt --install arm);
  • a Docker Hub account, <you> below;
  • the CLI tools 3.4.0 or later, or the SSH frontend where they are installed.

Write the image

In an empty directory, a file to add to the image:

mkdir -p rootfs/usr/local/bin
cat > rootfs/usr/local/bin/hello-iotlab <<'EOF'
#!/bin/sh
echo "Hello from $(hostname), my own image"
EOF
chmod +x rootfs/usr/local/bin/hello-iotlab

and the Dockerfile, on top of the IoT-LAB base image of the A8-M3 nodes, at the version of the sites. The base image has what a node needs in IoT-LAB (SSH, /root/shared, the tools of the M3), you add the packages of your experiment:

FROM fitiotlab/iot-lab-open-linux-base:1.0.4

RUN apt-get update \
    && apt-get install -y --no-install-recommends htop tcpdump \
    && rm -rf /var/lib/apt/lists/*

COPY rootfs/ /

Keep the FROM image: its org.iot-lab.linux-base.version label tells the site that the image can boot on its nodes. Don’t touch /boot/iotlab.

Build and push

The A8-M3 nodes are 32 bits ARM boards (linux/arm/v7). Build for that platform and push to Docker Hub in one command:

docker login
docker buildx create --name iotlab --use
docker buildx build --platform linux/arm/v7 -t docker.io/<you>/hello-iotlab:1 --push .

The repository must be public: the sites pull it without credentials. Check the label:

docker buildx imagetools inspect docker.io/<you>/hello-iotlab:1 \
    --format '{{ range .Image }}{{ index .Config.Labels "org.iot-lab.linux-base.version" }} {{ end }}'

It prints 1.0.4, the version of the IoT-LAB image.

Boot it

Submit an experiment with two A8-M3 nodes booting your image:

iotlab-experiment submit -n hello-image -d 30 -l grenoble,a8,1-2,image=docker.io/<you>/hello-iotlab:1
iotlab-experiment wait

The site pulls the image at the start of the experiment: the start takes a bit longer than with the default image. Then, from the SSH frontend of Grenoble:

iotlab-ssh wait-for-boot
ssh root@node-a8-1 hello-iotlab
ssh root@node-a8-1 htop

Each node has its own copy of the root filesystem: what you change on node-a8-1 is not on node-a8-2, and everything is removed at the end of the experiment. Put what you want to keep in /root/shared, your ~/shared directory of the frontend.

When the image is not booted

If hello-iotlab is not found, the nodes booted the default IoT-LAB image. The reason is in the files of the experiment, on the frontend:

cat ~/.iot-lab/<experiment id>/log/user-image.log

Common causes: a private repository, an image too large (4 GB to download, 8 GB unpacked), another platform than linux/arm/v7, or another FROM image than the IoT-LAB base image of the node type.

Other node types

The same steps work for the other open Linux nodes, with their IoT-LAB image and platform:

Nodes FROM --platform
Raspberry Pi 4 fitiotlab/iot-lab-open-linux-base:1.0.4 linux/arm/v7
Raspberry Pi 5 fitiotlab/iot-lab-open-linux-base:1.0.4 linux/arm64
Jetson Nano fitiotlab/iot-lab-open-linux-jetson-nano:1.0.4 linux/arm64
Jetson Orin fitiotlab/iot-lab-open-linux-jetson-orin:1.0.4 linux/arm64

The Jetson images are Ubuntu: use the Ubuntu package names. On the Raspberry Pi and Jetson nodes, Docker runs too: your experiment can also pull and run containers on the nodes.