I needed to transfer a docker container that I’d built on one machine, to another machine that I could access via SSH.
I recently had a challenge for one of my side projects. I needed to transfer a docker container that I’d built on one machine, to another machine that I could access via SSH. I didn’t want to push my container to a public docker registry, or go through the trouble of setting up my own private registry.
It didn’t take long to find an answer on stack overflow
docker save <image> | bzip2 | \ ssh [email protected] 'bunzip2 | docker load'
Lets break this down:
docker savetakes all the image data and serializes it, along with its tag, to a stream of binary data.
docker loadtakes a stream of binary data and deserializes it into an image with a tag.
bzip2compresses the stream and
bunzip2decompresses the stream.
ssh [email protected] 'some command'ssh’s into a remote host and runs the specified command.
It turns out that
docker load is able to automatically decompress
bzip’d content, so you can simplify the command to:
docker save <image> | bzip2 | \ ssh [email protected] 'docker load'
You can remove the
bzip2 but docker images are often big, and the compression from
bzip2 saves a massive amount of bandwidth.
I still had a problem though. I was doing all this over a slow 3G internet connection, and the remote host already had most of the layers in the image I wanted to push, I just needed to push the tiny new layer containing my application logic.
A bit more research and I found docker-push-ssh. With this command you can just do:
docker-push-ssh [email protected] <image>
It only transfers the layers that are needed. To do this, it:
docker pushto that local registry. This is fast because it’s all over the local machine’s network.
docker pullon the remote server. This pull is running over the SSH tunnel, but docker pull is smart enough to only pull the layers it doesn’t already have.
Docker Over SSH Diagram
This was a great idea, but there were three problems I had with it:
To solve all this, I decided to create a CLI in Node.js called docker-over-ssh. In spite of the name, docker-over-ssh is actually completely transport agnostic. It just requires a way to communicate over stdin and stdout with a remote instance of itself. To use it, you install
docker-over-ssh on both the local and remote machine. Then you can run:
docker-over-ssh push <image> \ ssh [email protected] "docker-over-ssh pull <image>"
The diagram looks exactly the same as for the previous solution.
docker-over-ssh push command starts a local docker registry, pushes the image to it, and then runs the “child command” (in this example that’s
ssh [email protected] "docker-over-ssh pull ") and proxies tcp traffic from that child command’s stdio to the local docker registry.
docker-over-ssh pull command starts a local TCP proxy (written in a few lines of node.js code) and connects that proxy to stdio so it can talk to the local docker registry. It then runs
docker pull pointing at the local registry. Only the new layers are transferred, which keeps everything very efficient.
The only permission required by the user is the ability to run
docker-over-ssh pull on the remote machine, nothing else is required.
At this point I had a working solution, but I wanted to automate this so that the deployments could be done from CircleCI. Adding an SSH key to CircleCI is fairly easy. The challenge was making this work with their complex docker networking setup:
dockerdaemon (used to run
docker push) cannot talk to containers run as services to the main CircleCI job.
I couldn’t find any practical way to solve either of these problems directly, but I found NGROK which makes it pretty easy to create an internet accessible address that proxies to a local service. With this I was able to tell CircleCI to start a docker registry as a service for my build, and then use ngrok to start a temporary proxy to expose it to the
docker daemon. It even supports securing it with a username and password (which I auto-generate on the fly) to keep everything nice and secure.
In the end all I had to do was update the CircleCI config:
docker: - image: circleci/node:12 environment: LOCAL_DOCKER_REGISTRY_PORT: '5000' - image: registry:2
and set the
DOCKER_REGISTRY_NGROK environment variable to my API key for ngrok, which you can get for free.
I then added some code to docker-over-ssh to handle those two environment variables.
The reason I’ve been doing all this is to try and setup a dokku server that I can run lots of side projects in and avoid spending loads of money on heroku for things that are rarely used. Thank you for reading!
The docker manifest command does not work independently to perform any action. In order to work with the docker manifest or manifest list, we use sub-commands along with it. This manifest sub-command can enable us to interact with the image manifests. Furthermore, it also gives information about the OS and the architecture, that a particular image was built for. The image manifest provides a configuration and a set of layers for a container image. This is an experimenta
Following the second video about Docker basics, in this video, I explain Docker architecture and explain the different building blocks of the docker engine; docker client, API, Docker Daemon. I also explain what a docker registry is and I finish the video with a demo explaining and illustrating how to use Docker hub.
What is DevOps? How are organizations transitioning to DevOps? Is it possible for organizations to shift to enterprise DevOps? Read more to find out!
Join me with guest Docker Captain Elton Stoneman to talk about the state of Docker Desktop and Docker Hub. Support this show on Patreon! It's the #1 way to support me interviewing DevOps and container experts, and doing this Live Q&A.
What is DevOps? What are the goals it helps achieves? What are its benefits? This article has answers!