リソース配置のクラスターの順序を制御する

適用対象: ✔️ ハブ クラスターを含む Fleet Manager

Azure Kubernetes Fleet Manager の段階的更新実行は、ステージごとのプロセスを使用して、複数のメンバークラスターに Kubernetes ワークロードをデプロイするための制御されたアプローチを提供します。 リスクを最小限に抑えるために、このアプローチは、オプションの待機時間とステージ間の承認ゲートを使用して、対象となるクラスターに順番にデプロイされます。

この記事では、段階的な更新実行を作成して実行し、ワークロードを段階的にデプロイし、必要に応じて以前のバージョンにロールバックする方法について説明します。

Azure Kubernetes Fleet Manager では、段階的な更新の 2 つのスコープがサポートされています。

  • クラスター全体への適用: インフラストラクチャ レベルの変更を管理するフリート管理者は、ClusterStagedUpdateRunClusterResourcePlacement を併用します。
  • 名前空間スコープ: StagedUpdateRun は、特定の名前空間内でのロールアウトを管理するアプリケーションチームの ResourcePlacement と併用してください。

Important

ResourcePlacement では、 placement.kubernetes-fleet.io/v1beta1 API バージョンが使用され、現在プレビュー段階です。 ResourceSnapshotなど、この記事で示されている一部の機能も v1beta1 API の一部であり、v1 API では使用できません。

この記事の例では、タブを使用する両方の方法を示します。 デプロイ スコープに一致するタブを選択します。

開始する前に

Prerequisites

  • アクティブなサブスクリプションを持つAzure アカウントが必要です。 無料でアカウントを作成できます

  • この記事で使用される概念と用語を理解するには、 段階的なロールアウト戦略の概念の概要を参照してください。

  • この記事Azure CLI完了するには、バージョン 2.58.0 以降がインストールされている必要があります。 インストールまたはアップグレードするには、Azure CLI のインストールを参照してください。

  • Kubernetes CLI (kubectl) がまだインストールされていない場合は、次のコマンドを使用してインストールしてください。

    az aks install-cli
    
  • fleet Azure CLI 拡張子が必要です。 これは、次のコマンドを実行してインストールできます。

    az extension add --name fleet
    

    最新バージョンの拡張機能に更新するには、az extension update コマンドを実行します。

    az extension update --name fleet
    

デモ環境を構成する

このデモは、ハブ クラスターと 3 つのメンバー クラスターを持つ Fleet Manager で実行されます。 お持ちでない場合は、 クイックスタート に従って、ハブ クラスターを使用して Fleet Manager を作成します。 次に、Azure Kubernetes Service (AKS) クラスターをメンバーとして参加させます。

このチュートリアルでは、次のラベルを持つ 3 つのメンバー クラスターを含むデモフリート環境を使用して、段階的な更新実行を示します。

メンバー名 labels
member1 environment=canary, order=2
member2 environment=staging
member3 environment=canary,order=1

これらのラベルを使用すると、ステージを作成し、各ステージ内の配置順序を制御できます。

表示されたコマンドを使用して、メンバー クラスターにラベルを適用します。

az fleet member update \
    --resource-group $GROUP \
    --fleet-name $FLEET_NAME \
    --name member2 \
    --labels environment=staging

配置用に Kubernetes ワークロードを準備する

Kubernetes ワークロードをハブ クラスターに発行して、メンバー クラスターに配置できるようにします。

ハブ クラスターでワークロードの名前空間と ConfigMap を作成します。

kubectl create namespace test-namespace
kubectl create configmap test-cm --from-literal=key=value1 -n test-namespace

リソースをデプロイするには、 ClusterResourcePlacementを作成します。

Note

spec.strategy.typeExternalに設定され、ClusterStagedUpdateRunでロールアウトがトリガーされます。

apiVersion: placement.kubernetes-fleet.io/v1
kind: ClusterResourcePlacement
metadata:
  name: example-placement
spec:
  resourceSelectors:
    - group: ""
      kind: Namespace
      name: test-namespace
      version: v1
  policy:
    placementType: PickAll
  strategy:
    type: External

PickAll ポリシーを使用するため、3 つのクラスターはすべてスケジュールする必要がありますが、ClusterStagedUpdateRunを作成していないため、まだメンバー クラスターにリソースをデプロイする必要はありません。

配置がスケジュールされていることを確認します。

kubectl get clusterresourceplacement example-placement

出力は次の例のようになります。

NAME                GEN   SCHEDULED   SCHEDULED-GEN   AVAILABLE   AVAILABLE-GEN   AGE
example-placement   1     True        1                                           51s

段階的な更新戦略を作成する

ClusterStagedUpdateStrategyは、クラスターをステージにグループ化するオーケストレーション パターンを定義し、ロールアウト シーケンスを指定します。 ラベルによってメンバー クラスターが選択されます。 デモでは、ステージングとカナリアの 2 段階 から成るものを作成します:

apiVersion: placement.kubernetes-fleet.io/v1
kind: ClusterStagedUpdateStrategy
metadata:
  name: example-strategy
spec:
  stages:
    - name: staging
      labelSelector:
        matchLabels:
          environment: staging
      afterStageTasks:
        - type: TimedWait
          waitTime: 1m
      maxConcurrency: 1
    - name: canary
      labelSelector:
        matchLabels:
          environment: canary
      sortingLabelKey: order
      beforeStageTasks:
        - type: Approval
      maxConcurrency: 50%

リソース スナップショットの操作

フリートマネージャーは、配置に RollingUpdateのロールアウト戦略がある場合、リソースが変更されたときにリソーススナップショットを作成します。 各スナップショットには、リソースの特定のバージョンを参照するために使用できる一意のインデックスがあります。

配置で External ロールアウト戦略が使用されている場合、リソース スナップショットは自動的に作成されません。 代わりに、段階的な更新プログラムの実行を実行するときに作成されます。 つまり、 External ロールアウト戦略を使用して配置を最初に作成した場合、最初の段階的な更新プログラムの実行を実行するまでリソース スナップショットは存在しません。

