Link Search Menu Expand Document

Using Docker Compose options in Swarm

Certain Docker Compose options, like devices, cap_add etc., cannot be used when deploying applications on Docker Swarm clusters. Below we describe this on the example of devices option, and provide workarounds to this and other associated issues.

Single node Docker endpoint

In Nuvla, the application definition page for apps to be deployed on Docker infrastructures supports devices option, as in Docker compose. Use this option to add a host device to the container

services:
    myIoTservice:
        image: your_repo/your_image
        devices:
            - "/dev/ttyUSB0"

Please note that by doing this, you’ll be deploying your app as a native Docker Compose application, and thus all of your containers will be executed within the host that is exposing your infrastructure endpoint.

Multi-node Docker swarm cluster

In the case of multi-node Docker cluster running in Docker swarm mode, the addition of the host devices to the containers is not natively supported by Docker. However, it is still possible to make this functionality work, following a docker-in-docker approach.

Docker-in-docker with sidecar container

Docker Swarm does not support devices, capabilities nor privileged mode. However, you can work around this limitation by adapting your compose file as shown below:

services:
    myIoTservice:
        image: your_repo/your_image
        devices:
            - "/dev/ttyUSB0"

becomes

services:
    myIoTserviceWrapper:
        image: docker
        volumes: 
            - /var/run/docker.sock:/var/run/docker.sock
        command: >
          sh -c '
          # Obtains the name of the base container.
          export PARENT=$$(basename "$$(head /proc/1/cgroup)");
          # Defines the name of the conatiner with your app for sidecar to follow.
          export CONT_NAME=myapp_$$(date +%s);
          docker run --rm -d -v /var/run/docker.sock:/var/run/docker.sock -e PARENT -e CHILD=$${CONT_NAME} sixsq/wrapper-cleaner:master 
          && docker run --rm --device /dev/ttyUSB0 --name $${CONT_NAME} your_repo/your_image'

This uses a sidecar container from sixsq/wrapper-cleaner image provided by SixSq that will make sure the container running your application is properly terminated after the deployment gets terminated.

Depending on the type of the device and access rights, you might need to provide extra capabilities to your container:

&& docker run --cap-add=SYS_ADMIN --device /dev/fuse --rm --name $${CONT_NAME} your_repo/your_image

In some cases, if apparmor on the host node is configured too strictly, you might need to use --privileged flag:

&& docker run --privileged --device /dev/fuse --rm --name $${CONT_NAME} your_repo/your_image

Application publishing ports

If your application is a service needing to publish ports, use the following example.

The example is based on Jupyter Notebook needing to add host’s /dev/fuse device to remotely mount S3 buckets. Jupyter Notebook runs web UI (TCP:8888) that is published on the host.

version: "3.7"

# Environment variables provided by Nuvla.
# They have to be used in publication of hostname deployment parameter.
x-nuvla_env: &nuvla_env
  NUVLA_DEPLOYMENT_ID: $NUVLA_DEPLOYMENT_ID
  NUVLA_ENDPOINT: $NUVLA_ENDPOINT
  NUVLA_API_KEY: $NUVLA_API_KEY
  NUVLA_API_SECRET: $NUVLA_API_SECRET

services:
  jupyter:
    image: docker
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      <<: *nuvla_env
    command: >
      sh -c 'set -x;
      export PARENT=$$(basename "$$(head /proc/1/cgroup)");
      export CONT_NAME=myapp_$$(date +%s);
      # Get IP address of the cluster node where the container is running.
      export NODE_IP=$$(docker node inspect self -f "{ { .Status.Addr } }");
      docker run -d -v /var/run/docker.sock:/var/run/docker.sock -e PARENT -e CHILD=$${CONT_NAME} nuvladev/docker-cleaner:linux
      && docker run -v /var/run/docker.sock:/var/run/docker.sock --cap-add=SYS_ADMIN --device /dev/fuse --rm -P --name $${CONT_NAME} -e NODE_IP -e CONT_NAME -e NUVLA_DEPLOYMENT_ID -e NUVLA_ENDPOINT -e NUVLA_API_KEY -e NUVLA_API_SECRET myrepo/myimage:tag'

In the command option above, we’ve added the discovery of the node’s IP (NODE_IP) where your container is running, and from which it publishes its ports (-P parameter is used). You need to wrap your image’s entrypoint into a script that would first publish the hostname deployment parameter and then start your application. The full example of how this must be done can be found here. Here is the link to the entrypoint script, and this is the “Nuvla integration” script that publishes the required deployment parameters to Nuvla.