この記事は 2 部構成の Part 1 です。
Part 2(近日公開):ClusterProfileを起点に、multicluster-runtime を使ってコントローラーから spoke クラスターへ接続します。
この記事で扱うこと
Cluster Inventory API(multicluster.x-k8s.io)は、SIG-Multicluster が進めている API で、中心になるリソースは ClusterProfile です。ただし、この API が意味を持つのは、誰かが ClusterProfile を作ってくれる場合に限られます。その役割を担うのが cluster manager です。現時点で本番利用できるオープンソースの選択肢としては Open Cluster Management(OCM) があり、feature gate を有効にすると、OCM の registration controller が ClusterProfile の cluster manager として動作できます。
この記事では、ローカルの kind 環境でそれをエンドツーエンドに動かす手順を紹介します。
- 3 つの kind クラスターを使った OCM hub-spoke 構成
- hub 側での
ClusterProfilefeature gate の有効化 -
ClusterProfile.status.accessProvidersに実際に使える接続情報を入れるためのcluster-proxy設定 - spoke 側の
ClusterPropertyを hub 側のClusterProfile.status.propertiesに反映する流れ
最後まで進めると、multicluster.x-k8s.io/v1alpha1 ClusterProfile のインベントリが動く状態になります。Cluster Inventory API を利用するコンシューマーは、このインベントリをそのまま読み取れます。Part 2 では、ここに multicluster-runtime をつなぎ込みます。
全体像
構成は次のとおりです。
- kind クラスターを 3 つ作成:
hub,cluster1,cluster2 - OCM hub が
cluster1とcluster2を managed cluster として管理 - managed cluster は
sandbox-fleetというManagedClusterSetに所属 - spoke に
cluster-proxyとmanaged-serviceaccountaddon をインストール -
ClusterProfileリソースはcluster-inventorynamespace に作成 - spoke の
ClusterPropertyの値が hub のClusterProfile.status.propertiesに反映
ClusterProperty の同期経路は次のようになります。図のボックスには実際の OCM コンポーネント名を書いていますが、ここで押さえておきたいのは向きです。spoke に設定した property が、hub の ClusterProfile.status.properties に入ります。
言い換えると、spoke の ClusterProperty は hub の ManagedCluster.status.clusterClaims に同期され、そこから hub の ClusterProfile.status.properties に反映されます。この最後のステップによって、OCM は Cluster Inventory API の cluster manager として振る舞えるようになります。spoke に設定した property が、ベンダー非依存の API を通じて、hub 上のインベントリ情報として読めるようになるわけです。
この記事のコマンドは、次のバージョンで確認しています。
kind v0.31.0
clusteradm v1.2.1
helm v3.20.1
kubectl v1.35.3
前提条件
ローカル環境で、以下のコマンドが使える必要があります。
clusteradm が未インストールの場合は、次のコマンドでインストールします。
curl -L https://raw.githubusercontent.com/open-cluster-management-io/clusteradm/main/install.sh | bash
インストール済みツールを確認します。
kind version
helm version
kubectl version --client
clusteradm version
clusteradm version は、現在の kubeconfig context に接続してサーバーバージョンも取得しようとします。この時点では、client version が表示されることだけ確認できれば十分です。
この記事では、Kubernetes context 名として以下を使います。
export HUB_CTX=kind-hub
export C1_CTX=kind-cluster1
export C2_CTX=kind-cluster2
OCM リポジトリを clone する
OCM リポジトリには、ローカル開発環境を作るためのスクリプトが含まれています。この記事では solutions/setup-dev-environment/local-up.sh を使います。
git clone https://github.com/open-cluster-management-io/ocm.git
cd ocm
hub / cluster1 / cluster2 の kind クラスターを作成する
local-up.sh は、次のセットアップをまとめて実行します。
-
hub,cluster1,cluster2の kind クラスターを作成 -
clusteradm initで hub を初期化 -
clusteradm joinで spoke クラスターを登録 -
clusteradm acceptで join request を承認
./solutions/setup-dev-environment/local-up.sh
スクリプトが完了したら、hub から managed cluster を確認します。
kubectl config use-context kind-hub
kubectl get managedclusters --context "$HUB_CTX"
確認した環境では、次のように表示されました。
NAME HUB ACCEPTED MANAGED CLUSTER URLS JOINED AVAILABLE AGE
cluster1 true https://cluster1-control-plane:6443 True True 2m
cluster2 true https://cluster2-control-plane:6443 True True 2m
cluster1 と cluster2 のどちらも、JOINED と AVAILABLE が True になっています。ここが、hub から spoke を管理するための出発点です。
cluster-proxy addon をインストールする
cluster-proxy をインストールする理由は 2 つあります。1 つ目は、hub から spoke の API に到達できるようにするためです。2 つ目、そしてこの記事でより重要なのは、ClusterProfile.status.accessProviders に、Cluster Inventory API の利用者が実際に使える接続エンドポイントを載せるためです。
OCM の Helm リポジトリを追加します。
helm repo add ocm https://open-cluster-management.io/helm-charts/
helm repo update
ここでは、開発用の fleet として sandbox-fleet という ManagedClusterSet を作成し、cluster1 と cluster2 をそこへ追加します。この同じ fleet を、addon の配布先および ClusterProfile 作成対象として使います。
OCM では、cluster set の扱いに 2 種類のリソースを使います。
-
ManagedClusterSetは、どのManagedClusterがその set に所属するかを決める -
ManagedClusterSetBindingは、その set を特定の namespace から使えるようにする
sandbox-fleet では ExclusiveClusterSetLabel を使います。ManagedCluster に cluster.open-cluster-management.io/clusterset=sandbox-fleet という label が付いていれば、そのクラスターは sandbox-fleet に所属します。
既存の cluster set について。 環境によっては、OCM の
DefaultClusterSetfeature gate によって管理されるdefaultやglobalというManagedClusterSetがすでに存在することがあります。特にglobalは空の label selector を使うため、すべての managed cluster を選択し、sandbox-fleetと重なります。この記事では、曖昧さを避けるために、addon とClusterProfileの対象を一貫してsandbox-fleetにしています。
printf '%s\n' \
'apiVersion: cluster.open-cluster-management.io/v1beta2' \
'kind: ManagedClusterSet' \
'metadata:' \
' name: sandbox-fleet' \
'spec:' \
' clusterSelector:' \
' selectorType: ExclusiveClusterSetLabel' | \
kubectl apply --context "$HUB_CTX" -f -
kubectl label managedcluster cluster1 \
cluster.open-cluster-management.io/clusterset=sandbox-fleet \
--overwrite \
--context "$HUB_CTX"
kubectl label managedcluster cluster2 \
cluster.open-cluster-management.io/clusterset=sandbox-fleet \
--overwrite \
--context "$HUB_CTX"
cluster-proxy v0.10.0 では、ClusterProfile の access provider を設定するためのサポートが追加されました。今回必要なのはこの機能です。Part 2 で ClusterProfile を使った動的アクセスを行えるようにするため、この記事では v0.10.0 に固定します。
v0.10.0 で回避策が必要な理由
Helm リポジトリにある最新の
ocm/cluster-proxychart は v0.10.0 ですが、この chart には schema の不一致があります。ManagedProxyConfigurationにspec.proxyAgent.additionalValuesを出力する一方で、v0.10.0 の CRD schema にはその field が宣言されていません。そのため、そのままhelm installを実行すると次のエラーで失敗します。failed to create typed patch object (... ManagedProxyConfiguration): .spec.proxyAgent.additionalValues: field not declared in schemacluster-proxy の main branch では、Fix chart error. (#272) によってこの出力が削除されています。次の chart release が出れば、この回避策は不要になるはずです。
Helm v3 では
--post-rendererに実行ファイルのパスを渡せますが、Helm v4 では post-renderer が plugin に変わり、raw path を直接渡せなくなりました。両方に対応するため、この記事ではhelm template | kubectl applyを使い、問題の field をperlで削除します。
hub に cluster-proxy をインストールします。ClusterProfileAccessProvider と userServer.enabled を有効にすると、cluster-proxy の接続情報が ClusterProfile.status.accessProviders に入るようになります。つまり、インベントリと実際の spoke API アクセスをつなぐ橋渡しです。
kubectl create namespace open-cluster-management-addon \
--context "$HUB_CTX" \
--dry-run=client \
-o yaml | kubectl apply --context "$HUB_CTX" -f -
printf '%s\n' \
'apiVersion: cluster.open-cluster-management.io/v1beta2' \
'kind: ManagedClusterSetBinding' \
'metadata:' \
' name: sandbox-fleet' \
' namespace: open-cluster-management-addon' \
'spec:' \
' clusterSet: sandbox-fleet' | \
kubectl apply --context "$HUB_CTX" -f -
printf '%s\n' \
'apiVersion: cluster.open-cluster-management.io/v1beta1' \
'kind: Placement' \
'metadata:' \
' name: cluster-proxy-placement' \
' namespace: open-cluster-management-addon' \
'spec:' \
' clusterSets:' \
' - sandbox-fleet' | \
kubectl apply --context "$HUB_CTX" -f -
ManagedProxyConfiguration は、cluster-proxy chart が提供する CRD です。まず CRD を適用し、Established になるまで待ちます。CRD と CR を同じ kubectl apply stream で流すと、Kubernetes の discovery が新しい CRD をまだ認識できず、no matches for kind "ManagedProxyConfiguration" が返ることがあります。
helm show crds ocm/cluster-proxy --version 0.10.0 | \
kubectl apply --context "$HUB_CTX" -f -
kubectl wait --for=condition=Established \
crd/managedproxyconfigurations.proxy.open-cluster-management.io \
--context "$HUB_CTX" \
--timeout=120s
kubectl wait --for=condition=Established \
crd/managedproxyserviceresolvers.proxy.open-cluster-management.io \
--context "$HUB_CTX" \
--timeout=120s
helm template cluster-proxy ocm/cluster-proxy \
-n open-cluster-management-addon \
--version 0.10.0 \
--set installByPlacement.placementName=cluster-proxy-placement \
--set installByPlacement.placementNamespace=open-cluster-management-addon \
--set featureGates.clusterProfileAccessProvider=true \
--set userServer.enabled=true | \
perl -0pe 's/\n additionalValues:\n enableImpersonation: "[^"]+"//g' | \
kubectl apply --context "$HUB_CTX" -f -
ManagedProxyConfiguration/cluster-proxy と、clusteradm proxy が使う証明書 Secret が作成されるのを待ちます。
kubectl wait --for=create \
managedproxyconfiguration/cluster-proxy \
--context "$HUB_CTX" \
--timeout=120s
kubectl wait --for=create \
secret/proxy-client \
-n open-cluster-management-addon \
--context "$HUB_CTX" \
--timeout=120s
kubectl wait --for=create \
secret/proxy-server \
-n open-cluster-management-addon \
--context "$HUB_CTX" \
--timeout=120s
kubectl wait --for=create \
secret/agent-server \
-n open-cluster-management-addon \
--context "$HUB_CTX" \
--timeout=120s
addon の状態を確認します。
kubectl rollout status deployment/cluster-proxy-addon-manager \
-n open-cluster-management-addon \
--context "$HUB_CTX" \
--timeout=180s
kubectl rollout status deployment/cluster-proxy \
-n open-cluster-management-addon \
--context "$HUB_CTX" \
--timeout=180s
clusteradm get addon cluster-proxy --context "$HUB_CTX"
kubectl get managedclusteraddon -A --context "$HUB_CTX" | grep cluster-proxy
kubectl wait --for=condition=Available \
managedclusteraddon/cluster-proxy \
-n cluster1 \
--context "$HUB_CTX" \
--timeout=180s
kubectl wait --for=condition=Available \
managedclusteraddon/cluster-proxy \
-n cluster2 \
--context "$HUB_CTX" \
--timeout=180s
managed-serviceaccount addon をインストールする
clusteradm proxy kubectl が managed service account を使って spoke クラスターへアクセスできるように、managed-serviceaccount addon をインストールします。
managed-serviceaccount chart もデフォルトでは global cluster set を使います。また、それを変更する Helm value は用意されていません。そこで agentInstallAll=false を設定して自動配布を無効にし、対象クラスターに対して明示的に ManagedClusterAddOn リソースを作成します。
helm install \
--kube-context "$HUB_CTX" \
-n open-cluster-management-managed-serviceaccount \
--create-namespace \
managed-serviceaccount \
ocm/managed-serviceaccount \
--set agentInstallAll=false
printf '%s\n' \
'apiVersion: addon.open-cluster-management.io/v1alpha1' \
'kind: ManagedClusterAddOn' \
'metadata:' \
' name: managed-serviceaccount' \
' namespace: cluster1' \
'spec:' \
' installNamespace: open-cluster-management-managed-serviceaccount' | \
kubectl apply --context "$HUB_CTX" -f -
printf '%s\n' \
'apiVersion: addon.open-cluster-management.io/v1alpha1' \
'kind: ManagedClusterAddOn' \
'metadata:' \
' name: managed-serviceaccount' \
' namespace: cluster2' \
'spec:' \
' installNamespace: open-cluster-management-managed-serviceaccount' | \
kubectl apply --context "$HUB_CTX" -f -
この流れでは、ManagedClusterAddOn リソースを直接作成しています。sandbox-fleet 内のクラスターを確認し、addon の対象が fleet と合っていることを確認します。
kubectl get managedclusters \
--context "$HUB_CTX" \
-L cluster.open-cluster-management.io/clusterset
addon の状態を確認します。
kubectl rollout status deployment/managed-serviceaccount-addon-manager \
-n open-cluster-management-managed-serviceaccount \
--context "$HUB_CTX" \
--timeout=180s
clusteradm get addon managed-serviceaccount --context "$HUB_CTX"
kubectl get managedclusteraddon -A --context "$HUB_CTX" | grep managed-serviceaccount
kubectl wait --for=condition=Available \
managedclusteraddon/managed-serviceaccount \
-n cluster1 \
--context "$HUB_CTX" \
--timeout=180s
kubectl wait --for=condition=Available \
managedclusteraddon/managed-serviceaccount \
-n cluster2 \
--context "$HUB_CTX" \
--timeout=180s
proxy 経路のヘルスチェックを行います。
clusteradm proxy health --context "$HUB_CTX"
ManagedServiceAccount を作成する
次の 3 セクション、つまり「ManagedServiceAccount を作成する」「RBAC を spoke に配布する」「clusteradm proxy で spoke API にアクセスする」は、Cluster Inventory API そのものというより寄り道です。
ClusterProfile.status.accessProvidersに入る接続エンドポイントが、本当に到達可能かどうかを確認するために置いています。インベントリ情報だけが目的で、あとから controller でアクセスを扱うつもりなら、このあたりは流し読みして ClusterProfile feature gate を有効にする まで進んでもかまいません。
cluster1 向けに、test という名前の ManagedServiceAccount を作成します。
printf '%s\n' \
'apiVersion: authentication.open-cluster-management.io/v1beta1' \
'kind: ManagedServiceAccount' \
'metadata:' \
' name: test' \
' namespace: cluster1' \
'spec:' \
' rotation: {}' | \
kubectl apply --context "$HUB_CTX" -f -
作成されたことを確認します。
kubectl get managedserviceaccount -n cluster1 --context "$HUB_CTX"
managed service account 用の hub 側 Secret が作成されるのを待ちます。
kubectl wait --for=create \
secret/test \
-n cluster1 \
--context "$HUB_CTX" \
--timeout=120s
status condition を確認し、token Secret が報告されていることを確認します。
kubectl get managedserviceaccount test \
-n cluster1 \
--context "$HUB_CTX" \
-o jsonpath='{range .status.conditions[*]}{.type}={.status}{"\n"}{end}'
RBAC を spoke に配布する
spoke クラスター上では、ManagedServiceAccount は通常の Kubernetes ServiceAccount として作成されます。namespace は ManagedClusterAddOn.spec.installNamespace で指定した open-cluster-management-managed-serviceaccount です。
kubectl get serviceaccount test \
-n open-cluster-management-managed-serviceaccount \
--context "$C1_CTX"
この確認では cluster-admin を付与しています。本番環境では、対象 API に必要な Role または ClusterRole だけを付与してください。
printf '%s\n' \
'apiVersion: rbac.authorization.k8s.io/v1' \
'kind: ClusterRoleBinding' \
'metadata:' \
' name: managed-sa-test' \
'roleRef:' \
' apiGroup: rbac.authorization.k8s.io' \
' kind: ClusterRole' \
' name: cluster-admin' \
'subjects:' \
' - kind: ServiceAccount' \
' name: test' \
' namespace: open-cluster-management-managed-serviceaccount' \
> /tmp/clusterrolebinding-managed-sa-test.yaml
clusteradm create work を使って、RBAC を cluster1 に適用します。
clusteradm create work managed-sa-test-rbac \
-f /tmp/clusterrolebinding-managed-sa-test.yaml \
--clusters cluster1 \
--context "$HUB_CTX"
ManifestWork と spoke 側の RBAC を確認します。
kubectl get manifestwork -n cluster1 --context "$HUB_CTX"
kubectl wait --for=condition=Applied \
manifestwork/managed-sa-test-rbac \
-n cluster1 \
--context "$HUB_CTX" \
--timeout=60s
kubectl get clusterrolebinding managed-sa-test --context "$C1_CTX"
clusteradm proxy で spoke API にアクセスする
ここまで来たら、hub 側から clusteradm proxy kubectl を使って cluster1 の API にアクセスしてみます。
clusteradm proxy kubectl \
--context "$HUB_CTX" \
--cluster=cluster1 \
--sa=test \
--args="get nodes"
cluster1 の node が返ってくれば、cluster-proxy と managed-serviceaccount を通した hub-to-spoke の API アクセスは動作しています。
cluster2 にも同じ ManagedServiceAccount と RBAC を作成します。
printf '%s\n' \
'apiVersion: authentication.open-cluster-management.io/v1beta1' \
'kind: ManagedServiceAccount' \
'metadata:' \
' name: test' \
' namespace: cluster2' \
'spec:' \
' rotation: {}' | \
kubectl apply --context "$HUB_CTX" -f -
clusteradm create work managed-sa-test-rbac \
-f /tmp/clusterrolebinding-managed-sa-test.yaml \
--clusters cluster2 \
--context "$HUB_CTX"
kubectl wait --for=condition=Applied \
manifestwork/managed-sa-test-rbac \
-n cluster2 \
--context "$HUB_CTX" \
--timeout=60s
clusteradm proxy kubectl \
--context "$HUB_CTX" \
--cluster=cluster2 \
--sa=test \
--args="get nodes"
ClusterProfile feature gate を有効にする
ここからが本題です。Cluster Inventory API を有効にします。
ClusterProfile は hub 側の registration controller が扱います。OCM リポジトリでは、feature gate 名は ClusterProfile です。ClusterManager の registration feature gate を設定します。
まず、既存の feature gate を確認します。
kubectl get clustermanager cluster-manager \
--context "$HUB_CTX" \
-o jsonpath='{range .spec.registrationConfiguration.featureGates[*]}{.feature}={.mode}{"\n"}{end}'
kubectl patch clustermanager.operator.open-cluster-management.io cluster-manager \
--context "$HUB_CTX" \
--type=merge \
-p '{"spec":{"registrationConfiguration":{"featureGates":[{"feature":"ResourceCleanup","mode":"Enable"},{"feature":"ClusterProfile","mode":"Enable"}]}}}'
local-up.sh で確認した環境では、ResourceCleanup はすでに設定済みでした。そのため、この patch では ResourceCleanup を残しつつ ClusterProfile を追加しています。なお、この patch は featureGates 配列全体を置き換えます。もし環境内で追加の feature gate を明示的に設定している場合は、それらも同じ配列に含めてください。
local-up.sh の環境では、registration controller は open-cluster-management-hub namespace にある cluster-manager-registration-controller Deployment です。名前を確認し、rollout を待ちます。
kubectl get deployment \
-n open-cluster-management-hub \
--context "$HUB_CTX"
kubectl rollout status deployment/cluster-manager-registration-controller \
-n open-cluster-management-hub \
--context "$HUB_CTX" \
--timeout=180s
ClusterManager の設定を確認します。
kubectl get clustermanager cluster-manager \
--context "$HUB_CTX" \
-o yaml
ClusterProfile CRD が存在することを確認します。
sleep 10
kubectl wait --for=condition=Established \
crd/clusterprofiles.multicluster.x-k8s.io \
--context "$HUB_CTX" \
--timeout=120s
API resource も確認します。
kubectl api-resources --context "$HUB_CTX" | grep -i clusterprofile
API group は multicluster.x-k8s.io です。つまり SIG-Multicluster の Cluster Inventory API そのものです。ここから先、ClusterProfile に入る情報は OCM 固有のものではなく、upstream API を対象にしたツールから読める情報になります。
ManagedClusterSetBinding を作成する
ClusterProfile リソースは、ManagedClusterSetBinding がある namespace に作成されます。ここでは、先ほど作成した sandbox-fleet cluster set を使い、cluster-inventory namespace に bind します。
cluster.open-cluster-management.io/clusterset=sandbox-fleet という label により、クラスターは sandbox-fleet に入ります。ここで作成する ManagedClusterSetBinding は、cluster-inventory namespace から sandbox-fleet を参照できるようにします。ClusterProfile controller は、その namespace の binding を監視し、対象クラスターに対して ClusterProfile リソースを作成します。
まず、sandbox-fleet cluster set が存在し、cluster1 と cluster2 がそこに所属していることを確認します。
kubectl get managedclusterset sandbox-fleet --context "$HUB_CTX"
kubectl get managedclusters \
--context "$HUB_CTX" \
-L cluster.open-cluster-management.io/clusterset
ClusterProfile リソースが作成される namespace を作ります。
kubectl create namespace cluster-inventory \
--dry-run=client \
-o yaml | kubectl apply --context "$HUB_CTX" -f -
sandbox-fleet cluster set を cluster-inventory namespace に bind します。
printf '%s\n' \
'apiVersion: cluster.open-cluster-management.io/v1beta2' \
'kind: ManagedClusterSetBinding' \
'metadata:' \
' name: sandbox-fleet' \
' namespace: cluster-inventory' \
'spec:' \
' clusterSet: sandbox-fleet' | \
kubectl apply --context "$HUB_CTX" -f -
binding が Bound になるまで待ちます。
kubectl wait --for=condition=Bound \
managedclustersetbinding/sandbox-fleet \
-n cluster-inventory \
--context "$HUB_CTX" \
--timeout=60s
kubectl get managedclustersetbinding sandbox-fleet \
-n cluster-inventory \
--context "$HUB_CTX" \
-o yaml
condition に Bound=True が含まれていれば OK です。
ClusterProfile を確認する
ClusterProfile は、multicluster.x-k8s.io/v1alpha1 の namespaced resource です。ここまでの手順により、cluster-inventory namespace に作成されます。
kubectl wait --for=create \
clusterprofile/cluster1 \
-n cluster-inventory \
--context "$HUB_CTX" \
--timeout=120s
kubectl wait --for=create \
clusterprofile/cluster2 \
-n cluster-inventory \
--context "$HUB_CTX" \
--timeout=120s
kubectl get clusterprofiles \
-n cluster-inventory \
--context "$HUB_CTX"
cluster1 と cluster2 の ClusterProfile リソースが作成されます。
NAME AGE
cluster1 1m
cluster2 1m
この実装では、作成された ClusterProfile の spec.clusterManager.name は open-cluster-management、spec.displayName は managed cluster 名になります。jsonpath でその field を取り出します。
kubectl get clusterprofile cluster1 \
-n cluster-inventory \
--context "$HUB_CTX" \
-o jsonpath='{.spec.clusterManager.name}{"\n"}{.spec.displayName}{"\n"}'
status には、ManagedCluster から同期された Kubernetes version、cluster claim 由来の property、condition が入ります。
kubectl get clusterprofile cluster1 \
-n cluster-inventory \
--context "$HUB_CTX" \
-o jsonpath='{.status.version.kubernetes}{"\n"}'
kubectl get clusterprofile cluster1 \
-n cluster-inventory \
--context "$HUB_CTX" \
-o jsonpath='{range .status.properties[*]}{.name}={.value}{"\n"}{end}'
kubectl get clusterprofile cluster1 \
-n cluster-inventory \
--context "$HUB_CTX" \
-o jsonpath='{range .status.conditions[*]}{.type}={.status}{"\n"}{end}'
ClusterProperty を作成し、ClusterProfile に反映する
ClusterProfile.status.properties には、spoke クラスターの ClusterProperty リソースも反映されます。ClusterProperty は about.k8s.io/v1alpha1 の cluster-scoped resource です。これも SIG-Multicluster の API であり、spoke クラスターが自分自身に関する情報を宣言するために使います。たとえば region、account ID、environment など、本来 label に詰め込みがちな情報です。
ここがこの記事のいちばん大事なポイントです。spoke に設定した property が、custom glue code なしで hub のインベントリ項目まで流れてきます。
まず、spoke 側の Klusterlet で ClusterProperty feature gate を有効にします。local-up.sh ですでに有効になっている ClusterClaim と AddonManagement feature gate は残し、ClusterProperty を追加します。
kubectl patch klusterlet klusterlet \
--context "$C1_CTX" \
--type=merge \
-p '{"spec":{"registrationConfiguration":{"featureGates":[{"feature":"ClusterClaim","mode":"Enable"},{"feature":"ClusterProperty","mode":"Enable"},{"feature":"AddonManagement","mode":"Enable"}]}}}'
kubectl patch klusterlet klusterlet \
--context "$C2_CTX" \
--type=merge \
-p '{"spec":{"registrationConfiguration":{"featureGates":[{"feature":"ClusterClaim","mode":"Enable"},{"feature":"ClusterProperty","mode":"Enable"},{"feature":"AddonManagement","mode":"Enable"}]}}}'
ClusterProperty CRD と registration agent の rollout を待ちます。
kubectl wait --for=condition=Established \
crd/clusterproperties.about.k8s.io \
--context "$C1_CTX" \
--timeout=120s
kubectl wait --for=condition=Established \
crd/clusterproperties.about.k8s.io \
--context "$C2_CTX" \
--timeout=120s
kubectl rollout status deployment/klusterlet-registration-agent \
-n open-cluster-management-agent \
--context "$C1_CTX" \
--timeout=180s
kubectl rollout status deployment/klusterlet-registration-agent \
-n open-cluster-management-agent \
--context "$C2_CTX" \
--timeout=180s
AWS region と AWS account ID の例を、spoke クラスター上の ClusterProperty として作成します。ここで使っている値は、動作確認用のダミー値です。
printf '%s\n' \
'apiVersion: about.k8s.io/v1alpha1' \
'kind: ClusterProperty' \
'metadata:' \
' name: aws.region.example.com' \
'spec:' \
' value: ap-northeast-1' \
'---' \
'apiVersion: about.k8s.io/v1alpha1' \
'kind: ClusterProperty' \
'metadata:' \
' name: aws.account-id.example.com' \
'spec:' \
' value: "111122223333"' | \
kubectl apply --context "$C1_CTX" -f -
printf '%s\n' \
'apiVersion: about.k8s.io/v1alpha1' \
'kind: ClusterProperty' \
'metadata:' \
' name: aws.region.example.com' \
'spec:' \
' value: us-west-2' \
'---' \
'apiVersion: about.k8s.io/v1alpha1' \
'kind: ClusterProperty' \
'metadata:' \
' name: aws.account-id.example.com' \
'spec:' \
' value: "444455556666"' | \
kubectl apply --context "$C2_CTX" -f -
spoke クラスター上のリソースを確認します。
kubectl get clusterproperties --context "$C1_CTX"
kubectl get clusterproperties --context "$C2_CTX"
registration agent は、spoke 側の ClusterProperty リソースを hub 上の ManagedCluster.status.clusterClaims に同期します。
kubectl wait --for=jsonpath='{.status.clusterClaims[?(@.name=="aws.region.example.com")].value}'=ap-northeast-1 \
managedcluster/cluster1 \
--context "$HUB_CTX" \
--timeout=180s
kubectl wait --for=jsonpath='{.status.clusterClaims[?(@.name=="aws.account-id.example.com")].value}'=111122223333 \
managedcluster/cluster1 \
--context "$HUB_CTX" \
--timeout=180s
kubectl wait --for=jsonpath='{.status.clusterClaims[?(@.name=="aws.region.example.com")].value}'=us-west-2 \
managedcluster/cluster2 \
--context "$HUB_CTX" \
--timeout=180s
kubectl wait --for=jsonpath='{.status.clusterClaims[?(@.name=="aws.account-id.example.com")].value}'=444455556666 \
managedcluster/cluster2 \
--context "$HUB_CTX" \
--timeout=180s
その後、ManagedCluster.status.clusterClaims は ClusterProfile.status.properties に同期されます。
kubectl wait --for=jsonpath='{.status.properties[?(@.name=="aws.region.example.com")].value}'=ap-northeast-1 \
clusterprofile/cluster1 \
-n cluster-inventory \
--context "$HUB_CTX" \
--timeout=180s
kubectl wait --for=jsonpath='{.status.properties[?(@.name=="aws.account-id.example.com")].value}'=111122223333 \
clusterprofile/cluster1 \
-n cluster-inventory \
--context "$HUB_CTX" \
--timeout=180s
kubectl wait --for=jsonpath='{.status.properties[?(@.name=="aws.region.example.com")].value}'=us-west-2 \
clusterprofile/cluster2 \
-n cluster-inventory \
--context "$HUB_CTX" \
--timeout=180s
kubectl wait --for=jsonpath='{.status.properties[?(@.name=="aws.account-id.example.com")].value}'=444455556666 \
clusterprofile/cluster2 \
-n cluster-inventory \
--context "$HUB_CTX" \
--timeout=180s
ClusterProfile から AWS 関連の property だけを表示します。
printf 'cluster1\n'
kubectl get clusterprofile cluster1 \
-n cluster-inventory \
--context "$HUB_CTX" \
-o jsonpath='{range .status.properties[*]}{.name}={.value}{"\n"}{end}' | grep '^aws\.'
printf 'cluster2\n'
kubectl get clusterprofile cluster2 \
-n cluster-inventory \
--context "$HUB_CTX" \
-o jsonpath='{range .status.properties[*]}{.name}={.value}{"\n"}{end}' | grep '^aws\.'
この環境では、次のように表示されました。
cluster1
aws.account-id.example.com=111122223333
aws.region.example.com=ap-northeast-1
cluster2
aws.account-id.example.com=444455556666
aws.region.example.com=us-west-2
cluster-proxy v0.10.0 で ClusterProfileAccessProvider を有効にしているため、status.accessProviders には cluster-proxy user-server の接続情報も入ります。
kubectl wait --for=jsonpath='{.status.accessProviders[0].name}'=open-cluster-management \
clusterprofile/cluster1 \
-n cluster-inventory \
--context "$HUB_CTX" \
--timeout=120s
kubectl get clusterprofile cluster1 \
-n cluster-inventory \
--context "$HUB_CTX" \
-o jsonpath='{range .status.accessProviders[*]}{.name}{"\n"}{.cluster.server}{"\n"}{end}'
この環境では、次のように表示されました。
open-cluster-management
https://cluster-proxy-addon-user.open-cluster-management-addon:9092/cluster1
cluster1 の ClusterProfile 全体を確認します。
kubectl get clusterprofile cluster1 \
-n cluster-inventory \
--context "$HUB_CTX" \
-o yaml \
--show-managed-fields=false
この環境での YAML は次のとおりです。上のコマンドでは完全な値が表示されますが、ここでは見やすさのため、status.accessProviders[].cluster.certificate-authority-data を "<base64 CA bundle>" に置き換えています。
apiVersion: multicluster.x-k8s.io/v1alpha1
kind: ClusterProfile
metadata:
creationTimestamp: "2026-05-05T07:09:27Z"
generation: 1
labels:
multicluster.x-k8s.io/clusterset: sandbox-fleet
open-cluster-management.io/cluster-name: cluster1
x-k8s.io/cluster-manager: open-cluster-management
name: cluster1
namespace: cluster-inventory
resourceVersion: "4889"
uid: 29ec181b-7a42-4642-b957-d7057d756575
spec:
clusterManager:
name: open-cluster-management
displayName: cluster1
status:
accessProviders:
- cluster:
certificate-authority-data: "<base64 CA bundle>"
extensions:
- extension:
clusterName: cluster1
name: client.authentication.k8s.io/exec
server: https://cluster-proxy-addon-user.open-cluster-management-addon:9092/cluster1
name: open-cluster-management
conditions:
- lastTransitionTime: "2026-05-05T07:09:27Z"
message: Managed cluster is available
reason: ManagedClusterAvailable
status: "True"
type: ControlPlaneHealthy
- lastTransitionTime: "2026-05-05T07:09:27Z"
message: Managed cluster joined
reason: ManagedClusterJoined
status: "True"
type: Joined
properties:
- name: aws.account-id.example.com
value: "111122223333"
- name: aws.region.example.com
value: ap-northeast-1
version:
kubernetes: v1.35.0
この YAML から、いくつかのことが分かります。
-
metadata.labels["multicluster.x-k8s.io/clusterset"]により、このClusterProfileがsandbox-fleetから作成されたことが分かる -
spec.clusterManager.nameはopen-cluster-managementであり、このClusterProfileは OCM によって管理されている -
status.propertiesには、spoke 側のClusterPropertyリソースから来た値が入っている -
status.accessProviders[0].cluster.serverは、cluster-proxy user-server のクラスター固有エンドポイントになっている -
status.accessProviders[0].cluster.extensions[0].extension.clusterNameは、対象の spoke クラスター名を示している -
status.conditionsは、元のManagedClusterのAvailableとJoinedcondition を反映している
Cluster Inventory API の利用者が必要とする情報、つまり identity、property、Kubernetes version、condition、到達可能な endpoint と CA bundle が、この 1 つのリソースにまとまっています。これこそが ClusterProfile の狙いであり、OCM はそれをエンドツーエンドで実現しています。
clusteradm proxy と ClusterProfile を比較する
最後に、ClusterProfile のインベントリ表示と、実際の spoke API アクセスを比較してみます。
kubectl get clusterprofiles -n cluster-inventory --context "$HUB_CTX"
clusteradm proxy kubectl \
--context "$HUB_CTX" \
--cluster=cluster1 \
--sa=test \
--args="get ns"
ClusterProfile は hub 側のインベントリ表示です。一方、clusteradm proxy kubectl は、ClusterProfile.status.accessProviders が指している spoke API アクセス経路が実際に使えることを確認するものです。
次にやること
ここまでで、OCM を cluster manager とする Cluster Inventory API のセットアップができました。
- managed cluster ごとに
ClusterProfileリソースが自動作成される - spoke の
ClusterPropertyが hub のstatus.propertiesに反映される -
status.accessProvidersに、実際に到達可能な spoke endpoint が入る
Part 2 では、このインベントリに multicluster-runtime を向けて、cluster1 と cluster2 をまたいで reconcile する controller を書きます。クラスターの検出と接続には ClusterProfile だけを使います。ベンダー非依存のインベントリが本当に効いてくるのはここです。controller のコードには、OCM 固有の import は 1 つも入りません。
クリーンアップ
kind クラスターを削除します。
kind delete cluster --name hub
kind delete cluster --name cluster1
kind delete cluster --name cluster2