以下の内容はhttps://rheb.hatenablog.com/entry/2025/12/18/200236より取得しました。


OpenShift で使用するコンテナレジストリ選定ガイド(Harbor編)

OpenShift Advent Calendar 2025 18日目の記事です。

担当の志田です。よろしくお願いします。

qiita.com

はじめに

Kubernetes / OpenShift などのコンテナプラットフォームにおいて、コンテナレジストリは単なる保存先ではなく、ライフサイクルの中心となる必須コンポーネントです。そのため、コンテナレジストリの選定と運用設計は、プロジェクトの成否を分ける重要なテーマとなります。

本記事では、実務で直面する以下のポイントを中心に解説します。

  • OpenShift で利用できるレジストリ比較(内部レジストリ / Quay / Harbor / Nexus)
  • Harbor 運用の実践ポイント(HA、レプリケーション、セキュリティ機能)
  • Helm + CloudNativePG (CNPG) を使った Harbor の OpenShift デプロイ手順

👉 最近、お客様環境で Harbor を扱う機会があり、その軽量さと多機能ぶりのバランスが非常に良かったため、本記事ではHarborにフォーカスして、その特徴や実際のデプロイ手順を共有したいと思います。

OpenShift におけるコンテナレジストリの役割

OpenShift のエコシステムでは、あらゆるフローがレジストリを介して動いています。

  • Build: BuildConfig や Tekton でビルドされたイメージを Registry へ push
  • Deploy: Deployment や ArgoCD が Registry から pull
  • Governance: イメージの脆弱性スキャンや署名検証による実行制御
  • Air-gapped: oc mirror v2 による Operator カタログやリリースイメージの取り込み先

企業向け基盤としてレジストリを選定する場合、特に以下の要素が重視されます。

  • 高可用性(HA): 停止がデプロイ停止に直結するため
  • 脆弱性スキャン: Trivy や Clair による自動検知
  • イメージ署名: cosign 等による「正しいイメージ」の証明
  • SBOM 管理: ソフトウェアサプライチェーンの透明性確保
  • 外部ストレージ対応: S3(ODF / MinIO / RGW等)によるスケーラビリティ

コンテナレジストリ比較

オンプレミスのOpenShift環境でよく使われてるコンテナレジストリをいくつか比較してみましょう。 大規模なシステムで利用する場合はQuay、軽量に始めたい場合はHarborといった選択が良さそうですね。

項目 OpenShift 内部レジストリ Red Hat Quay Harbor Nexus Repository
OpenShift との統合 ◎ 完全統合 ◎ 完全統合 ○(外部連携) ○(外部連携)
HA構成 △(Shared PVC依存) ◎(分散構成) ◎(CNPG + S3) △(OSS版は不可)
脆弱性スキャン △(外部依存) ◎ Clair ◎ Trivy ×(OSS版なし)
イメージ署名 cosign のみ cosign / Notary v2 cosign / Notary v2 ○(Proのみ)
SBOM 管理
OCI Artifact 対応
S3 バックエンド
OSS での利用 OpenShift 付属 ×(UpstreamはProject Quay)[1] ◎ 100% OSS ◎(ただし機能制限あり)
主な用途 小〜中規模・開発環境 大規模・ミッションクリティカル OSS志向・閉域網・多機能 Java/npm等の統合リポジトリ

[1] Red Hat Quay のサブスクリプションは個別購入頂くか、セルフマネージド型OpenShiftの最上位エディションである Red Hat OpenShift Platform Plus をご購入頂くことでご利用可能です。また、フルマネージド型の Red Hat Quay.io も展開しております。

Harbor 運用の実践ポイント

ここからは、オンプレミス環境で非常に人気の高い Harbor にフォーカスし、その特徴を見てみましょう。

Harbor の HA(高可用性)構成

