<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">\documentclass[onecolumn,letterpaper,11pt]{article}

%===================================================================
% Include common packages, environments, counters etc
\input{common_latex/common_latex_packages}
\input{common_latex/common_latex_lab_format}
\input{common_latex/common_latex_definitions}
%===================================================================
\setcounter{tocdepth}{2}
%===================================================================

\begin{document}
\wikidoc{file=Lab.DockerCompose}
\wikidoc{auth=mikerb@mit.edu}
\helplevelsubsec

%===================================================================
% Build the Cover Page in an environment style easy for wiki parsing
\begin{labcover}
\labtitle{Docker Lab 5 - }
\vspace{0.2in}
\labtitle{Using Docker Compose to Launch Groups of Containers}
\vspace{0.2in}
%\labcourse{Pavlab: Getting Started with Docker Containers}
\vspace{0.5in}
\labpng{0.85}{figures/lab5_header.png}
\vspace{0.25in}
\labdate{Jun 23rd, 2022}
\vspace{0.23in}
\labauth{Michael Benjamin, mikerb@mit.edu}
\labauth{Department of Mechanical Engineering}
\labauth{MIT, Cambridge MA 02139}
\labtoc{small}
\labtocdepth{2}
\end{labcover}

%===================================================================
% Add a blank page so first body page is on the right when doubleside
\blankpage

%===================================================================
% Begin the body of the text
%===================================================================
\section{Overview and Objectives}
%===================================================================

In this lab we use Docker Compose to begin to automate the launching
of docker containers. In the previous lab, only one container was
launched in the uField Alpha mission, so it was easy to just launch
the vehicle with the \cmd{docker run} command. When multiple
\cmd{docker run} commands are involved in starting a multi-vehicle or
multi-container mission, then hand-typing multiple \cmd{docker run}
commands become cumbersome. Normally this can be fixed by writing a
shell script to run a sequence of \cmd{docker run} commands. However,
the Docker Compose utility offers some advantage over shell scripts
for this task. In this lab we will get comfortable with Docker Compose,
remaining in the context of the uField Alpha mission to launch
multiple vehicles, each slightly different in configuration. 

\vspace{0.1in}
\noindent
The goals of this lab is:

\begin{labinfo}
%\labinfoheader{Topics:}
\begin{packed_itemize}[2]
\item Introduction to Docker Compose
\item Use simple Docker Compose file to launch a single vehicle
\item Use a more complex Docker Compose file to launch muliple vehicles

\end{packed_itemize}
\end{labinfo}


%=======================================================================
\section{A Simple Dockerfile Compose File to Launch a Vehicle}
%=======================================================================
In the previous lab, we launched the alpha mission by (a) launching a
shoreside community, and (b) launching a vehicle community.
In this section we progress through launching the uField Alpha mission:

\begin{packed_itemize}
\item Launch the shoreside locally (native OS), and the vehicle locally. 
\item Launch the shoreside locally, the vehicle launched with \cmd{docker run}
\item Launch the shoreside locally, the vehicle launched with \cmd{docker compose}
\end{packed_itemize}

%=======================================================================
\subsection{Launching Shoreside and Vehicle Locally (review)}

If both were run natively, it would look like:

\vspace{0.1in}
\begin{consoleverb}
# In Terminal 1:
$ ./launch_shoreside.sh  
\end{consoleverb}
\vspace{0.1in}

\begin{consoleverb}
# In Terminal 2:
$ ./launch_vehicle.sh  
\end{consoleverb}
\vspace{0.1in}

\noindent
In this case both the vehicle and shoreside have a "localhost" IP address. All
parameters take on default values and no command line arguments are needed.

%=======================================================================
\subsection{Launching Shoreside Locally, Vehicle with Docker Run (review)}

\noindent
If launching the vehicle using our Docker image, the vehicle is launched
instead with: 

\vspace{0.1in}
\begin{consoleverb}
# In Terminal 1:
$ ./launch_shoreside.sh  
\end{consoleverb}
\vspace{0.1in}

\begin{consoleverb}
# In Terminal 2:
$ docker run -it -p 9201:9201/udp --rm --env SHORE_IP=192.168.7.5 mikerbenj/moos-ivp-ualpha:latest
\end{consoleverb}
\vspace{0.1in}