Note

配置が以前に RollingUpdate 戦略を使用していて、 Externalに変更された場合、既存のリソース スナップショットは引き続き使用できます。 ステージング更新実行を作成するときに、これらの既存のスナップショットを参照できます。

Tip

リソース スナップショットとその動作の詳細については、 リソース スナップショットを参照してください。

現在のリソース スナップショットを確認する

ClusterResourcePlacementExternal戦略を使用するため、リソース スナップショットはまだ存在しません。 次のことを確認しましょう。

kubectl get clusterresourcesnapshots --show-labels

出力にはリソースが表示されないはずです。

No resources found

最初のリソース スナップショットを作成する

最初のリソース スナップショットを作成するには、ClusterStagedUpdateRun フィールドを省略してresourceSnapshotIndexを作成する必要があります。 更新実行コントローラーは、スナップショットが存在しないことを検出し、スナップショットを自動的に作成します。

apiVersion: placement.kubernetes-fleet.io/v1
kind: ClusterStagedUpdateRun
metadata:
  name: example-initial-run
spec:
  placementName: example-placement
  stagedRolloutStrategyName: example-strategy
  state: Run

更新プログラムの実行が完了したら、リソース スナップショットを確認します。

kubectl get clusterresourcesnapshots --show-labels

出力は次の例のようになります。

NAME                           GEN   AGE   LABELS
example-placement-0-snapshot   1     60s   kubernetes-fleet.io/is-latest-snapshot=true,kubernetes-fleet.io/parent-CRP=example-placement,kubernetes-fleet.io/resource-index=0

これで、スナップショットのバージョンが 1 つになりました。 最新の (kubernetes-fleet.io/is-latest-snapshot=true) であり、リソース インデックス 0 (kubernetes-fleet.io/resource-index=0) を持ちます。

新しいリソース スナップショットを作成する

次に、ConfigMap を新しい値で変更します。

kubectl edit configmap test-cm -n test-namespace

値を value1 から value2 に更新します。

kubectl get configmap test-cm -n test-namespace -o yaml

出力は次の例のようになります。

apiVersion: v1
data:
  key: value2 # value updated here, old value: value1
kind: ConfigMap
metadata:
  creationTimestamp: ...
  name: test-cm
  namespace: test-namespace
  resourceVersion: ...
  uid: ...

配置では External 戦略が使用されるため、新しいリソース スナップショットは自動的に作成されません。 新しいスナップショットの作成をトリガーするために、ClusterStagedUpdateRun フィールドを省略して別のresourceSnapshotIndexを作成します。

apiVersion: placement.kubernetes-fleet.io/v1
kind: ClusterStagedUpdateRun
metadata:
  name: example-snapshot-run
spec:
  placementName: example-placement
  stagedRolloutStrategyName: example-strategy
  state: Run

更新プログラムの実行が完了すると、それぞれインデックス 0 と 1 の 2 つのバージョンのリソース スナップショットが表示されます。最新のバージョンはインデックス 1 です。

kubectl get clusterresourcesnapshots --show-labels

出力は次の例のようになります。

NAME                           GEN   AGE    LABELS
example-placement-0-snapshot   1     2m6s   kubernetes-fleet.io/is-latest-snapshot=false,kubernetes-fleet.io/parent-CRP=example-placement,kubernetes-fleet.io/resource-index=0
example-placement-1-snapshot   1     10s    kubernetes-fleet.io/is-latest-snapshot=true,kubernetes-fleet.io/parent-CRP=example-placement,kubernetes-fleet.io/resource-index=1

最新のラベルは、最新の ConfigMap データを含む example-placement-1-snapshot に設定されます。

kubectl get clusterresourcesnapshots example-placement-1-snapshot -o yaml

出力は次の例のようになります。

apiVersion: placement.kubernetes-fleet.io/v1
kind: ClusterResourceSnapshot
metadata:
  annotations:
    kubernetes-fleet.io/number-of-enveloped-object: "0"
    kubernetes-fleet.io/number-of-resource-snapshots: "1"
    kubernetes-fleet.io/resource-hash: 10dd7a3d1e5f9849afe956cfbac080a60671ad771e9bda7dd34415f867c75648
  creationTimestamp: "2025-07-22T21:26:54Z"
  generation: 1
  labels:
    kubernetes-fleet.io/is-latest-snapshot: "true"
    kubernetes-fleet.io/parent-CRP: example-placement
    kubernetes-fleet.io/resource-index: "1"
  name: example-placement-1-snapshot
  ownerReferences:
  - apiVersion: placement.kubernetes-fleet.io/v1
    blockOwnerDeletion: true
    controller: true
    kind: ClusterResourcePlacement
    name: example-placement
    uid: e7d59513-b3b6-4904-864a-c70678fd6f65
  resourceVersion: "19994"
  uid: 79ca0bdc-0b0a-4c40-b136-7f701e85cdb6
spec:
  selectedResources:
  - apiVersion: v1
    kind: Namespace
    metadata:
      labels:
        kubernetes.io/metadata.name: test-namespace
      name: test-namespace
    spec:
      finalizers:
      - kubernetes
  - apiVersion: v1
    data:
      key: value2 # latest value: value2, old value: value1
    kind: ConfigMap
    metadata:
      name: test-cm
      namespace: test-namespace

ロールアウトの変更に合わせて段階的な更新プログラムの実行を準備する

ClusterStagedUpdateRunは、ClusterResourcePlacementに従ってClusterStagedUpdateStrategyのロールアウトを実行します。 ClusterResourcePlacement (CRP) のステージング更新実行をトリガーするために、CRP 名、updateRun 戦略名、最新のリソース スナップショット インデックス ("1")、状態を "Initialize" として指定する ClusterStagedUpdateRun を作成します。

Note

Externalロールアウト戦略を使用する場合は、最新のリソースをデプロイする場合は、resourceSnapshotIndex フィールドを省略できます。 更新実行コントローラーでは、 resourceSnapshotIndex を省略すると、新しいリソース スナップショットが自動的に作成されます。

