4 points par GN⁺ 2025-06-19 | 1 commentaires | Partager sur WhatsApp
  • Unregistry est un registre d’images de conteneurs léger qui stocke et sert directement les images depuis le magasin du démon Docker, et la commande docker pussh permet d’envoyer une image directement à un serveur Docker distant via SSH
  • Les options existantes sont Docker Hub/GitHub Container Registry, un registre auto-hébergé, docker save | ssh... docker load ou une reconstruction à distance, mais chacune pose des problèmes de dépôt public ou payant, de charge d’exploitation, de transfert de l’image entière ou de gaspillage des ressources serveur
  • docker pussh myapp:latest user@server crée un tunnel SSH, lance un conteneur unregistry temporaire sur le serveur distant, puis exécute docker push via un port localhost redirigé afin de ne transférer que les couches absentes à distance
  • Le serveur distant doit disposer d’un environnement d’exécution Docker, des droits pour utiliser la commande docker, et au premier lancement d’un accès à ghcr.io/psviderski/unregistry:latest ; le conteneur unregistry s’exécute en root car il doit accéder à /run/containerd/containerd.sock
  • L’activation du containerd image store de Docker rend l’image transférée immédiatement utilisable dans Docker et évite le stockage en double, mais les images et conteneurs créés avec l’ancien classic storage driver peuvent temporairement ne plus apparaître

Le problème de déploiement que résout Unregistry

  • Unregistry est un registre d’images de conteneurs léger conçu pour transférer des images Docker vers un serveur distant sans registre externe
  • La commande du plugin Docker CLI incluse est docker pussh, le s supplémentaire signifiant SSH
  • Lorsqu’on veut transférer vers un serveur une image Docker construite localement, les options habituelles ont les limites suivantes
    • Docker Hub / GitHub Container Registry : il faut soit rendre le code public, soit payer pour des dépôts privés
    • Self-hosted registry : cela ajoute un service distinct avec maintenance, sécurité et coûts de stockage
    • Save/Load : docker save | ssh <remote server> docker load transfère l’image entière même si 90 % est déjà présent sur le serveur
    • Remote rebuild : cela consomme du temps et des ressources serveur, et il peut falloir déboguer des échecs de build en production

Fonctionnement de docker pussh

  • L’usage de base tient en une seule ligne
docker pussh myapp:latest user@server
  • Cette commande envoie uniquement les couches manquantes directement via SSH, sans configuration de registre, sans abonnement, sans stockage intermédiaire et sans port exposé publiquement
  • En interne, elle suit les étapes suivantes
    • Création d’un tunnel SSH vers le serveur distant
    • Démarrage sur le serveur d’un conteneur unregistry temporaire
    • Redirection d’un port localhost aléatoire à travers le tunnel vers le port d’unregistry
    • Exécution de docker push via le port redirigé afin de ne transférer que les couches absentes à distance
    • L’image transférée est immédiatement utilisable par le démon Docker distant
    • Arrêt du conteneur unregistry et fermeture du tunnel SSH
  • Le projet vise un transfert simple et efficace, comparable à rsync pour les images Docker
  • Unregistry a été créé pour Uncloud, un outil léger de déploiement de conteneurs sur plusieurs hôtes Docker, qui avait besoin d’une approche plus simple qu’un registre complet et plus efficace que save/load

