4 ポイント 投稿者 GN⁺ 2025-06-19 | 1件のコメント | WhatsAppで共有
  • Unregistry は、Docker デーモンのストアからイメージを直接保存・配信する軽量なコンテナイメージレジストリで、docker pussh コマンドによりリモート Docker サーバーへイメージを SSH でそのまま転送できる
  • 従来の選択肢は Docker Hub/GitHub Container Registry、自前運用レジストリ、docker save | ssh... docker load、リモート再ビルドだが、それぞれ公開・有料リポジトリ、運用負担、イメージ全体の転送、サーバー資源の浪費といった問題がある
  • docker pussh myapp:latest user@server は SSH トンネルを作成し、リモートサーバー上で一時的な unregistry コンテナ を起動したうえで、転送した localhost ポートに対して docker push を実行し、リモートに存在しないレイヤーだけを送る
  • リモートサーバー側には Docker の実行環境、docker コマンド実行権限、初回実行時に ghcr.io/psviderski/unregistry:latest へアクセスできることが必要で、unregistry コンテナは /run/containerd/containerd.sock へアクセスするため root で実行される
  • Docker の containerd image store を有効にすると、転送されたイメージを Docker からすぐ利用でき、重複保存も避けられるが、既存の classic storage driver で作成したイメージやコンテナが一時的に見えなくなる場合がある

Unregistry が解決するデプロイの課題

  • Unregistry は、外部レジストリなしで Docker イメージをリモートサーバーへ移すための軽量なコンテナイメージレジストリである
  • 付属の Docker CLI プラグインコマンドは docker pussh で、名前の追加された s は SSH を意味する
  • ローカルで作成した Docker イメージをサーバーへ移す際、一般的な選択肢には次のような制約がある
    • Docker Hub / GitHub Container Registry: コードを公開するか、private リポジトリの費用を負担する必要がある
    • Self-hosted registry: 保守、セキュリティ、ストレージコストを伴う別サービスの運用が必要になる
    • Save/Load: docker save | ssh <remote server> docker load は、サーバー側にすでに 90% あってもイメージ全体を転送してしまう
    • Remote rebuild: 時間とサーバー資源を消費し、本番環境でビルド失敗の原因をデバッグしなければならないことがある

docker pussh の動作方式

  • 基本的な使い方は次の 1 行である
docker pussh myapp:latest user@server
  • このコマンドは、レジストリ設定、サブスクリプション、中間ストレージ、公開ポートなしで、不足しているレイヤーだけ を SSH 経由で直接転送する
  • 内部動作は次の順序で進む
    • リモートサーバーへの SSH トンネル を作成する
    • サーバー上で一時的な unregistry コンテナを起動する
    • 任意の localhost ポートをトンネル経由で unregistry のポートへフォワードする
    • フォワードされたポートに対して docker push を実行し、リモートに存在しないレイヤーだけを転送する
    • 転送されたイメージはリモート Docker デーモンですぐ利用できる
    • unregistry コンテナを停止し、SSH トンネルを閉じる
  • このプロジェクトは、Docker イメージ向けの rsync のような、シンプルで効率的な転送方式を目指している
  • Unregistry は、複数の Docker ホストへコンテナをデプロイする軽量ツール Uncloud のために作られ、フル機能のレジストリより簡単で、save/load より効率的な方式が必要だった

実行要件と制約

  • ローカルマシンでは Docker CLI プラグイン対応 が必要で、Docker 19.03 以上と OpenSSH クライアントが必要である
  • リモートサーバーには Docker がインストールされ、実行中である必要がある
  • SSH ユーザーには docker コマンドを実行する権限が必要である
    • ユーザーが root であるか、非 root ユーザーが docker グループに属している必要がある
    • 関連ドキュメントとして Docker の Manage Docker as a non-root user を参照する
    • sudo が必要な場合は、パスワードプロンプトなしで sudo docker を実行できる必要がある
  • 初回の docker pussh 実行時には、リモートサーバーが ghcr.io から ghcr.io/psviderski/unregistry:latest イメージを取得できる必要がある
    • プロキシが必要なサーバーは、Docker の Daemon proxy configuration ガイドに従って設定する必要がある
    • air-gapped 環境や ghcr.io へのアクセスが制限された環境では、必要な unregistry イメージのバージョンを確認したうえで手動でプリロードできる
  • unregistry コンテナは /run/containerd/containerd.sock にアクセスする必要があるため、必要権限を確保するため root で実行される