apiVersion: placement.kubernetes-fleet.io/v1
kind: ClusterStagedUpdateRun
metadata:
  name: example-run
spec:
  placementName: example-placement
  resourceSnapshotIndex: "1"
  stagedRolloutStrategyName: example-strategy
  state: Initialize

ステージングされた更新プログラムの実行は初期化されますが、実行されていません。

kubectl get clusterstagedupdaterrun example-run

出力は次の例のようになります。

NAME          PLACEMENT           RESOURCE-SNAPSHOT-INDEX   POLICY-SNAPSHOT-INDEX   INITIALIZED   SUCCEEDED   AGE
example-run   example-placement   1                         0                       True                      7s

段階的な更新プログラムの実行を開始する

段階的な更新プログラムの実行を開始するには、仕様の state フィールドに修正プログラムを適用して Runする必要があります。

kubectl patch clusterstagedupdaterrun example-run --type merge -p '{"spec":{"state":"Run"}}'

Note

最初にstateに設定されたRun フィールドを使用して更新プログラムの実行を作成することもできます。このフィールドは、更新プログラムの実行を 1 回の手順で初期化して開始します。

ステージングされた更新プログラムの実行が初期化され、実行されます。

kubectl get clusterstagedupdaterrun example-run

出力は次の例のようになります。

NAME          PLACEMENT           RESOURCE-SNAPSHOT-INDEX   POLICY-SNAPSHOT-INDEX   INITIALIZED   SUCCEEDED   AGE
example-run   example-placement   1                         0                       True                      7s

1 分間の TimedWait が経過した後の状態の詳細を次に示します。

kubectl get clusterstagedupdaterrun example-run -o yaml

出力は次の例のようになります。

apiVersion: placement.kubernetes-fleet.io/v1
kind: ClusterStagedUpdateRun
metadata:
  ...
  name: example-run
  ...
spec:
  placementName: example-placement
  resourceSnapshotIndex: "1"
  stagedRolloutStrategyName: example-strategy
  state: Run
status:
  conditions:
  - lastTransitionTime: "2025-07-22T21:28:08Z"
    message: ClusterStagedUpdateRun initialized successfully
    observedGeneration: 2
    reason: UpdateRunInitializedSuccessfully
    status: "True" # the updateRun is initialized successfully
    type: Initialized
  - lastTransitionTime: "2025-07-22T21:29:53Z"
    message: The updateRun is waiting for after-stage tasks in stage canary to complete
    observedGeneration: 2
    reason: UpdateRunWaiting
    status: "False" # the updateRun is still progressing and waiting for approval
    type: Progressing
  deletionStageStatus:
    clusters: [] # no clusters need to be cleaned up
    stageName: kubernetes-fleet.io/deleteStage
  policyObservedClusterCount: 3 # number of clusters to be updated
  policySnapshotIndexUsed: "0"
  resourceSnapshotIndexUsed: "1"
  stagedUpdateStrategySnapshot: # snapshot of the strategy used for this update run
    stages:
    - afterStageTasks:
      - type: TimedWait
        waitTime: 1m0s
      labelSelector:
        matchLabels:
          environment: staging
      maxConcurrency: 1
      name: staging
    - beforeStageTasks:
      - type: Approval
      labelSelector:
        matchLabels:
          environment: canary
      maxConcurrency: 50%
      name: canary
      sortingLabelKey: order
  stagesStatus: # detailed status for each stage
  - afterStageTaskStatus:
    - conditions:
      - lastTransitionTime: "2025-07-22T21:29:23Z"
        message: Wait time elapsed
        observedGeneration: 2
        reason: StageTaskWaitTimeElapsed
        status: "True" # the wait after-stage task has completed
        type: WaitTimeElapsed
      type: TimedWait
    clusters:
    - clusterName: member2 # stage staging contains member2 cluster only
      conditions:
      - lastTransitionTime: "2025-07-22T21:28:08Z"
        message: Cluster update started
        observedGeneration: 2
        reason: ClusterUpdatingStarted
        status: "True"
        type: Started
      - lastTransitionTime: "2025-07-22T21:28:23Z"
        message: Cluster update completed successfully
        observedGeneration: 2
        reason: ClusterUpdatingSucceeded
        status: "True" # member2 is updated successfully
        type: Succeeded
    conditions:
    - lastTransitionTime: "2025-07-22T21:28:23Z"
      message: All clusters in the stage are updated and after-stage tasks are completed
      observedGeneration: 2
      reason: StageUpdatingSucceeded
      status: "False"
      type: Progressing
    - lastTransitionTime: "2025-07-22T21:29:23Z"
      message: Stage update completed successfully
      observedGeneration: 2
      reason: StageUpdatingSucceeded
      status: "True" # stage staging has completed successfully
      type: Succeeded
    endTime: "2025-07-22T21:29:23Z"
    stageName: staging
    startTime: "2025-07-22T21:28:08Z"
  - beforeStageTaskStatus:
    - approvalRequestName: example-run-before-canary # ClusterApprovalRequest name for this stage for before stage task
      conditions:
      - lastTransitionTime: "2025-07-22T21:29:53Z"
        message: ClusterApprovalRequest is created
        observedGeneration: 2
        reason: StageTaskApprovalRequestCreated
        status: "True"
        type: ApprovalRequestCreated
      type: Approval
    conditions:
    - lastTransitionTime: "2025-07-22T21:29:53Z"
      message: Not all before-stage tasks are completed, waiting for approval
      observedGeneration: 2
      reason: StageUpdatingWaiting
      status: "False" # stage canary is waiting for approval task completion
      type: Progressing
    stageName: canary
    startTime: "2025-07-22T21:29:23Z"

ステージング ステージの TimedWait 期間が経過し、カナリア ステージの承認タスクの ClusterApprovalRequest オブジェクトが作成されたことがわかります。 生成された ClusterApprovalRequest を確認し、まだ誰も承認していないことがわかります