Exigences d’exécution et limites

  • La machine locale doit prendre en charge les plugins Docker CLI et nécessite Docker 19.03 ou plus ainsi qu’un client OpenSSH
  • Le serveur distant doit avoir Docker installé et en cours d’exécution
  • L’utilisateur SSH doit avoir le droit d’exécuter la commande docker
    • Soit l’utilisateur est root, soit un utilisateur non root doit appartenir au groupe docker
    • Voir la documentation Docker Manage Docker as a non-root user
    • Si sudo est requis, il doit être possible d’exécuter sudo docker sans invite de mot de passe
  • Lors du premier lancement de docker pussh, le serveur distant doit pouvoir récupérer l’image ghcr.io/psviderski/unregistry:latest depuis ghcr.io
    • Si le serveur a besoin d’un proxy, il faut le configurer selon la documentation Docker Daemon proxy configuration
    • Dans un environnement air-gapped ou restreint vis-à-vis de ghcr.io, il est possible de précharger manuellement la version requise de l’image unregistry après l’avoir identifiée
  • Le conteneur unregistry doit accéder à /run/containerd/containerd.sock et s’exécute donc en root avec les privilèges nécessaires

Installation et plateformes prises en charge

  • Sur macOS/Linux, docker-pussh peut être installé avec Homebrew
brew install psviderski/tap/docker-pussh
  • Après une installation Homebrew, pour utiliser docker pussh comme plugin Docker CLI, il faut créer un lien symbolique ~/.docker/cli-plugins/docker-pussh
  • Une méthode par téléchargement direct est aussi proposée pour macOS/Linux
    • L’exemple de version actuelle consiste à télécharger le script docker-pussh de v0.4.3 dans le répertoire des plugins Docker puis à lui donner les droits d’exécution
    • Une méthode est aussi fournie pour télécharger le script le plus récent depuis la branche main
  • Sur Debian, l’installation est possible via le dépôt de paquets non officiel unregistry-debian, créé et maintenu par @dariogriffo
  • Windows n’est pas pris en charge pour le moment, mais il est possible d’essayer WSL 2 et la procédure d’installation Linux
  • La vérification de l’installation s’effectue avec la commande suivante
docker pussh --help

Configuration du containerd image store

  • Unregistry stocke directement les images dans l’image store de containerd, le runtime de conteneurs sous-jacent utilisé par Docker
  • Dans le fonctionnement par défaut de Docker, Docker conserve sa propre couche de stockage et n’utilise pas directement les images de containerd
  • En activant le containerd image store dans Docker, Docker peut utiliser directement les images stockées par unregistry, ce qui supprime les doublons
  • Lorsque le containerd image store est activé

    • Les images poussées via unregistry sont immédiatement utilisables dans Docker
    • Les images ne sont stockées qu’une seule fois dans containerd, sans consommer d’espace supplémentaire
    • L’étape de docker pull supplémentaire depuis unregistry vers le classic Docker image store disparaît, ce qui accélère l’opération pussh
  • Lorsque le comportement par défaut de Docker est conservé

    • Après le push, pussh exécute un docker pull supplémentaire sur l’hôte distant pour rendre l’image utilisable dans Docker
    • L’image est stockée à la fois dans containerd et dans le classic Docker image store, donc en double
    • Les images non gérées de containerd peuvent finir par occuper de l’espace disque au fil du temps
    • La gestion manuelle peut se faire avec les commandes suivantes
    sudo ctr -n moby images ls
    sudo ctr -n moby images rm <image>
    
  • Points d’attention pour cette configuration

    • Pour activer le containerd image store dans Docker Engine, suivre la documentation officielle Docker
    • En basculant vers le containerd image store, les images et conteneurs créés avec le classic storage driver peuvent temporairement ne plus apparaître
    • Ces ressources restent présentes sur le système de fichiers et peuvent être récupérées en désactivant la fonctionnalité containerd image store

Exemples d’utilisation

  • Pour un transfert de base, il suffit d’indiquer le nom de l’image et la cible SSH distante
docker pussh myapp:latest user@server.example.com
  • Si aucune clé privée n’est enregistrée dans l’agent SSH, on peut spécifier une clé avec -i
docker pussh myapp:latest ubuntu@192.168.1.100 -i ~/.ssh/id_rsa
  • Un port SSH personnalisé s’indique en l’ajoutant après la cible
docker pussh myapp:latest user@server:2222
  • Un fichier de configuration SSH personnalisé peut être indiqué avec -F