Harbor を本番運用する場合、複数コンポーネントを冗長化し、外部サービスを HA 化することで安定稼働が実現できます。

  • 外部 DB(CNPG): PostgreSQL をクラスタ化し、単一障害点(SPOF)を排除。
  • 外部 Redis(HA): セッション・キャッシュ・ジョブキューを扱うため、Redis Sentinel/Cluster で冗長化。
  • 共有オブジェクトストレージ: Registry は全ノードで同一のオブジェクトストレージを利用。
  • Harbor コンポーネントの水平スケール: core / portal / jobservice / registry は複数レプリカで運用。
  • ロードバランサ/Ingerss: すべてのリクエストを LB 経由で複数ノードに分散。

Harbor レプリケーションの強み

Harbor はレプリケーション機能が極めて優秀です。

  • Pull-based / Push-based: 両方に対応。
  • フィルタリング: 特定のタグやプロジェクトのみを同期。
  • 完全同期: replicate_deletion を有効にすれば、削除操作も同期可能。
  • ユースケース: DMZ(検疫用)から内部レジストリへの転送や、DR 環境へのイメージコピーに最適です。

セキュリティ(スキャン・署名・SBOM)

Harbor はセキュリティスイートとして完成されています。

  • 脆弱性スキャン: Trivy が組み込まれており、Push 時に自動スキャンが可能。
  • 署名 (cosign): cosign sign で署名したイメージを Harbor UI 上で視覚的に確認でき、署名がないイメージの Pull を禁止するポリシーも設定可能。
  • SBOM: syft 等で生成した SBOM をイメージに添付し、Harbor で管理できます。

Helm を使った Harbor デプロイ手順(実践編)

では、実際の本番運用に耐えうるようにできるだけ冗長な構成でOpenShiftにHarborをデプロイしてみましょう。 OpenShift 上に Helm + CNPG + ODF(Red Hat OpenShift Data Foundation) で Harbor を構築する手順の要約です。 使用する Helm Chart は goharbor/harbor-helm です。

※本手順は参考例であり、本番環境での利用に際しては必ず十分な検証を実施した上で適用してください。

名前空間とサービスアカウントの作成

まず、Harborをデプロイするための名前空間とサービスアカウントを作成し、必要なSCC(Security Context Constraints)を付与します。

# Namespace作成します。
oc create ns harbor

# ServiceAccount作成します。
oc create sa harbor -n harbor

# ServiceAccountに必要なSCCを付与します。
oc adm policy add-scc-to-user anyuid -z harbor -n harbor
oc adm policy add-scc-to-user containerized-data-importer -z harbor -n harbor

# DB(CNPG)接続用認証情報をSecretで定義します。
oc create secret generic harbor-db-secret \
  -n harbor \
  --type=kubernetes.io/basic-auth \
  --from-literal=username=harbor \
  --from-literal=password=harbor

CNPG による DB 構築

次に、DBを作成します。 CNPGクラスターを作成し、データベースregistryを初期セットアップします。 データベースの永続領域(PV)には、ODFのブロックストレージである RBD(RADOS Block Device) を使用します。

※前提として、CNPG Operatorを予めOpenShiftにインストールしておいてください。

# CNPGクラスタをデプロイします。
cat <<'EOF' | oc apply -f -
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: harbor-db
  namespace: harbor
spec:
  bootstrap:
    initdb:
      database: registry
      encoding: UTF8
      localeCType: C
      localeCollate: C
      owner: harbor
      secret:
        name: harbor-db-secret
  enableSuperuserAccess: true
  instances: 3
  storage:
    size: 1Gi
    storageClass: ocs-storagecluster-ceph-rbd
EOF

# デプロイ状況を確認します。
oc get cluster.oistgresql.cnpg.io -n harbor
# NAME        AGE   INSTANCE   READY    STATUS                    PRIMARY
# harbor-db   5m    3          3        Cluster in healthy state  harbor-db-1
#  ステータスがhealthyになればOKです

オブジェクトストレージバケットの作成

