Readit News logoReadit News
Posted by u/psviderski 6 months ago
Show HN: Unregistry – “docker push” directly to servers without a registrygithub.com/psviderski/unr...
I got tired of the push-to-registry/pull-from-registry dance every time I needed to deploy a Docker image.

In certain cases, using a full-fledged external (or even local) registry is annoying overhead. And if you think about it, there's already a form of registry present on any of your Docker-enabled hosts — the Docker's own image storage.

So I built Unregistry [1] that exposes Docker's (containerd) image storage through a standard registry API. It adds a `docker pussh` command that pushes images directly to remote Docker daemons over SSH. It transfers only the missing layers, making it fast and efficient.

  docker pussh myapp:latest user@server
Under the hood, it starts a temporary unregistry container on the remote host, pushes to it through an SSH tunnel, and cleans up when done.

I've built it as a byproduct while working on Uncloud [2], a tool for deploying containers across a network of Docker hosts, and figured it'd be useful as a standalone project.

Would love to hear your thoughts and use cases!

[1]: https://github.com/psviderski/unregistry

[2]: https://github.com/psviderski/uncloud

shykes · 6 months ago
Docker creator here. I love this. In my opinion the ideal design would have been:

1. No distinction between docker engine and docker registry. Just a single server that can store, transfer and run containers as needed. It would have been a much more robust building block, and would have avoided the regrettable drift between how the engine & registry store images.

2. push-to-cluster deployment. Every production cluster should have a distributed image store, and pushing images to this store should be what triggers a deployment. The current status quo - push image to registry; configure cluster; individual nodes of the cluster pull from registry - is brittle and inefficient. I advocated for a better design, but the inertia was already too great, and the early Kubernetes community was hostile to any idea coming from Docker.

psviderski · 6 months ago
Hey Solomon, thank you for sharing your thoughts, love your work!

1. Yeah agreed, it's a bit of a mess that we have at least three different file system layouts for images and two image stores in the engine. I believe it's still not too late for Docker to achieve what you described without breaking the current model. Not sure if they care though, they're having hard times

2. Hm, push-to-cluster deployment sounds clever. I'm definitely thinking about a distributed image store, e.g. embedding unregistry in every node so that they can pull and share images between each other. But triggering a deployment on push is something I need to think through. Thanks for the idea!

nine_k · 6 months ago
Nice. And the `pussh` command definitely deserves the distinction of one of the most elegant puns: easy to remember, self-explanatory, and just one letter away from its sister standard command.
gchamonlive · 6 months ago
It's fine, but it wouldn't hurt to have a more formal alias like `docker push-over-ssh`.

EDIT: why I think it's important because on automations that are developed collaboratively, "pussh" could be seen as a typo by someone unfamiliar with the feature and cause unnecessary confusion, whereas "push-over-ssh" is clearly deliberate. Think of them maybe as short-hand/full flags.

psviderski · 6 months ago
That's a valid concern. You can very easily give it whatever name you like. Docker looks for `docker-COMAND` executables in ~/.docker/cli-plugins directory making COMMAND a `docker` subcommand.

Rename the file to whatever you like, e.g. to get `docker pushoverssh`:

  mv ~/.docker/cli-plugins/docker-pussh ~/.docker/cli-plugins/docker-pushoverssh
Note that Docker doesn't allow dashes in plugin commands.

whalesalad · 6 months ago
can easily see an engineer spotting pussh in a ci/cd workflow or something and thinking "this is a mistake" and changing it.
EricRiese · 6 months ago
> The extra 's' is for 'sssh'

> What's that extra 's' for?

> That's a typo

someothherguyy · 6 months ago
and prone to collision!
nine_k · 6 months ago
Indeed so! Because it's art, not engineering. The engineering approach would require a recognizably distinct command, eliminating the possibility of such a pun.
richardc323 · 6 months ago
I naively sent the Docker developers a PR[1] to add this functionality into mainline Docker back in 2015. I was rapidly redirected into helping out in other areas - not having to use a registry undermined their business model too much I guess.

[1]: https://github.com/richardcrichardc/docker2docker

psviderski · 6 months ago
You're the OG! Hats off, mate.

It's a bummer docker still doesn't have an API to explore image layers. I guess their plans to eventually transition to containerd image store as the default. Once we have containerd image store both locally and remotely we will finally be able to do what you've done without the registry wrapper.

cik · 6 months ago
You're bang on, but you can do things with dive (https://github.com/wagoodman/dive) and use chunks of the code in other projects... That's what I've been doing. The license is MIT so it's permissive.

But yes, an API would be ideal. I've wasted far too much time on this.

alisonatwork · 6 months ago
This is a cool idea that seems like it would integrate well with systems already using push deploy tooling like Ansible. It also seems like it would work as a good hotfix deployment mechanism at companies where the Docker registry doesn't have 24/7 support.

