eCAL in Docker
Contents
eCAL in Docker#
Here we will show how to deploy eCAL into a docker container, and how to use its image from other containers.
Important
This will work with eCAL 5.10 and up. Older eCAL versions will lack Shared Memory communication when being run in a docker container.
Prerequisite#
Install Docker as described in the Docker installation documentation
See also
If you have problems regarding to the installation, check these links:
Optional: If you want your docker container to talk to eCAL Nodes on other machines: Set up you multicast routes on the host.
Optional: If you want to use eCAL on your host to subscribe to data from your docker containers: Install eCAL on the host.
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_in_docker ├─docker-compose.yaml
| ├─ ecal_runtime_container | └─Dockerfile
| ├─ pub_container | ├─Dockerfile
| ├─CMakeLists.txt
| └─main.cpp
| └─ sub_container ├─Dockerfile
├─CMakeLists.txt
└─main.cpp
eCAL runtime container#
Create the file
ecal_runtime_container/Dockerfile
and paste the following installation commands:1# Base image: 2FROM ubuntu:focal 3 4# Install eCAL from PPA: 5RUN apt-get update && \ 6 apt-get install -y software-properties-common && \ 7 rm -rf /var/lib/apt/lists/* 8RUN add-apt-repository ppa:ecal/ecal-latest 9RUN 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. 13RUN 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. 17RUN 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 22RUN ecal_config
Build the image:
cd ecal_in_docker sudo docker build . --rm -t ecal-runtime
Test the image
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 runecal_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 orecal_sample_person_rec
.Note
--ipc=host
will enable Shared Memory communication with your host system and other docker containers that are started with the same parameter. This is important for local communication.--network=host
will share the host’s network. This is important for network communcation with other machines. It is also important for local shared memory communication, as it affects the hostname of the container. The hostname is used to determine whether an eCAL topic is avaialble via shared memory.--pid=host
will share the Process-ID range with the host. Otherwise processes from different containers may get the same Process ID, which will prevent communication between those two processes.
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.
Create a file
pub_container/Dockerfile
and paste the following content:1#ecal base image: 2FROM ecal-runtime 3 4WORKDIR /src/pub 5 6COPY CMakeLists.txt main.cpp ./ 7RUN cmake . && make 8CMD ./hello_world_snd
Create publisher source code:
pub_container/main.cpp
1#include <ecal/ecal.h> 2#include <ecal/msg/string/publisher.h> 3 4#include <iostream> 5#include <thread> 6 7int 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}
Create file
pub_container/CMakeLists.txt
1cmake_minimum_required(VERSION 3.0) 2set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) 3 4project(hello_world_snd) 5 6set(CMAKE_CXX_STANDARD 14) 7set(CMAKE_CXX_STANDARD_REQUIRED ON) 8 9find_package(eCAL REQUIRED) 10 11set(source_files 12 main.cpp 13) 14 15add_executable(${PROJECT_NAME} ${source_files}) 16 17target_link_libraries(${PROJECT_NAME} 18 eCAL::core 19)
Build the image:
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.
Create a file:
sub_container/Dockerfile
1#ecal base image: 2FROM ecal-runtime 3 4WORKDIR /src/sub 5 6COPY CMakeLists.txt main.cpp ./ 7RUN cmake . && make 8CMD ./hello_world_rec
Create subscriber source code:
sub_container/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 8void HelloWorldCallback(const std::string& message) 9{ 10 std::cout << "Received Message: " << message << std::endl; 11} 12 13int 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}
Create file
sub_container/CMakeLists.txt
1cmake_minimum_required(VERSION 3.0) 2set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) 3 4project(hello_world_rec) 5 6set(CMAKE_CXX_STANDARD 14) 7set(CMAKE_CXX_STANDARD_REQUIRED ON) 8 9find_package(eCAL REQUIRED) 10 11set(source_files 12 main.cpp 13) 14 15add_executable(${PROJECT_NAME} ${source_files}) 16 17target_link_libraries(${PROJECT_NAME} 18 eCAL::core 19)
Build the image:
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
.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.
In the parent folder create file
docker-compose.yaml
and paste the following content:1version: "3" 2 3services: 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
You can now use that docker-compose to build/run the publisher and subscriber containers:
sudo docker-compose build sudo docker-compose up