【问题标题】:execute a command within docker swarm service在 docker swarm 服务中执行命令
【发布时间】:2017-01-14 16:47:39
【问题描述】:
  1. 初始化群模式:

    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. 加入第二个节点:

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

要向该 swarm 添加管理器,请运行“docker swarm join-token manager”并按照说明进行操作。

# 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

如上所示,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 命令不适用于 swarm 模式。

【问题讨论】:

  • 0 个副本表示容器启动失败。我建议您在 docker 守护程序上配置日志驱动程序(例如 syslog 驱动程序),因为默认情况下它会吞下容器的所有输出。它可能会一遍又一遍地重新启动,您会想知道原因。如果是这种情况,您将无法附加并修复它;您需要修复 docker service 命令,以便它不会因错误而退出。

标签: docker-swarm


【解决方案1】:

我编辑了上面添加的脚本 Brian van Rooijen。因为我的声望太低了,不能加了

#! /bin/bash

set -e

service=${1}
shift
task="$*"

echo $task

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


docker -H $node exec -it $serviceName"."$serviceID $task

我遇到的问题是硬编码的 .1 不存在容器。在执行中。

【讨论】:

    【解决方案2】:

    对于那些拥有多个副本并且只想在其中任何一个副本中运行命令的人,这里有另一个快捷方式:

    docker exec -it $(docker ps -q -f name=SERVICE_NAME | head -1) bash
    

    【讨论】:

      【解决方案3】:

      我发现将docker exec 连接到运行服务$SERVICE_NAME(例如mystack_myservice)的群节点(群管理器位于$SWARM_MANAGER_HOST)中的最简单命令如下:

      SERVICE_JSON=$(ssh $SWARM_MANAGER_HOST "docker service ps $SERVICE_NAME --no-trunc --format '{{ json . }}' -f desired-state=running")
      ssh -t $(echo $SERVICE_JSON | jq -r '.Node') "docker exec -it $(echo $SERVICE_JSON | jq -r '.Name').$(echo $SERVICE_JSON | jq -r '.ID') bash"
      

      这表明您对 $SWARM_MANAGER_HOST 以及当前运行服务任务的 swarm 节点具有 ssh 访问权限。

      这也断言您已安装 jq (apt install jq),但如果您不能或不想安装它并且已安装 python,您可以创建以下别名(基于 this answer ):

      alias jq="python3 -c 'import sys, json; print(json.load(sys.stdin)[sys.argv[2].partition(\".\")[-1]])'"
      

      【讨论】:

        【解决方案4】:

        看看我的解决方案:https://github.com/binbrayer/swarmServiceExec

        此方法基于 Docker 机器。我还创建了脚本的原型来异步调用容器,结果是同时调用。

        【讨论】:

          【解决方案5】:

          见附录 2...

          在节点master 上输入数据库my_db 的单行器示例:

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

          如果你想配置,说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

          这种方法允许通过相应地设置DB_NODE_ID 变量来输入所有数据库节点(例如从属节点)。

          从机s2的例子:

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

          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

          将其放入 master / s2KiTTYPuTTY 配置中的 Data/Command 下,即可设置。


          作为附录:

          旧的非 swarm 模式版本读起来很简单

          docker exec -it master mysql my_db
          

          分别

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

          附录 2:

          正如示例所证明的那样,术语docker ps -q -f name=$DB_NODE_ID 在某些情况下可能会返回错误的值。

          以下方法可以正常工作:

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

          您可以相应地替换上面的示例。


          附录 3:

          嗯,这些术语看起来很糟糕,而且打字起来肯定很痛苦,所以你可能想放松一下你的工作。在 Linux 上,每个人都知道如何做到这一点。在 Windows 上,您可能想要使用 AHK。

          这是我使用的 AHK 术语:

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

          所以当我输入ii 时——这很简单——我得到了所需的术语,光标就位,只需要填写容器名称。

          【讨论】:

            【解决方案6】:

            为我们的 docker swarm 集群创建了一个小脚本。 这个脚本需要 3 个参数。首先是您要连接的服务,其次是您要运行的任务,这可以是/bin/bash 或您要运行的任何其他进程。第三个是可选的,将为 bash 或 sh 填充 -c 选项

            -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"
            

            注意:这要求 docker 节点通过在工作节​​点上的 2375 端口上公开 docker 来接受 tcp 连接

            【讨论】:

              【解决方案7】:

              我在 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
              

              【讨论】:

                【解决方案8】:

                您可以通过过滤容器名称来执行命令,而无需传递整个 swarm 容器哈希,只需通过服务名称即可。像这样:

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

                【讨论】:

                • 这应该是推荐的方法。如果您需要更多内容,可以轻松编写这个小 sn-p 脚本。
                • 这不应该是推荐的方法。该服务可能有多个副本,并且该命令将仅在其中一个中运行,如果有很多散列的话
                【解决方案9】:

                您可以跳入 Swarm 节点并列出正在运行的 docker 容器:

                docker container ls
                

                这将为您提供类似于以下格式的容器名称:containername.1.q5k89uctyx27zmntkcfooh68f

                然后您可以使用常规 exec 选项在其上运行命令:

                docker container exec -it containername.1.q5k89uctyx27zmntkcfooh68f bash
                

                【讨论】:

                • 明确地说,这将只允许访问在该特定节点中运行的服务。
                【解决方案10】:

                编辑 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 swarm 模式服务上运行docker exec。但是你仍然可以找出,哪个节点在运行容器,然后直接在容器上运行 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 会显示一些有用的错误消息..?

                【讨论】:

                  【解决方案11】:

                  本地主机访问对应的服务实例只有一条线路:

                  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。

                  【讨论】:

                  • 单行需要-q后面的--no-trunc
                  • 关于 bash 脚本 $ docker service ls ID NAME MODE REPLICAS IMAGE jiecd0kb524e bar replicated 3/3 jwilder/whoami:latest 以及当我调用它时 $ ./swarm_bash bar 1 Executing in swarmm-agentpublic-31109991000004 bar.1.vj6lqkz4wi03hn7hr6574cipg Host does not exist: "swarmm-agentpublic-31109991000004" Error response from daemon: No such container: bar.1.vj6lqkz4wi03hn7hr6574cipg 我真的可以在 swarm 模式下执行吗?一个班轮也不适合我
                  • 需要 --no-trunc 和 head -n1
                  • 我在使用最近安装的docker-ce 时遇到了docker-machine eval 错误,但脚本的其余部分运行良好。
                  • 这个答案只适用于本地主机。尽管已说明,但也许会更多地强调这一事实。此解决方案不是在 swarm 服务中执行命令的通用答案。对于 bash,默认的 docker TAB 键完成也在 localhost 上完成工作:docs.docker.com/compose/completion/#bash
                  【解决方案12】:

                  使用 Docker API

                  目前,Docker 没有为此提供像 docker service execdocker stack exec 这样的 API。但是关于这一点,处理这个功能已经存在两个问题:

                  (关于第一个问题,对我来说,并不清楚这个问题是否完全处理这种功能。但Exec for Swarm 已关闭并标记为Docker service exec 问题的重复。)

                  通过 HTTP 使用 Docker 守护进程

                  正如BMitchrun docker exec from swarm manager 上提到的,您还可以将 Docker 守护进程配置为使用 HTTP,而不是连接到每个节点而不需要 ssh。但是你应该使用已经集成到 Docker 中的TLS authentication 来保护它。之后你就可以像这样执行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 daemon socket,以便能够在本地执行docker exec
                  欲了解更多信息,请查看:skopos-plugin-swarm-exec

                  使用 docker swarm 助手

                  还有另一个名为docker swarm helpers 的项目似乎或多或少是sshdocker exec 的包装。

                  参考:

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2017-12-17
                    • 2018-07-25
                    • 1970-01-01
                    • 1970-01-01
                    • 2021-10-28
                    • 1970-01-01
                    相关资源
                    最近更新 更多