Harborにpushされたイメージを格納するためのバックエンドストレージとしてオブジェクトストレージバケットを作成します。オプジェクトストレージには、ODFの RGW (RADOS Gateway) を使用します。

cat <<'EOF' | oc apply -f -
apiVersion: objectbucket.io/v1alpha1
kind: ObjectBucketClaim
metadata:
  name: harbor-bucket
  namespace: harbor
spec:
  genetateBucketName: harbor-bucket
  storageClass: ocs-storagecluster-ceph-rgw
EOF

バケットが作成されたら、バケットのアクセスキーとシークレットキーが格納されたsecretが作成されますので抽出します。さらに、RGWのTLS証明書を抽出し、Harborが利用するためのsecretに格納します。

# バケットのアクセスキーとシークレットキーを取得します。
export S3_ACCESSKEY=$(oc get secerts harbor-bucket -n harbor -o jsonpath='{.data.AWS_ACCESS_KEY_ID}'|base64 -d)
export S3_SECRETKEY=$(oc get secerts harbor-bucket -n harbor -o jsonpath='{.data.AWS_SECRET_ACCESS_KEY_ID}'|base64 -d)

# RGWのTLS証明書を取得します。
oc get secrets -n openshift-storage ocs-storagecluster-cos-ceph-rgw-tls-cert -o jsonpath='{.data..tls\.crt}'|base64 -d > ca.crt

# Harborが使用するためのsecretを作成します。
oc create secret generic harbor-s3-secret \
  -n harbor \
  --from-file=ca.crt=ca.crt \
  --from-literal=REGISTRY_STORAGE_S3_ACCESSKEY=$S3_ACCESSKEY \
  --from-literal=REGISTRY_STORAGE_S3_SECRETKEY=$S3_SECRETKEY

Harbor のインストール(values.yaml のポイント)

Helm Chartのパラメータファイル values.yaml を作成し、OpenShiftにインストールします。 (※ values.yaml のパラメータはシステム要件の合わせて適宜変更してください。)

注意) Harborのキャッシュやセッション管理にRedisが使用できますが、このHelm Chartでは冗長構成が取れないため高可用性が求められる環境では外部のインメモリーデータストアを使用してくだい。

# 前項で作成したHarbor用オブジェクトストレージのエンドポイントとバケット名を取得します。
HARBER_BUCKET_HOST=$(oc get cm -n harbor harbor-bucket -o jsonpath='{.data.BUCKET_HOST}{\n}')
HARBER_BUCKET_NAME=$(oc get cm -n harbor harbor-bucket -o jsonpath='{.data.BUCKET_NAME}{\n}')

# values.yamlを作成します。
cat << EOF > ./values.yaml
expose:
  type: ingress
  tls:
    enabled: true
    certSource: auto
  ingress:
    hosts:
      core: harbor.apps.cluster.ocp.local # Harborのホスト名
    controller: default
externalURL: https://harbor.apps.cluster.ocp.local # 外部接続用URL
persistence:
  enabled: true
  resourcePolicy: "keep"
  persistentVolumeClaim:
    registry:
      existingClaim: ""
      storageClass: "ocs-storagecluster-ceph-rbd"  # ODFのRBDを使用
      subPath: ""
      accessMode: ReadWriteOnce
      size: 5Gi
      annotations: {}
    jobservice:
      jobLog:
        existingClaim: ""
       storageClass: "ocs-storagecluster-ceph-rbd"  # ODFのRBDを使用
        subPath: ""
        accessMode: ReadWriteOnce
        size: 1Gi
        annotations: {}
