4 pontos por GN⁺ 2025-06-19 | 1 comentários | Compartilhar no WhatsApp
  • Unregistry é um registro leve de imagens de contêiner que armazena e serve imagens diretamente do image store do daemon Docker, e o comando docker pussh envia imagens diretamente para um servidor Docker remoto via SSH
  • As opções existentes eram Docker Hub/GitHub Container Registry, um registro self-hosted, docker save | ssh... docker load ou rebuild remoto, mas cada uma tem problemas como repositórios públicos ou pagos, carga operacional, transferência da imagem inteira ou desperdício de recursos do servidor
  • docker pussh myapp:latest user@server cria um túnel SSH, inicia um contêiner unregistry temporário no servidor remoto e então executa docker push por uma porta localhost encaminhada, enviando apenas as camadas que não existem no destino
  • No servidor remoto, é necessário um ambiente Docker em execução, permissão para usar o comando docker e, na primeira execução, acesso a ghcr.io/psviderski/unregistry:latest; o contêiner unregistry é executado como root por causa do acesso a /run/containerd/containerd.sock
  • Ao ativar o containerd image store do Docker, a imagem transferida fica imediatamente disponível no Docker e evita armazenamento duplicado, mas imagens e contêineres criados com o classic storage driver podem ficar temporariamente invisíveis

O problema de deploy que o Unregistry resolve

  • Unregistry é um registro leve de imagens de contêiner para mover imagens Docker para servidores remotos sem depender de um registro externo
  • O comando incluído como plugin da CLI do Docker é docker pussh, em que o s extra significa SSH
  • Ao mover para um servidor uma imagem Docker criada localmente, as opções comuns têm as seguintes limitações
    • Docker Hub / GitHub Container Registry: é preciso tornar o código público ou pagar por repositórios privados
    • Self-hosted registry: cria um serviço separado que exige manutenção, segurança e custo de armazenamento
    • Save/Load: docker save | ssh <remote server> docker load transfere a imagem inteira, mesmo que 90% dela já exista no servidor
    • Remote rebuild: consome tempo e recursos do servidor, e pode exigir depuração de falhas de build em produção

Como o docker pussh funciona

  • O uso básico é esta única linha
docker pussh myapp:latest user@server
  • Esse comando envia apenas as camadas ausentes diretamente por SSH, sem configurar registro, assinatura, armazenamento intermediário ou portas públicas
  • O funcionamento interno segue esta ordem
    • Cria um túnel SSH para o servidor remoto
    • Inicia um contêiner unregistry temporário no servidor
    • Encaminha uma porta localhost aleatória pelo túnel até a porta do unregistry
    • Executa docker push pela porta encaminhada, enviando apenas as camadas que não existem no destino
    • A imagem transferida fica imediatamente disponível no daemon Docker remoto
    • Para o contêiner unregistry e fecha o túnel SSH
  • O projeto busca uma forma simples e eficiente de transferência, como um rsync para imagens Docker
  • O Unregistry foi criado para o Uncloud, uma ferramenta leve para implantar contêineres em vários hosts Docker, pois era necessário algo mais simples que um registro completo e mais eficiente que save/load

Requisitos de execução e limitações

  • Na máquina local, é necessário suporte a plugins da Docker CLI, além de Docker 19.03 ou superior e um cliente OpenSSH
  • No servidor remoto, o Docker deve estar instalado e em execução
  • O usuário SSH precisa ter permissão para executar o comando docker
    • O usuário deve ser root ou um usuário não root pertencente ao grupo docker
    • A documentação relacionada é Manage Docker as a non-root user
    • Se for necessário sudo, deve ser possível executar sudo docker sem prompt de senha
  • Na primeira execução de docker pussh, o servidor remoto precisa conseguir baixar a imagem ghcr.io/psviderski/unregistry:latest a partir de ghcr.io
    • Servidores que exigem proxy devem ser configurados conforme Daemon proxy configuration
    • Em ambientes air-gapped ou com acesso restrito a ghcr.io, é possível verificar a versão necessária da imagem unregistry e pré-carregá-la manualmente
  • Como o contêiner unregistry precisa acessar /run/containerd/containerd.sock, ele é executado como root para ter as permissões necessárias

Instalação e plataformas compatíveis

  • Em macOS/Linux, é possível instalar docker-pussh com Homebrew
