Выполнить команду в сервисе роуминга докеров

  • Инициализировать режим роя:

    [email protected]:/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:
    
  • Присоедините второй node:

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

Чтобы добавить менеджера в этот рой, запустите "диспетчер подключений маркеров докеров" и следуйте инструкциям.

# start 2 services
docker service create continuumio/miniconda3 

docker service create --name redis redis:3.0.6


[email protected]:/home/ubuntu# docker service ls
ID            NAME        REPLICAS  IMAGE                   COMMAND
2yc1xjmita67  miniconda3  0/1       continuumio/miniconda3
c3ptcf2q9zv2  redis       1/1       redis:3.0.6

Как показано выше, redis имеет реплику, в то время как miniconda делает не реплицированным.

Обычно я подключаюсь к контейнеру miniconda для ввода следующих команд:

/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

Проблема заключается в том, что команда docker exec -it XXX bash не работает с режимом роя.

Ответ 1

Существует один вкладыш для доступа к соответствующему экземпляру службы для localhost:

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

Он протестирован на PowerShell, но bash должен быть таким же. Oneliner обращается к первому экземпляру, но замените "1" номером экземпляра, к которому вы хотите получить доступ, в двух местах, чтобы получить другой.

Более сложный пример для распределенного случая:

#! /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

Этот скрипт может быть использован позже как:

swarm_bash stack_task 1

Он просто выполняет bash на требуемом узле.

Ответ 2


РЕДАКТИРОВАТЬ 2017-10-06:

В настоящее время вы можете создать оверлейную сеть с флагом --attachable чтобы позволить любому контейнеру присоединиться к сети. Это отличная функция, поскольку она обеспечивает большую гибкость.

Например

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

Вы правы, вы не можете запустить docker exec в сервисе Docker Swarm Mode. Но вы все равно можете узнать, на каком узле выполняется контейнер, а затем запустить exec непосредственно в контейнере. Например

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

В вашем случае вы сначала должны выяснить, почему сервис не может поднять контейнер Miniconda. Может быть, при запуске docker service ps miniconda3 показывает некоторые полезные сообщения об ошибках..?

Ответ 3

Использование Docker API

Прямо сейчас Docker не предоставляет API, такой как docker service exec docker stack exec или docker stack exec Docker для этого. Но в связи с этим уже существуют две проблемы, связанные с этой функциональностью:

(Что касается первой проблемы, для меня неясно, что эта проблема касается именно такой функциональности. Но Exec for Swarm был закрыт и помечен как дубликат проблемы exec службы Docker.)

Использование Docker через HTTP

Как упомянуто BMitch при запуске docker exec из swarm manager, вы также можете настроить демон Docker на использование HTTP и затем подключаться к каждому узлу без использования ssh. Но вы должны защитить это с помощью аутентификации TLS, которая уже интегрирована в Docker. После этого вы сможете выполнить docker exec следующим образом:

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

Использование skopos-plugin-swarm-exec

Существует проект github, который утверждает, что решает проблему и обеспечивает желаемую функциональность, привязывающую демон docker:

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

Насколько я вижу, это работает, создавая другой контейнер в том же узле, где находится контейнер, в котором должен выполняться docker exec. На этом узле этот контейнер монтирует сокет демона docker exec локального выполнения docker exec.
Для получения дополнительной информации взгляните на: skopos-plugin-swarm-exec

Использование помощников Docker Swarm

Существует также другой проект, называемый docker swarm helpers, который, похоже, является более или менее оболочкой для ssh и docker exec.

Ссылка:

Ответ 4

Вы можете выполнять команды путем фильтрации имени контейнера без необходимости передавать весь хэш контейнера Swarm, только по имени службы. Как это:

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

Ответ 5

Вы можете перейти в узел Swarm и перечислить докер-контейнеры, работающие с использованием:

docker container ls

Это даст вам имя контейнера в формате, похожем на: containername.1.q5k89uctyx27zmntkcfooh68f

Затем вы можете использовать обычную опцию exec для запуска команд:

docker container exec -it containername.1.q5k89uctyx27zmntkcfooh68f bash

Ответ 6

Я написал скрипт для выполнения команды в Docker Swarm по имени службы. Например, его можно использовать в cron. Также вы можете использовать конвейеры bash и передавать все параметры в команду docker exec. Но работает только на том же узле, где запущен сервис. Я хотел бы, чтобы это могло помочь кому-то

#!/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;

Пример использования:

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

Ответ 7

создал небольшой скрипт для нашего кластера Docker Swarm. этот скрипт занимает 3 параметра. во-первых, это служба, к которой вы хотите подключиться, во-вторых, задача, которую вы хотите запустить, это может быть /bin/bash или любой другой процесс, который вы хотите запустить. Третий не обязателен и заполнит опцию -c для bash или sh

-n является необязательным для принудительного подключения к узлу

он извлекает узел, который запускает службу, и запускает команду.

#! /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"

примечание: для этого требуется, чтобы док-узлы принимали tcp-соединения, предоставляя докер через порт 2375 на рабочих узлах