kubectl get clusterapprovalrequest

出力は次の例のようになります。

NAME                        UPDATE-RUN    STAGE    APPROVED   APPROVALACCEPTED   AGE
example-run-before-canary   example-run   canary                                 2m39s

段階的な更新プログラムの実行を承認する

json パッチ ファイルを作成して適用することで、 ClusterApprovalRequest を承認できます。

cat << EOF > approval.json
"status": {
    "conditions": [
        {
            "lastTransitionTime": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
            "message": "lgtm",
            "observedGeneration": 1,
            "reason": "testPassed",
            "status": "True",
            "type": "Approved"
        }
    ]
}
EOF

注: observedGeneration が承認オブジェクトの生成と一致していることを確認してください。

作成された JSON ファイルを使用して、承認するパッチ要求を送信します。

kubectl patch clusterapprovalrequests example-run-before-canary --type='merge' --subresource=status --patch-file approval.json

次に、要求が承認されたことを確認します。

kubectl get clusterapprovalrequest

出力は次の例のようになります。

NAME                        UPDATE-RUN    STAGE    APPROVED   APPROVALACCEPTED   AGE
example-run-before-canary   example-run   canary   True       True               3m35s

現在、ClusterStagedUpdateRun は処理を進めて完了できます。

kubectl get clusterstagedupdaterrun example-run

出力は次の例のようになります。

NAME          PLACEMENT           RESOURCE-SNAPSHOT-INDEX   POLICY-SNAPSHOT-INDEX   INITIALIZED   SUCCEEDED   AGE
example-run   example-placement   1                         0                       True          True        5m28s

ロールアウトの完了を確認する

ClusterResourcePlacementには、ロールアウトが完了し、リソースがすべてのメンバー クラスターで使用できることも示されています。

kubectl get clusterresourceplacement example-placement

出力は次の例のようになります。

NAME                GEN   SCHEDULED   SCHEDULED-GEN   AVAILABLE   AVAILABLE-GEN   AGE
example-placement   1     True        1               True        1               8m55s

ConfigMap test-cm は、最新のデータを使用して、3 つのメンバー クラスターすべてにデプロイする必要があります。

apiVersion: v1
data:
  key: value2
kind: ConfigMap
metadata:
  ...
  name: test-cm
  namespace: test-namespace
  ...

段階的な更新プログラムの実行を停止する

実行中のクラスター ステージング更新実行の実行を停止するには、仕様の state フィールドに修正プログラムを適用して Stopする必要があります。 このアクションにより、更新プログラムの実行が正常に停止され、進行中のクラスターはロールアウト プロセス全体を停止する前に更新を完了できます。

kubectl patch clusterstagedupdaterun example-run --type merge -p '{"spec":{"state":"Stop"}}'

ステージングされた更新プログラムの実行が初期化され、実行されなくなりました。

kubectl get clusterstagedupdaterun example-run

出力は次の例のようになります。

NAME          PLACEMENT           RESOURCE-SNAPSHOT-INDEX   POLICY-SNAPSHOT-INDEX   INITIALIZED   SUCCEEDED   AGE
example-run   example-placement   1                         0                       True                      7s

更新プログラムの実行が停止した後の状態の詳細を次に示します。

kubectl get clusterstagedupdaterun example-run -o yaml

出力は次の例のようになります。

apiVersion: placement.kubernetes-fleet.io/v1
kind: ClusterStagedUpdateRun
metadata:
  ...
  name: example-run
  ...
spec:
  placementName: example-placement
  resourceSnapshotIndex: "1"
  stagedRolloutStrategyName: example-strategy
  state: Stop
status:
  conditions:
  - lastTransitionTime: "2025-07-22T21:28:08Z"
    message: ClusterStagedUpdateRun initialized successfully
    observedGeneration: 3
    reason: UpdateRunInitializedSuccessfully
    status: "True" # the updateRun is initialized successfully
    type: Initialized
  - lastTransitionTime: "2025-07-22T21:28:23Z"
    message: The update run has been stopped
    observedGeneration: 3
    reason: UpdateRunStopped
    status: "False" # the updateRun has stopped progressing
    type: Progressing
  deletionStageStatus:
    clusters: [] # no clusters need to be cleaned up
    stageName: kubernetes-fleet.io/deleteStage
  policyObservedClusterCount: 3 # number of clusters to be updated
  policySnapshotIndexUsed: "0"
  resourceSnapshotIndexUsed: "1"
  stagedUpdateStrategySnapshot: # snapshot of the strategy used for this update run
    stages:
    - afterStageTasks:
      - type: TimedWait
        waitTime: 1m0s
      labelSelector:
        matchLabels:
          environment: staging
      maxConcurrency: 1
      name: staging
    - beforeStageTasks:
      - type: Approval
      labelSelector:
        matchLabels:
          environment: canary
      maxConcurrency: 50%
      name: canary
      sortingLabelKey: order
  stagesStatus: # detailed status for each stage
  - clusters:
    - clusterName: member2 # stage staging contains member2 cluster only
      conditions:
      - lastTransitionTime: "2025-07-22T21:28:08Z"
        message: Cluster update started
        observedGeneration: 3
        reason: ClusterUpdatingStarted
        status: "True"
        type: Started
      - lastTransitionTime: "2025-07-22T21:28:23Z"
        message: Cluster update completed successfully
        observedGeneration: 3
        reason: ClusterUpdatingSucceeded
        status: "True" # member2 is updated successfully
        type: Succeeded
    conditions:
    - lastTransitionTime: "2025-07-22T21:28:23Z"
      message: All the updating clusters have finished updating, the stage is now stopped, waiting to be resumed
      observedGeneration: 3
      reason: StageUpdatingStopped
      status: "False"
      type: Progressing
    stageName: staging
    startTime: "2025-07-22T21:28:08Z"

以前のバージョンにロールバックする 2 番目の段階的な更新プログラムの実行をデプロイする

