Skip to content

tutorials vrx_docker_scripted

crvogt edited this page Aug 7, 2023 · 15 revisions

Option 2: Automated Image Creation using a Dockerfile

In this tutorial we automate the entire process of creating the competitor image using a script called a Dockerfile.

  • This is one of two options for building a competitor image.
  • The first option is described here.
  • The primary advantage of this approach is that it is self-documenting and easily reproducible.

Build a Working Image

Follow the steps below to get a minimal version of a competitor image up and running:

Step 1: Create your Dockerfile

  • Create a directory to store your Dockerfile and any other files needed for your image.

    mkdir ~/my_vrx_docker; cd ~/my_vrx_docker
    
  • Create a file called Dockerfile, open it in a text editor, and copy in the following:

    FROM ros:humble-ros-base-jammy
    
    # Set up timezone
    ENV TZ=Etc/UTC
    RUN echo $TZ > /etc/timezone && \
      ln -fs /usr/share/zoneinfo/$TZ /etc/localtime
    
    # Install required utilities
    RUN apt update \
    && apt install -y --no-install-recommends \
        build-essential \
        cmake \
        git \
        gnupg2 \
        nano \
        python3-dbg \
        python3-setuptools \
        python3-vcstool \
        ruby \
        sudo \
    && rm -rf /var/lib/apt/lists/* \
    && apt clean -qq
    
    # Set up locale
    RUN sudo apt update && sudo apt install locales \
    && sudo locale-gen en_US en_US.UTF-8 \
    && sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
    
    ARG ROSDIST=humble
    
    # Install example ROS package
    RUN apt update \
    && apt install -y --no-install-recommends \
      ros-${ROSDIST}-actuator-msgs \
    && rm -rf /var/lib/apt/lists/* \
    && apt clean -qq
    
    # Copy over script to Docker container
    COPY ./run_my_system.bash /
    
    # Use your ros_entrypoint
    COPY ./ros_entrypoint.sh /
    

    This script does the following:

    • The FROM command tells Docker to begin from the ros:humble-ros-base-jammy image.
    • The RUN command executes arbitrary commands as if running them on the command line. In this case, they execute apt to install utilities and ROS packages. We don't actually need these packages yet. They are included as an examples of packages that might be useful later.
      • Note that a common pattern for RUN commands is to chain sequences of commands together using && so each command is only run if the previous command succeeds. The reason for grouping commands together in this way is that environmental variables do not persist from one line of the Dockerfile to the next. Only changes that would be written to disk persist.
      • Conversely, we clear out temporary files after each call to apt to avoid writing unneeded files to the disk and cluttering the image.
    • The COPY command copies files from the host file system into the specified location in the docker image. In this case, the two files referenced do not yet exist. We will create them in the next step.

Step 2: Create scripts locally

By default, the ros:humble-ros-base-jammy image we started from is configured to look for and execute the ros_entrypoint.sh script when it is run. We will create this script and use it to call a custom script that will control our WAMV.

  • First, use a text editor to create a file called ros_entrypoint.sh and copy the following text into the file:

    #!/bin/bash
    set -e
    
    # setup ros environment
    source "/opt/ros/$ROS_DISTRO/setup.bash"
    
    /run_my_system.bash
    
  • Run chmod +x ros_entrypoint.sh to make it executable.

  • Now use a text editor to create a file called run_my_system.bash and copy the following text into the file:

    #!/bin/bash
    
    # Start ROS2 daemon for discovery  if not already started
    ros2 topic list
    
    # Send forward command
    RATE=1
    CMD=2
    echo "Sending forward command"
    ros2 topic pub -r ${RATE} -p 20 /wamv/thrusters/left/thrust std_msgs/msg/Float64 "{data: ${CMD}}" &
    ros2 topic pub -r ${RATE} -p 20 /wamv/thrusters/right/thrust std_msgs/msg/Float64  "{data: ${CMD}}" 
    
  • Run chmod +x run_my_system.bash to make it executable.

Step 3: Build your image

We will now use the docker build command to create an image from the Dockerfile.

  • First, set up some useful variables, substituting the appropriate values below:

    USERNAME=<dockerhub_username>
    IMAGE_NAME=<name_your_image>
    TAG=<image_version>
    
    • USERNAME must match the username of your Dockerhub account.
    • IMAGE_NAME should describe your image. It will be used to find your image locally and on Dockerhub.
    • TAG can be anything, but we recommend you use it to store version information.
  • Build the image:

    docker build --tag ${USERNAME}/${IMAGE_NAME}:${TAG} .
    
  • For example:

    USERNAME=virtualrobotx
    IMAGE_NAME=vrx-competitor-example
    TAG=v1.2023
    docker build --tag ${USERNAME}/${IMAGE_NAME}:${TAG} .
    

Step 4: Test the image

  • Run your image
    docker run -it ${USERNAME}/${IMAGE_NAME}:${TAG}
    
    • This will use the image you created in the previous step to run a container.
    • The container will call the ros_entrypoint.sh script, which will execute /run_my_system.bash.
    • The -it option specifies that we want an interactive terminal. This is useful for our current example because it allows us to see terminal output and kill the container with CTRL+C when we're finished. (Without this option we would need to use a docker stop command to close the container.)
  • You should see output that looks something like:
    /parameter_events
    /rosout
    Sending forward command
    publisher: beginning loop
    publishing #1: std_msgs.msg.Float64(data=2.0)
    
    publisher: beginning loop
    publishing #1: std_msgs.msg.Float64(data=2.0)
    
    
  • To verify that the script is working as expected, open a new terminal and get the container ID:
    docker container ls
    
    Copy the container ID in the far left column.
  • Now we will create a second connection to the running container:
    docker exec -it <container_id> bash
    
    Where <container_id> is the ID you just copied. This command will open an interactive bash session into the container.
  • In this session, check that your script is publishing data as expected.
    source /opt/ros/humble/setup.bash
    ros2 topic echo /wamv/thrusters/left/thrust
    
  • You should receive /wamv/thrusters/left/thrust data (which is set to 2.0 in the script).
  • Switch back to your original terminal and hit CTRL+C to exit the container.
  • Note that this will also terminate your secondary bash session.

Step 5: Push your image to Dockerhub

  • Run docker login and enter your credentials.
  • Push your image:
    docker push ${USERNAME}/${IMAGE_NAME}:${TAG}
    
  • You should be able to log onto your Dockerhub account at https://hub.docker.com and see your new repository.

Optional: Make your repository private

  • If you want to keep your repository private, you can click on your repository, then click Settings, then Make Private.
  • To ensure we can access and evaluate your image, you must click Collaborators and add virtualrobotx.

Developing your image further

  • Once you have a minimal image working, you can continue to develop it by adding commands to the Dockerfile and rebuilding.
  • For efficiency, Docker will only rerun build commands starting from the first line in the Dockerfile which has changed, so it is advantageous to put commands that are unlikely to change (or take a long time to run) toward the top of the file.
  • See Docker's Best Practices for Writing Dockerfiles for many useful tips on how to write good Dockerfiles, as well as an extensive list of available commands.

Building ROS Workspaces using a Dockerfile

  • Because each command in a Dockerfile runs with a clean environment, commands that depend on sourcing many environment variables can be tricky.
  • Commands that rely on ROS tools are an obvious example.
  • The solution is generally to make sure the same run command both sets up the environment and immediately calls the command that requires it.
  • For specific working examples, please see the minimal working examples provided in the vrx-repository and discussed in the next tutorial.
Back: Option 1: Interactive Up: VRX Docker Image Overview Next: Minimal Working Examples
Clone this wiki locally