AI

OSSのコンテナレジストリ比較:Harbor vs Zot vs Distribution でDockerHub代替をセルフホストする

オープンソースラボ編集部2026年6月13日

OSSのコンテナレジストリ比較:Harbor vs Zot vs Distribution でDockerHub代替をセルフホストする

DockerHub(月$5/ユーザー〜・Pull Rate Limit あり)・AWS ECR($0.10/GB/月)・GitHub Container Registry(GitHub Actions連動)に対して、Harbor(最も機能が豊富なOSSコンテナレジストリ・セキュリティスキャン搭載)・Zot(最軽量・OCI専用)・Distribution(DockerのOSSレジストリ・公式実装)はOSSのコンテナイメージレジストリです。

セルフホストコンテナレジストリを選ぶ理由

  • DockerHubのRate Limit回避: 匿名ユーザーは6時間で100 Pull制限。大規模CIで頻繁にブロックされる
  • コスト: ECRはデータ転送費用が高い($0.09/GB〜)。同一VPC内のセルフホストならほぼ無料
  • セキュリティ: 社内専用レジストリでイメージを外部に公開しない
  • スキャン: HarborはTrivyでイメージの脆弱性を自動スキャン

主要ツールの概要

Harbor

2016年にVMwareが開発したGo製のOSSコンテナレジストリです。CNCF Graduatedプロジェクト・GitHubスター24k+。OCI準拠・Docker/Helm Chart対応・Trivyによる脆弱性スキャン・LDAP/OIDC認証・レプリケーション・ガベージコレクションを備えた本番グレードのレジストリです。KubernetesクラスタのCNI(Harbor→k8s)構成で広く使われています。

# Harbor を Docker Compose でセルフホスト(本番構成)
curl -L https://github.com/goharbor/harbor/releases/download/v2.11.0/harbor-online-installer-v2.11.0.tgz | tar xz
cd harbor

# 設定ファイルを生成
cp harbor.yml.tmpl harbor.yml

# harbor.yml を編集(最低限の設定)
cat > harbor.yml << 'EOF'
hostname: registry.yourcompany.com

https:
  port: 443
  certificate: /etc/letsencrypt/live/registry.yourcompany.com/fullchain.pem
  private_key: /etc/letsencrypt/live/registry.yourcompany.com/privkey.pem

harbor_admin_password: your-secure-admin-password

database:
  password: your-db-password
  max_idle_conns: 50
  max_open_conns: 100

data_volume: /data/harbor

trivy:
  ignore_unfixed: false
  skip_update: false
  offline_scan: false
  security_check: vuln
  insecure: false

jobservice:
  max_job_workers: 10

log:
  level: warning
  local:
    rotate_count: 50
    rotate_size: 200m
    location: /var/log/harbor
EOF

# インストール・起動(--with-trivy でスキャン機能を有効化)
./install.sh --with-trivy
# HarborにDockerイメージをプッシュ・プル
# Docker loginでHarborに認証
docker login registry.yourcompany.com   -u admin   -p your-secure-admin-password

# イメージをビルドしてHarborにプッシュ
docker build -t myapp:v1.2.3 .
docker tag myapp:v1.2.3 registry.yourcompany.com/myproject/myapp:v1.2.3
docker push registry.yourcompany.com/myproject/myapp:v1.2.3

# Kubernetes からHarborのイメージをプル
kubectl create secret docker-registry harbor-secret   --docker-server=registry.yourcompany.com   --docker-username=robot-account   --docker-password=robot-token   --namespace=production

# Deploymentでシークレットを使用
kubectl patch serviceaccount default -n production   -p '{"imagePullSecrets": [{"name": "harbor-secret"}]}'
# .github/workflows/build-push.yml
# GitHub Actions でHarborにイメージをビルド・プッシュ
name: Build and Push to Harbor
on:
  push:
    branches: [main]
    tags: ['v*']

env:
  REGISTRY: registry.yourcompany.com
  IMAGE_NAME: myproject/myapp

jobs:
  build-push:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Login to Harbor
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ secrets.HARBOR_USER }}
          password: ${{ secrets.HARBOR_TOKEN }}

      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=semver,pattern={{version}}
            type=sha,prefix=sha-

      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache
          cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache,mode=max

      - name: Trivy Security Scan
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}
          format: sarif
          output: trivy-results.sarif
          severity: CRITICAL,HIGH

      - name: Upload Trivy scan results
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: trivy-results.sarif
# Harbor REST APIでイメージ管理を自動化
import httpx
from base64 import b64encode

HARBOR_URL = "https://registry.yourcompany.com"
AUTH = b64encode(b"admin:your-secure-admin-password").decode()

headers = {
    "Authorization": f"Basic {AUTH}",
    "Content-Type": "application/json",
}

# プロジェクト一覧を取得
def list_projects():
    resp = httpx.get(f"{HARBOR_URL}/api/v2.0/projects", headers=headers)
    return resp.json()

# リポジトリのタグ一覧を取得
def list_tags(project: str, repo: str):
    resp = httpx.get(
        f"{HARBOR_URL}/api/v2.0/projects/{project}/repositories/{repo}/artifacts",
        headers=headers,
        params={"page": 1, "page_size": 50, "with_scan_overview": True},
    )
    return resp.json()