docker pussh myapp:latest prod-server -F ~/.ssh/config.prod
  • Pour ne pousser qu’une plateforme précise d’une image multi-plateforme, utiliser --platform
    • Pour que Docker local prenne en charge les images multi-plateforme, il faut utiliser le containerd image store
docker pussh myapp:latest user@server --platform linux/amd64
  • Pour utiliser une version précise de l’image unregistry sur l’hôte distant, définir la variable d’environnement UNREGISTRY_IMAGE
UNREGISTRY_IMAGE=ghcr.io/psviderski/unregistry:A.B.C docker pussh myapp:latest user@server.example.com

Cas d’usage représentatifs

  • Pour un déploiement sur un serveur de production, on peut pousser directement sur le serveur puis lancer l’image construite localement, sans registre intermédiaire
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
  • Dans un pipeline CI/CD, on peut envoyer l’image directement vers la cible de déploiement sans la complexité d’un registre
- name: Build and deploy
  run: |
    docker build -t myapp:${{ github.sha }} .
    docker pussh myapp:${{ github.sha }} deploy@staging-server
  • Dans un homelab ou un environnement air-gapped, il est possible de déployer des images sur un réseau isolé sans accès Internet à un registre public

Usage avancé et projets liés

  • Unregistry peut aussi être utilisé comme registre local autonome
    • Il suffit de monter /run/containerd/containerd.sock et d’exécuter le conteneur ghcr.io/psviderski/unregistry
    • Ensuite, localhost:5000 peut être utilisé comme un registre classique avec docker tag et docker push
  • La configuration SSH peut s’appuyer sur le fichier standard SSH config ou sur un fichier distinct fourni avec -F
  • Des projets tiers liés sont disponibles
  • L’implémentation mentionne Spegel et Docker Distribution
    • Spegel est un registre d’images de conteneurs P2P qui a inspiré l’implémentation d’un registre utilisant le containerd image store comme backend
    • Docker Distribution est l’implémentation de registre Docker sur laquelle unregistry s’appuie

