コンテナレジストリアプローチの比較
ベアメタルサーバーや隔離されたエアギャップネットワーク向けのイメージレジストリを選択する場合、通常は以下の3つのパターンに集約されます。
- エンタープライズ向けマルチサービススイート(例: Harbor): Harborはきめ細かなRBAC、プロジェクトクォータ、ロボットアカウント、レプリケーションポリシーを提供します。しかし難点は、約10個もの個別コンテナ(PostgreSQL、Redis、Core、Jobservice、Trivy、Nginx、Registry)を起動することです。4GB RAMのエッジゲートウェイや共有ステージングVM上では、Harborがアイドル状態のまま約2GBのメモリを消費してしまいます。
- 従来のDistributionレジストリ(Docker Registry v2): 古典的な
distribution/distributionバイナリは非常に小型で、消費RAMは40MB未満です。しかし、ネイティブなOCIアーティファクトのディスカバリ、検索インデックス、組み込みアクセス制御リスト(ACL)、脆弱性スキャンフックなどの機能が不足しています。 - 最新の単一バイナリOCIレジストリ(Zot): ZotはGoで書かれたCNCFインキュベーティングプロジェクトです。外部データベースに依存せず、単一の静的バイナリまたはコンテナとして提供されます。フットプリントが小さいにもかかわらず、OCI Image Index、Helmチャート、Cosign署名、SBOMアタッチメントをネイティブにインデックス化します。
| 機能 | Harbor | Docker Registry v2 | Zot Registry |
|---|---|---|---|
| アーキテクチャ | マイクロサービス(8〜10コンテナ) | 単一プロセス | 単一バイナリ / コンテナ |
| アイドル時メモリフットプリント | 約1.5 GB 〜 3.0 GB | 約20 MB 〜 40 MB | 約30 MB 〜 60 MB |
| OCI 1.1サポート | Coreレイヤー経由でサポート | 基本 / レガシースキーマ | ネイティブOCI 1.1仕様 |
| 依存バックエンド | PostgreSQL、Redis | なし | なし(組み込みストレージエンジン) |
Zotの評価:強みとトレードオフ
環境にZotを導入する前に、その優れた点と限界となり得るトレードオフを比較検討しておきましょう。
Zotの優れた点
- ほぼ瞬時の起動と低いオーバーヘッド: 200ミリ秒未満で起動し、アイドル時のRAM消費量は約35MBです。CIビルドエージェント、テストランナー、エッジデバイスに直接簡単に組み込めます。
- ネイティブなOCIアーティファクト処理: 標準ツールを使用して、Helm 3チャート、WebAssemblyバイナリ、Cosign署名、CycloneDX SBOMを直接プッシュできます。追加プラグインは不要です。
- 手軽なエアギャップ環境のメンテナンス: バックアップは非常にシンプルです。データディレクトリと単一のJSON設定ファイルをアーカイブするだけで完了します。
- モジュール式拡張機能: 1つのファイルでブール値フラグを切り替えるだけで、htpasswd、LDAP、OAuth2/OIDC、重複排除、Trivyによるイメージスキャンを有効化できます。
考慮すべきトレードオフ
- シンプルなWebコンソール: Zotにはリポジトリやタグを探索するためのクリーンなUIが含まれていますが、Harborのようなプロジェクト単位のきめ細かなチームダッシュボードはありません。
- 組織規模のガバナンス: 複数部門にわたるクォータ管理や複雑な承認ワークフローを必要とする大企業では、依然としてHarborが好まれる場合があります。
対象アーキテクチャ
本番環境では、TLS終端を処理するNginxまたはTraefikリバースプロキシの背後で、Docker Compose内のZotを実行します。containerdやCRI-Oなどのコンテナランタイムは、デフォルトで安全でないHTTPレジストリを拒否します。前段にプロキシを配置することで、Zot内部でカスタムTLS証明書を設定することなくHTTPSをスムーズに処理できます。
ホスト上のディレクトリ構造は次のとおりです。
/opt/zot/
├── config.json
├── htpasswd
├── docker-compose.yml
└── data/
ステップバイステップのデプロイ手順
Docker EngineおよびDocker Compose v2を実行しているLinuxサーバー上で、安全で機能的なZotレジストリをセットアップします。
ステップ 1: ディレクトリ構造の作成
プロジェクトのベースディレクトリとデータマウントポイントを作成します。
sudo mkdir -p /opt/zot/data
cd /opt/zot
ステップ 2: 認証情報の生成
使い捨てコンテナを使用して、bcryptでハッシュ化されたパスワードファイルを生成します。
# ユーザー「developer」のhtpasswdエントリを作成
docker run --rm httpd:alpine htpasswd -B -b -n developer SecurePassword123 > htpasswd
# 所有者のみに権限を制限
chmod 600 htpasswd
ステップ 3: Zotの設定 (config.json)
ローカルストレージパス、重複排除、ガベージコレクション、認証を設定するconfig.jsonを作成します。
cat <<'EOF' > config.json
{
"distSpecVersion": "1.1.0",
"storage": {
"rootDirectory": "/var/lib/registry",
"gc": true,
"gcDelay": "1h",
"dedupe": true
},
"http": {
"address": "0.0.0.0",
"port": "5000",
"auth": {
"htpasswd": {
"path": "/etc/zot/htpasswd"
}
}
},
"extensions": {
"ui": {
"enable": true
},
"search": {
"enable": true
}
}
}
EOF
ステップ 4: Docker Composeサービスの定義
公式のフル機能版Zotイメージを参照するdocker-compose.ymlを記述します。
cat <<'EOF' > docker-compose.yml
services:
zot:
image: ghcr.io/project-zot/zot-linux-amd64:v2.1.1
container_name: zot-registry
restart: unless-stopped
ports:
- "5000:5000"
volumes:
- ./config.json:/etc/zot/config.json:ro
- ./htpasswd:/etc/zot/htpasswd:ro
- ./data:/var/lib/registry
user: "1000:1000"
EOF
データボリュームの所有権をUID/GID 1000:1000 に付与します。
sudo chown -R 1000:1000 /opt/zot/data
ステップ 5: 起動とヘルスチェック
デタッチモードでコンテナを起動します。
docker compose up -d
# 起動ログを確認
docker compose logs -f zot
レジストリのカタログエンドポイントがHTTP 200を返すことを確認します。
curl -u developer:SecurePassword123 http://localhost:5000/v2/_catalog
空のリポジトリ配列 {"repositories":[]} が返されます。
ステップ 6: Dockerイメージのプッシュとプル
Docker CLIでログインし、サンプルのAlpineイメージをプッシュします。
# 1. 認証
echo "SecurePassword123" | docker login localhost:5000 -u developer --password-stdin
# 2. ベースイメージを取得
docker pull alpine:3.20
# 3. ローカルレジストリ用にタグ付け
docker tag alpine:3.20 localhost:5000/base/alpine:3.20
# 4. イメージをプッシュ
docker push localhost:5000/base/alpine:3.20
# 5. ローカルキャッシュを削除してZotからプル
docker rmi localhost:5000/base/alpine:3.20
docker pull localhost:5000/base/alpine:3.20
ステップ 7: OCI経由でのHelmチャートのプッシュ
Helm 3はOCIレジストリをネイティブなチャートリポジトリとして扱います。
# 1. サンプルチャートを作成
helm create webapp
# 2. .tgzアーカイブにパッケージ化
helm package webapp
# 3. HelmでZotにログイン
echo "SecurePassword123" | helm registry login localhost:5000 -u developer --password-stdin
# 4. チャートアーカイブをZotに直接プッシュ
helm push webapp-0.1.0.tgz oci://localhost:5000/charts
# 5. プルと展開のテスト
helm pull oci://localhost:5000/charts/webapp --version 0.1.0 --untar
ステップ 8: アーティファクトの検証
再度カタログエンドポイントに問い合わせます。
curl -u developer:SecurePassword123 http://localhost:5000/v2/_catalog
# 期待される出力: {"repositories":["base/alpine","charts/webapp"]}
ブラウザで http://<サーバーIP>:5000 を開き、Web UIでイメージレイヤー、脆弱性レポート、OCI記述子を確認します。

