DockerでのZot Registryのデプロイ:オンプレミスおよびエアギャップ環境向け軽量OCIネイティブレジストリ

DevOps tutorial - IT technology blog
DevOps tutorial - IT technology blog

コンテナレジストリアプローチの比較

ベアメタルサーバーや隔離されたエアギャップネットワーク向けのイメージレジストリを選択する場合、通常は以下の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記述子を確認します。

Share: