Skip to content

Docker Swarm generic resources

There are two kind of generic resources, discreet and named.

Both are declared in /etc/docker/daemon.json and both are accessible in your containers as environment variables.

Named resources

Named resources should be used when you have a small number of things you want accessed. The best example is gpu devices. Each gpu has a UUID, which can be the name of this resources. Actually you could also use an index, and this index would have to be the "name" of the gpu.

Since we want to show they're generic, let's take something else than GPUs for this example.

Let's say you have 5 hamsters connected to your node, making an app run:

logo logo logo logo logo

They are named Robert, Lucie, Annie, James and Stacy.

You'll define one service that needs one hamster and one that needs three. We'll call them my_light_service and my_heavy_service.

Let's declare the hamsters in the /etc/docker/daemon.json. If this file doesn't exist on your system, you can create it.

Mine looks like this, note that you don't need the insecure registries part:

{
    "insecure-registries": ["127.0.0.1:5000"],
    "node-generic-resources": [
        "hamster=Robert",
        "hamster=Lucie",
        "hamster=Annie",
        "hamster=James",
        "hamster=Stacy"
    ]
}

Restart your Docker daemon with sudo service docker restart.

Then create a Docker swarm: docker swarm init

The hamster are declared, up and ready to go! You can check they're here with docker node ls and docker node inspect.

Creating services with the CLI

It's time to create services and hit those hamsters!

First we'll create the services with the CLI and then with the command line.

$ docker service create --generic-resource "hamster=1" --name my_light_service ubuntu bash -c "env && sleep infinity"
$ docker service create --generic-resource "hamster=3" --name my_heavy_service ubuntu bash -c "env && sleep infinity"

We have one replica for the light and heavy service. They use 4 hamster. Let's try to use moooooooore!

$ docker service scale -d my_light_service=10 my_heavy_service=10
$ docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE               PORTS
tt436bxjtdn7        my_heavy_service    replicated          1/10                 ubuntu:latest
ksiq5x0bxch1        my_light_service    replicated          2/10                 ubuntu:latest

Remember, we only have 5 hamsters, and 3 are needed for the heavy service and one for the light service. Hence here 2 * 1 + 1 * 3 = 5 ! This is what we wanted. So how does each container knows which hamster to use?

We asked each container to print the environment variables and sleep for infinity env && sleep infinity. Let's take a look with docker logs:

$ docker ps
CONTAINER ID IMAGE          COMMAND                NAMES
1e8932f5a985 ubuntu:latest  "bash -c 'env && sle…" my_light_service.4.shys2wwxz7jjfjg2g2e0xl1sw
0a5c4ddd303a ubuntu:latest  "bash -c 'env && sle…" my_heavy_service.1.p39satddgf6j1uhspdiohcyzh
e2872006cc97 ubuntu:latest  "bash -c 'env && sle…" my_light_service.1.bbo0fbi5d5e2zdwozgbse8x57

$ docker logs my_light_service.4.shys2wwxz7jjfjg2g2e0xl1sw
DOCKER_RESOURCE_HAMSTER=Stacy
HOSTNAME=1e8932f5a985

$ docker logs my_heavy_service.1.p39satddgf6j1uhspdiohcyzh
DOCKER_RESOURCE_HAMSTER=Lucie,Annie,James
HOSTNAME=0a5c4ddd303a

$ docker logs my_light_service.1.bbo0fbi5d5e2zdwozgbse8x57
DOCKER_RESOURCE_HAMSTER=Robert
HOSTNAME=e2872006cc97

So we can see that each container is aware of it's hamster with an environment variable. The process running in the container can grab it and then use the correct hamster without making two containers use the same hamster.

Using hamsters with Docker stack

Here is a docker-compose.yml that will declare the services exactly like we did in the CLI:

version: "3.8"

services:
  my_light_service:
    command:
      - bash
      - -c
      - env && sleep infinity
    image: ubuntu
    deploy:
      replicas: 10
      resources:
        reservations:
          generic_resources:
            - discrete_resource_spec:
                kind: 'hamster'
                value: 1

  my_heavy_service:
    command:
      - bash
      - -c
      - env && sleep infinity
    image: ubuntu
    deploy:
      replicas: 10
      resources:
        reservations:
          generic_resources:
            - discrete_resource_spec:
                kind: 'hamster'
                value: 3
$ docker stack deploy -c docker-compose.yml my_stack_using_hamsters
Creating network my_stack_using_hamsters_default
Creating service my_stack_using_hamsters_my_heavy_service
Creating service my_stack_using_hamsters_my_light_service

$ docker service ls
ID              NAME                                       MODE        REPLICAS  IMAGE 
nlhwfnz0d1cx    my_stack_using_hamsters_my_heavy_service   replicated  1/10      ubuntu:latest
c77tv3czzfw2    my_stack_using_hamsters_my_light_service   replicated  2/10      ubuntu:latest

How does that fit with Nvidia GPUs?

Well, if you remember, the Nvidia runtime uses environment variables in the container to know which gpu to use.

By modifying the /etc/nvidia-container-runtime/config.toml, and setting swarm-resource = "DOCKER_RESOURCE_GPU", it indicates the nvidia-docker runtime that it should watch for this environment variable when deciding which gpu to use. Make the nvidia-docker runtime the default one for this node, replace hamster by gpu and you're good to go as long as you used UUID as your hamster's names.

A more in depth guide can be found here.

Discreet resources

TODO (just put "node-generic-resources": ["hamster=100"] in the daemon.json, there are too many hamsters to give them names).