Does it integrate cleanly with OCI tooling like buildah etc, or if you need to have a full-blown Docker install on both ends? I haven't dug deeply into this yet because it's related to some upcoming work, but it seems like bootstrapping a mini registry on the remote server is the missing piece for skopeo to be able to work for this kind of setup.

psviderski · 6 months ago
You need a containerd on the remote end (Docker and Kubernetes use containerd) and anything that speaks registry API (OCI Distribution spec: https://github.com/opencontainers/distribution-spec) on the client. Unregistry reuses the official Docker registry code for the API layer so it looks and feels like https://hub.docker.com/_/registry

You can use skopeo, crane, regclient, BuildKit, anything that speaks OCI-registry on the client. Although you will need to manually run unregistry on the remote host to use them. 'docker pussh' command just automates the workflow using the local Docker.

Just check it out, it's a bash script: https://github.com/psviderski/unregistry/blob/main/docker-pu...

You can hack your own way pretty easily.

dirkc · 6 months ago
I agree! For a bunch of services I manage I build the image locally, save it and then use ansible to upload the archive and restore the image. This usually takes a lot longer than I want it to!
0x457 · 6 months ago
It needs docker daemon on both ends. This is just a clever way to share layer between two daemons via ssh.
metadat · 6 months ago
This should have always been a thing! Brilliant.

Docker registries have their place but are overall over-engineered and an antithesis to the hacker mentality.

password4321 · 6 months ago
As a VC-funded company Docker had to make money somehow.
dreis_sw · 6 months ago
I recommend using GitHub's registry, ghcr.io, with GitHub Actions.

I invested just 20 minutes to setup a .yaml workflow that builds and pushes an image to my private registry on ghcr.io, and 5 minutes to allow my server to pull images from it.

It's a very practical setup.

ezekg · 6 months ago
I think the complexity lies in the dance required to push blobs to the registry. I've built an OCI-compliant pull-only registry before and it wasn't that complicated.
amne · 6 months ago
Takes a look at pipeline that builds image in gitlab, pushes to artifactory, triggers deployment that pulls from artifactory and pushes to AWS ECR, then updates deployment template in EKS which pulls from ECR to node and boots pod container.

I need this in my life.

forix · 6 months ago
Out of curiosity why do you use both Artifactory and ECR? We're currently considering a switch from Artifactory to ECR for cost savings reasons.
maccard · 6 months ago
My last projects pipeline spent more time pulling and pushing containers than it did actually building the app. All of that was dwarfed by the health check waiting period, when we knew in less than a second from startup if we were actually healthy or not.
lxe · 6 months ago
Ooh this made me discover uncloud. Sounds like exactly what I was looking for. I wanted something like dokku but beefier for a sideproject server setup.
vhodges · 6 months ago
There is also https://skateco.github.io/ which (at quick glance) seems similar
byrnedo · 6 months ago
Skate author here: please try it out! I haven’t gotten round to diving deep into uncloud yet, but I think maybe the two projects differ in that skate has no control plane; the cli is the control plane.

I built skate out of that exact desire to have a dokku like experience that was multi host and used a standard deployment configuration syntax ( k8s manifests ).

https://skateco.github.io/docs/getting-started/

nodesocket · 6 months ago
A recommendation for Portainer if you haven't used or considered it. I'm running two EC2 instances on AWS using portainer community edition and portainer agent and works really well. The stack feature (which is just docker compose) is also super nice. One EC2 instance; running Portainer agent runs Caddy in a container which acts as the load balancer and reverse proxy.
lxe · 6 months ago
I'm actually running portainer for my homelab setup hosting things like octoprint and omada controller etc.
psviderski · 6 months ago
I'm glad the idea of uncloud resonated with you. Feel free to join our Discord if you have questions or need help
modeless · 6 months ago
It's very silly that Docker didn't work this way to start with. Thank you, it looks cool!
TheRoque · 6 months ago
You can already achieve the same thing by making your image into an archive, pushing it to your server, and then running it from the archive on your server.

Saving as archive looks like this: `docker save -o may-app.tar my-app:latest`

And loading it looks like this: `docker load -i /path/to/my-app.tar`

Using a tool like ansible, you can achieve easily what "Unregistry" is doing automatically. According to the github repo, save/load has the drawback of tranfering the whole image over the network, which could be an issue that's true. And managing the images instead of archive files seems more convenient.

nine_k · 6 months ago
If you have an image with 100MB worth of bottom layers, and only change the tiny top layer, the unregistry will only send the top layer, while save / load would send the whole 100MB+.

Hence the value.

francislavoie · 6 months ago
If you read the README, you'll see that replacing the "save | upload | load" workflow is the whole point of this, to drastically reduce the amount of data to upload by only sending new layers instead of everything, and you can use this inside your ansible setup to speed it up.
authorfly · 6 months ago
Good advice and beware the difference between docker export (which will fail if you lack enough storage, since it saves volumes) and docker save. Running the wrong command might knock out your only running docker server into an unrecoverable state...