execute a command within docker swarm service

别来无恙 提交于 2019-11-27 05:27:52

问题


  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.


回答1:


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.




回答2:



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..?




回答3:


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




回答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



回答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



回答6:


created a small script for our docker swarm cluster. this script takes 3 params. first is the service you want to connect to second the task you want to run this can be /bin/bash or any other process you want to run. Third is optional and will fill -c option for bash or sh

-n is optional to force it to connect to a node

it retrieves the node that runs the service and runs the command.

#! /bin/bash

set -e

task=${1}
service=$2
bash=$3

serviceID=$(sudo docker service ps -f name=$service -f desired-state=running $service -q --no-trunc |head -n1)
node=$(sudo docker service ps -f name=$service -f desired-state=running $service --format="{{.Node}}"| head -n1 )

sudo docker -H $node exec -it $service".1."$serviceID $bash -c "$task"

note: this requires the docker nodes to accept tcp connections by exposing docker on port 2375 on the worker nodes




回答7:


I wrote script to exec command in docker swarm by service name. For example it can be used in cron. Also you can use bash pipelines and passes all params to docker exec command. But works only on same node where service started. I wish it could help someone

#!/bin/bash
# swarm-exec.sh
set -e

for ((i=1;i<=$#;i++)); do
    val=${!i}
    if [ ${val:0:1} != "-" ]; then
        service_id=$(docker ps -q -f "name=$val");
        if [[ $service_id  == "" ]]; then
            echo "Container $val not found!";
            exit 1;
        fi
        docker exec ${@:1:$i-1} $service_id ${@:$i+1:$#};
        exit 0;
    fi
done
echo "Usage: $0 [OPTIONS] SERVICE_NAME COMMAND [ARG...]";
exit 1;

Example of using:

./swarm-exec.sh app_postgres pg_dump -Z 9 -F p -U postgres app > /backups/app.sql.gz

echo ls | ./swarm-exec.sh -i app /bin/bash

./swarm-exec.sh -it some_app /bin/bash



回答8:


See addendum 2...

Example of a oneliner for entering the database my_db on node master:

DB_NODE_ID=master && docker exec -it $(docker ps -q -f name=$DB_NODE_ID) mysql my_db

In case you want to configure, say max_connections:

DB_NODE_ID=master && $(docker exec -it $(docker ps -q -f name=$DB_NODE_ID) mysql -e "SET GLOBAL max_connections = 1000") && docker exec -it $(docker ps -q -f name=$DB_NODE_ID) mysql my_db

This approach allows to enter all database nodes (e.g. slaves) just by setting the DB_NODE_ID variable accordingly.

Example for slave s2:

DB_NODE_ID=s2 && docker exec -it $(docker ps -q -f name=$DB_NODE_ID) mysql my_db

or

DB_NODE_ID=s2 && $(docker exec -it $(docker ps -q -f name=$DB_NODE_ID) mysql -e "SET GLOBAL max_connections = 1000") && docker exec -it $(docker ps -q -f name=$DB_NODE_ID) mysql my_db

Put this into your KiTTY or PuTTY configuration for master / s2 under Data/Command and you are set.


As an addendum:

The old, non swarm mode version reads simply

docker exec -it master mysql my_db

resp.

DB_ID=master && $(docker exec -it $DB_ID mysql -e "SET GLOBAL max_connections = 1000") && docker exec -it $DB_ID mysql tmp

Addendum 2:

As it turned out by example, the term docker ps -q -f name=$DB_NODE_ID may return wrong values under certain conditions.

The following approach works correctily:

docker ps -a  | grep "_$DB_NODE_ID." | awk '{print $1}'

You may substitute the examples above accordingly.


Addendum 3:

Well, these terms look awful and they certainly are painful to type, so you may want to ease your work. On Linux, everybody knows how to do this. On Windws, you may want to use AHK.

This is the AHK term I use:

:*:ii::DB_NODE_ID=$(docker ps -a  | grep "_." | awk '{{}print $1{}}') && docker exec -it $id ash{Left 49} 

So when I type ii -- which is as simple as it can get -- I get the desired term with the cursor in place and just have to fill in the container name.




回答9:


Take a look at my solution: https://github.com/binbrayer/swarmServiceExec.

This approach is based on Docker Machines. I also created the prototype of the script to call containers asynchronously and as a result simultaneously.



来源:https://stackoverflow.com/questions/39362363/execute-a-command-within-docker-swarm-service

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!