execute a command within docker swarm service

Solution 1:

You can execute commands by filtering container name without needing to pass the entire swarm container hash, just by the service name. Like this:

docker exec $(docker ps -q -f name=servicename) ls

Solution 2:

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.

Solution 3:


EDIT 2017-10-06:

Nowadays you can create the overlay network with --attachable flag to enable any container to join the network. This is great feature as it allows a lot of flexibility.

E.g.

$ docker network create --attachable --driver overlay my-network
$ docker service create --network my-network --name web --publish 80:80 nginx
$ docker run --network=my-network -ti alpine sh
(in alpine container) $  wget -qO- web

<!DOCTYPE html>
<html>
<head>
....

You are right, you cannot run docker exec on docker swarm mode service. But you can still find out, which node is running the container and then run exec directly on the container. E.g.

docker service ps miniconda3  # find out, which node is running the container
eval `docker-machine env <node name here>`
docker ps  # find out the container id of miniconda
docker exec -it <container id here> sh

In your case you first have to find out, why service cannot get the miniconda container up. Maybe running docker service ps miniconda3 shows some helpful error messages..?

Solution 4:

Using the Docker API

Right now, Docker does not provide an API like docker service exec or docker stack exec for this. But regarding this, there already exists two issues dealing with this functionality:

  • github.com - moby/moby - Docker service exec
  • github.com - docker/swarmkit - Support for executing into a task

(Regarding the first issue, for me, it was not directly clear that this issue deals with exactly this kind of functionality. But Exec for Swarm was closed and marked as duplicate of the Docker service exec issue.)

Using Docker daemon over HTTP

As mentioned by BMitch on run docker exec from swarm manager, you could also configure the Docker daemon to use HTTP and than connect to every node without the need of ssh. But you should protect this using TLS authentication which is already integrated into Docker. Afterwards you would be able to execute the docker exec like this:

docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem \
    -H=$HOST:2376 exec $containerId $cmd

Using skopos-plugin-swarm-exec

There exists a github project which claims to solve the problem and provide the desired functionality binding the docker daemon:

docker run -v /var/run/docker.sock:/var/run/docker.sock \
    datagridsys/skopos-plugin-swarm-exec \
    task-exec <taskID> <command> [<arguments>...]

As far as I can see, this works by creating another container at same node where the container reside where the docker exec should by executed on. On this node this container mounts the docker daemon socket to be able to execute docker exec there locally.
For more information have a look at: skopos-plugin-swarm-exec

Using docker swarm helpers

There is also another project called docker swarm helpers which seems to be more or less a wrapper around ssh and docker exec.

Reference:

  • https://github.com/docker/swarmkit/issues/1895#issuecomment-302147604
  • https://github.com/docker/swarmkit/issues/1895#issuecomment-358925313

Solution 5:

You can jump in a Swarm node and list the docker containers running using:

docker container ls

That will give you the container name in a format similar to: containername.1.q5k89uctyx27zmntkcfooh68f

You can then use the regular exec option to run commands on it:

docker container exec -it containername.1.q5k89uctyx27zmntkcfooh68f bash