It can be very handy to have a remote Docker server instead of the default local configuration from our workstation because of bandwidth issues, testing a production environment, maybe a customer demonstration, or distant team collaboration. Being able to send the usual Docker commands to a remote server has a multitude of advantages. For speed and comfort, we'll deploy a basic CoreOS system, add one user (Jane) and its public key. Docker will be modified to listen to the network through a socket kind of systemd service, and we'll configure the server time zone to be in New York.
To step through this recipe, you will need:
Let's start by simply calling this server "docker"
:
#cloud-config hostname: "docker"
In the final system, this will set the hostname to the correct value:
$ hostname docker $ cat /etc/hostname docker
Now let's create the Jane
user, so she can log in to the instance to remotely help us. She needs to be in the docker
group, so she can manipulate the containers, and she gave us her SSH public key. This is how it translates in the cloud-config file:
#cloud-config users: - name: "jane" gecos: "Jane Docker" groups: - "docker" ssh-authorized-keys: - "ssh-rsa AAAAB[...] jane"
In the final system, Jane is able to log in using her private key, and interact with the docker daemon as she's a member of the docker group:
jane@docker ~ $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
The SSH public key ends up in the following file:
jane@docker ~ $ cat .ssh/authorized_keys.d/coreos-cloudinit ssh-rsa AAAAB [..] jane
CoreOS uses a system built around NTP (short for Network Time Protocol), controlled by the timedatectl
command. We won't find the usual /etc/timezone
on CoreOS, so the default timezone
directive from cloud-init we've seen earlier in this book won't work. To set the time zone to New York on CoreOS, we would set it like this:
$ /usr/bin/timedatectl set-timezone America/New_York
Easy! So let's launch that command through a systemd unit in the cloud-config file, so we're sure the time zone is set. In-depth knowledge of systemd is out of the scope of this book, but to do that, we'll have to add two options to the unit: one that tells systemd to not think the unit has crashed because the command exited (RemainAfterExit=yes
), and one that tells the unit type is not executing a long running process, but instead a short one that should exit before continuing (Type=oneshot
).
Here's the unit in the cloud-config.yml
file:
coreos: units: - name: settimezone.service command: start content: | [Unit] Description=Setting the timezone [Service] ExecStart=/usr/bin/timedatectl set-timezone America/New_York RemainAfterExit=yes Type=oneshot
Our final objective is to be able to use a Docker Engine remotely from our workstation. The default Docker configuration is to listen to the Unix socket (/var/run/docker.sock
)—and we want it to listen to a TCP socket on port 2375 (the default unencrypted port, it's highly recommended to configure TLS encryption; this will use the TCP/2376 by convention). To configure this, we'll use a systemd feature—socket activation. To make it short, this creates a systemd service that listens on port 2375, and spawns the regular docker.service
unit along with the socket description. This way, this particular Docker Engine will answer to requests on the TCP socket and not on the Unix socket (while keeping the possibility to activate more TCP sockets, or keeping the default docker.service
clean). Here's how it looks:
coreos: units: - name: docker-tcp.socket command: start enable: true content: | [Unit] Description=Docker Socket for the API [Socket] ListenStream=2375 BindIPv6Only=both Service=docker.service [Install] WantedBy=sockets.target
Let's start a remote server with this whole configuration and use it a little for the demonstration (in this example, the Docker remote host is 52.211.117.98
, and we'll launch an nginx
container with HTTP port forwarding). Refer to the Docker section of this book for more information on the command-line options used:
user@workstation $ docker -H 52.211.117.98 run -it --rm -p 80:80 nginx Unable to find image 'nginx:latest' locally latest: Pulling from library/nginx 6a5a5368e0c2: Pull complete 4aceccff346f: Pull complete c8967f302193: Pull complete Digest: sha256:1ebfe348d131e9657872de9881fe736612b2e8e1630e0508c354acb0350a4566 Status: Downloaded newer image for nginx:latest 1.2.3.4 - - [25/Sep/2016:16:06:30 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36" "-"
Make some requests on the remote Docker host HTTP port and it will answer. We now have a full on-demand CoreOS host, capable of giving us control over a Docker Engine remotely, using a nifty systemd configuration feature!
When connecting to various remote Docker Engines, we will sooner or later connect to a server not using the same version of the server as our client. In this case, we'll get the following error:
Error response from daemon: client is newer than server (client API version: 1.24, server API version: 1.22)
The easy workaround is to override the DOCKER_API_VERSION
environment variable and set it to the same value as the server (1.22 in this example):
$ DOCKER_API_VERSION=1.22 docker -H 52.211.117.98 ps
3.144.227.9