execute a command within docker swarm service

shantanuo picture shantanuo · Sep 7, 2016 · Viewed 54.9k times · Source
  1. Initialize swarm mode:

    root@ip-172-31-44-207:/home/ubuntu# docker swarm init --advertise-addr 172.31.44.207
    
    Swarm initialized: current node (4mj61oxcc8ulbwd7zedxnz6ce) is now a manager.
    
    To add a worker to this swarm, run the following command:
    
  2. Join the second node:

    docker swarm join \
    --token SWMTKN-1-4xvddif3wf8tpzcg23tem3zlncth8460srbm7qtyx5qk3ton55-6g05kuek1jhs170d8fub83vs5 \
    172.31.44.207:2377
    

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

# start 2 services
docker service create continuumio/miniconda3 

docker service create --name redis redis:3.0.6


root@ip-172-31-44-207:/home/ubuntu# docker service ls
ID            NAME        REPLICAS  IMAGE                   COMMAND
2yc1xjmita67  miniconda3  0/1       continuumio/miniconda3
c3ptcf2q9zv2  redis       1/1       redis:3.0.6

As shown above, redis has it's replica while miniconda does not seem to be replicated.

I do usually log-in to miniconda container to type these commands:

/opt/conda/bin/conda install jupyter -y --quiet && mkdir /opt/notebooks && /opt/conda/bin/jupyter notebook --notebook-dir=/opt/notebooks --ip='*' --port=8888 --no-browser

The problem is that docker exec -it XXX bash command does not work with swarm mode.

Answer

user1936595 picture user1936595 · Mar 22, 2017

There is one liner for accessing corresponding instance of the service for localhost:

docker exec -ti stack_myservice.1.$(docker service ps -f 'name=stack_myservice.1' stack_myservice -q --no-trunc | head -n1) /bin/bash

It is tested on PowerShell, but bash should be the same. The oneliner accesses the first instance, but replace '1' with the number of the instance you want to access in two places to get other one.

More complex example is for distributed case:

#! /bin/bash

set -e

exec_task=$1
exec_instance=$2

strindex() { 
  x="${1%%$2*}"
  [[ "$x" = "$1" ]] && echo -1 || echo "${#x}"
}

parse_node() {
  read title
  id_start=0
  name_start=`strindex "$title" NAME`
  image_start=`strindex "$title" IMAGE`
  node_start=`strindex "$title" NODE`
  dstate_start=`strindex "$title" DESIRED`
  id_length=name_start
  name_length=`expr $image_start - $name_start`
  node_length=`expr $dstate_start - $node_start`

  read line
  id=${line:$id_start:$id_length}
  name=${line:$name_start:$name_length}
  name=$(echo $name)
  node=${line:$node_start:$node_length}
  echo $name.$id
  echo $node
}

if true; then 
   read fn 
   docker_fullname=$fn
   read nn
   docker_node=$nn 
fi < <( docker service ps -f name=$exec_task.$exec_instance --no-trunc -f desired-state=running $exec_task | parse_node )

echo "Executing in $docker_node $docker_fullname" 

eval `docker-machine env $docker_node`

docker exec -ti $docker_fullname /bin/bash

This script could be used later as:

swarm_bash stack_task 1

It just execute bash on required node.