Skip to content

eCAL in Docker

Here we will show how to deploy eCAL into a docker container, and how to use its image from other containers.

Prerequisite

Getting Started

In this tutorial we are going to create:

  • A general purpose eCAL Docker container
  • A publisher container with a custom Hello World Publisher
  • A subscriber container receiving the Hello World data.

The file hierarchy that we are going to follow:

eCAL runtime container

  1. Create the file ecal_runtime_container/Dockerfile and paste the following installation commands:

    Dockerfile
    1
    # Base image:
    2
    FROM ubuntu:focal
    3
    4
    # Install eCAL from PPA:
    5
    RUN apt-get update && \
    6
    apt-get install -y software-properties-common && \
    7
    rm -rf /var/lib/apt/lists/*
    8
    RUN add-apt-repository ppa:ecal/ecal-latest
    9
    RUN apt-get install -y ecal
    10
    11
    # Install dependencies for compiling the hello world examples.
    12
    # You can omit this, if you don't want to build applications in the container.
    13
    RUN apt-get install -y cmake g++ libprotobuf-dev protobuf-compiler
    14
    15
    # Set network_enabled = true in ecal.ini.
    16
    # You can omit this, if you only need local communication.
    17
    RUN awk -F"=" '/^network_enabled/{$2="= true"}1' /etc/ecal/ecal.ini > /etc/ecal/ecal.tmp && \
    18
    rm /etc/ecal/ecal.ini && \
    19
    mv /etc/ecal/ecal.tmp /etc/ecal/ecal.ini
    20
    21
    # Print the eCAL config
    22
    RUN ecal_config
  2. Build the image:

    Terminal window
    cd ecal_in_docker
    sudo docker build . --rm -t ecal-runtime
  3. Test the image

    Terminal window
    sudo docker run --rm -it --ipc=host --pid=host --network=host ecal-runtime

    At this point you are in the docker container. You can exit it with exit. If you run ecal_sample_person_snd in the docker container and have an eCAL installation on your host, you can subscribe to the data via the eCAL Monitor or ecal_sample_person_rec.

Publisher container

The publisher container will be built on top of the ecal-runtime container. It will contain the Hello World Sample from the Getting Started Section.

  1. Create a file pub_container/Dockerfile and paste the following content:

    Dockerfile
    1
    #ecal base image:
    2
    FROM ecal-runtime
    3
    4
    WORKDIR /src/pub
    5
    6
    COPY CMakeLists.txt main.cpp ./
    7
    RUN cmake . && make
    8
    CMD ./hello_world_snd
  2. Create publisher source code: pub_container/main.cpp

    main.cpp
    1
    #include <ecal/ecal.h>
    2
    #include <ecal/msg/string/publisher.h>
    3
    4
    #include <iostream>
    5
    #include <thread>
    6
    7
    int main(int argc, char** argv)
    8
    {
    9
    // Initialize eCAL. The name of our Process will be "Hello World Publisher"
    10
    eCAL::Initialize(argc, argv, "Hello World Publisher");
    11
    12
    // Create a String Publisher that publishes on the topic "hello_world_topic"
    13
    eCAL::string::CPublisher<std::string> publisher("hello_world_topic");
    14
    15
    // Create a counter, so something changes in our message
    16
    int counter = 0;
    17
    18
    // Infinite loop (using eCAL::Ok() will enable us to gracefully shutdown the
    19
    // Process from another application)
    20
    while (eCAL::Ok())
    21
    {
    22
    // Create a message with a counter an publish it to the topic
    23
    std::string message = "Hello World " + std::to_string(++counter);
    24
    std::cout << "Sending message: " << message << std::endl;
    25
    publisher.Send(message);
    26
    27
    // Sleep 500 ms
    28
    std::this_thread::sleep_for(std::chrono::milliseconds(500));
    29
    }
    30
    31
    // finalize eCAL API
    32
    eCAL::Finalize();
    33
    }
  3. Create file pub_container/CMakeLists.txt

    CMakeLists.txt
    1
    cmake_minimum_required(VERSION 3.0)
    2
    set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON)
    3
    4
    project(hello_world_snd)
    5
    6
    set(CMAKE_CXX_STANDARD 14)
    7
    set(CMAKE_CXX_STANDARD_REQUIRED ON)
    8
    9
    find_package(eCAL REQUIRED)
    10
    11
    set(source_files
    12
    main.cpp
    13
    )
    14
    15
    add_executable(${PROJECT_NAME} ${source_files})
    16
    17
    target_link_libraries(${PROJECT_NAME}
    18
    eCAL::core
    19
    )
  4. Build the image:

    Terminal window
    cd pub_container
    sudo docker build . --rm -t ecal-publisher:1.0.0

Subscriber container

The subscriber container will also be based on the ecal-runtime container and contain the Hello World Sample from the Getting Started Section.

  1. Create a file: sub_container/Dockerfile

    Dockerfile
    1
    #ecal base image:
    2
    FROM ecal-runtime
    3
    4
    WORKDIR /src/sub
    5
    6
    COPY CMakeLists.txt main.cpp ./
    7
    RUN cmake . && make
    8
    CMD ./hello_world_rec
  2. Create subscriber source code: sub_container/main.cpp

    main.cpp
    1
    #include <ecal/ecal.h>
    2
    #include <ecal/msg/string/subscriber.h>
    3
    4
    #include <iostream>
    5
    #include <thread>
    6
    7
    // Callback for receiving messages
    8
    void HelloWorldCallback(const std::string& message)
    9
    {
    10
    std::cout << "Received Message: " << message << std::endl;
    11
    }
    12
    13
    int main(int argc, char** argv)
    14
    {
    15
    // Initialize eCAL
    16
    eCAL::Initialize(argc, argv, "Hello World Subscriber");
    17
    18
    // Create a subscriber that listenes on the "hello_world_topic"
    19
    eCAL::string::CSubscriber<std::string> subscriber("hello_world_topic");
    20
    21
    // Set the Callback
    22
    subscriber.AddReceiveCallback(std::bind(&HelloWorldCallback, std::placeholders::_2));
    23
    24
    // Just don't exit
    25
    while (eCAL::Ok())
    26
    {
    27
    std::this_thread::sleep_for(std::chrono::milliseconds(500));
    28
    }
    29
    30
    // finalize eCAL API
    31
    eCAL::Finalize();
    32
    }
  3. Create file sub_container/CMakeLists.txt

    CMakeLists.txt
    1
    cmake_minimum_required(VERSION 3.0)
    2
    set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON)
    3
    4
    project(hello_world_rec)
    5
    6
    set(CMAKE_CXX_STANDARD 14)
    7
    set(CMAKE_CXX_STANDARD_REQUIRED ON)
    8
    9
    find_package(eCAL REQUIRED)
    10
    11
    set(source_files
    12
    main.cpp
    13
    )
    14
    15
    add_executable(${PROJECT_NAME} ${source_files})
    16
    17
    target_link_libraries(${PROJECT_NAME}
    18
    eCAL::core
    19
    )
  4. Build the image:

    Terminal window
    cd sub_container
    sudo docker build . --rm -t ecal-subscriber:1.0.0

Run the docker containers

  • You can run the publisher and subscriber images manually with docker run.

    Terminal window
    sudo docker run --rm -it --ipc=host --network=host --pid=host ecal-subscriber:1.0.0
    sudo docker run --rm -it --ipc=host --network=host --pid=host ecal-publisher:1.0.0
  • You can also use the docker-compose file to manage multiple containers.

    1. In the parent folder create file docker-compose.yaml and paste the following content:

      docker-compose.yaml
      1
      version: "3"
      2
      3
      services:
      4
      subscriber:
      5
      build: ./sub_container
      6
      image: ecal-subscriber:1.0.0
      7
      container_name: ecal-subscriber
      8
      network_mode: host
      9
      ipc: host
      10
      pid: host
      11
      publisher:
      12
      build: ./pub_container
      13
      image: ecal-publisher:1.0.0
      14
      container_name: ecal-publisher
      15
      network_mode: host
      16
      ipc: host
      17
      pid: host
    2. You can now use that docker-compose to build/run the publisher and subscriber containers:

      Terminal window
      sudo docker-compose build
      sudo docker-compose up

Seamless IPC-Communication across host borders

In eCAL, you are able to set host belonging over network borders by utilizing the ecal.ini configuration file with the same host_group_name - in the following steps, you will learn how to set this up.

  1. To encapsulate your container network from your host network, you need to create a new docker network with the following command:

    Terminal window
    sudo docker network create --driver=bridge --subnet=10.0.10.0/24 my_network
  2. Edit your ecal.ini and run your Container within the newly created docker network

    • You will use our previously discussed ecal-runtime-image for the next step.

    • First, open /etc/ecal/ecal.ini from your preferred editor.

    • Search for the line network_enabled and set it to true.

    • Search for the line host_group_name and write your preferred name.

    • Save and close the ecal.ini file.

    • Now your ecal.ini file is prepared. We want to use it not only for our Host-System but also for our Container, so we don’t need to edit the ecal.ini in our Container again. To achieve that, run following command to start your container:

    Terminal window
    sudo docker run --rm -it --ipc=host --pid=host --network=my_network --name=container1 -h=container1 --ip=10.0.10.10 -v /etc/ecal/ecal.ini:/etc/ecal/ecal.ini ecal-runtime
    • You should now be inside the root shell of your Container. Check if your ecal.ini file is correct.

    • Now your Container is prepared and configured correctly, so we are ready to start an eCAL example.

    Terminal window
    ecal_sample_person_snd
  3. Configure the Host network

    • eCAL is sending UDP messages to a multicast IP group 239.0.0.0/24, further information in Getting Started Section. The idea is now, to successfully receive those messages from your previously started container on your host. For that, you need to add a route to your routing table. By typing ifconfig in your shell, you can identify the right docker network. In our case, the prefix of the docker network is always br followed by random numbers. After identifying the right network, run following command.
    Terminal window
    sudo ip route add 239.0.0.0/24 dev <br-xxx> metric 1
    • Review your network configuration. Your eCAL-Monitor should resemble this example: eCAL Monitor
  4. (optional) After adding the route, you register the Container with IP address and name in /etc/hosts for DNS resolution, enabling easy access to it by hostname within the network.

    Terminal window
    sudo nano /etc/hosts

    console /etc/hosts

When you are done, all eCAL nodes can communicate seamlessly from docker to the host and vice versa.