在远程主机上运行`docker run`

30

使用docker命令或docker-py API,是否可以直接从远程主机启动容器呢?

假设我有两台不同体系结构的机器: - A是一个x86机器 - B是一个ARM机器

我想要在B机器上使用A机器运行一个容器。起初,我以为可以使用这个命令:

[A]$> DOCKER_HOST=$MACHINE_B_IP:$MACHIN_B_PORT docker run hello-from-B

但是这个命令实际上拉取了图像 hello-from-B 并尝试在机器A上运行它,结果出现了一些 exec格式错误,显然你不能在 x86 机器上运行特定于 ARM 的图像。

机器A和B之间的通信正常。我可以运行像 imagesps 这样的命令,并且得到了预期的结果:

[A]$> DOCKER_HOST=$MACHINE_B_IP:$MACHIN_B_PORT docker images
REPOSITORY                TAG                 IMAGE ID            CREATED             SIZE
hello-from-B              <none>              fd5059044831        13 hours ago        1.26GB

我听说过 docker-machine,但还没有尝试过,但据我所知,这不会解决我的问题。

有没有办法直接使用 docker 来实现呢?一个变通的方法可能是使用 ssh 连接到远程主机,并直接从远程主机上使用 docker 客户端,但是我尽量想避免这种解决方案。

提前感谢您的帮助,


简而言之:

如何通过 DOCKER_HOST=... docker run somethingDOCKER_HOST 上运行 something,而不是在本地计算机上运行它。


你尝试使用-H标志了吗?像这样:docker -H tcp://[host]:[port][path] run hello-from-B - fishi0x01
Stack Overflow是一个关于编程和开发问题的网站。这个问题似乎不属于编程或开发范畴。请参阅帮助中心的我可以在这里问什么话题。也许超级用户Unix&Linux Stack Exchange更适合提问。另请参见我在哪里发布关于Dev Ops的问题? - jww
1
@jww,我的错,你是完全正确的。 :) - ccharly
1
使用docker 18.09版本可以执行docker -H ssh://me@server run -it --rm busybox命令。请参考我的回答 - VonC
4个回答

41

检查最新的docker 18.09版本是否包括该功能。
请参阅docker/cli PR 1014

增加了对SSH连接的支持,例如:docker -H ssh://me@server

  • cli应该接受ssh://me@server作为DOCKER_HOST-H参数。使用该参数将执行带有传递配置的ssh。
  • ssh命令会在远程侧的docker CLI二进制文件上调用隐藏命令。例如,docker dial-stdio。

此命令将连接到本地DOCKER_HOST变量(几乎总是默认的本地套接字),并将该连接转发到命令的stdio。
即使该命令应该在本地运行dockerd二进制文件,我们认为删除本地的docker二进制文件来依赖它始终存在是一种无效的配置,因此不能采用这种方式实现该功能。

如何验证

docker -H ssh://me@server run -it --rm busybox

到目前为止的反应:

来自各地的运维和系统管理员,我们感谢您提供这个出色且意想不到的功能。
我希望这将大大减少我看到人们打开未启用 TLS 的 dockerd TCP 的次数,并只选择 SSH 端点进行远程管理。


3
谢谢您发布这个答案,它非常有效! - Sa'ad

8
如果您的目标机器B可以在这些平台之一上创建,那么我猜docker-machine会满足您的需求。
您可以使用docker-machine create --driver <..driver setup..> MACHINE_B来创建您的机器,然后使用eval $(docker-machine env MACHINE_B)来激活它。 docker-machine env MACHINE_B将打印出一些导出语句:
export DOCKER_TLS_VERIFY="1"
export DOCKER_HOST="tcp://...."
export DOCKER_CERT_PATH="/..."
export DOCKER_MACHINE_NAME="MACHINE_B"

一旦您的机器处于活动状态,您可以像在本地一样使用docker命令来远程操作MACHINE_B

