<-- back

Docker + toolchains

I heard about Docker and container technology last year, but I’ve never really used it, until I notice that toolchains, depending on the OS, are easy or hard to set-up. In that moment, I remembered the main characteristic of container technology: a piece of software with all its dependencies.

Docker is a tool designed to create, deploy and run an application by using containers, and a container is a unit of software that includes all its dependencies so the application runs regardless the installed OS, because all that application might need is already in the container.

Docker Terms

Let’s define some terms so it will be easier to explain later other concepts.

  • Docker Image: Binary file that includes everything needed to run an application.
  • Container: Runtime instance of the image. Your application runs on a container.
  • Dockerfile: File with commands to build a Docker image.

Building a Docker Image

First I need a Dockerfile, in which I should specify the dependencies of my application and configuration of the image (setting the path, for example). Since I’m working with cortex-M microcontrollers, I will need basically the following:

  • GNU ARM embedded toolchain link
  • The toolchain runs on a OS, I’ll use Alpine Linux
  • make to compile my applications

My Dockerfile is as follows:

FROM frolvlad/alpine-glibc:alpine-3.9_glibc-2.29 

# Set up a tools dev directory
WORKDIR /home/dev

# Packages that will be needed once
RUN apk --update --no-cache add --virtual build-dependencies  \
    w3m \
    wget \
    openssl \
    ca-certificates \
    bzip2-dev \
    tar

# Install make
RUN apk --update --no-cache add make

# Install GNU ARM toolchain
RUN wget https://developer.arm.com/-/media/Files/downloads/gnu-rm/8-2018q4/gcc-arm-none-eabi-8-2018-q4-major-linux.tar.bz2 \
    && tar xvf gcc-arm-none-eabi-8-2018-q4-major-linux.tar.bz2 \
    &&  rm gcc-arm-none-eabi-8-2018-q4-major-linux.tar.bz2 

RUN apk del build-dependencies

# Set up the compiler path
ENV PATH="/home/dev/gcc-arm-none-eabi-8-2018-q4-major/bin:${PATH}"

Building a Docker image is super easy, just run docker build -t IMAGE_TAG .. In my case

$ docker build -t daniel/test .

To verify my image was built, I used docker image ls.

$  docker image ls
REPOSITORY              TAG                     IMAGE ID            CREATED             SIZE
daniel/test             latest                  71b2c630bd2f        11 minutes ago      507MB
frolvlad/alpine-glibc   alpine-3.9_glibc-2.29   4e5eb981daf8        5 weeks ago         12.2MB

There are two images because my image daniel/test depends on the base Linux image frolvlad/alpine-glibc.

Note: if you want to know more about the Alpine Linux package manager (apk) check this link

Running an application

In your Host machine, where you have installed the Docker, will coexist the Docker environment, where the containers will run, and your files or working directories.

Docker environment

Let’s say I want to run my application which is in the application directory WD1 on the container C1. In order to do so, I have to mount WD1 on the container.

Docker mount

In Docker it is done as follows:

docker run \
    -rm \
    -v SOURCE_DIR:VOLUME \
    --name CONTAINER_NAME \
    -w DOCKER_WORKDIR  \
    IMAGE \ 
    COMMANDS

A short explanation:

  • run: command to run a container.
  • -rm: Automatically remove the container when it exits. The alternative would be to manually stop it and then remove it.
  • -v or --volume: Mounts a local directory (WD1) on a Docker volume
  • --name: Assign a name to the container, it’s useful for debugging or to recognize containers.
  • -w or --workdir: sets the working directory of the container to where any commands will be executed.

In my case I execute docker run with make on my WD1 to compile my C application.

$ docker run \
        -v $(pwd):/usr/src/myapp \
        -w /usr/src/myapp \
        --name my_container \
        daniel/test 
        make 

To check my running container docker container ls -a

CONTAINER ID   IMAGE          COMMAND     CREATED              STATUS       NAMES
971d9feae531   daniel/test    "make"      About a minute ago   Exited (0)   my_container

Dockerhub

There is a image repository called Dockerhub, where you can push your customized docker images or just pull one

Dockerhub workflow

Pushing your customized image

If you want to push your image, just execute

$ docker push YOUR_REPO

Of course you should have a Dockerhub account. More info in this link

Pulling an image

Pulling an image is also easy. First we need to find an image. For example if I want a docker image that has already the ARM toolchain, I will use the following command:

$ docker search arm eabi

I will get some outputs

NAME                        DESCRIPTION                                     STARS  AUTOMATED
modm/arm-none-eabi-gcc      Arm-none-eabi-gcc toolchain in a docker cont…   0      [OK]
thomasf/arm-eabi-toolchain  Build toolchain for cross compiling arm         0      [OK]
0fff/gcc-arm-none-eabi      This repo contains a set of different versio…   0      [OK]

It’s better to use official packages when possible because a good unofficial image will have a decent set of instructions about its use and how it was built. Unfortunately, most don’t have any information and you can waste a substantial amount of time trawling through the repositories looking for a suitable image. To filter packages I use the option --filter.

$ docker search --filter "is-official=true" arm

There is unfortunately no official ARM docker image, so I should search for a good one or build a customized as I did it before using Dockerfiles. Anyway, let’s say I want to try one of the unofficial images I found modm/arm-none-eabi-gcc. To pull the image I execute:

$ docker pull modm/arm-none-eabi-gcc      

To check if the image is in my host

$ docker image ls -a
REPOSITORY               TAG                     IMAGE ID            CREATED             SIZE
daniel/test              latest                  71b2c630bd2f        17 hours ago        507MB
modm/arm-none-eabi-gcc   latest                  1977388e9003        3 weeks ago         834MB
frolvlad/alpine-glibc    alpine-3.9_glibc-2.29   4e5eb981daf8        5 weeks ago         12.2MB

It’s already there.

Clean up

Using Docker can get very messy, so to clean up your host try the following commands:

  • To show running containers docker container ls -a
  • To delete use the ID, because is unique docker container rm CONTAINER ID. Or you can automatically delete the container with --rm, as it was already mentioned.
  • To delete images docker image rm IMAGE
  • To delete or prune all the containers docker container prune

Summary

Docker is a super useful tool to encapsulate your application and dependencies. Now I feel I can use any OS without worry whether my toolchains or apps will run. However, it can be also very annoying, specially when working with multiple containers, or trying to put too much in one container. It’s better to keep the containers simple, and use multiple simple containers to do your work rather than one big messy one.

If you think what I do is cool, send me a mail or buy me a beer. I'll appreciate it!
Similar Posts