brew install psviderski/tap/docker-pussh
  • Depois da instalação via Homebrew, para usar docker pussh como plugin da Docker CLI é necessário criar o link simbólico ~/.docker/cli-plugins/docker-pussh
  • Em macOS/Linux, também existe instalação por download direto
    • O exemplo da versão atual usa o script docker-pussh da versão v0.4.3, baixado para o diretório de plugins do Docker e com permissão de execução
    • Também é oferecida a opção de baixar o script mais recente da branch main
  • No Debian, a instalação pode ser feita pelo repositório não oficial unregistry-debian, criado e mantido por @dariogriffo
  • Windows não é compatível no momento, mas é possível tentar com WSL 2 e o procedimento de instalação para Linux
  • A verificação da instalação é feita com o comando abaixo
docker pussh --help

Configuração do containerd image store

  • O Unregistry grava imagens diretamente no image store do containerd, o runtime de contêiner subjacente usado pelo Docker
  • No comportamento padrão do Docker, ele mantém uma camada de armazenamento separada e não usa diretamente as imagens do containerd
  • Ao ativar o containerd image store no Docker, o Docker pode usar diretamente as imagens armazenadas pelo unregistry, eliminando duplicação
  • Ao ativar o containerd image store

    • As imagens enviadas com unregistry ficam imediatamente disponíveis no Docker
    • As imagens são armazenadas apenas uma vez no containerd, sem consumo extra de espaço
    • Como não há etapa adicional de pull do unregistry para o classic Docker image store, a operação pussh fica mais rápida
  • Ao manter o comportamento padrão do Docker

    • Depois do push, o pussh executa um docker pull adicional no host remoto para tornar a imagem utilizável no Docker
    • A imagem é armazenada tanto no containerd quanto no classic Docker image store, ou seja, duas vezes
    • Imagens unmanaged no containerd podem ocupar espaço em disco com o tempo
    • O gerenciamento manual pode ser feito com os comandos abaixo
    sudo ctr -n moby images ls
    sudo ctr -n moby images rm <image>
    
  • Cuidados ao configurar

    • Para ativar o containerd image store no Docker Engine, siga a documentação oficial do Docker
    • Ao migrar para o containerd image store, imagens e contêineres criados com o classic storage driver ficam temporariamente invisíveis
    • Esses recursos continuam no sistema de arquivos e podem ser recuperados ao desativar o containerd image store

Exemplos de uso

  • Na transferência básica, basta informar o nome da imagem e o destino SSH remoto
docker pussh myapp:latest user@server.example.com
  • Se a private key não estiver carregada no SSH agent, é possível informar a chave com -i
docker pussh myapp:latest ubuntu@192.168.1.100 -i ~/.ssh/id_rsa
  • Uma porta SSH personalizada pode ser informada acrescentando a porta ao destino
docker pussh myapp:latest user@server:2222
  • Um arquivo de configuração SSH personalizado pode ser informado com -F
docker pussh myapp:latest prod-server -F ~/.ssh/config.prod
  • Para enviar apenas uma plataforma específica de uma imagem multi-plataforma, use --platform
    • Para que o Docker local ofereça suporte a imagens multi-plataforma, é necessário usar containerd image store
docker pussh myapp:latest user@server --platform linux/amd64
  • Para usar uma versão específica da imagem unregistry no host remoto, defina a variável de ambiente UNREGISTRY_IMAGE
UNREGISTRY_IMAGE=ghcr.io/psviderski/unregistry:A.B.C docker pussh myapp:latest user@server.example.com

Casos de uso representativos

  • Em deploy para servidores de produção, é possível fazer push direto da imagem criada localmente para o servidor e executá-la sem um registro intermediário
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
  • Em pipelines de CI/CD, é possível enviar a imagem diretamente ao destino do deploy, pulando a complexidade de um registro
- name: Build and deploy
  run: |
    docker build -t myapp:${{ github.sha }} .
    docker pussh myapp:${{ github.sha }} deploy@staging-server
  • Em ambientes homelab e air-gapped, é possível implantar imagens em redes isoladas sem acesso à internet para registros públicos

Uso avançado e projetos relacionados

  • O Unregistry também pode ser usado como um registro local standalone
    • Monte /run/containerd/containerd.sock e execute o contêiner ghcr.io/psviderski/unregistry
    • Depois disso, localhost:5000 pode ser usado como um registro comum com docker tag e docker push
  • A configuração SSH pode usar o arquivo padrão de config do SSH ou um arquivo separado via -F
  • Existem projetos de terceiros relacionados
  • A implementação menciona Spegel e Docker Distribution
    • Spegel é um registro P2P de imagens de contêiner que inspirou a implementação de um registro usando o containerd image store como backend
    • Docker Distribution é a implementação de registry Docker que serve de base para o unregistry