ワークロード管理者が ConfigMap の変更をロールバックし、 value2 値を value1に戻したいとします。 ハブから ConfigMap を手動で更新する代わりに、コンテキストで以前のリソース スナップショット インデックス "0" を使用して新しい ClusterStagedUpdateRun を作成し、同じ戦略を再利用できます。

apiVersion: placement.kubernetes-fleet.io/v1
kind: ClusterStagedUpdateRun
metadata:
  name: example-run-2
spec:
  placementName: example-placement
  resourceSnapshotIndex: "0"
  stagedRolloutStrategyName: example-strategy
  state: Run

新しい ClusterStagedUpdateRunを確認してみましょう。

kubectl get clusterstagedupdaterun

出力は次の例のようになります。

NAME            PLACEMENT           RESOURCE-SNAPSHOT-INDEX   POLICY-SNAPSHOT-INDEX   INITIALIZED   SUCCEEDED   AGE
example-run     example-placement   1                         0                       True          True        13m
example-run-2   example-placement   0                         0                       True                      9s

1 分間のTimedWaitが経過すると、新しいClusterApprovalRequest用に作成されたClusterStagedUpdateRun オブジェクトが表示されます。

kubectl get clusterapprovalrequest

出力は次の例のようになります。

NAME                          UPDATE-RUN      STAGE    APPROVED   APPROVALACCEPTED   AGE
example-run-2-before-canary   example-run-2   canary                                 75s
example-run-before-canary     example-run     canary   True       True               14m

新しい ClusterApprovalRequest オブジェクトを承認するには、同じ approval.json ファイルを再利用して修正プログラムを適用しましょう。

kubectl patch clusterapprovalrequests example-run-2-before-canary --type='merge' --subresource=status --patch-file approval.json

新しいオブジェクトが承認されているかどうかを確認します。

kubectl get clusterapprovalrequest                                                                            

出力は次の例のようになります。

NAME                          UPDATE-RUN      STAGE    APPROVED   APPROVALACCEPTED   AGE
example-run-2-before-canary   example-run-2   canary   True       True               2m7s
example-run-before-canary     example-run     canary   True       True               15m

ConfigMap test-cm は、次の 3 つのメンバー クラスターすべてにデプロイされ、データは value1に戻されます。

apiVersion: v1
data:
  key: value1
kind: ConfigMap
metadata:
  ...
  name: test-cm
  namespace: test-namespace
  ...

リソースをクリーンアップする

このチュートリアルが完了したら、作成したリソースをクリーンアップできます。

kubectl delete clusterstagedupdaterun example-run example-run-2
kubectl delete clusterstagedupdatestrategy example-strategy
kubectl delete clusterresourceplacement example-placement
kubectl delete namespace test-namespace

配置用に Kubernetes ワークロードを準備する

Kubernetes ワークロードをハブ クラスターに発行して、メンバー クラスターに配置できるようにします。

ハブ クラスターでワークロードの名前空間と ConfigMap を作成します。

kubectl create namespace test-namespace
kubectl create configmap test-cm --from-literal=key=value1 -n test-namespace

ResourcePlacementは名前空間スコープであるため、まず、選択スコープのClusterResourcePlacementを指定して、NamespaceOnlyを使用して名前空間をすべてのメンバー クラスターにデプロイします。

apiVersion: placement.kubernetes-fleet.io/v1
kind: ClusterResourcePlacement
metadata:
  name: test-namespace-placement
spec:
  resourceSelectors:
    - group: ""
      kind: Namespace
      name: test-namespace
      version: v1
      selectionScope: NamespaceOnly
  policy:
    placementType: PickAll

名前空間がすべてのメンバー クラスターにデプロイされていることを確認します。

kubectl get clusterresourceplacement test-namespace-placement

出力は次の例のようになります。

NAME                       GEN   SCHEDULED   SCHEDULED-GEN   AVAILABLE   AVAILABLE-GEN   AGE
test-namespace-placement   1     True        1               True        1               30s

ConfigMap をデプロイするには、名前空間スコープの ResourcePlacementを作成します。

Note

spec.strategy.typeExternalに設定され、StagedUpdateRunでロールアウトがトリガーされます。

apiVersion: placement.kubernetes-fleet.io/v1beta1
kind: ResourcePlacement
metadata:
  name: example-placement
  namespace: test-namespace
spec:
  resourceSelectors:
    - group: ""
      kind: ConfigMap
      name: test-cm
      version: v1
  policy:
    placementType: PickAll
  strategy:
    type: External

PickAll ポリシーを使用するため、3 つのクラスターはすべてスケジュールする必要がありますが、StagedUpdateRunを作成していないため、ConfigMap をメンバー クラスターにまだデプロイしないでください。

配置がスケジュールされていることを確認します。

kubectl get resourceplacement example-placement -n test-namespace

出力は次の例のようになります。

NAME                GEN   SCHEDULED   SCHEDULED-GEN   AVAILABLE   AVAILABLE-GEN   AGE
example-placement   1     True        1                                           51s

段階的な更新戦略を作成する

StagedUpdateStrategyは、クラスターをステージにグループ化するオーケストレーション パターンを定義し、ロールアウト シーケンスを指定します。 ラベルによってメンバー クラスターが選択されます。 デモでは、ステージングとカナリアの 2 段階 から成るものを作成します:

apiVersion: placement.kubernetes-fleet.io/v1
kind: StagedUpdateStrategy
metadata:
  name: example-strategy
  namespace: test-namespace
spec:
  stages:
    - name: staging
      labelSelector:
        matchLabels:
          environment: staging
      afterStageTasks:
        - type: TimedWait
          waitTime: 1m
      maxConcurrency: 1
    - name: canary
      labelSelector:
        matchLabels:
          environment: canary
      sortingLabelKey: order
      beforeStageTasks:
        - type: Approval
      maxConcurrency: 50%

リソース スナップショットの操作

フリートマネージャーは、配置に RollingUpdateのロールアウト戦略がある場合、リソースが変更されたときにリソーススナップショットを作成します。 各スナップショットには、リソースの特定のバージョンを参照するために使用できる一意のインデックスがあります。

