OpenShift Advent Calendar 2025 18日目の記事です。
担当の志田です。よろしくお願いします。
はじめに
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 もお楽しみに!