0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

OCM で Cluster Inventory API を始める(Part 1: ClusterProfile)

0
Posted at

この記事は 2 部構成の Part 1 です。
Part 2(近日公開): ClusterProfile を起点に、multicluster-runtime を使ってコントローラーから spoke クラスターへ接続します。

この記事で扱うこと

Cluster Inventory APImulticluster.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 側での ClusterProfile feature 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 が cluster1cluster2 を managed cluster として管理
  • managed cluster は sandbox-fleet という ManagedClusterSet に所属
  • spoke に cluster-proxymanaged-serviceaccount addon をインストール
  • ClusterProfile リソースは cluster-inventory namespace に作成
  • 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

cluster1cluster2 のどちらも、JOINEDAVAILABLETrue になっています。ここが、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 を作成し、cluster1cluster2 をそこへ追加します。この同じ fleet を、addon の配布先および ClusterProfile 作成対象として使います。

OCM では、cluster set の扱いに 2 種類のリソースを使います。

  • ManagedClusterSet は、どの ManagedCluster がその set に所属するかを決める
  • ManagedClusterSetBinding は、その set を特定の namespace から使えるようにする

sandbox-fleet では ExclusiveClusterSetLabel を使います。ManagedClustercluster.open-cluster-management.io/clusterset=sandbox-fleet という label が付いていれば、そのクラスターは sandbox-fleet に所属します。

既存の cluster set について。 環境によっては、OCM の DefaultClusterSet feature gate によって管理される defaultglobal という 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-proxy chart は v0.10.0 ですが、この chart には schema の不一致があります。ManagedProxyConfigurationspec.proxyAgent.additionalValues を出力する一方で、v0.10.0 の CRD schema にはその field が宣言されていません。そのため、そのまま helm install を実行すると次のエラーで失敗します。

failed to create typed patch object (... ManagedProxyConfiguration): .spec.proxyAgent.additionalValues: field not declared in schema

cluster-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 をインストールします。ClusterProfileAccessProvideruserServer.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-proxymanaged-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 が存在し、cluster1cluster2 がそこに所属していることを確認します。

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"

cluster1cluster2ClusterProfile リソースが作成されます。

NAME       AGE
cluster1   1m
cluster2   1m

この実装では、作成された ClusterProfilespec.clusterManager.nameopen-cluster-managementspec.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 リソースも反映されます。ClusterPropertyabout.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 ですでに有効になっている ClusterClaimAddonManagement 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.clusterClaimsClusterProfile.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

cluster1ClusterProfile 全体を確認します。

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"] により、この ClusterProfilesandbox-fleet から作成されたことが分かる
  • spec.clusterManager.nameopen-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 は、元の ManagedClusterAvailableJoined condition を反映している

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 を向けて、cluster1cluster2 をまたいで reconcile する controller を書きます。クラスターの検出と接続には ClusterProfile だけを使います。ベンダー非依存のインベントリが本当に効いてくるのはここです。controller のコードには、OCM 固有の import は 1 つも入りません。

クリーンアップ

kind クラスターを削除します。

kind delete cluster --name hub
kind delete cluster --name cluster1
kind delete cluster --name cluster2
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?