配置で External ロールアウト戦略が使用されている場合、リソース スナップショットは自動的に作成されません。 代わりに、段階的な更新プログラムの実行を実行するときに作成されます。 つまり、 External ロールアウト戦略を使用して配置を最初に作成した場合、最初の段階的な更新プログラムの実行を実行するまでリソース スナップショットは存在しません。

Note

配置が以前に RollingUpdate 戦略を使用していて、 Externalに変更された場合、既存のリソース スナップショットは引き続き使用できます。 ステージング更新実行を作成するときに、これらの既存のスナップショットを参照できます。

Tip

リソース スナップショットとその動作の詳細については、 リソース スナップショットを参照してください。

現在のリソース スナップショットを確認する

ResourcePlacementExternal戦略を使用するため、リソース スナップショットはまだ存在しません。 次のことを確認しましょう。

kubectl get resourcesnapshots -n test-namespace --show-labels

出力にはリソースが表示されないはずです。

No resources found in test-namespace namespace.

最初のリソース スナップショットを作成する

最初のリソース スナップショットを作成するには、StagedUpdateRun フィールドを省略してresourceSnapshotIndexを作成する必要があります。 更新実行コントローラーは、スナップショットが存在しないことを検出し、スナップショットを自動的に作成します。

apiVersion: placement.kubernetes-fleet.io/v1
kind: StagedUpdateRun
metadata:
  name: example-initial-run
  namespace: test-namespace
spec:
  placementName: example-placement
  stagedRolloutStrategyName: example-strategy
  state: Run

更新プログラムの実行が完了したら、リソース スナップショットを確認します。

kubectl get resourcesnapshots -n test-namespace --show-labels

出力は次の例のようになります。

NAME                           GEN   AGE   LABELS
example-placement-0-snapshot   1     60s   kubernetes-fleet.io/is-latest-snapshot=true,kubernetes-fleet.io/parent-CRP=example-placement,kubernetes-fleet.io/resource-index=0

これで、スナップショットのバージョンが 1 つになりました。 最新の (kubernetes-fleet.io/is-latest-snapshot=true) であり、リソース インデックス 0 (kubernetes-fleet.io/resource-index=0) を持ちます。

新しいリソース スナップショットを作成する

次に、ConfigMap を新しい値で変更します。

kubectl edit configmap test-cm -n test-namespace

値を value1 から value2 に更新します。

kubectl get configmap test-cm -n test-namespace -o yaml

出力は次の例のようになります。

apiVersion: v1
data:
  key: value2 # value updated here, old value: value1
kind: ConfigMap
metadata:
  creationTimestamp: ...
  name: test-cm
  namespace: test-namespace
  resourceVersion: ...
  uid: ...

配置では External 戦略が使用されるため、新しいリソース スナップショットは自動的に作成されません。 新しいスナップショットの作成をトリガーするために、StagedUpdateRun フィールドを省略して別のresourceSnapshotIndexを作成します。

apiVersion: placement.kubernetes-fleet.io/v1
kind: StagedUpdateRun
metadata:
  name: example-snapshot-run
  namespace: test-namespace
spec:
  placementName: example-placement
  stagedRolloutStrategyName: example-strategy
  state: Run

更新プログラムの実行が完了すると、それぞれインデックス 0 と 1 の 2 つのバージョンのリソース スナップショットが表示されます。

kubectl get resourcesnapshots -n test-namespace --show-labels

出力は次の例のようになります。

NAME                           GEN   AGE    LABELS
example-placement-0-snapshot   1     2m6s   kubernetes-fleet.io/is-latest-snapshot=false,kubernetes-fleet.io/parent-CRP=example-placement,kubernetes-fleet.io/resource-index=0
example-placement-1-snapshot   1     10s    kubernetes-fleet.io/is-latest-snapshot=true,kubernetes-fleet.io/parent-CRP=example-placement,kubernetes-fleet.io/resource-index=1

最新のラベルは、最新の ConfigMap データを含む example-placement-1-snapshot に設定されます。

kubectl get resourcesnapshots example-placement-1-snapshot -n test-namespace -o yaml

出力は次の例のようになります。

apiVersion: placement.kubernetes-fleet.io/v1beta1
kind: ResourceSnapshot
metadata:
  annotations:
    kubernetes-fleet.io/number-of-enveloped-object: "0"
    kubernetes-fleet.io/number-of-resource-snapshots: "1"
    kubernetes-fleet.io/resource-hash: 10dd7a3d1e5f9849afe956cfbac080a60671ad771e9bda7dd34415f867c75648
  creationTimestamp: "2025-07-22T21:26:54Z"
  generation: 1
  labels:
    kubernetes-fleet.io/is-latest-snapshot: "true"
    kubernetes-fleet.io/parent-CRP: example-placement
    kubernetes-fleet.io/resource-index: "1"
  name: example-placement-1-snapshot
  namespace: test-namespace
  ownerReferences:
  - apiVersion: placement.kubernetes-fleet.io/v1beta1
    blockOwnerDeletion: true
    controller: true
    kind: ResourcePlacement
    name: example-placement
    uid: e7d59513-b3b6-4904-864a-c70678fd6f65
  resourceVersion: "19994"
  uid: 79ca0bdc-0b0a-4c40-b136-7f701e85cdb6
spec:
  selectedResources:
  - apiVersion: v1
    data:
      key: value2 # latest value: value2, old value: value1
    kind: ConfigMap
    metadata:
      name: test-cm
      namespace: test-namespace

ロールアウトの変更に合わせて段階的な更新プログラムの実行を準備する

StagedUpdateRunは、ResourcePlacementに従ってStagedUpdateStrategyのロールアウトを実行します。 ResourcePlacement (RP) の段階的な更新実行をトリガーするために、RP 名、updateRun 戦略名、最新のリソース スナップショット インデックス ("1")、状態を "Initialize" として指定する StagedUpdateRun を作成します。

