In this guide we will use Airbotics to deploy Docker containers to our robot.

Goals

  • Upload your first docker-compose.
  • Connect your robot to a private container registry.
  • Deploy the docker-compose to your robot to start the containers.
  • Upload and deploy a second docker-compose to stop the old containers and start the new ones.

Prerequisites

  • An account with Airbotics.
  • A valid API key with the compose-files:read and compose-files:write permissions.
  • Device running Ubuntu 22.04 with:
    • ROS 2 installed
    • Airbotics agent installed
    • Docker engine installed
  • Physical or SSH access to your device.

Prepare environment

Ensure you have the following two environment variables set before you get started:

export AIR_API_KEY=<your_api_key>
export AIR_ROBOT_ID=<your_robot_id>
export COMPOSE_ID="demo-compose-app-v1"

Run the agent

airbotics

Prepare a docker-compose

The first step in a new deployment is to prepare a new docker-compose file. In this guide, the docker-compose starts two containers, one from a public registry and another from a private Dockerhub registry. Take note of the json representation of the compose file, we will use this in the next step.

If you’re using your own docker-compose file that’s written in yaml you can convert it to json with the following: docker compose -f <path-to-docker-compose.yaml> convert --format json

version: "3.8"
services:
  demo_public:
    image: alpine:latest
    container_name: air_public
    command: ["tail", "-f", "/dev/null"]
  demo_private:
    image: airboticsdev/test:v1
    container_name: air_private
    command: ["tail", "-f", "/dev/null"]

Create the docker-compose

Before we deploy the compose file to a robot, an API call must be made to create the compose file. Provide an id and name and set the content to the json serialized compose file:

curl --request POST https://api.airbotics.io/compose-files \
--header 'content-type: application/json' \
--header "air-api-key: $AIR_API_KEY" \
--data '{
    "id": "'$COMPOSE_ID'", 
    "name": "Robot application v1",
    "content": {
      "services": {
        "demo_public": {
          "command": [
            "tail",
            "-f",
            "/dev/null"
          ],
          "container_name": "air_public",
          "image": "alpine:latest"
        },
        "demo_private": {
          "command": [
            "tail",
            "-f",
            "/dev/null"
          ],
          "container_name": "air_private",
          "image": "airboticsdev/test:v1"
        }
      }
    }
  }'

Authenticate with private container registry

Our docker-compose contains the image airboticsdev/test:v1 which is stored in a private Dockerhub registry. If your compose file contains images from a private registry, you must ensure the Agent can authenticate with the private registry otherwise the deployment will fail.

The following process may vary slightly depending on the container registry you are using (Dockerhub, ECR, GHCR etc.)

Log in Dockerhub and go to account settings > security > New Access Token, create a token with read permissions.

You will be presented with the personal access token, now you must set the following environment variables on your robot.

# This is the base URL for Dockerhub, change this if you are using another registry provider
export AIR_CONTAINER_REGISTRY_URL="https://index.docker.io/v1/"

# Your Dockerhub username
export AIR_CONTAINER_REGISTRY_USERNAME="airboticsdev"

# The personal access token you just created
export AIR_CONTAINER_REGISTRY_PASSWORD="dckr_pat_*******************"

Deploy the docker-compose

Now We are ready to deploy the compose file we created earlier (with id=$COMPOSE_ID) to a robot (with id=$AIR_ROBOT_ID). Make another API call, make sure you set the correct robot id in the url and the correct compose file id in the request body.

curl --request POST https://api.airbotics.io/robots/$AIR_ROBOT_ID/compose-file \
  --header "content-type: application/json" \
  --header "air-api-key: $AIR_API_KEY" \
  --data '{
    "id": "'$COMPOSE_ID'"
  }'

Verifying the state

You can verify the state of deployed docker-compose with the following API request:

curl --request GET https://api.airbotics.io/robots/$AIR_ROBOT_ID/compose-file \
  --header "content-type: application/json" \
  --header "air-api-key: $AIR_API_KEY"

If the agent is running and network connected and the compose file has finished pulling and starting the containers, you should see the status has been set to up.

If the agent is not running or is not network connected the status should be sent to pending_up. As soon as the agent starts or re-connects to the network, it will receive the compose file and attempt to start the containers.

If the status is error, something went wrong trying to execute the compose file, check the log output for more information.

You can also confirm the if the containers are running on your robot by running:

docker container ls --format 'table {{.Names}}\t {{.Image}} \t {{.Status}}'

Which should produce the output:

NAMES          IMAGE                    STATUS
air_private    airboticsdev/test:v1     Up 15 seconds
air_public     alpine:latest            Up 15 seconds

Upload and deploy a second docker-compose

Create a second compose file that contains two updated containers with a new id demo-compose-app-v2.

curl --request POST https://api.airbotics.io/compose-files \
--header 'content-type: application/json' \
--header "air-api-key: $AIR_API_KEY" \
--data '{
    "id": "demo-compose-app-v2", 
    "name": "Robot application v2",
    "content": {
      "services": {
        "demo_public_two": {
          "command": [
            "tail",
            "-f",
            "/dev/null"
          ],
          "container_name": "air_public",
          "image": "alpine:3.16"
        },
        "demo_private_two": {
          "command": [
            "tail",
            "-f",
            "/dev/null"
          ],
          "container_name": "air_private",
          "entrypoint": null,
          "image": "airboticsdev/test:v2"
        }
      }
    }
  }'

Now deploy the demo-compose-app-v1 compose file to the robot:

curl --request POST https://api.airbotics.io/robots/$AIR_ROBOT_ID/compose-file \
  --header "content-type: application/json" \
  --header "air-api-key: $AIR_API_KEY" \
  --data '{
    "id": "demo-compose-app-v2"
  }'

On the robot you can verify that the old containers were stopped the new ones were started:

docker container ls --format 'table {{.Names}}\t {{.Image}} \t {{.Status}}'

Which should produce the output:

NAMES          IMAGE                    STATUS
air_private    airboticsdev/test:v2     Up 5 seconds
air_public     alpine:3.16              Up 5 seconds

Wrapping up

In this guide you have created and deployed multiple docker-compose files to your robot to orchestrate which containers should be running.

You can use the same ideas and API to create customer portals, internal tools, applications and control panels using whatever tools you like to work with.

Cleaning up

To stop all containers running on your robot make the following API call:

curl --request DELETE https://api.airbotics.io/robots/$AIR_ROBOT_ID/compose-file \
  --header "content-type: application/json" \
  --header "air-api-key: $AIR_API_KEY" 

To stop delete the 2 docker-compose files we created make the following API calls:

curl --request DELETE https://api.airbotics.io/compose-files/$COMPOSE_ID \
  --header 'content-type: application/json' \
  --header "air-api-key: $AIR_API_KEY"
curl --request DELETE https://api.airbotics.io/compose-files/demo-compose-app-v1 \
  --header 'content-type: application/json' \
  --header "air-api-key: $AIR_API_KEY"