インストールと対応プラットフォーム

  • macOS/Linux では Homebrew で docker-pussh をインストールできる
brew install psviderski/tap/docker-pussh
  • Homebrew インストール後に Docker CLI プラグインとして docker pussh を使うには、~/.docker/cli-plugins/docker-pussh のシンボリックリンクを作成する必要がある
  • macOS/Linux では直接ダウンロード方式も提供されている
    • 現在のバージョン例は v0.4.3docker-pussh スクリプトを Docker プラグインディレクトリへダウンロードし、実行権限を付与する方式である
    • main ブランチの最新スクリプトをダウンロードする方式も提供されている
  • Debian では、@dariogriffo が作成・保守している非公式パッケージリポジトリ unregistry-debian を通じてインストールできる
  • Windows は現在未対応だが、WSL 2 と Linux 向けインストール手順を試すことはできる
  • インストール確認は次のコマンドで行う
docker pussh --help

containerd image store の設定

  • Unregistry は、Docker が使用する下位コンテナランタイムである containerd の image store にイメージを直接保存する
  • Docker のデフォルト動作では、Docker は別の保存レイヤーを維持し、containerd のイメージを直接利用しない
  • Docker で containerd image store を有効にすると、Docker は unregistry が保存したイメージを直接利用でき、重複をなくせる
  • containerd image store を有効にした場合

    • unregistry で push したイメージを Docker からすぐ利用できる
    • イメージは containerd に一度だけ保存され、追加のストレージ容量を使わない
    • unregistry から classic Docker image store へ追加で pull する手順が不要になるため、pussh の処理がより速くなる
  • Docker のデフォルト動作を維持する場合

    • push 後に pussh がリモートホストで追加の docker pull を実行し、イメージを Docker から利用できるようにする
    • イメージは containerd と classic Docker image store の両方に保存され、二重保存 される
    • containerd の unmanaged image が時間とともにディスク容量を圧迫する可能性がある
    • 手動管理は次のコマンドで行う
    sudo ctr -n moby images ls
    sudo ctr -n moby images rm <image>
    
  • 設定時の注意点

    • Docker Engine で containerd image store を有効にする方法は 公式 Docker ドキュメント に従う
    • containerd image store へ切り替えると、classic storage driver で作成したイメージやコンテナが一時的に見えなくなる
    • それらのリソースはファイルシステム上には残っており、containerd image store 機能を無効にすれば再び利用できる

使用例

  • 基本転送ではイメージ名とリモート SSH 対象だけを指定する
docker pussh myapp:latest user@server.example.com
  • SSH agent に private key が登録されていない場合は -i でキーを指定できる
docker pussh myapp:latest ubuntu@192.168.1.100 -i ~/.ssh/id_rsa
  • カスタム SSH ポートは対象の後ろにポートを付けて指定する
docker pussh myapp:latest user@server:2222
  • カスタム SSH config ファイルは -F で指定する
docker pussh myapp:latest prod-server -F ~/.ssh/config.prod
  • マルチプラットフォームイメージで特定のプラットフォームだけを push するには --platform を使う
    • ローカル Docker がマルチプラットフォームイメージをサポートするには containerd image store を使う必要がある
docker pussh myapp:latest user@server --platform linux/amd64
  • リモートホストで特定の unregistry イメージバージョンを使うには UNREGISTRY_IMAGE 環境変数を指定する
UNREGISTRY_IMAGE=ghcr.io/psviderski/unregistry:A.B.C docker pussh myapp:latest user@server.example.com

代表的なユースケース

  • 本番サーバーへのデプロイでは、ローカルでビルドしたイメージを中間レジストリなしでサーバーへ直接 push して実行できる
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
  • CI/CD パイプラインでは、レジストリの複雑さを省いてデプロイ先へ直接イメージを送れる
- name: Build and deploy
  run: |
    docker build -t myapp:${{ github.sha }} .
    docker pussh myapp:${{ github.sha }} deploy@staging-server
  • homelab や air-gapped 環境では、公開レジストリへインターネット経由でアクセスできない隔離ネットワークでもイメージをデプロイできる