1 comentários

 
GN⁺ 2025-06-19
Opiniões no Hacker News
  • Como alguém que criou o Docker, gostei. Acho que o design ideal teria sido um único servidor sem distinção entre o engine do Docker e o registry
    Se ele pudesse armazenar, transferir e executar contêineres conforme necessário, teria se tornado um componente muito mais robusto, e também teria evitado o rumo infeliz em que o engine e o registry armazenam imagens de maneiras divergentes
    Também acho que todo cluster de produção deveria ter um armazenamento distribuído de imagens, e fazer push de imagens para esse armazenamento deveria acionar o deploy. O modelo atual — fazer push para um registry, configurar o cluster e fazer cada nó puxar do registry — é frágil e ineficiente. Defendi um design melhor, mas a inércia já era grande demais, e a comunidade inicial do Kubernetes era hostil a ideias vindas do Docker

    • É definitivamente bagunçado haver pelo menos três layouts de sistema de arquivos para imagens e dois armazenamentos de imagens dentro do engine. Ainda assim, acho que não é tarde demais para o Docker alcançar essa direção sem quebrar o modelo atual. Só não sei se o Docker se importa com isso, e hoje em dia parece estar passando por uma fase difícil
      Deploy por push para o cluster parece uma ideia inteligente. Estou pensando em um armazenamento distribuído de imagens em que se coloca o unregistry em todos os nós para que eles busquem e compartilhem imagens entre si, mas ainda preciso pensar melhor no modelo em que o push aciona o deploy
  • Muito bom. O comando pussh realmente merece reconhecimento como um trocadilho elegante. É fácil de lembrar, o significado fica claro de imediato e difere do comando padrão irmão por apenas uma letra

    • Não é ruim, mas seria bom haver também um alias mais oficial, como docker push-over-ssh
      Em automações desenvolvidas em colaboração, pussh pode parecer um typo para alguém que não conhece a funcionalidade, criando confusão desnecessária. Já push-over-ssh deixa claro que é um nome intencional. É como pensar em opções curtas e opções longas
    • O s extra é o s de sssh
      “O que é esse s extra?”
      “É um typo”
    • Também é fácil dar conflito
  • Em 2015, ingenuamente enviei um PR[1] tentando colocar esse recurso no Docker principal, mas logo fui redirecionado para ajudar em outras áreas. Tornar possível não usar um registry talvez abalasse demais o modelo de negócios do Docker
    [1]: https://github.com/richardcrichardc/docker2docker

    • Então você foi o original. É uma pena que o Docker ainda não tenha uma API para explorar camadas de imagem
      Imagino que o plano seja, no fim, mudar o padrão para o armazenamento de imagens do containerd. Quando tanto o lado local quanto o remoto usarem o armazenamento de imagens do containerd, deve ser possível fazer o que você implementou originalmente sem um wrapper de registry
  • É uma ideia ótima que parece combinar bem com sistemas que já usam ferramentas de deploy por push, como Ansible. Para empresas que não têm suporte 24/7 para um Docker registry, também parece um bom mecanismo de deploy de hotfixes
    Fico curioso se isso se integra de forma limpa com ferramentas OCI como buildah, ou se exige uma instalação completa do Docker nas duas pontas. Ainda não olhei a fundo, mas para o skopeo funcionar nesse tipo de configuração, inicializar um mini-registry no servidor remoto parece ser a peça que faltava

    • No lado remoto, é preciso ter containerd. Docker e Kubernetes também usam containerd. No lado cliente, basta qualquer ferramenta que fale a API de registry, ou seja, a especificação OCI Distribution (https://github.com/opencontainers/distribution-spec)
      O Unregistry reutiliza o código oficial do Docker registry na camada de API, então ele se parece e se comporta de forma semelhante a https://hub.docker.com/_/registry
      No cliente, é possível usar skopeo, crane, regclient, BuildKit e outras ferramentas que falem OCI registry. Porém, para usá-las, é preciso executar manualmente o unregistry no host remoto. O comando docker pussh apenas automatiza esse fluxo usando o Docker local
      Dá para pensar nele apenas como um script Bash: https://github.com/psviderski/unregistry/blob/main/docker-pu...
      É fácil adaptá-lo diretamente do jeito que você quiser
    • Concordo. Em vários serviços que administro, eu construo as imagens localmente, salvo, faço upload do arquivo com Ansible e depois restauro as imagens, e isso normalmente leva muito mais tempo do que eu gostaria
    • É preciso ter o daemon do Docker nas duas pontas. É uma forma inteligente de compartilhar camadas entre os dois daemons via SSH
  • Sempre deveria ter sido assim desde o começo. Excelente
    Docker registries têm seus usos, mas, no geral, foram superprojetados e estão no lado oposto do espírito hacker

    • Era uma empresa financiada por venture capital, então o Docker precisava ganhar dinheiro de algum jeito
    • Recomendo usar o registry do GitHub, o ghcr.io, junto com GitHub Actions
      Levei uns 20 minutos para configurar um workflow .yaml que constrói e faz push da imagem para um registry privado no ghcr.io, e mais uns 5 minutos para permitir que o servidor puxe a imagem de lá. É uma configuração bem prática
    • A complexidade parece estar no procedimento de fazer push de blobs para o registry. Já criei antes um registry somente leitura compatível com OCI, e ele não era tão complicado assim
  • Estou vendo um pipeline em que a imagem é construída no GitLab e enviada para o Artifactory; então um deploy é acionado, a imagem é puxada do Artifactory e enviada de novo para o AWS ECR; aí o template de deploy no EKS é atualizado, os nós puxam a imagem do ECR e sobem os contêineres dos Pods
    Eu preciso disso na minha vida

    • Pergunta por curiosidade: por que usar tanto Artifactory quanto ECR? Estamos avaliando migrar do Artifactory para o ECR por causa da redução de custos
    • No pipeline do meu último projeto, o tempo gasto puxando e enviando contêineres era maior do que o build do app em si. Além disso, todo esse tempo ainda era pequeno em comparação com a espera por health checks, embora desse para saber em menos de 1 segundo após iniciar se estava realmente saudável ou não
  • Ao ver isso, acabei conhecendo o uncloud. Eu estava procurando algo parecido com o dokku, mas um pouco mais robusto, para montar servidores de projetos paralelos, e a sensação é exatamente essa.

    • Também existe https://skateco.github.io/. Olhando por alto, parece semelhante.
    • Recomendo o Portainer se você ainda não usou ou não avaliou. Estou rodando o Portainer Community Edition e o Portainer Agent em duas instâncias EC2 na AWS, e funciona bem.
      O recurso de stacks também é muito bom; na prática, é Docker Compose. Em uma instância EC2, o Portainer Agent executa o Caddy dentro de um contêiner, e o Caddy atua como balanceador de carga e proxy reverso.
    • Fico feliz que a ideia do uncloud tenha feito sentido para você. Se tiver perguntas ou precisar de ajuda, pode entrar no Discord.
  • É bem estranho que o Docker não tenha funcionado assim desde o começo. Parece legal.

    • Já dá para fazer a mesma coisa criando um arquivo de imagem, enviando-o para o servidor e depois executando a partir desse arquivo no servidor.
      Para salvar o arquivo, use algo como docker save -o may-app.tar my-app:latest; para carregar, algo como docker load -i /path/to/my-app.tar.
      Com ferramentas como Ansible, dá para conseguir facilmente o que o “Unregistry” automatiza. Segundo o repositório no GitHub, a desvantagem do método save/load é que ele transfere a imagem inteira pela rede, e isso pode de fato ser um problema. Gerenciar a própria imagem, em vez de um arquivo de archive, também parece mais conveniente.
  • É um projeto e uma abordagem elegantes. Cansei de registries caros e acabei auto-hospedando o Zot[1], mas para alguns usos isso parece muito mais fácil.
    Fico curioso se há mais alguém que gostaria de um serviço de registry privado fácil de configurar, barato e cobrado conforme o uso.
    [1]: https://zotregistry.dev

    • Caso você não soubesse, o certificado SSL do zothub.io expirou.
  • É uma boa ideia. Mas pode haver a desvantagem de o deploy ficar acoplado ao serviço. Por exemplo, não sei como ficariam scale-out ou deploy red/green, e imagino que o componente que faz isso teria que conhecer o push.
    Edit: quem faz isso era o uncloud. Acabei de descobrir.
    Ainda assim, é um trade-off. Se a escala for pequena, você usa uma única VM na Hetzner, está satisfeito com a simplicidade e não se importa de buildar a imagem localmente, é excelente.

    • Com certeza é sempre um trade-off. É bom ter opções para poder escolher a ferramenta mais adequada para cada trabalho.