Note

Externalロールアウト戦略を使用する場合は、最新のリソースをデプロイする場合は、resourceSnapshotIndex フィールドを省略できます。 更新実行コントローラーでは、 resourceSnapshotIndex を省略すると、新しいリソース スナップショットが自動的に作成されます。

apiVersion: placement.kubernetes-fleet.io/v1
kind: StagedUpdateRun
metadata:
  name: example-run
  namespace: test-namespace
spec:
  placementName: example-placement
  resourceSnapshotIndex: "1"
  stagedRolloutStrategyName: example-strategy
  state: Initialize

ステージングされた更新プログラムの実行は初期化されますが、実行されていません。

kubectl get stagedupdaterrun example-run -n test-namespace

出力は次の例のようになります。

NAME          PLACEMENT           RESOURCE-SNAPSHOT-INDEX   POLICY-SNAPSHOT-INDEX   INITIALIZED   SUCCEEDED   AGE
example-run   example-placement   1                         0                       True                      7s

段階的な更新プログラムの実行を開始する

段階的な更新プログラムの実行を開始するには、仕様の state フィールドに修正プログラムを適用して Runする必要があります。

kubectl patch stagedupdaterrun example-run -n test-namespace --type merge -p '{"spec":{"state":"Run"}}'

Note

最初にstateに設定されたRun フィールドを使用して更新プログラムの実行を作成することもできます。このフィールドは、更新プログラムの実行を 1 回の手順で初期化して開始します。

ステージングされた更新プログラムの実行が初期化され、実行されます。

kubectl get stagedupdaterrun example-run -n test-namespace

出力は次の例のようになります。

NAME          PLACEMENT           RESOURCE-SNAPSHOT-INDEX   POLICY-SNAPSHOT-INDEX   INITIALIZED   SUCCEEDED   AGE
example-run   example-placement   1                         0                       True                      7s

1 分間の TimedWait が経過したら、承認要求を確認します。

kubectl get approvalrequests -n test-namespace

出力は次の例のようになります。

NAME                        STAGED-UPDATE-RUN   STAGE    APPROVED   APPROVALACCEPTED   AGE
example-run-before-canary   example-run         canary                                 2m39s

段階的な更新プログラムの実行を承認する

json パッチ ファイルを作成して適用することで、 ApprovalRequest を承認できます。

cat << EOF > approval.json
"status": {
    "conditions": [
        {
            "lastTransitionTime": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
            "message": "lgtm",
            "observedGeneration": 1,
            "reason": "testPassed",
            "status": "True",
            "type": "Approved"
        }
    ]
}
EOF

Note

observedGenerationが承認オブジェクトの生成と同じであることを確認します。

作成された JSON ファイルを使用して、承認するパッチ要求を送信します。

kubectl patch approvalrequests example-run-before-canary -n test-namespace --type='merge' --subresource=status --patch-file approval.json

次に、要求が承認されたことを確認します。

kubectl get approvalrequests -n test-namespace

出力は次の例のようになります。

NAME                        STAGED-UPDATE-RUN   STAGE    APPROVED   APPROVALACCEPTED   AGE
example-run-before-canary   example-run         canary   True       True               3m35s

現在、StagedUpdateRun は処理を進めて完了できます。

kubectl get stagedupdaterrun example-run -n test-namespace

出力は次の例のようになります。

NAME          PLACEMENT           RESOURCE-SNAPSHOT-INDEX   POLICY-SNAPSHOT-INDEX   INITIALIZED   SUCCEEDED   AGE
example-run   example-placement   1                         0                       True          True        5m28s

ロールアウトの完了を確認する

ResourcePlacementには、ロールアウトが完了し、リソースがすべてのメンバー クラスターで使用できることも示されています。

kubectl get resourceplacement example-placement -n test-namespace

出力は次の例のようになります。

NAME                GEN   SCHEDULED   SCHEDULED-GEN   AVAILABLE   AVAILABLE-GEN   AGE
example-placement   1     True        1               True        1               8m55s

ConfigMap test-cm は、最新のデータを使用して、3 つのメンバー クラスターすべてにデプロイする必要があります。

apiVersion: v1
data:
  key: value2
kind: ConfigMap
metadata:
  ...
  name: test-cm
  namespace: test-namespace
  ...

段階的な更新プログラムの実行を停止する

実行中のステージング更新実行の実行を停止するには、仕様の state フィールドに修正プログラムを適用して Stopする必要があります。 このアクションにより、更新プログラムの実行が正常に停止され、進行中のクラスターはロールアウト プロセス全体を停止する前に更新を完了できます。

kubectl patch stagedupdaterun example-run -n test-namespace --type merge -p '{"spec":{"state":"Stop"}}'

ステージングされた更新プログラムの実行が初期化され、実行されなくなりました。

kubectl get stagedupdaterun example-run -n test-namespace

出力は次の例のようになります。

NAME          PLACEMENT           RESOURCE-SNAPSHOT-INDEX   POLICY-SNAPSHOT-INDEX   INITIALIZED   SUCCEEDED   AGE
example-run   example-placement   1                         0                       True                      7s

更新プログラムの実行が停止した後の状態の詳細を次に示します。

kubectl get stagedupdaterun example-run -n test-namespace -o yaml

出力は次の例のようになります。

apiVersion: placement.kubernetes-fleet.io/v1
kind: StagedUpdateRun
metadata:
  ...
  name: example-run
  namespace: test-namespace
  ...
spec:
  placementName: example-placement
  resourceSnapshotIndex: "1"
  stagedRolloutStrategyName: example-strategy
  state: Stop