高度な使い方と関連プロジェクト

  • Unregistry はスタンドアロンのローカルレジストリとしても利用できる
    • /run/containerd/containerd.sock をマウントし、ghcr.io/psviderski/unregistry コンテナを起動する
    • その後 localhost:5000 を通常のレジストリのように docker tagdocker push に使える
  • SSH 設定は標準の SSH config ファイルを使うか、-F で別の config ファイルを渡せる
  • 関連するサードパーティプロジェクトも提供されている
  • 実装には SpegelDocker Distribution が言及されている
    • Spegel は、containerd image store をバックエンドに使うレジストリ実装への着想を与えた P2P コンテナイメージレジストリである
    • Docker Distribution は、unregistry の基盤となっている Docker registry 実装である

1件のコメント

 
GN⁺ 2025-06-19
Hacker News のコメント
  • Docker を作った側としては気に入っている。理想的な設計は、Docker エンジンとレジストリの区別がない単一サーバーだったのだと思う
    必要に応じてコンテナを保存・転送・実行できていれば、はるかに堅牢な構成要素になり、エンジンとレジストリでイメージの保存方式が食い違うという残念な流れも避けられたはず
    さらに、すべての本番クラスタには分散イメージストアがあるべきで、そのストアへイメージをプッシュすることがデプロイのトリガーになるべきだったと思う。今のようにレジストリへプッシュし、クラスタを設定し、各ノードがレジストリからプルする方式は脆弱で非効率だ。より良い設計を主張したが、すでに慣性が大きすぎ、初期の Kubernetes コミュニティは Docker 発のアイデアに敵対的だった

    • イメージについてファイルシステム上の配置が少なくとも3種類あり、エンジン内のイメージストアも2種類あるのは確かに雑然としている。それでも Docker が現在のモデルを壊さずにその方向へ到達するには、まだ遅すぎるわけではないと思う。ただ Docker がそれを気にするかは分からないし、最近は厳しい時期を過ごしているようだ
      クラスタへプッシュするデプロイは賢そうに見える。全ノードに unregistry を入れて互いにイメージを取得・共有させるような分散イメージストアは考えているが、プッシュがデプロイをトリガーする方式はもう少し考える必要がありそう
  • 良い。pussh コマンドは本当に優雅な言葉遊びとして認められるべきだ。覚えやすく、意味がすぐ分かり、姉妹標準コマンドと1文字違いでしかない

    • 悪くはないが、docker push-over-ssh のような、もっと公式っぽい別名があってもよさそう
      共同で開発される自動化では、pussh は機能を知らない人にはタイポに見えて、不要な混乱を招くかもしれない。一方 push-over-ssh なら意図された名前だと明確だ。短いオプションと長いオプションのように考えればいい
    • 追加された sssshs
      「その余分な s は何?」
      「タイポだよ」
    • 衝突もしやすい
  • 2015年にこの機能を Docker 本流に入れようと、素朴に PR[1] を送ったら、すぐに別の領域を手伝う方向へ回された。レジストリを使わなくてもよくするのは、Docker のビジネスモデルを揺るがしすぎたのかもしれない
    [1]: https://github.com/richardcrichardc/docker2docker

    • 元祖だったのですね。いまだに Docker にイメージレイヤーを探索する APIがないのは残念
      結局、デフォルトを containerd のイメージストアへ切り替える計画なのだと思う。ローカルとリモートの両方で containerd のイメージストアを使うようになれば、レジストリラッパーなしで、もともと実装していたことができるようになりそう
  • Ansible のようなプッシュ型デプロイツールをすでに使っているシステムと相性が良さそうな、素晴らしいアイデアだ。Docker レジストリに24時間365日のサポートがない会社では、良いホットフィックス配布メカニズムとしても使えそう
    buildah のような OCI ツールときれいに統合できるのか、それとも両端に完全な Docker インストールが必要なのかが気になる。まだ深くは見ていないが、こうした構成で skopeo が動くには、リモートサーバー上にミニレジストリをブートストラップすることが欠けているピースに見える

    • リモート側には containerd が必要。Docker も Kubernetes も containerd を使っている。クライアント側には、レジストリ API、つまり OCI Distribution 仕様(https://github.com/opencontainers/distribution-spec)を話す任意のツールがあればよい
      Unregistry は API 層に公式 Docker レジストリのコードを再利用しているので、https://hub.docker.com/_/registry と同じように見え、動作する
      クライアントでは skopeo、crane、regclient、BuildKit など、OCI レジストリを話すツールを使える。ただしそれらを使うには、リモートホストで unregistry を手動実行する必要がある。docker pussh コマンドはローカル Docker を使ってその流れを自動化しているだけ
      単なる Bash スクリプトだと思えばいい: https://github.com/psviderski/unregistry/blob/main/docker-pu...
      自分の望む方式に簡単に直せる
    • 同意。管理している複数のサービスで、イメージをローカルにビルドして保存し、Ansible でアーカイブをアップロードしてからイメージを復元しているが、たいてい望むよりずっと時間がかかる
    • 両端にDocker デーモンが必要。SSH 経由で2つのデーモン間でレイヤーを共有する賢いやり方だ
  • 最初からこうあるべきだった。素晴らしい
    Docker レジストリにも使いどころはあるが、全体としては過剰設計で、ハッカー精神とは反対側にある

    • ベンチャー投資を受けた会社だったので、Docker も何らかの形で稼がなければならなかった
    • GitHub Actions と一緒に GitHub のレジストリである ghcr.io を使うのがおすすめ
      非公開 ghcr.io レジストリにイメージをビルドしてプッシュする .yaml ワークフローを設定するのに20分、サーバーがそこからイメージを取得できるよう許可するのに5分ほどかかった。かなり実用的な構成だ
    • 複雑さは、レジストリへブロブをプッシュする手順にあるように思う。以前 OCI 互換の読み取り専用レジストリを作ったことがあるが、それはそこまで複雑ではなかった
  • GitLab でイメージをビルドして Artifactory にプッシュし、その後デプロイがトリガーされて Artifactory から取得し AWS ECR へ再度プッシュし、EKS のデプロイテンプレートを更新すると ECR からノードへ取得して Pod コンテナを立ち上げる、というパイプラインを見ている
    自分の人生にはこれが必要だ

    • 気になって聞くのですが、Artifactory と ECR の両方を使う理由は何ですか? 私たちはコスト削減のために Artifactory から ECR への移行を検討中です
    • 前のプロジェクトのパイプラインでは、実際のアプリのビルドよりもコンテナをプルしてプッシュする時間のほうが長かった。しかも、この時間すべてでさえヘルスチェックの待ち時間に比べれば小さく、実際に正常かどうかは起動後1秒もしないうちに分かった
  • これを見て uncloud を知った。サイドプロジェクトのサーバー構成用に、dokku みたいだけどもう少し強力なものを探していたところで、まさにそんな感じだった

    • https://skateco.github.io/ もある。ざっと見たところ似ているように見える
    • Portainer を使ったことがない、または検討したことがないならおすすめ。AWS で2つの EC2 インスタンスに Portainer Community Edition と Portainer Agent を動かしているが、うまく動作している
      スタック機能も本当に良くて、実質的には Docker Compose。ある EC2 インスタンスでは Portainer Agent がコンテナ内の Caddy を実行し、Caddy がロードバランサーとリバースプロキシの役割を果たしている
    • uncloud のアイデアがしっくり来たようでうれしい。質問や手助けが必要なら Discord に来てもよい
  • Docker が最初からこのように動作していなかったのは、かなり不思議。かっこよく見える

    • すでにイメージをアーカイブ化してサーバーにプッシュし、そのアーカイブをサーバー上で実行する方式で同じことはできる
      アーカイブの保存は docker save -o may-app.tar my-app:latest のように行い、ロードは docker load -i /path/to/my-app.tar のように行う
      Ansible のようなツールを使えば、「Unregistry」が自動化していることを簡単に実現できる。GitHub リポジトリによると、save/load 方式の欠点は イメージ全体をネットワーク経由で転送する点で、実際に問題になり得る。アーカイブファイルではなくイメージ自体を管理するほうが便利そうでもある
  • すっきりしたプロジェクトとアプローチ。高価なレジストリにうんざりして Zot[1] を自分でホストするようになったが、一部の用途ではこちらのほうがずっと簡単そう
    簡単に設定できて、安価で、従量課金の プライベートレジストリサービス があればいいと思っている人がほかにもいるのか気になる
    [1]: https://zotregistry.dev

    • もし知らなかったなら、zothub.io の SSL 証明書が期限切れになっている
  • 悪くないアイデア。ただしデプロイがサービスに結合されるという欠点はあり得る。たとえばスケールアウトや red/green デプロイをどうするのかは分からないし、そういうことをする側がプッシュを把握している必要がありそう
    修正: それをやるのが uncloud だった。今気づいた
    それでもトレードオフではある。規模が小さく、Hetzner の VM 1台を使い、シンプルさに満足できて、ローカルでイメージをビルドするのも問題ないなら素晴らしい

    • 確かに常にトレードオフ。選択肢があって、作業ごとに最も合うツールを選べるのは良いこと