4 Punkte von GN⁺ 2025-06-19 | 1 Kommentare | Auf WhatsApp teilen
  • Unregistry ist eine leichtgewichtige Container-Image-Registry, die Images direkt aus dem Docker-Daemon-Speicher speichert und bereitstellt; mit dem Befehl docker pussh werden Images per SSH direkt an einen entfernten Docker-Server übertragen
  • Bisherige Optionen sind Docker Hub/GitHub Container Registry, eine selbst gehostete Registry, docker save | ssh... docker load oder ein Remote-Rebuild – jeweils mit Problemen wie öffentlichen bzw. kostenpflichtigen Repositories, Betriebsaufwand, Übertragung des gesamten Images oder verschwendeten Serverressourcen
  • docker pussh myapp:latest user@server erstellt einen SSH-Tunnel, startet auf dem entfernten Server einen temporären unregistry-Container und führt dann über einen weitergeleiteten localhost-Port docker push aus, sodass nur Layer übertragen werden, die auf dem Ziel fehlen
  • Auf dem entfernten Server werden eine laufende Docker-Umgebung, Berechtigungen für den docker-Befehl sowie beim ersten Start Zugriff auf ghcr.io/psviderski/unregistry:latest benötigt; der unregistry-Container läuft wegen des Zugriffs auf /run/containerd/containerd.sock als root
  • Wenn der containerd image store von Docker aktiviert ist, sind die übertragenen Images sofort in Docker nutzbar und doppelte Speicherung wird vermieden; Images und Container, die mit dem bisherigen classic storage driver erstellt wurden, können jedoch vorübergehend unsichtbar sein

Welches Deployment-Problem Unregistry löst

  • Unregistry ist eine leichtgewichtige Container-Image-Registry, um Docker-Images ohne externe Registry auf entfernte Server zu übertragen
  • Der enthaltene Docker-CLI-Plugin-Befehl heißt docker pussh; das zusätzliche s steht für SSH
  • Beim Übertragen lokal gebauter Docker-Images auf einen Server haben die üblichen Optionen folgende Einschränkungen
    • Docker Hub / GitHub Container Registry: Der Code wird öffentlich oder es fallen Kosten für private Repositories an
    • Self-hosted registry: Es entsteht ein zusätzlicher Dienst mit Wartungs-, Sicherheits- und Storage-Aufwand
    • Save/Load: docker save | ssh <remote server> docker load überträgt das gesamte Image, selbst wenn 90 % davon bereits auf dem Server vorhanden sind
    • Remote rebuild: Verbraucht Zeit und Serverressourcen; in der Produktion müssen unter Umständen Build-Fehler debuggt werden

Funktionsweise von docker pussh

  • Die Grundnutzung besteht aus einer einzigen Zeile
docker pussh myapp:latest user@server
  • Dieser Befehl überträgt nur fehlende Layer direkt per SSH, ohne Registry-Konfiguration, Abonnement, Zwischenspeicher oder offene Ports
  • Intern läuft der Vorgang in folgender Reihenfolge ab
    • Ein SSH-Tunnel zum entfernten Server wird erstellt
    • Auf dem Server wird ein temporärer unregistry-Container gestartet
    • Ein beliebiger localhost-Port wird über den Tunnel auf den unregistry-Port weitergeleitet
    • Über den weitergeleiteten Port wird docker push ausgeführt, sodass nur Layer übertragen werden, die auf dem Ziel fehlen
    • Das übertragene Image ist im entfernten Docker-Daemon sofort nutzbar
    • Der unregistry-Container wird gestoppt und der SSH-Tunnel geschlossen
  • Das Projekt zielt auf eine einfache und effiziente Übertragung nach Art von rsync für Docker-Images
  • Unregistry wurde für das leichtgewichtige Tool Uncloud entwickelt, das Container auf mehrere Docker-Hosts verteilt; benötigt wurde ein Ansatz, der einfacher als eine vollständige Registry und effizienter als save/load ist

Laufzeitvoraussetzungen und Einschränkungen

  • Auf dem lokalen Rechner wird Docker-CLI-Plugin-Support benötigt, außerdem Docker 19.03 oder neuer und ein OpenSSH-Client
  • Auf dem entfernten Server muss Docker installiert sein und laufen
  • Der SSH-Benutzer muss berechtigt sein, den Befehl docker auszuführen
    • Der Benutzer ist entweder root oder ein non-root-Benutzer in der Gruppe docker
    • Siehe dazu die Docker-Dokumentation Manage Docker as a non-root user
    • Falls sudo erforderlich ist, muss sudo docker ohne Passwort-Prompt ausführbar sein
  • Beim ersten Ausführen von docker pussh muss der entfernte Server das Image ghcr.io/psviderski/unregistry:latest von ghcr.io abrufen können
    • Server, die einen Proxy benötigen, müssen gemäß Dockers Anleitung Daemon proxy configuration konfiguriert werden
    • In air-gapped Umgebungen oder Umgebungen mit eingeschränktem Zugriff auf ghcr.io kann die benötigte Version des unregistry-Images ermittelt und manuell vorgeladen werden
  • Der unregistry-Container benötigt Zugriff auf /run/containerd/containerd.sock und wird daher mit den nötigen Berechtigungen als root ausgeführt

Installation und unterstützte Plattformen

  • Unter macOS/Linux kann docker-pussh per Homebrew installiert werden
brew install psviderski/tap/docker-pussh
  • Nach der Homebrew-Installation muss ein symbolischer Link ~/.docker/cli-plugins/docker-pussh erstellt werden, damit docker pussh als Docker-CLI-Plugin nutzbar ist
  • Unter macOS/Linux steht außerdem ein Direktdownload bereit
    • Das aktuelle Versionsbeispiel ist v0.4.3, bei dem das Skript docker-pussh in das Docker-Plugin-Verzeichnis heruntergeladen und ausführbar gemacht wird
    • Auch das Herunterladen des neuesten Skripts aus dem main-Branch wird angeboten
  • Debian kann über das inoffizielle Paket-Repository unregistry-debian installiert werden, das von @dariogriffo erstellt wurde und gepflegt wird
  • Windows wird derzeit nicht unterstützt, aber WSL 2 und die Linux-Installationsschritte können ausprobiert werden
  • Die Installation wird mit folgendem Befehl geprüft
docker pussh --help

Konfiguration des containerd image store

  • Unregistry speichert Images direkt im image store von containerd, der darunterliegenden Container-Runtime, die Docker verwendet
  • Im Standardverhalten von Docker pflegt Docker eine separate Speicherschicht und nutzt Images aus containerd nicht direkt
  • Wenn in Docker der containerd image store aktiviert wird, kann Docker die von unregistry gespeicherten Images direkt verwenden und Duplikate vermeiden
  • Bei aktiviertem containerd image store

    • Per unregistry gepushte Images sind in Docker sofort nutzbar
    • Images werden nur einmal in containerd gespeichert und verbrauchen keinen zusätzlichen Speicherplatz
    • Da kein zusätzlicher Pull von unregistry in den classic Docker image store nötig ist, werden pussh-Vorgänge schneller
  • Bei Beibehaltung des Docker-Standardverhaltens

    • Nach dem Push führt pussh auf dem entfernten Host zusätzlich docker pull aus, um das Image in Docker nutzbar zu machen
    • Das Image wird jeweils in containerd und im classic Docker image store gespeichert und damit zweimal gespeichert
    • Unmanaged Images in containerd können mit der Zeit Speicherplatz füllen
    • Die manuelle Verwaltung erfolgt mit folgenden Befehlen
    sudo ctr -n moby images ls
    sudo ctr -n moby images rm <image>
    
  • Hinweise zur Konfiguration

    • Zum Aktivieren des containerd image store in Docker Engine ist der offiziellen Docker-Dokumentation zu folgen
    • Nach der Umstellung auf den containerd image store sind Images und Container, die mit dem classic storage driver erstellt wurden, vorübergehend nicht sichtbar
    • Diese Ressourcen bleiben im Dateisystem erhalten und können wiederhergestellt werden, indem die Funktion containerd image store deaktiviert wird

Nutzungsbeispiele

  • Für eine einfache Übertragung werden nur der Image-Name und das entfernte SSH-Ziel angegeben
docker pussh myapp:latest user@server.example.com
  • Wenn kein Private Key im SSH-Agent registriert ist, kann der Key mit -i angegeben werden
docker pussh myapp:latest ubuntu@192.168.1.100 -i ~/.ssh/id_rsa
  • Ein benutzerdefinierter SSH-Port wird angegeben, indem der Port an das Ziel angehängt wird
docker pussh myapp:latest user@server:2222
  • Eine benutzerdefinierte SSH-Config-Datei wird mit -F angegeben