# 古いイメージタグを削除(30日以上前のshaタグを削除)
def cleanup_old_tags(project: str, repo: str, keep_days: int = 30):
    from datetime import datetime, timezone, timedelta
    cutoff = datetime.now(timezone.utc) - timedelta(days=keep_days)
    artifacts = list_tags(project, repo)
    deleted = 0
    for artifact in artifacts:
        push_time = datetime.fromisoformat(
            artifact["push_time"].replace("Z", "+00:00")
        )
        # sha- で始まるビルドキャッシュタグのみ削除(latest等は除外)
        is_sha_tag = any(
            t["name"].startswith("sha-") for t in artifact.get("tags", [])
        )
        if push_time < cutoff and is_sha_tag:
            digest = artifact["digest"]
            httpx.delete(
                f"{HARBOR_URL}/api/v2.0/projects/{project}/repositories/{repo}/artifacts/{digest}",
                headers=headers,
            )
            deleted += 1
    print(f"Deleted {deleted} old artifacts")
    return deleted

Distribution(旧 Docker Registry 2.0)

Dockerが2013年に公開したGo製のOSSコンテナレジストリです。GitHubスター9k+。OCIとDockerイメージフォーマットに対応したシンプルなレジストリで、UIはなくAPIとDockerコマンドで操作します。Harborの内部レジストリとしても使われています。

# docker-compose.yml - Distribution(最軽量構成)
version: '3.8'
services:
  registry:
    image: registry:2
    restart: unless-stopped
    ports:
      - "5000:5000"
    environment:
      REGISTRY_AUTH: htpasswd
      REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
      REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
      REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /var/lib/registry
      REGISTRY_HTTP_TLS_CERTIFICATE: /certs/fullchain.pem
      REGISTRY_HTTP_TLS_KEY: /certs/privkey.pem
    volumes:
      - registry_data:/var/lib/registry
      - ./auth:/auth
      - ./certs:/certs

volumes:
  registry_data:
# htpasswdファイルを作成(Basic認証)
mkdir auth
docker run --rm   --entrypoint htpasswd   httpd:2 -Bbn myuser mypassword > auth/htpasswd

# 使用例
docker login localhost:5000 -u myuser -p mypassword
docker pull nginx:alpine
docker tag nginx:alpine localhost:5000/nginx:alpine
docker push localhost:5000/nginx:alpine

機能比較表

比較項目HarborZotDistribution
WebUI
脆弱性スキャン✅(Trivy)✅(Trivy連携)
Helm Chart対応
LDAP/OIDC❌(別途)
マルチレプリカ
メモリ使用量1GB〜64MB〜32MB〜
GitHub Stars24k+0.7k+9k+

HarborのTrivyスキャン結果をDevOpsカテゴリ/categories/devopsのSigNoz・Grafanaダッシュボードで可視化してセキュリティダッシュボードを構築します。KubernetesクラスタのコンテナをHarborから取得するにはDevOpsカテゴリ/categories/devopsのHelm・k9sと組み合わせてデプロイを管理します。

FAQ

Q. DockerHubのRate Limitを回避するためにHarborをミラーとして使うには?

A. Harborのプロキシキャッシュ機能を使います。①Harbor WebUI → Administration → Registries → New Endpoint → DockerHubを追加②Projects → New Project → プロキシキャッシュを有効化・DockerHubエンドポイントを選択③Kubernetes等のimagePullPolicyでHarborのミラーURLを使う(registry.yourcompany.com/dockerhub-proxy/library/nginx:latest)。初回はDockerHubから取得してHarborにキャッシュされ、以降はHarborから高速に返します。DockerHubのRate Limitが発生しなくなり、内部ネットワークの帯域も節約できます。

Q. HarborのTrivyスキャンで脆弱性が見つかったときに自動でPushを拒否するには?

A. Harbor Webhook + CI/CDで実装します。①Harborのプロジェクト → Webhooks → SCanning Completedイベントでエンドポイントに通知②カスタムAPIがスキャン結果を確認してCRITICAL/HIGH脆弱性があればCI/CDのデプロイを停止③または Harbor のタグ不変性 + Cosign でサイン済みイメージのみデプロイを許可。GitHub Actionsではtrivy-actionでスキャンして--exit-code 1を設定するとCRITICAL検出時にステップを失敗させられます。

Q. AWS ECRとHarborセルフホストのコスト比較は?

A. 月100GB転送・500GBストレージの場合: AWS ECR: ストレージ$50 + データ転送(ECR→EC2同リージョンは無料・別リージョンは$0.09/GB)で合計約$50〜。Harbor(EC2 t3.small): インスタンス$15.18/月 + EBSストレージ500GB $50 + 運用コスト(バックアップ・モニタリング)で合計約$70〜。ECRはシンプルさとAWSサービス統合の価値があるが、Harborはスキャン・LDAP・WebUIが無料で付いてくる点でエンタープライズ価値が高い。マルチクラウド・オンプレ・Kubernetes環境が複数あるならHarborのコスパが良くなります。

Q. HarborのOIDC設定でOktaと連携するには?

A. Harbor WebUI → Administration → Configuration → Authentication → OIDC を選択。設定値: OIDC Endpoint = OktaのIssuer URL(https://yourorg.okta.com/oauth2/default)、OIDC Client ID/Secret = OktaアプリのClient ID/Secret、OIDC Scope = openid profile email groups、OIDC Admin Group = harbor-admins(OktaグループをHarborAdminにマッピング)。Harborへのログインで「Login via OIDC Provider」ボタンが表示されOktaSSO認証が使えます。

まとめ

ユースケース推奨ツール
本番グレード・脆弱性スキャン・LDAP/OIDC統合Harbor
OCI専用・超軽量・CI向けミラーZot
シンプルなセルフホスト・最低限の機能Distribution

関連外部リソース

他の記事も読む

Let's Build Together

OSS導入、自社だけで悩まない。

ツール選定から構築・運用・AI活用まで、オープンソースラボ運営元のClasslessが伴走します。初回のご相談は無料です。