27

is it possible (using the docker command or the docker-py API directly) to start a container from a remote host?

Lets assume I have two machines that have different architectures: - A is an x86 machine - B is an ARM machine

I would like to run a container on the B machine using my A machine. At first, I thought it was possible using this command:

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

But this command actually pulls the image hello-from-B and tries to run it on the machine A which ends up on some exec format error cause obviously you can't run images that are specific to ARM to an x86 machine.

Communication between machine A and B is working well. I can run commands like images or ps and it gives me the expected results:

[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

I've heard about docker-machine and haven't tried it yet, but from my understanding, this won't solve my problem.

Is there any way to achieve that using docker directly. A workaround might be to using ssh to connect to the remote host and use the docker client directly from the remote host, but I'd like to avoid this solution as much as possible.

Thanks in advance,


TL;DR;

How can DOCKER_HOST=... docker run something runs something on the DOCKER_HOST rather than running it on my local machine.

ccharly
  • 368
  • 1
  • 3
  • 8
  • Did you try to use the `-H` flag? Like `docker -H tcp://[host]:[port][path] run hello-from-B`? – fishi0x01 Jun 08 '17 at 22:34
  • Stack Overflow is a site for programming and development questions. This question appears to be off-topic because it is not about programming or development. See [What topics can I ask about here](http://stackoverflow.com/help/on-topic) in the Help Center. Perhaps [Super User](http://superuser.com/) or [Unix & Linux Stack Exchange](http://unix.stackexchange.com/) would be a better place to ask. Also see [Where do I post questions about Dev Ops?](http://meta.stackexchange.com/q/134306) – jww Jun 09 '17 at 10:17
  • 1
    @jww my bad, you're totally right. :) – ccharly Jun 09 '17 at 13:55
  • 1
    `docker -H ssh://me@server run -it --rm busybox` is possible with docker 18.09. See [my answer below](https://stackoverflow.com/a/53252524/6309) – VonC Nov 11 '18 at 19:45

4 Answers4

38

Check if the latest docker 18.09 includes that feature.
See docker/cli PR 1014

Added support for SSH connection. e.g. docker -H ssh://me@server

  • The cli should accept ssh://me@server for DOCKER_HOST and -H. Using that would execute ssh with the passed config.
  • The ssh command would call a hidden command on the docker CLI binary on the remote side. For example, docker dial-stdio.

This command will make a connection to the local DOCKER_HOST variable (almost always the default local socket) and forward that connection on the commands stdio.
Even though this command is supposed to run locally to the dockerd binary, we think that it is an invalid configuration for this feature to remove the local docker binary so we can rely on it always being present.

How to verify it

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

The reaction so far:

From ops and sysadmins everywhere, we thank you for this fantastic and unexpected feature.
I'm hoping this will seriously cut down the number of times I see people opening dockerd TCP w/o TLS and just opt for SSH endpoints for remote mgmt.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
8

If your targeted machine B could be created on one of these platform then, I guess, docker-machine would serve your needs.

You would create your machine using docker-machine create --driver <..driver setup..> MACHINE_B then you activate it using eval $(docker-machine env MACHINE_B).

docker-machine env MACHINE_B will print out some export statements:

export DOCKER_TLS_VERIFY="1"
export DOCKER_HOST="tcp://...."
export DOCKER_CERT_PATH="/..."
export DOCKER_MACHINE_NAME="MACHINE_B"

Once your machine is active, you can use the docker command as you would locally to act remotely on MACHINE_B.

Simeon Leyzerzon
  • 18,658
  • 9
  • 54
  • 82
ShabbY
  • 896
  • 5
  • 9
  • It seems that `generic` driver might be one solution. I've also heard about `none` driver but can't really find documentation about this one. Anyway, `docker-machine` is probably what I'm looking for, I'll give it a try. Thanks! – ccharly Jun 09 '17 at 08:49
  • Good Luck with that, I haven't used driver `none` myself, but I found [here](https://github.com/docker/machine/issues/4045) an [article](https://www.boerngen-schmidt.de/2017/05/connect-to-a-remote-docker-host-with-docker-machine-using-tls-certificates/) about it. – ShabbY Jun 09 '17 at 10:19
  • It seems that `docker-machine` isn't solving my problem. It still ends up with an: `exec format error`. For some reason, it tries to execute my container (which is based on `aarch64` arch) on my `x86_64` machine). Maybe that's because of the driver and other driver works as expected. Also.. I'm trying to use `docker` in non-conventional way. Thanks anyway! – ccharly Jun 09 '17 at 13:52
  • Actually I'm doing wrong. I'm trying to start the `hello-world` image which is built by default for `x86` machine. So it might works... I'll keep you in touch. – ccharly Jun 09 '17 at 13:59
  • 1
    Everything works fine with `docker-machine`. At first point, I was only using `DOCKER_HOST` which should also work as expected. I wasn't paying attention to the images I tried to run. Also, as a side note, `docker-machine` requires TLS (or at least, older versions), I've tried a bunch of `tlsnoverify` blahblah for testing purposes but none were working. Now that I've set up the whole TLS environment, I can use it without any trouble. Thanks again. – ccharly Jun 12 '17 at 08:49
  • I'm glad to hear that and happy that I could help – ShabbY Jun 12 '17 at 22:59
  • Short but very clear explanation, helps understand the *how* for `docker-machine use`. – LittleTiger Jul 29 '18 at 10:25
5

This article explains the concept very well: https://docs.docker.com/engine/reference/commandline/dockerd/#bind-docker-to-another-hostport-or-a-unix-socket

Considering the huge warning on the page, I suggest you resort to using a secure connection via SSH ie. ssh user@host 'docker run hello-from-B'

Warning: Changing the default docker daemon binding to a TCP port or Unix docker user group will increase your security risks by allowing non-root users to gain root access on the host. Make sure you control access to docker. If you are binding to a TCP port, anyone with access to that port has full Docker access; so it is not advisable on an open network.


With -H it is possible to make the Docker daemon to listen on a specific IP and port. By default, it will listen on unix:///var/run/docker.sock to allow only local connections by the root user. You could set it to 0.0.0.0:2375 or a specific host IP to give access to everybody, but that is not recommended because then it is trivial for someone to gain root access to the host where the daemon is running.

Similarly, the Docker client can use -H to connect to a custom port. The Docker client will default to connecting to unix:///var/run/docker.sock on Linux, and tcp://127.0.0.1:2376 on Windows.

-H accepts host and port assignment in the following format:

tcp://[host]:[port][path] or 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
yosefrow
  • 2,128
  • 20
  • 29
  • I would recommend using a non ssh option, as passing environment variables like jenkins credentials is tricky (not impossible) – Mohammed Rafeeq Oct 26 '18 at 10:41
  • @MohammedRafeeq Tricky how? – yosefrow Oct 26 '18 at 10:49
  • from a jenkins file try passing a environment variable to the target docker machine which is reached via ssh. you will understand what i mean – Mohammed Rafeeq Oct 26 '18 at 13:32
  • sh " DOCKER_HOST=\"${DOCKER_HOST}\" docker run -e ENVIRONMENT_VARIABLE ..." is preferred. its easy to send the environment variable values, otherwise whatever values are set , they wont be accessible after ssh ing into the target docker host .DOCKER_HOSTvariable is of format tcp://hostname:2376 2376 is the default port – Mohammed Rafeeq Oct 26 '18 at 13:50
1

As you said the connectivity is available between the servers, you can make use of Docker rich APIs.

There are 2 ways in configuring the docker daemon port

1) Configuring at /etc/default/docker file:

DOCKER_OPTS="-H tcp://127.0.0.1:5000 -H unix:///var/run/docker.sock"

2) Configuring at /etc/docker/daemon.json:

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

For more details on configuring docker daemon port, refer configure-docker-daemon-port

Once the Docker ports are configured, you can access the Docker APIs in the remote host.

JSON input file:

#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 to create a container:

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

The ID generated is the container ID and status will not be active/running.

API for starting the created container.

# curl -X POST http://192.168.56.101:6000/containers/f5d3273e48350/start | jq .  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current

API to check the status/inspect the container:

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

Refer this for more info:

DOCKER APIs

How to build an Image using Docker API?

How to commit Docker Container using API

Hope this info will he helpful.

Here_2_learn
  • 5,013
  • 15
  • 50
  • 68
  • I've been using the `docker-py` API to test and tried to run the container on the machine B using `DOCKER_HOST`, unfortunately this ends up in the same result as calling `DOCKER_HOST=... docker run...`. Anyway, your solution seems to do what I'm looking for, I'll try it if `docker-machine` isn't working as expected. Thanks! – ccharly Jun 09 '17 at 08:59
  • can you update for appropriate answer which has helped you.. it will help others as well when they face similar issue..Thanks.. – Here_2_learn Jun 12 '17 at 06:47