docker pussh myapp:latest prod-server -F ~/.ssh/config.prod
  • Um bei einem Multi-Plattform-Image nur eine bestimmte Plattform zu pushen, wird --platform verwendet
docker pussh myapp:latest user@server --platform linux/amd64
  • Um auf dem entfernten Host eine bestimmte Version des unregistry-Images zu verwenden, wird die Umgebungsvariable UNREGISTRY_IMAGE gesetzt
UNREGISTRY_IMAGE=ghcr.io/psviderski/unregistry:A.B.C docker pussh myapp:latest user@server.example.com

Typische Anwendungsfälle

  • Bei Deployments auf Produktionsservern können lokal gebaute Images ohne Zwischen-Registry direkt auf den Server gepusht und dort ausgeführt werden
docker build --platform linux/amd64 -t myapp:1.2.3 .
docker pussh myapp:1.2.3 deploy@prod-server
ssh deploy@prod-server docker run -d myapp:1.2.3
  • In CI/CD-Pipelines lässt sich die Komplexität einer Registry überspringen, indem Images direkt an das Deployment-Ziel gesendet werden
- name: Build and deploy
  run: |
    docker build -t myapp:${{ github.sha }} .
    docker pussh myapp:${{ github.sha }} deploy@staging-server
  • In Homelab- und air-gapped Umgebungen können Images in isolierte Netzwerke deployed werden, die keinen Internetzugang zu öffentlichen Registries haben

Fortgeschrittene Nutzung und verwandte Projekte

  • Unregistry kann auch als eigenständige lokale Registry verwendet werden
    • Dazu wird /run/containerd/containerd.sock gemountet und der Container ghcr.io/psviderski/unregistry ausgeführt
    • Anschließend kann localhost:5000 wie eine normale Registry mit docker tag und docker push verwendet werden
  • Für SSH-Konfigurationen kann die Standard-SSH-Config-Datei genutzt oder mit -F eine separate Config-Datei übergeben werden
  • Verwandte Third-Party-Projekte werden angeboten
  • Für die Implementierung werden Spegel und Docker Distribution erwähnt
    • Spegel ist eine P2P-Container-Image-Registry, die als Inspiration für eine Registry-Implementierung mit containerd image store als Backend diente
    • Docker Distribution ist die Docker-Registry-Implementierung, auf der unregistry basiert