似乎“通用”驱动程序可能是一个解决方案。我也听说过“无”驱动程序,但实际上找不到关于这个的文档。不管怎样,“docker-machine”可能是我正在寻找的,我会尝试一下。谢谢! - ccharly
祝你好运,我自己没有使用过驱动程序“none”,但我在这里找到了一篇关于它的文章(https://github.com/docker/machine/issues/4045),其中介绍了如何使用TLS证书连接到远程Docker主机的方法。 - ShabbY
似乎 docker-machine 没有解决我的问题。它仍然出现一个“exec格式错误”。由于某种原因,它尝试在我的 x86_64 机器上执行基于 aarch64 架构的容器。也许这是因为驱动程序的缘故,而其他驱动程序正常工作。此外...我正在尝试以非传统方式使用 docker。无论如何谢谢! - ccharly
实际上我做错了。我试图启动默认为x86机器构建的hello-world镜像。所以它可能会起作用... 我会保持联系。 - ccharly
1
一切在docker-machine上都运行良好。起初,我只使用了DOCKER_HOST,这也应该按预期工作。我没有注意到我尝试运行的镜像。另外,顺便提一下,docker-machine需要TLS(或者至少是旧版本),我尝试了一堆用于测试目的的tlsnoverify之类的东西,但都没有起作用。现在我已经设置好了整个TLS环境,可以毫无问题地使用它。再次感谢。 - ccharly
显示剩余2条评论

5
本文很好地解释了概念:https://docs.docker.com/engine/reference/commandline/dockerd/#bind-docker-to-another-hostport-or-a-unix-socket 考虑到页面上的警告,我建议您通过SSH使用安全连接,例如:ssh user@host 'docker run hello-from-B' 警告:更改默认的 Docker 守护程序绑定到 TCP 端口或 Unix docker 用户组将增加您的安全风险,因为它允许非根用户在主机上获得 root 访问权限。确保您控制对 Docker 的访问。如果绑定到 TCP 端口,则拥有该端口访问权限的任何人都具有完整的 Docker 访问权限;因此,在开放网络上不建议这样做。
使用-H 可以使 Docker 守护程序监听特定的 IP 和端口。默认情况下,它将监听unix: ///var/run/docker.sock,仅允许本地用户以 root 身份进行连接。您可以设置它为0.0.0.0:2375或特定主机 IP,以向所有人提供访问权限,但不建议这样做,因为这样很容易让某人轻易地获得守护程序所在主机的 root 访问权限。
同样,Docker 客户端可以使用-H连接到自定义端口。在 Linux 上,默认情况下,Docker 客户端将连接到unix: ///var/run/docker.sock,在 Windows 上默认为 tcp://127.0.0.1:2376
-H 接受以下格式的主机和端口分配:tcp://[host]:[port][path] 或 unix://path

You can use multiple -H, for example, if you want to listen on both TCP and a Unix socket

# Run docker in daemon mode
$ sudo <path to>/dockerd -H tcp://127.0.0.1:2375 -H unix:///var/run/docker.sock &
# Download an ubuntu image, use default Unix socket
$ docker pull ubuntu
# OR use the TCP port
$ docker -H tcp://127.0.0.1:2375 pull ubuntu

我建议使用非ssh选项,因为传递像Jenkins凭据这样的环境变量是棘手的(但不是不可能的)。 - Mohammed Rafeeq
@MohammedRafeeq 有什么难点? - yosefrow
尝试在Jenkins文件中将环境变量传递到通过SSH访问的目标Docker机器。你会明白我的意思。 - Mohammed Rafeeq
首选使用以下命令:sh " DOCKER_HOST="${DOCKER_HOST}" docker run -e ENVIRONMENT_VARIABLE ..."。这样可以轻松地发送环境变量值,否则在ssh进入目标docker主机后,无法访问设置的任何值。DOCKER_HOST变量的格式为tcp://hostname:2376,其中2376是默认端口。 - Mohammed Rafeeq

1
由于您说服务器之间存在可用的连接,因此您可以利用Docker丰富的API。有两种方法来配置Docker守护程序端口:1)在/etc/default/docker文件中进行配置;
DOCKER_OPTS="-H tcp://127.0.0.1:5000 -H unix:///var/run/docker.sock"

2) 在/etc/docker/daemon.json中进行配置:

{
"hosts": ["tcp://127.0.0.1:5000", "unix:///var/run/docker.sock"]
}

如需了解有关配置Docker守护程序端口的详细信息,请参阅配置Docker守护程序端口

一旦配置了Docker端口,您就可以在远程主机上访问Docker API。

JSON输入文件:

#cat container_create.json 
{
  "AttachStdin": true,
  "AttachStdout": true,
  "AttachStderr": true,
  "ExposedPorts": {
    "property1": {},
    "property2": {}
  },
  "Tty": true,
  "OpenStdin": true,
  "StdinOnce": true,
  "Cmd": null,
  "Image": "ubuntu:14.04",
  "Volumes": {
    "additionalProperties": {}
  },
  "Labels": {
    "property1": "string",
    "property2": "string"
  }
}

创建容器的 API:
curl -X POST http://192.168.56.101:6000/containers/create -d @container_create.json --header "Content-Type: application/json" | jq .
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   602  100    90  100   512   1737   9883 --:--:-- --:--:-- --:--:-- 10039
{
  "Warnings": null,
  "Id": "f5d3273e48350d606bd8b9d2a5bd876dc5c2d1a73183f876a1dd56473cad8940"
}

生成的ID是容器ID,状态不会处于活动/运行状态。 用于启动已创建容器的API。
# curl -X POST http://192.168.56.101:6000/containers/f5d3273e48350/start | jq .  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current