1 commentaires

 
GN⁺ 2025-06-19
Avis sur Hacker News
  • En tant que créateur de Docker, j’aime bien. La conception idéale aurait sans doute été un serveur unique sans distinction entre le moteur Docker et le registre.
    Si l’on avait pu stocker, transférer et exécuter des conteneurs selon les besoins, cela aurait donné un composant bien plus robuste, et on aurait évité cette évolution regrettable où le moteur et le registre divergent dans leur façon de stocker les images.
    Je pense aussi que tous les clusters de production devraient disposer d’un stockage d’images distribué, et que pousser une image vers ce stockage devrait déclencher le déploiement. La méthode actuelle — pousser vers un registre, configurer le cluster, puis faire tirer l’image par chaque nœud depuis le registre — est fragile et inefficace. J’ai défendu une meilleure conception, mais l’inertie était déjà trop forte, et la communauté Kubernetes des débuts était hostile aux idées venues de Docker.

    • Le fait qu’il existe au moins trois agencements de système de fichiers pour les images, et même deux stockages d’images dans le moteur, est clairement brouillon. Cela dit, je pense qu’il n’est pas encore trop tard pour que Docker aille dans cette direction sans casser le modèle actuel. Reste à savoir si Docker s’en préoccupe, et ces derniers temps l’entreprise semble traverser une période difficile.
      Le déploiement par push vers le cluster paraît astucieux. Je réfléchis à un stockage d’images distribué où l’on placerait unregistry sur tous les nœuds afin qu’ils récupèrent et partagent les images entre eux, mais il faut que je creuse davantage l’idée d’un push qui déclenche le déploiement.
  • Sympa. La commande pussh mérite vraiment d’être saluée comme un jeu de mots élégant. Facile à retenir, son sens est immédiatement visible, et elle ne diffère de la commande standard sœur que d’une seule lettre.

    • Ce n’est pas mauvais, mais ce serait bien d’avoir aussi un alias plus officiel comme docker push-over-ssh.
      Dans une automatisation développée en collaboration, pussh peut ressembler à une faute de frappe pour quelqu’un qui ne connaît pas la fonctionnalité, créant une confusion inutile. À l’inverse, push-over-ssh indique clairement que le nom est intentionnel. On peut voir ça comme des options courtes et longues.
    • Le s ajouté est le s de sssh.
      « C’est quoi ce s en plus ? »
      « Une faute de frappe »
    • C’est aussi susceptible de provoquer des collisions.
  • En 2015, j’ai naïvement envoyé une PR[1] pour essayer d’intégrer cette fonctionnalité dans Docker upstream, puis on m’a rapidement réorienté vers l’aide sur d’autres sujets. Rendre possible de se passer du registre bousculait peut-être trop le modèle économique de Docker.
    [1]: https://github.com/richardcrichardc/docker2docker

    • Vous étiez donc le précurseur. Dommage que Docker ne dispose toujours pas d’une API pour parcourir les couches d’image.
      Je pense qu’à terme le plan sera de basculer le stockage d’images par défaut vers celui de containerd. Une fois le stockage d’images containerd utilisé à la fois en local et à distance, il devrait être possible de faire ce que vous aviez implémenté à l’origine, sans wrapper de registre.
  • C’est une excellente idée, qui semble bien s’intégrer aux systèmes utilisant déjà des outils de déploiement par push comme Ansible. Dans les entreprises où le registre Docker n’a pas de support 24/7, cela pourrait aussi servir de bon mécanisme de déploiement de hotfix.
    Je me demande si cela s’intègre proprement avec des outils OCI comme buildah, ou s’il faut une installation Docker complète aux deux extrémités. Je n’ai pas encore regardé en détail, mais dans cette configuration, faire fonctionner skopeo semble nécessiter comme pièce manquante le bootstrap d’un mini-registre sur le serveur distant.

    • Côté distant, il faut containerd. Docker et Kubernetes utilisent aussi containerd. Côté client, n’importe quel outil parlant l’API de registre, c’est-à-dire la spécification OCI Distribution (https://github.com/opencontainers/distribution-spec), suffit.
      Unregistry réutilise le code officiel du registre Docker au niveau de la couche API, donc il ressemble à https://hub.docker.com/_/registry et se comporte de manière similaire.
      Côté client, on peut utiliser skopeo, crane, regclient, BuildKit, etc., bref des outils qui parlent registre OCI. Mais pour les utiliser, il faut lancer manuellement unregistry sur l’hôte distant. La commande docker pussh ne fait qu’automatiser ce flux en utilisant le Docker local.
      On peut la voir comme un simple script Bash : https://github.com/psviderski/unregistry/blob/main/docker-pu...
      On peut facilement l’adapter directement comme on le souhaite.
    • D’accord. Pour plusieurs services que je gère, je construis les images localement, puis je les sauvegarde, j’envoie l’archive avec Ansible et je restaure les images ; en général, cela prend beaucoup plus de temps que je ne le voudrais.
    • Il faut un daemon Docker aux deux extrémités. C’est une manière astucieuse de partager les couches entre deux daemons via SSH.
  • C’est comme ça que cela aurait dû fonctionner depuis le début. Excellent.
    Les registres Docker ont leur utilité, mais globalement ils sont trop conçus et se situent à l’opposé de l’esprit hacker.

    • Docker était une entreprise financée par du capital-risque, donc il fallait bien qu’elle gagne de l’argent d’une manière ou d’une autre.
    • Je recommande d’utiliser ghcr.io, le registre de GitHub, avec GitHub Actions.
      Il m’a fallu environ 20 minutes pour configurer un workflow .yaml qui construit et pousse l’image vers un registre privé ghcr.io, puis environ 5 minutes pour autoriser le serveur à récupérer l’image à partir de là. C’est une configuration assez pratique.
    • La complexité semble résider dans la procédure de push des blobs vers le registre. J’ai déjà créé un registre en lecture seule compatible OCI, et ce n’était pas si compliqué.
  • Je regarde un pipeline qui construit les images dans GitLab et les pousse vers Artifactory, puis où un déploiement est déclenché, récupère depuis Artifactory pour repousser vers AWS ECR, met à jour le template de déploiement dans EKS, puis ECR est utilisé pour tirer l’image vers les nœuds et lancer les conteneurs des Pods.
    J’ai besoin de ça dans ma vie.

    • Par curiosité, pourquoi utilisez-vous à la fois Artifactory et ECR ? Nous envisageons de passer d’Artifactory à ECR pour réduire les coûts.
    • Dans le pipeline de mon dernier projet, le temps passé à tirer et pousser des conteneurs était plus long que la compilation de l’application elle-même. Et tout ce temps restait faible par rapport à l’attente des health checks, alors qu’en réalité on savait en moins d’une seconde après le démarrage si c’était sain ou non.
  • C’est en voyant ça que j’ai découvert uncloud. Je cherchais quelque chose comme dokku, mais un peu plus costaud, pour configurer le serveur de mes side projects, et c’est exactement l’impression que ça donne.

    • Il y a aussi https://skateco.github.io/. À première vue, ça semble similaire.
    • Si vous n’avez pas essayé ou envisagé Portainer, je le recommande. Je fais tourner Portainer Community Edition et Portainer Agent sur deux instances EC2 chez AWS, et ça fonctionne bien.
      La fonctionnalité de stacks est vraiment bonne aussi ; en pratique, c’est du Docker Compose. Sur une instance EC2, Portainer Agent lance Caddy dans un conteneur, et Caddy sert de répartiteur de charge et de reverse proxy.
    • Ravi que l’idée d’uncloud vous parle. Si vous avez des questions ou besoin d’aide, vous pouvez passer sur Discord.
  • C’est assez étrange que Docker n’ait pas fonctionné comme ça dès le départ. Ça a l’air sympa.

    • On peut déjà faire la même chose en créant une archive de l’image, en la poussant vers le serveur, puis en l’exécutant depuis cette archive sur le serveur.
      Pour enregistrer l’archive, on fait par exemple docker save -o may-app.tar my-app:latest, et pour la charger, docker load -i /path/to/my-app.tar.
      Avec un outil comme Ansible, on peut facilement obtenir ce qu’« Unregistry » automatise. D’après le dépôt GitHub, l’inconvénient de l’approche save/load est qu’elle transfère toute l’image sur le réseau, ce qui peut effectivement poser problème. Gérer l’image elle-même plutôt qu’un fichier d’archive semble aussi plus pratique.
  • Projet et approche élégants. Lassé des registres coûteux, j’ai fini par auto-héberger Zot[1], mais pour certains usages, ceci semble beaucoup plus simple.
    Je me demande si d’autres aimeraient disposer d’un service de registre privé facile à configurer, bon marché et facturé à l’usage.
    [1]: https://zotregistry.dev

    • Au cas où vous ne le sauriez pas, le certificat SSL de zothub.io a expiré.
  • Bonne idée. Cela dit, il peut y avoir l’inconvénient de coupler le déploiement au service. Par exemple, je ne sais pas comment on gérerait le scale-out ou les déploiements red/green, et il me semble que le composant qui s’en charge devrait être au courant du push.
    Édit : c’est uncloud qui fait ça. Je viens de m’en rendre compte.
    Ça reste un compromis. Si l’échelle est modeste, que vous utilisez une seule VM Hetzner, que la simplicité vous convient et que ça ne vous dérange pas de construire les images en local, c’est excellent.

    • C’est clairement toujours une histoire de compromis. C’est bien d’avoir le choix pour pouvoir sélectionner l’outil le plus adapté à chaque tâche.