# DBにはCNPGを使用するので同梱されているdatabaseは使用しない
#    database:
#      existingClaim: ""
#      storageClass: ""
#      subPath: ""
#      accessMode: ReadWriteOnce
#      size: 1Gi
#      annotations: {}
    redis:
      existingClaim: ""
      storageClass: "ocs-storagecluster-ceph-rbd"  # ODFのRBDを使用
      subPath: ""
      accessMode: ReadWriteOnce
      size: 1Gi
      annotations: {}
    trivy:
      existingClaim: ""
      storageClass: "ocs-storagecluster-ceph-rbd"  # ODFのRBDを使用
      subPath: ""
      accessMode: ReadWriteOnce
      size: 5Gi
      annotations: {}
  imageChartStorage:
    disableredirect: false
    type: s3  # ODF RGWのオブジェクトストレージを使うのでs3を指定
    s3:
      region: us-west-1
      existingSecret: "harbor-s3-secret"  # バケット接続用secret
      bucket: $HARBER_BUCKET_NAME  # バケット名は環境変数から取得
      regionendpoint: https://$HARBER_BUCKET_HOST  # エンドポイントは環境変数から取得
      secure: true
harborAdminPassword: "Harbor12345"
portal:
  replicas: 2  # レプリカ数2で冗長化
  image:
    repository: docker.io/goharbor/harbor-portal
    tag: v2.13.1
  serviceAccountName: "harbor"
core:
  replicas: 2  # レプリカ数2で冗長化
  image:
    repository: docker.io/goharbor/harbor-core
    tag: v2.13.1
  serviceAccountName: "harbor"
jobservice:
  replicas: 2  # レプリカ数2で冗長化
  image:
    repository: docker.io/goharbor/harbor-jobservice
    tag: v2.13.1
  serviceAccountName: "harbor"
registry:
  replicas: 2  # レプリカ数2で冗長化
  registry:
    image:
      repository: docker.io/goharbor/registry-photon
      tag: v2.13.1
  controller:
    image:
      repository: docker.io/goharbor/harbor-registryctl
      tag: v2.13.1
  serviceAccountName: "harbor"
trivy:
  replicas: 2  # レプリカ数2で冗長化
  enabled: true
  image:
    repository: docker.io/goharbor/trivy-adapter-photon
    tag: v2.13.1
  serviceAccountName: "harbor"
  resources:
    requests:
      cpu: 200m
      memory: 512Mi
    limits:
      cpu: 1
      memory: 1Gi
database:
  type: external
  external:
    host: "harbor-db-rw.harbor.svc"  # CNPGクラスタのservice
    port: "5432"
    username: "harbor"
    password: "harbor"
    coreDatabase: "registry"
    sslmode: "disable"
# 注意) redisを冗長化する場合は外部のインメモリーデータストアを用意する
redis:
  type: internal
  internal:
    image:
      repository: docker.io/goharbor/redis-photon
      tag: v2.13.1
   serviceAccountName: "harbor" 
exporter:
  replicas: 2  # レプリカ数2で冗長化
  image:
    repository: docker.io/goharbor/harbor-exporter
    tag: v2.13.1
 serviceAccountName: "harbor" 
EOF

# Harborをインストールします。
helm install harbor . -f values.yaml -n harbor

# 状態を確認します。
helm list -n harbor
# NAME    NAMESPACE    REVISION    UPDATED                                  STATUS    CHART     APP VERSION
# harbor  harbor       1           2025-12-18 01:55:18.831366388 +0000 UTC  deployed  deployed  2.13.1
# STATUSがdeployedになればOK

使ってみる

これで、Harborが立ち上がったのでブラウザから繋いでみます。 UIはシンプルですが、コンテナイメージの運用に必要な機能はほとんど網羅されており直感的で使いやすい印象です。

まとめ

ユースケース 推奨レジストリ
学習・検証・リソース節約 OpenShift 内部レジストリ
大規模・全社共通・高度なセキュリティ・Red Hat サポート Red Hat Quay
軽量・OSS 活用・閉域網運用・高いコストパフォーマンス Harbor (CNPG 構成)

コンテナレジストリは一度運用を始めると切り替えが難しいため、コンテナレジストリに何を使おうか迷ったら Harbor または Quay を選択することをお勧めします。

明日の Advent Calendar もお楽しみに!




以上の内容はhttps://rheb.hatenablog.com/entry/2025/12/18/200236より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14