Back to the Overview

Docker support in ELinOS

Node.js Web Server with Docker for Embedded Linux ELinOS

ELinOS, IoT, Linux

The ELinOS embedded Linux distribution supports Docker by means of software distribution. This tutorial showcases a setup process of a Node.js web server embedded in a Docker image, running on an ELinOS-based Linux system.

Introduction

Docker is a set of tools and services that use OS-level virtualization to execute software. The software is distributed in form of images – packages which contain the software itself, all the necessary libraries and configuration files. The host operating system/kernel executes those as so called “containers”. While those are technically self-sufficient, it’s possible to define communication channels which can be used to orchestrate software into bigger groups of independent components.

While Docker can be also executed on Windows, in context of ELinOS, a Linux kernel is used. There, Docker uses several kernel features to implement the virtual machines. Among others, they enable management of CPU/memory resources (cgroups), network (netfilter) and storage (overlayfs). Using the kernel namespace support, also the operating system environment is virtualized – including process trees, user IDs, mounted filesystems etc.

Benefits

This lightweight nature of the containers allows better scaling – running many services in parallel is easily possible, as opposed to traditional full-blown virtualization, where the whole operating system including the kernel is virtualized. Additionally, compared to the traditional Unix package-based software management, the images can easily use the same libraries of different versions, which may be advantageous for simple setup.

There are several ways to prepare the software images. The simplest one is taking one of the 3.8+ million community-provided images (or 500+ from official/verified publishers) – this covers the most widely used software like web servers, databases and various micro-services. The docker tools also allow building custom images, while the most basic image can even be a ELinOS filesystem.

The containers are often designed stateless, in which case updating them is a question of downloading the new version and restarting the service. The simple update process also improves security – if the components are configured correctly, the update can be performed seamlessly.

The Docker images (and containers) are independent on the environment/packages provided by ELinOS. This makes ELinOS a suitable platform for running Docker-based software. Even the Docker registry (image storage server) is provided as a image, and thus custom hosting can be setup – without the need to use the public services.

Integration in ELinOS

To showcase the usage of Docker within ELinOS, we will be using a clean Node.js environment, which is part of the Docker base library. A minimal http server will be integrated. While it’s possible to use various packages from NPM, only the base Node component will be used for simplicity.

System Project Setup

System image preparation in ELinOS starts by choosing the base configuration of the project – either one of the pre-configured templates can be used, or an empty configuration with just a few options enabled. This enables to minimize the system size – which helps both security and quick deployment. CODEO tool will used in the following example – which is a GUI configuration tool based on Eclipse. 



Afterwards, the target board needs to be selected. This configures the Linux kernel and userspace binaries to properly function. We choose QEMU x86_64 board – the same architecture as the host, again to simplify the setup procedure:



User-mode QEMU networking will be used. In this configuration, all the network communication is effectively hidden from outside of QEMU. Thus QEMU has to be instructed to forward the required target machine ports and make them visible on localhost machine. For the demonstration, port 3000 will be used. Additionally, more than the default amount of memory is needed – 3000MiB will suffice for quick setup.

CODEO allows configuration of all these options via GUI, however it’s all translated to standard QEMU command line options when QEMU is eventually executed:



Docker Image Preparation

The docker image will be prepared on the host development machine and integrated on the target system. The following Node.js server code will be used:

//server.js

["SIGINT", "SIGTERM"].forEach((signal) => process.on(signal, () => process.exit(0)));
 
const interface = '0.0.0.0';
const port = 3000;
 
const http = require('http');
 
server = http.createServer((req, res) => {
    res.statusCode = 200;
    res.setHeader('Content-Type', 'text/html');
    res.end("Hello world at " + req.url + "");
});
 
server.listen(port, interface, () => {
    console.log(`Server running at http://${interface}:${port}/`);
});

It creates a http-based listener at port 3000 and returns a basic webpage with the requested URL as part of the contents. Signal handlers are included to allow terminating by pressing Ctrl+C, though this is not strictly needed (standard ‘docker kill‘ command would work as well).

To build the image, the following configuration file in the same directory is needed:

//Dockerfile

FROM node:slim
EXPOSE 3000
 
COPY server.js ./
 
CMD [ "node", "server.js" ]

Execute the commands:

docker build -t serverjs . docker save serverjs | gzip -9 > serverjs.tgz

This will generate an image that can be stored on the ELinOS target filesystem and started by docker. For example, the ELinOS filesystem editor can be used to put it into /image target directory:

System Test

Once the QEMU system image is built, executing it via CODEO will result into a system, where Docker can be used to load and run the previously generated nodejs image from serverjs.tar:

docker load < /image/serverjs.tgz docker run -p 3000:3000 --rm --name serverjs-run serverjs

This finishes the chain, the network channel now looks as follows:

Host machine  |    [localhost:3000]
   |          |           |
Qemu          |    forwards the localhost:3000 port to qemu simulated machine, $IP:3000
==============|===========|===================================================================================================
Linux         |           |
   |          |           |
Docker        |    configures Linux to forward the (qemu simulated-)localhost:3000 to port 3000 of the Docker container
--------------|-----------|---------------------------------------------------------------------------------------------------
Node.js       |           |
   |          |           |
server.js     |    listens on port 3000

Finally accessing the webpage from the development host is possible:

More information at www.sysgo.com/elinos