status:
  conditions:
  - lastTransitionTime: "2025-07-22T21:28:08Z"
    message: StagedUpdateRun initialized successfully
    observedGeneration: 3
    reason: UpdateRunInitializedSuccessfully
    status: "True" # the updateRun is initialized successfully
    type: Initialized
  - lastTransitionTime: "2025-07-22T21:28:23Z"
    message: The update run has been stopped
    observedGeneration: 3
    reason: UpdateRunStopped
    status: "False" # the updateRun has stopped progressing
    type: Progressing
  deletionStageStatus:
    clusters: [] # no clusters need to be cleaned up
    stageName: kubernetes-fleet.io/deleteStage
  policyObservedClusterCount: 3 # number of clusters to be updated
  policySnapshotIndexUsed: "0"
  resourceSnapshotIndexUsed: "1"
  stagedUpdateStrategySnapshot: # snapshot of the strategy used for this update run
    stages:
    - afterStageTasks:
      - type: TimedWait
        waitTime: 1m0s
      labelSelector:
        matchLabels:
          environment: staging
      maxConcurrency: 1
      name: staging
    - beforeStageTasks:
      - type: Approval
      labelSelector:
        matchLabels:
          environment: canary
      maxConcurrency: 50%
      name: canary
      sortingLabelKey: order
  stagesStatus: # detailed status for each stage
  - clusters:
    - clusterName: member2 # stage staging contains member2 cluster only
      conditions:
      - lastTransitionTime: "2025-07-22T21:28:08Z"
        message: Cluster update started
        observedGeneration: 3
        reason: ClusterUpdatingStarted
        status: "True"
        type: Started
      - lastTransitionTime: "2025-07-22T21:28:23Z"
        message: Cluster update completed successfully
        observedGeneration: 3
        reason: ClusterUpdatingSucceeded
        status: "True" # member2 is updated successfully
        type: Succeeded
    conditions:
    - lastTransitionTime: "2025-07-22T21:28:23Z"
      message: All the updating clusters have finished updating, the stage is now stopped, waiting to be resumed
      observedGeneration: 3
      reason: StageUpdatingStopped
      status: "False"
      type: Progressing
    stageName: staging
    startTime: "2025-07-22T21:28:08Z"

以前のバージョンにロールバックする 2 番目の段階的な更新プログラムの実行をデプロイする

ワークロード管理者が ConfigMap の変更をロールバックし、 value2 値を value1に戻したいとします。 ハブから configmap を手動で更新する代わりに、コンテキストで以前のリソース スナップショット インデックス "0" を使用して新しい StagedUpdateRun を作成し、同じ戦略を再利用できます。

apiVersion: placement.kubernetes-fleet.io/v1
kind: StagedUpdateRun
metadata:
  name: example-run-2
  namespace: test-namespace
spec:
  placementName: example-placement
  resourceSnapshotIndex: "0"
  stagedRolloutStrategyName: example-strategy
  state: Run

新しい StagedUpdateRunを確認してみましょう。

kubectl get stagedupdaterun -n test-namespace

出力は次の例のようになります。

NAME            PLACEMENT           RESOURCE-SNAPSHOT-INDEX   POLICY-SNAPSHOT-INDEX   INITIALIZED   SUCCEEDED   AGE
example-run     example-placement   1                         0                       True          True        13m
example-run-2   example-placement   0                         0                       True                      9s

1 分間のTimedWaitが経過すると、新しいApprovalRequest用に作成されたStagedUpdateRun オブジェクトが表示されます。

kubectl get approvalrequests -n test-namespace

出力は次の例のようになります。

NAME                          STAGED-UPDATE-RUN   STAGE    APPROVED   APPROVALACCEPTED   AGE
example-run-2-before-canary   example-run-2       canary                                 75s
example-run-before-canary     example-run         canary   True       True               14m

新しい ApprovalRequest オブジェクトを承認するには、同じ approval.json ファイルを再利用して修正プログラムを適用しましょう。

kubectl patch approvalrequests example-run-2-before-canary -n test-namespace --type='merge' --subresource=status --patch-file approval.json

新しいオブジェクトが承認されているかどうかを確認します。

kubectl get approvalrequests -n test-namespace

出力は次の例のようになります。

NAME                          STAGED-UPDATE-RUN   STAGE    APPROVED   APPROVALACCEPTED   AGE
example-run-2-before-canary   example-run-2       canary   True       True               2m7s
example-run-before-canary     example-run         canary   True       True               15m

ConfigMap test-cm は、次の 3 つのメンバー クラスターすべてにデプロイされ、データは value1に戻されます。

apiVersion: v1
data:
  key: value1
kind: ConfigMap
metadata:
  ...
  name: test-cm
  namespace: test-namespace
  ...

リソースをクリーンアップする

このチュートリアルが完了したら、作成したリソースをクリーンアップできます。

kubectl delete stagedupdaterun example-run example-run-2 -n test-namespace
kubectl delete stagedupdatestrategy example-strategy -n test-namespace
kubectl delete resourceplacement example-placement -n test-namespace
kubectl delete clusterresourceplacement test-namespace-placement
kubectl delete namespace test-namespace

アプローチの主な違い

特徴 Cluster-Scoped Namespace-Scoped
戦略リソース ClusterStagedUpdateStrategy (短い名前: csus) StagedUpdateStrategy (短い名前: sus)
実行リソースの更新 ClusterStagedUpdateRun (短い名前: csur) StagedUpdateRun (短い名前: sur)
ターゲットの配置 ClusterResourcePlacement (短い名前: crp) ResourcePlacement (短い名前: rp)
承認リソース ClusterApprovalRequest (短い名前: careq) ApprovalRequest (短い名前: areq)
スナップショット リソース ClusterResourceSnapshot ResourceSnapshot
Scope クラスター全体 名前空間バインド
ユースケース(事例) インフラストラクチャのロールアウト アプリケーションのロールアウト
アクセス許可 クラスター管理者レベル 名前空間レベル

次のステップ

この記事では、段階的な更新実行を使用して、メンバー クラスター間のロールアウトを調整する方法について説明しました。 クラスター スコープデプロイと名前空間スコープデプロイの両方に対して段階的な更新戦略を作成し、プログレッシブ ロールアウトを実行し、以前のバージョンへのロールバックを実行しました。

段階的な更新プログラムの実行と関連する概念の詳細については、次のリソースを参照してください。