1 Kommentare

 
GN⁺ 2025-06-19
Meinungen auf Hacker News
  • Als jemand, der Docker entwickelt hat, gefällt mir das. Das ideale Design wäre wohl ein einzelner Server gewesen, ohne Trennung zwischen Docker Engine und Registry.
    Wenn er Container je nach Bedarf hätte speichern, übertragen und ausführen können, wäre daraus eine deutlich robustere Komponente geworden; außerdem hätte man die unglückliche Entwicklung vermeiden können, dass Engine und Registry Images unterschiedlich speichern.
    Außerdem sollte meiner Ansicht nach jeder Produktions-Cluster einen verteilten Image-Speicher haben, und das Pushen eines Images in diesen Speicher sollte das Deployment auslösen. Die heutige Vorgehensweise – in eine Registry pushen, den Cluster konfigurieren und dann jeden Node aus der Registry pullen lassen – ist fragil und ineffizient. Ich habe für ein besseres Design argumentiert, aber die Trägheit war schon zu groß, und die frühe Kubernetes-Community stand Ideen aus Docker feindselig gegenüber.

    • Dass es mindestens drei Dateisystem-Layouts für Images und sogar zwei Image-Speicher innerhalb der Engine gibt, ist definitiv unübersichtlich. Trotzdem glaube ich, dass es für Docker noch nicht zu spät ist, in diese Richtung zu gehen, ohne das aktuelle Modell aufzubrechen. Ob Docker sich darum kümmert, weiß ich allerdings nicht; derzeit scheint das Unternehmen schwierige Zeiten durchzumachen.
      Deployment per Push in den Cluster klingt clever. Ich denke über einen verteilten Image-Speicher nach, bei dem unregistry auf allen Nodes läuft, sodass sie Images voneinander holen und teilen können; aber darüber, dass ein Push das Deployment auslöst, muss ich noch etwas länger nachdenken.
  • Gefällt mir. Der Befehl pussh verdient wirklich Anerkennung als elegantes Wortspiel. Leicht zu merken, die Bedeutung ist sofort erkennbar, und er unterscheidet sich vom verwandten Standardbefehl nur um einen Buchstaben.

    • Nicht schlecht, aber ein offizieller wirkender Alias wie docker push-over-ssh wäre auch gut.
      In gemeinsam entwickelter Automatisierung kann pussh für jemanden, der die Funktion nicht kennt, wie ein Tippfehler aussehen und unnötige Verwirrung stiften. push-over-ssh macht dagegen klar, dass der Name beabsichtigt ist. Man kann es wie kurze und lange Optionen betrachten.
    • Das zusätzliche s ist das s aus sssh.
      „Was ist dieses zusätzliche s?“
      „Ein Tippfehler.“
    • Es ist auch kollisionsanfällig.
  • 2015 habe ich naiv versucht, diese Funktion per PR[1] in den Docker-Mainline-Zweig zu bringen, wurde aber schnell darauf umgelenkt, in anderen Bereichen zu helfen. Etwas, das die Registry überflüssig macht, hätte Dockers Geschäftsmodell vielleicht zu stark erschüttert.
    [1]: https://github.com/richardcrichardc/docker2docker

    • Sie waren also der Ursprung. Schade, dass Docker immer noch keine API zum Durchsuchen von Image-Layern hat.
      Ich vermute, der Plan ist letztlich, den Standard auf den containerd-Image-Speicher umzustellen. Wenn lokal wie remote der containerd-Image-Speicher genutzt wird, sollte man das, was ursprünglich implementiert wurde, auch ohne Registry-Wrapper umsetzen können.
  • Eine tolle Idee, die gut zu Systemen passen dürfte, die bereits Push-Deployment-Tools wie Ansible verwenden. In Unternehmen ohne 24/7-Support für die Docker Registry könnte das auch ein guter Mechanismus für Hotfix-Deployments sein.
    Ich frage mich, ob es sich sauber in OCI-Tools wie buildah integrieren lässt oder ob an beiden Enden eine vollständige Docker-Installation nötig ist. Ich habe noch nicht tiefer hineingeschaut, aber damit skopeo in so einer Konfiguration funktioniert, scheint das Bootstrappen einer Mini-Registry auf dem Remote-Server das fehlende Puzzleteil zu sein.

    • Auf der Remote-Seite wird containerd benötigt. Docker und Kubernetes verwenden ebenfalls containerd. Auf Client-Seite reicht irgendein Tool, das die Registry-API spricht, also die OCI Distribution Specification (https://github.com/opencontainers/distribution-spec).
      Unregistry verwendet auf der API-Schicht den offiziellen Docker-Registry-Code wieder und sieht daher ähnlich aus und verhält sich ähnlich wie https://hub.docker.com/_/registry.
      Auf dem Client kann man Tools verwenden, die eine OCI Registry sprechen, etwa skopeo, crane, regclient, BuildKit usw. Um diese zu nutzen, muss man unregistry allerdings manuell auf dem Remote-Host starten. Der Befehl docker pussh automatisiert diesen Ablauf lediglich mithilfe des lokalen Docker.
      Man kann es einfach als Bash-Skript betrachten: https://github.com/psviderski/unregistry/blob/main/docker-pu...
      Es lässt sich leicht nach eigenen Vorstellungen anpassen.
    • Stimme zu. Bei mehreren von mir verwalteten Services baue ich die Images lokal, speichere sie, lade das Archiv mit Ansible hoch und stelle das Image dann wieder her; das dauert normalerweise viel länger, als mir lieb ist.
    • An beiden Enden wird ein Docker-Daemon benötigt. Es ist eine clevere Methode, Layer per SSH zwischen zwei Daemons zu teilen.
  • So hätte es von Anfang an sein sollen. Hervorragend.
    Docker Registries haben zwar ihre Einsatzbereiche, aber insgesamt sind sie überdesignt und stehen dem Hacker-Geist eher entgegen.

    • Docker war ein VC-finanziertes Unternehmen, also musste es irgendwie Geld verdienen.
    • Ich empfehle, zusammen mit GitHub Actions die GitHub-Registry ghcr.io zu verwenden.
      Es dauerte etwa 20 Minuten, einen .yaml-Workflow einzurichten, der Images baut und in eine private ghcr.io-Registry pusht, und etwa 5 Minuten, dem Server zu erlauben, Images von dort abzurufen. Eine ziemlich pragmatische Konfiguration.
    • Die Komplexität scheint im Ablauf zum Pushen von Blobs in die Registry zu liegen. Ich habe früher einmal eine OCI-kompatible Read-only-Registry gebaut, und die war nicht so kompliziert.
  • Ich sehe mir gerade eine Pipeline an, die Images in GitLab baut und nach Artifactory pusht; dann wird ein Deployment ausgelöst, das sie aus Artifactory holt und erneut nach AWS ECR pusht, anschließend die Deployment-Templates in EKS aktualisiert, woraufhin die Nodes die Images aus ECR ziehen und Pod-Container starten.
    Ich brauche das in meinem Leben.

    • Aus Neugier: Warum verwendet ihr sowohl Artifactory als auch ECR? Wir prüfen gerade wegen Kostensenkungen den Wechsel von Artifactory zu ECR.
    • In der Pipeline meines letzten Projekts dauerte das Pullen und Pushen von Containern länger als das eigentliche Bauen der App. Und selbst diese ganze Zeit war noch wenig im Vergleich zur Wartezeit für Health Checks, obwohl man in Wirklichkeit weniger als eine Sekunde nach dem Start wusste, ob alles in Ordnung war oder nicht.
  • Dadurch bin ich auf uncloud aufmerksam geworden. Ich suchte für den Server-Aufbau eines Nebenprojekts nach etwas wie dokku, nur etwas leistungsfähiger – und genau danach fühlt es sich an.

    • Es gibt auch https://skateco.github.io/. Auf den ersten Blick wirkt es ähnlich.
    • Falls du Portainer noch nicht genutzt oder evaluiert hast, kann ich es empfehlen. Ich betreibe auf AWS auf zwei EC2-Instanzen Portainer Community Edition und Portainer Agent, und es funktioniert gut.
      Die Stack-Funktion ist ebenfalls wirklich gut; im Grunde ist sie Docker Compose. Auf einer EC2-Instanz startet Portainer Agent Caddy in einem Container, und Caddy dient als Load Balancer und Reverse Proxy.
    • Freut mich, dass dich die Idee von uncloud anspricht. Wenn du Fragen hast oder Hilfe brauchst, kannst du auch in den Discord kommen.
  • Es ist ziemlich seltsam, dass Docker nicht von Anfang an so funktioniert hat. Sieht cool aus.

    • Man kann dasselbe bereits erreichen, indem man ein Image als Archiv erstellt, es auf den Server pusht und es dort aus diesem Archiv ausführt.
      Speichern lässt sich das Archiv etwa mit docker save -o may-app.tar my-app:latest, Laden mit docker load -i /path/to/my-app.tar.
      Mit Tools wie Ansible lässt sich leicht erreichen, was „Unregistry“ automatisiert. Laut GitHub-Repository ist der Nachteil des save/load-Ansatzes, dass das gesamte Image über das Netzwerk übertragen wird, was in der Praxis ein Problem sein kann. Außerdem scheint es bequemer, das Image selbst statt einer Archivdatei zu verwalten.
  • Ein sauberes Projekt und ein sauberer Ansatz. Ich hatte genug von teuren Registries und hoste deshalb Zot[1] selbst, aber für manche Einsatzzwecke wirkt das hier deutlich einfacher.
    Ich frage mich, ob es noch andere gibt, die sich einen privaten Registry-Service wünschen, der einfach einzurichten, günstig und nutzungsbasiert abgerechnet ist.
    [1]: https://zotregistry.dev

    • Falls du es noch nicht bemerkt hast: Das SSL-Zertifikat von zothub.io ist abgelaufen.
  • Eine gute Idee. Allerdings könnte es den Nachteil geben, dass das Deployment an den Service gekoppelt wird. Zum Beispiel ist mir nicht klar, wie Scale-out oder Red/Green-Deployments funktionieren sollen; wer so etwas übernimmt, müsste wohl vom Push wissen.
    Nachtrag: Das macht offenbar uncloud. Habe ich gerade erst erfahren.
    Trotzdem ist es ein Trade-off. Wenn der Umfang klein ist, man eine einzelne Hetzner-VM nutzt, mit Einfachheit zufrieden ist und es in Ordnung findet, Images lokal zu bauen, ist es großartig.

    • Es ist definitiv immer ein Trade-off. Es ist gut, Optionen zu haben, damit man für jede Aufgabe das passendste Tool auswählen kann.