检查状态/检查容器的API:

# curl -X GET http://192.168.56.101:6000/containers/f5d3273e48350/json | jq .
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  4076    0  4076    0     0   278k      0 --:--:-- --:--:-- --:--:--  306k
{
  "NetworkSettings": {
    "Networks": {
      "bridge": {
        "MacAddress": "02:42:ac:11:00:03",
        "GlobalIPv6PrefixLen": 0,
        "GlobalIPv6Address": "",
        "IPv6Gateway": "",
        "IPAMConfig": null,
        "Links": null,
        "Aliases": null,
        "NetworkID": "689d6b65ce1b06c93b2c70f41760a3e7fb2b50697d71cd9c1f39c64c865e5fa6",
        "EndpointID": "76bf1f8638d1ff0387e6c3fe89e8ccab1670c709ad550f9acc6f46e559654bee",
        "Gateway": "172.17.0.1",
        "IPAddress": "172.17.0.3",
        "IPPrefixLen": 16
      }
    },
    "MacAddress": "02:42:ac:11:00:03",
    "SecondaryIPAddresses": null,
    "SandboxKey": "/var/run/docker/netns/24a031d9dfda",
    "Ports": {
      "0/tcp": null
    },
    "LinkLocalIPv6PrefixLen": 0,
    "LinkLocalIPv6Address": "",
    "HairpinMode": false,
    "SandboxID": "24a031d9dfda70026a875f4841269c5e790b12ccafcc11869111faa240020b99",
    "Bridge": "",
    "SecondaryIPv6Addresses": null,
    "EndpointID": "76bf1f8638d1ff0387e6c3fe89e8ccab1670c709ad550f9acc6f46e559654bee",
    "Gateway": "172.17.0.1",
    "GlobalIPv6Address": "",
    "GlobalIPv6PrefixLen": 0,
    "IPAddress": "172.17.0.3",
    "IPPrefixLen": 16,
    "IPv6Gateway": ""
  },

    },
    "AttachStderr": true,
    "AttachStdout": true,
    "AttachStdin": true,
    "User": "",
    "Domainname": "",
    "Hostname": "f5d3273e4835",
    "OpenStdin": true,
    "StdinOnce": true,
    "Env": [
      "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
    ],
    "Cmd": [
      "/bin/bash"
    ],
    "ArgsEscaped": true,
    "Image": "ubuntu:14.04",

<*************REMOVING THE OUTPUT CONTENT********>

  "ExecIDs": null,
  "HostnamePath": "/var/lib/docker/containers/f5d3273e48350d606bd8b9d2a5bd876dc5c2d1a73183f876a1dd56473cad8940/hostname",
  "ResolvConfPath": "/var/lib/docker/containers/f5d3273e48350d606bd8b9d2a5bd876dc5c2d1a73183f876a1dd56473cad8940/resolv.conf",
  "Image": "sha256:132b7427a3b40f958aaeae8716e0cbb2177658d2410554ed142e583ef522309f",
  "State": {
    "FinishedAt": "0001-01-01T00:00:00Z",
    "StartedAt": "2017-06-09T06:53:45.120357144Z",
    "Error": "",
    "Status": "running",
    "Running": true,
    "Paused": false,
    "Restarting": false,

  "Path": "/bin/bash",
  "Created": "2017-06-09T06:52:51.820429355Z",
  "Id": "f5d3273e48350d606bd8b9d2a5bd876dc5c2d1a73183f876a1dd56473cad8940",
  "HostsPath": "/var/lib/docker/containers/f5d3273e48350d606bd8b9d2a5bd876dc5c2d1a73183f876a1dd56473cad8940/hosts",
  "LogPath": "/var/lib/docker/containers/f5d3273e48350d606bd8b9d2a5bd876dc5c2d1a73183f876a1dd56473cad8940/f5d3273e48350d606bd8b9d2a5bd876dc5c2d1a73183f876a1dd56473cad8940-json.log",
  "Name": "/objective_bartik",
  "RestartCount": 0,
  "Driver": "aufs",
  "MountLabel": "",
  "ProcessLabel": "",
  "AppArmorProfile": "docker-default"
}

更多信息请参考:

DOCKER APIs

如何使用 Docker API 构建镜像?

如何使用 API 提交 Docker 容器

希望这些信息对您有所帮助。


我一直在使用 docker-py API 进行测试,并尝试使用 DOCKER_HOST 在机器 B 上运行容器,但不幸的是,这与调用 DOCKER_HOST=... docker run... 得到了相同的结果。无论如何,您的解决方案似乎可以满足我的需求,如果 docker-machine 没有按预期工作,我会尝试它。谢谢! - ccharly
你能否更新适当的答案,因为它已经帮助了你。这将有助于其他人在面对类似问题时也能受益。谢谢。 - Here_2_learn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接