\noindent
In the Docker version, the vehicle running in the container cannot
reach the shoreside community as "localhost". The container is instead
given the IP address of the local machine on the local router, as the
\var{SHORE\_IP} location.

%=======================================================================
\subsection{Launching Shoreside Locally, Vehicle with Docker Compose (new)}

In this step, the vehicle is again launched with a container, but we use
\cmd{docker compose} instead:

\vspace{0.1in}
\begin{consoleverb}
# In Terminal 1:
$ ./launch_shoreside.sh  
\end{consoleverb}
\vspace{0.1in}

\begin{consoleverb}
# In Terminal 2:
$ docker-compose up
\end{consoleverb}
\vspace{0.1in}

\noindent
The above \cmd{docker-compose} command presumes there is a
\var{docker-compose.yml} file in the folder where \cmd{docker-compose} was
run. The contents of this file look like:

\begin{fileverb}
# docker-compose.yml file
version: '3.7'
services:
  abe:
    image: "mikerbenj/moos-ivp-ualpha:latest"
    ports:
      - "9201:9201/udp"
    environment:
      - SHORE_IP=192.168.1.224
      - TIME_WARP=2
      - CONFIRM=no
\end{fileverb}
\vspace{0.15in}

\noindent
Much of the information passed previously to the \cmd{docker run} command
is now set in the \var{docker-compose.yml} file. When launching with
\cmd{docker compose}, we really don't want to prompted for confirmation
before continuing with the launch, so we set \var{CONFIRM=no}.

%=======================================================================
\section{Launching Multiple Vehicles with Docker Compose}
%=======================================================================

The uField Alpha mission is normally used for testing a wide variety
of single-vehicle-plus-shoreside missions. It does also support
connecting any number of additional vehicles. Normally subsequent
vehicles are given different unique names, starting positions, ip
addresses, and \app{pShare} listen ports. These are normally passed in
as command line arguments to the \var{launch\_vehicle.sh} script. When
only launching one vehicle as we have above, the default arguments
suffice. Here we describe how to launch three vehicles in the
uField Alpha mission, conveyed in Figure \ref{fig_ualpha4}

\vspace{0.15in}
\begin{figure}[H]
  \centering 
  \includegraphics[width=0.85\textwidth]{figures/ualpha3.png}
  \caption{The uField Alpha mission is run with four simulated vehicle
    each running in their own container, launched with \cmd{docker-compose}.}
\label{fig_ualpha4}
\end{figure}
\vspace{0.1in}

%=======================================================================
\subsection{A First Version of a Multi-Vehicle Docker Compose}

\noindent
Below is the \var{docker-compose.yml} file for bringing up all three
vehicles in three separate containers.

\vspace{0.1in}
\begin{fileverb}
version: '3.7'
services:
  abe:
    image: "mikerbenj/moos-ivp-ualpha:latest"
    ports:
      - "9201:9201/udp"
    environment:
      - SHORE_IP=192.168.1.224
      - TIME_WARP=2
      - CONFIRM=no
  ben:
    image: "mikerbenj/moos-ivp-ualpha:latest"
    ports:
      - "9202:9202/udp"
    environment:
      - SHORE_IP=192.168.1.224
      - TIME_WARP=2
      - VNAME=ben
      - CONFIRM=no
      - INDEX=2
      - START_POS=20,0
  cal:
    image: "mikerbenj/moos-ivp-ualpha:latest"
    ports:
      - "9203:9203/udp"
    environment:
      - SHORE_IP=192.168.1.224
      - TIME_WARP=2
      - VNAME=cal
      - CONFIRM=no
      - INDEX=3
      - START_POS=40,0
\end{fileverb}
\vspace{0.1in}

\noindent
Just as a reminder, each of these containers, one for vehicles abe, ben, and cal,
are launched from the same image \var{mikerbenj/moos-ivp-ualpha:latest}, with
an \var{entrypoint.sh} script:

\vspace{0.1in}
\begin{fileverb}
#!/bin/bash                                                                             
cd /home/moos/moos-ivp/ivp/missions/ufld_alpha
./launch_vehicle.sh
\end{fileverb}
\vspace{0.1in}

\noindent
If vehicle \var{cal} were launched on the command line in our local
operating system, the \var{launch\_vehicle.sh} script would probably look
like:

\vspace{0.1in}
\begin{consoleverb}
$ ./launch_vehicle.sh --shore=192.168.1.224 --vname=cal --noconfirm --index=3 \
--startpos=40,0 2 
\end{consoleverb}
\vspace{0.1in}

\noindent
These command line arguments are replaced by setting environment variables.
Again, leaving Docker aside for a moment, the above launch could also be
accomplished with:

\vspace{0.1in}
\begin{consoleverb}
$ export SHORE_IP=192.168.1.224
$ export TIME_WARP=2
$ export VNAME=cal
$ export CONFIRM=no
$ export INDEX=3
$ export START_POS=40,0
$ ./launch_vehicle.sh  
\end{consoleverb}
\vspace{0.1in}

\noindent
Or, if we don't like setting environment variables with persistent scope that
are only needed for a one-time use, we could do this instead:
\vspace{0.1in}
\begin{consoleverb}
$ SHORE_IP=192.168.1.224 TIME_WARP=2 VNAME=cal CONFIRM=no INDEX=3 \
START_POS=40,0 ./launch_vehicle.sh  
\end{consoleverb}
\vspace{0.1in}

\noindent
And, final review point, these environment variables are handled in the
\var{launch\_vehicle.sh} script because they are initialized from the
incoming value of the environment variable if it is set. For example, inside
the \var{launch\_vehicle.sh} script one will find:

\vspace{0.1in}
\begin{fileverb}
START_POS=${START_POS:-0,0}
\end{fileverb}
\vspace{0.1in}

\noindent
The \var{START\_POS} variable will default to \var{(0,0)} if the process
that called \var{launch\_vehicle.sh} did not have the environment variable
\var{START\_POS} set. If it {\em is} set in the calling process, it will
be used instead as the default value for \var{START\_POS}. If the caller
also invokes the \var{--startpos=x,y} command line argument, the command line
argument overwrites any other value.

%=======================================================================
\subsection{A More Flexible Version of a Multi-Vehicle Docker Compose}

In the \var{docker-compose.yml} file above, there are a couple parameters
for each vehicle that may change often between runs. The time warp, and
the IP address of the shoreside computer. When or if these values change,
they must be edited for each service/container/vehicle block. We handle
this by setting them from an environment variable passed in when launching
\cmd{docker-compose}. Our new file looks like:

\vspace{0.1in}
\begin{fileverb}
version: '3.7'
services:
  abe:
    image: "mikerbenj/moos-ivp-ualpha:latest"
    ports:
      - "9201:9201/udp"
    environment:
      - SHORE_IP=${SHORE_IP:-192.168.1.224}     &lt;--change
      - TIME_WARP=${TIME_WARP:-2}               &lt;--change
      - CONFIRM=no
  ben:
    image: "mikerbenj/moos-ivp-ualpha:latest"
    ports:
      - "9202:9202/udp"
    environment:
      - SHORE_IP=${SHORE_IP:-192.168.1.224}     &lt;--change
      - TIME_WARP=${TIME_WARP:-2}               &lt;--change
      - VNAME=ben
      - CONFIRM=no
      - INDEX=2
      - START_POS=20,0
  cal:
    image: "mikerbenj/moos-ivp-ualpha:latest"
    ports:
      - "9203:9203/udp"
    environment:
      - SHORE_IP=${SHORE_IP:-192.168.1.224}     &lt;--change
      - TIME_WARP=${TIME_WARP:-2}               &lt;--change
      - VNAME=cal
      - CONFIRM=no
      - INDEX=3
      - START_POS=40,0
\end{fileverb}
\vspace{0.1in}

\noindent
When running \cmd{docker-compose} on the command line, the values are
passed in like this:

\vspace{0.1in}
\begin{consoleverb}
$ SHORE_IP=192.168.7.5 TIME_WARP=8 docker-compose up
\end{consoleverb}
\vspace{0.1in}


\newpage



\noindent
{\bf URL References}
\footnotesize

\vspace{0.1in}


\noindent
[1] Mouat, Adrian. Using Docker: Developing and Deploying Software
with Containers (Kindle Locations 488-490). O'Reilly Media. Kindle
Edition.

\end{document}

</pre></body></html>