Condividi tramite


Controllare l'ordine del cluster per il posizionamento delle risorse

Si applica a: ✔️ Gestione flotta con un cluster hub

Le esecuzioni di aggiornamento a fasi di posizionamento di Gestione flotta Kubernetes di Azure offrono un approccio controllato alla distribuzione di carichi di lavoro Kubernetes tra più cluster membri mediante un processo fase per fase. Per ridurre al minimo i rischi, questo approccio viene distribuito in sequenza nei cluster di destinazione, con tempi di attesa facoltativi e controlli di approvazione tra fasi.

Questo articolo illustra come creare ed eseguire esecuzioni di aggiornamenti a fasi per distribuire i carichi di lavoro progressivamente ed eseguire il rollback alle versioni precedenti quando necessario.

Azure Kubernetes Fleet Manager supporta due ambiti per gli aggiornamenti a fasi:

  • Ambito cluster: usare ClusterStagedUpdateRun con ClusterResourcePlacement per gli amministratori della flotta che gestiscono le modifiche a livello di infrastruttura.
  • Con ambito spazio dei nomi: usare StagedUpdateRun con ResourcePlacement per i team delle applicazioni che gestiscono le distribuzioni negli spazi dei nomi specifici.

Importante

ResourcePlacement usa la versione dell'API placement.kubernetes-fleet.io/v1beta1 ed è attualmente in anteprima. Alcune funzionalità illustrate in questo articolo, ad esempio ResourceSnapshot, fanno anche parte dell'API v1beta1 e non sono disponibili nell'API v1.

Gli esempi in questo articolo illustrano entrambi gli approcci usando le schede. Scegliere la scheda corrispondente all'ambito di distribuzione.

Prima di iniziare

Prerequisites

  • È necessario un account Azure con una sottoscrizione attiva. Creare un account gratuito.

  • Per comprendere i concetti e la terminologia usati in questo articolo, leggere la panoramica concettuale delle strategie di implementazione a fasi.

  • Per completare questo articolo, è necessario Azure CLI versione 2.58.0 o successiva. Per installare o aggiornare, vedere Installare il Azure CLI.

  • Se l'interfaccia della riga di comando di Kubernetes (kubectl) non è ancora disponibile, è possibile installarla usando questo comando:

    az aks install-cli
    
  • È necessaria l'estensione fleet Azure CLI. È possibile installarlo eseguendo il comando seguente:

    az extension add --name fleet
    

    Eseguire il comando az extension update per effettuare l'aggiornamento alla versione più recente dell'estensione:

    az extension update --name fleet
    

Configurare l'ambiente demo

Questa demo viene eseguita in un Fleet Manager con un cluster hub e tre cluster membri. Se non è disponibile, seguire la guida introduttiva per creare un Fleet Manager con un cluster hub. Successivamente, aggiungi i cluster del servizio Azure Kubernetes (AKS) come membri.

Questa esercitazione illustra le esecuzioni degli aggiornamenti a fasi usando un ambiente demo fleet con tre cluster membri con le etichette seguenti:

nome membro labels
member1 environment=canary, order=2
member2 environment=staging
member3 environment=canary, order=1

Queste etichette consentono la creazione di fasi e di controllare l'ordine di distribuzione all'interno di ogni fase.

Applicare etichette ai cluster membri usando il comando visualizzato.

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

Preparare i carichi di lavoro di Kubernetes per il posizionamento

Pubblicare carichi di lavoro Kubernetes nel cluster hub in modo che possano essere inseriti nei cluster membri.

Creare un namespace e un oggetto ConfigMap per il carico di lavoro nel cluster hub:

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

Per distribuire le risorse, creare un oggetto ClusterResourcePlacement:

Note

spec.strategy.type è impostato su External per consentire l'implementazione attivata con 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

Tutti e tre i cluster devono essere pianificati perché vengono usati i criteri PickAll, ma non è necessario distribuire risorse nei cluster membri perché non è stato ancora creato un oggetto ClusterStagedUpdateRun.

Verificare che il posizionamento sia pianificato:

kubectl get clusterresourceplacement example-placement

L'output dovrebbe essere simile all'esempio seguente:

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

Creare una strategia di aggiornamento a fasi

Un ClusterStagedUpdateStrategy oggetto definisce il modello di orchestrazione che raggruppa i cluster in fasi e specifica la sequenza di implementazione. Seleziona i cluster membri in base alle etichette. Per la dimostrazione, ne viene creata una con due fasi, staging e canary:

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%

Gestire gli snapshot delle risorse

Fleet Manager crea snapshot delle risorse quando queste cambiano se il posizionamento è stato configurato con una strategia di implementazione di RollingUpdate. Ogni snapshot ha un indice univoco che è possibile usare per fare riferimento a versioni specifiche delle risorse.

Quando un'implementazione utilizza la strategia di rollout External, gli snapshot delle risorse non vengono creati automaticamente. Vengono invece creati quando si esegue un'esecuzione di aggiornamento a fasi. Ciò significa che quando si crea per la prima volta un posizionamento con una strategia di implementazione External, non esistono snapshot di risorse fino a quando non si esegue la prima esecuzione dell'aggiornamento a fasi.

Note

Se in precedenza un posizionamento usava la RollingUpdate strategia e viene modificato in External, tutti gli snapshot di risorse esistenti rimangono disponibili. È possibile fare riferimento a questi snapshot esistenti durante la creazione di esecuzioni di aggiornamenti a fasi.

Tip

Per altre informazioni sugli snapshot delle risorse e sul relativo funzionamento, vedere Snapshot delle risorse.

Controllare gli snapshot delle risorse correnti

Poiché ClusterResourcePlacement utilizza la strategia External, non esistono ancora snapshot delle risorse. Verifichiamo:

kubectl get clusterresourcesnapshots --show-labels

L'output non dovrebbe mostrare alcuna risorsa:

No resources found

Creare il primo snapshot della risorsa

Per creare il primo snapshot della risorsa, è necessario creare un ClusterStagedUpdateRun con il campo resourceSnapshotIndex omesso. Il controller di esecuzione dell'aggiornamento rileva che non esistono snapshot e ne crea uno automaticamente.

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

Al termine dell'esecuzione dell'aggiornamento, controllare gli snapshot delle risorse:

kubectl get clusterresourcesnapshots --show-labels

L'output dovrebbe essere simile all'esempio seguente:

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

È ora disponibile una versione dello snapshot. È l'ultima versione (kubernetes-fleet.io/is-latest-snapshot=true) e contiene l'indice delle risorse 0 (kubernetes-fleet.io/resource-index=0).

Creare un nuovo snapshot della risorsa

Modificare ora ConfigMap con un nuovo valore:

kubectl edit configmap test-cm -n test-namespace

Aggiornare il valore da value1 a value2:

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

L'output dovrebbe essere simile all'esempio seguente:

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

Poiché il posizionamento usa la External strategia, il nuovo snapshot delle risorse non viene creato automaticamente. Creare un'altra ClusterStagedUpdateRun con il resourceSnapshotIndex campo omesso per attivare la creazione di un nuovo snapshot:

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

Al termine dell'esecuzione dell'aggiornamento, verranno visualizzate due versioni degli snapshot delle risorse con indice 0 e 1 rispettivamente, ovvero l'indice 1 più recente:

kubectl get clusterresourcesnapshots --show-labels

L'output dovrebbe essere simile all'esempio seguente:

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

L'etichetta più recente è impostata su example-placement-1-snapshot, che contiene i dati ConfigMap più recenti:

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

L'output dovrebbe essere simile all'esempio seguente:

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

Preparare un'esecuzione di aggiornamento a fasi per l'implementazione delle modifiche

Un ClusterStagedUpdateRun esegue il rollout di un ClusterResourcePlacement in seguito a un ClusterStagedUpdateStrategy. Per attivare l'esecuzione dell'aggiornamento a fasi per ClusterResourcePlacement (CRP), viene creato un ClusterStagedUpdateRun oggetto che specifica il nome CRP, il nome della strategia updateRun, l'indice dello snapshot della risorsa più recente ("1") e lo stato "Initialize":

Note

Quando si usa la External strategia di implementazione, è possibile omettere il resourceSnapshotIndex campo se si desidera distribuire le risorse più recenti. Il controller di esecuzione dell'aggiornamento crea automaticamente un nuovo snapshot della risorsa quando resourceSnapshotIndex viene omesso.

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

L'esecuzione dell'aggiornamento a fasi viene inizializzata ma non è in esecuzione:

kubectl get clusterstagedupdaterrun example-run

L'output dovrebbe essere simile all'esempio seguente:

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

Avviare un'esecuzione di aggiornamento a fasi

Per avviare l'esecuzione di un aggiornamento a fasi, è necessario applicare una patch al campo state a Run:

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

Note

È anche possibile creare un'esecuzione di aggiornamento con il campo state impostato inizialmente su Run, che inizializza e avvia l'esecuzione dell'aggiornamento in un solo passaggio.

L'esecuzione dell'aggiornamento a fasi viene inizializzata ed eseguita:

kubectl get clusterstagedupdaterrun example-run

L'output dovrebbe essere simile all'esempio seguente:

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

Un'analisi più dettagliata dello stato dopo la scadenza di un minuto TimedWait :

kubectl get clusterstagedupdaterrun example-run -o yaml

L'output dovrebbe essere simile all'esempio seguente:

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"

È possibile notare che il periodo TimedWait per la fase di staging scade e si nota anche che è stato creato l'oggetto per l'attività ClusterApprovalRequest di approvazione nella fase canary. È possibile controllare il generato ClusterApprovalRequest e vedere che nessuno lo ha ancora approvato

kubectl get clusterapprovalrequest

L'output dovrebbe essere simile all'esempio seguente:

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

Approvare l'esecuzione dell'aggiornamento a fasi

È possibile approvare ClusterApprovalRequest creando un file di patch JSON e applicandolo:

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

Nota: assicurarsi che observedGeneration corrisponda alla generazione dell'oggetto approvato.

Inviare una richiesta di patch per approvare usando il file JSON creato.

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

Verificare quindi di aver approvato la richiesta:

kubectl get clusterapprovalrequest

L'output dovrebbe essere simile all'esempio seguente:

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

Ora è possibile procedere e completare ClusterStagedUpdateRun:

kubectl get clusterstagedupdaterrun example-run

L'output dovrebbe essere simile all'esempio seguente:

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

Verificare il completamento dell'implementazione

ClusterResourcePlacement mostra anche l'implementazione completata e le risorse sono disponibili in tutti i cluster membri:

kubectl get clusterresourceplacement example-placement

L'output dovrebbe essere simile all'esempio seguente:

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

Il test-cm di ConfigMap deve essere distribuito in tutti e tre i cluster membri, con i dati più recenti:

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

Interrompere un'esecuzione di aggiornamento a fasi

Per arrestare l'esecuzione di un aggiornamento del cluster a fasi in esecuzione, è necessario applicare patch al campo state nella specifica a Stop. Questa azione interrompe normalmente l'esecuzione dell'aggiornamento, consentendo ai cluster in corso di completare gli aggiornamenti prima di arrestare l'intero processo di implementazione:

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

L'esecuzione dell'aggiornamento a fasi viene inizializzata e non è più in esecuzione:

kubectl get clusterstagedupdaterun example-run

L'output dovrebbe essere simile all'esempio seguente:

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

Uno stato più dettagliato dopo l'arresto dell'esecuzione dell'aggiornamento:

kubectl get clusterstagedupdaterun example-run -o yaml

L'output dovrebbe essere simile all'esempio seguente:

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"

Avviare una seconda esecuzione di aggiornamento suddiviso in fasi per effettuare il rollback a una versione precedente

Si supponga che l'amministratore del carico di lavoro voglia eseguire il rollback della modifica di ConfigMap, ripristinando il valore value2 in value1. Anziché aggiornare manualmente il ConfigMap dall'hub, è possibile creare un nuovo oggetto ClusterStagedUpdateRun con un indice snapshot della risorsa precedente, "0" nel contesto, e riutilizzare la stessa strategia:

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

Esaminiamo il nuovo ClusterStagedUpdateRun:

kubectl get clusterstagedupdaterun

L'output dovrebbe essere simile all'esempio seguente:

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

Al termine di un minutoTimedWait, verrà visualizzato l'oggetto ClusterApprovalRequest creato per il nuovo ClusterStagedUpdateRun.

kubectl get clusterapprovalrequest

L'output dovrebbe essere simile all'esempio seguente:

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

Per approvare il nuovo ClusterApprovalRequest oggetto, riutilizzare lo stesso approval.json file per applicare patch:

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

Verificare se il nuovo oggetto è approvato:

kubectl get clusterapprovalrequest                                                                            

L'output dovrebbe essere simile all'esempio seguente:

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

L'oggetto ConfigMap test-cm dovrebbe ora essere distribuito in tutti e tre i cluster membri, con i dati ripristinati in value1:

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

Pulire le risorse

Al termine di questa esercitazione, è possibile pulire le risorse create:

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

Preparare i carichi di lavoro di Kubernetes per il posizionamento

Pubblicare carichi di lavoro Kubernetes nel cluster hub in modo che possano essere inseriti nei cluster membri.

Creare un namespace e un oggetto ConfigMap per il carico di lavoro nel cluster hub:

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

Poiché ResourcePlacement ha l'ambito di spazio dei nomi, distribuire prima Namespace in tutti i cluster membri usando ClusterResourcePlacement, specificando NamespaceOnly per l'ambito di selezione:

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

Verificare che Namespace sia distribuito in tutti i cluster membri:

kubectl get clusterresourceplacement test-namespace-placement

L'output dovrebbe essere simile all'esempio seguente:

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

Per distribuire ConfigMap, creare un oggetto ResourcePlacement con ambito di spazio dei nomi:

Note

spec.strategy.type è impostato su External per consentire l'implementazione attivata con 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

Tutti e tre i cluster devono essere pianificati perché viene usato il PickAll criterio, ma la ConfigMap non deve ancora essere distribuita nei cluster membri perché non è stato ancora creato un StagedUpdateRun.

Verificare che il posizionamento sia pianificato:

kubectl get resourceplacement example-placement -n test-namespace

L'output dovrebbe essere simile all'esempio seguente:

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

Creare una strategia di aggiornamento a fasi

Un StagedUpdateStrategy oggetto definisce il modello di orchestrazione che raggruppa i cluster in fasi e specifica la sequenza di implementazione. Seleziona i cluster membri in base alle etichette. Per la dimostrazione, ne viene creata una con due fasi, staging e canary:

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%

Gestire gli snapshot delle risorse

Fleet Manager crea snapshot delle risorse quando le risorse cambiano se il posizionamento ha una strategia di implementazione di RollingUpdate. Ogni snapshot ha un indice univoco che è possibile usare per fare riferimento a versioni specifiche delle risorse.

Quando un posizionamento usa la strategia di implementazione External, gli snapshot delle risorse non vengono creati automaticamente. Vengono invece creati quando si esegue un'esecuzione di aggiornamento a fasi. Ciò significa che quando si crea per la prima volta un posizionamento con una strategia di implementazione External, non esistono snapshot di risorse fino a quando non si esegue la prima esecuzione dell'aggiornamento a fasi.

Note

Se in precedenza un posizionamento usava la RollingUpdate strategia e viene modificato in External, tutti gli snapshot di risorse esistenti rimangono disponibili. È possibile fare riferimento a questi snapshot esistenti durante la creazione di esecuzioni di aggiornamenti a fasi.

Tip

Per altre informazioni sugli snapshot delle risorse e sul relativo funzionamento, vedere Snapshot delle risorse.

Controllare gli snapshot delle risorse correnti

Poiché ResourcePlacement usa la strategia External, non esistono ancora istantanee di risorse. Verifichiamo:

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

L'output non dovrebbe mostrare alcuna risorsa:

No resources found in test-namespace namespace.

Creare il primo snapshot della risorsa

Per creare il primo snapshot della risorsa, è necessario creare un StagedUpdateRun con il campo resourceSnapshotIndex omesso. Il controller di esecuzione dell'aggiornamento rileva che non esistono snapshot e ne crea uno automaticamente.

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

Al termine dell'esecuzione dell'aggiornamento, controllare gli snapshot delle risorse:

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

L'output dovrebbe essere simile all'esempio seguente:

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

È ora disponibile una versione dello snapshot. È l'ultima versione (kubernetes-fleet.io/is-latest-snapshot=true) e contiene l'indice delle risorse 0 (kubernetes-fleet.io/resource-index=0).

Creare un nuovo snapshot della risorsa

Modificare ora ConfigMap con un nuovo valore:

kubectl edit configmap test-cm -n test-namespace

Aggiornare il valore da value1 a value2:

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

L'output dovrebbe essere simile all'esempio seguente:

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

Poiché il posizionamento usa la External strategia, il nuovo snapshot delle risorse non viene creato automaticamente. Creare un'altra StagedUpdateRun con il resourceSnapshotIndex campo omesso per attivare la creazione di un nuovo snapshot:

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

Al termine dell'esecuzione dell'aggiornamento, verranno visualizzate due versioni degli snapshot delle risorse con indice 0 e 1 rispettivamente:

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

L'output dovrebbe essere simile all'esempio seguente:

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

L'etichetta più recente è impostata su example-placement-1-snapshot, che contiene i dati ConfigMap più recenti:

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

L'output dovrebbe essere simile all'esempio seguente:

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

Preparare un'esecuzione di aggiornamento a fasi per l'implementazione delle modifiche

Un StagedUpdateRun esegue il rollout di un ResourcePlacement in seguito a un StagedUpdateStrategy. Per attivare l'esecuzione dell'aggiornamento a fasi per ResourcePlacement (RP), viene creato un StagedUpdateRun oggetto che specifica il nome di RP, il nome della strategia di updateRun, l'indice più recente dello snapshot delle risorse ("1") e lo stato come "Initialize".

Note

Quando si usa la External strategia di implementazione, è possibile omettere il resourceSnapshotIndex campo se si desidera distribuire le risorse più recenti. Il controller di esecuzione dell'aggiornamento crea automaticamente un nuovo snapshot della risorsa quando resourceSnapshotIndex viene omesso.

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

L'esecuzione dell'aggiornamento a fasi viene inizializzata ma non è in esecuzione:

kubectl get stagedupdaterrun example-run -n test-namespace

L'output dovrebbe essere simile all'esempio seguente:

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

Avviare un'esecuzione di aggiornamento a fasi

Per avviare l'esecuzione di un aggiornamento a fasi, è necessario applicare una patch al campo state a Run:

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

Note

È anche possibile creare un'esecuzione di aggiornamento con il campo state impostato inizialmente su Run, che inizializza e avvia l'esecuzione dell'aggiornamento in un solo passaggio.

L'esecuzione dell'aggiornamento a fasi viene inizializzata ed eseguita:

kubectl get stagedupdaterrun example-run -n test-namespace

L'output dovrebbe essere simile all'esempio seguente:

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

Dopo la scadenza di un minuto TimedWait, verificare la richiesta di approvazione:

kubectl get approvalrequests -n test-namespace

L'output dovrebbe essere simile all'esempio seguente:

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

Approvare l'esecuzione dell'aggiornamento a fasi

È possibile approvare ApprovalRequest creando un file di patch JSON e applicandolo:

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

Assicurarsi che observedGeneration sia uguale alla generazione dell'oggetto di approvazione.

Inviare una richiesta di patch per approvare usando il file JSON creato.

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

Verificare quindi di aver approvato la richiesta:

kubectl get approvalrequests -n test-namespace

L'output dovrebbe essere simile all'esempio seguente:

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

Ora è possibile procedere e completare StagedUpdateRun:

kubectl get stagedupdaterrun example-run -n test-namespace

L'output dovrebbe essere simile all'esempio seguente:

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

Verificare il completamento dell'implementazione

ResourcePlacement mostra anche l'implementazione completata e le risorse sono disponibili in tutti i cluster membri:

kubectl get resourceplacement example-placement -n test-namespace

L'output dovrebbe essere simile all'esempio seguente:

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

Il test-cm di ConfigMap deve essere distribuito in tutti e tre i cluster membri, con i dati più recenti:

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

Interrompere un'esecuzione di aggiornamento a fasi

Per arrestare un aggiornamento a fasi in esecuzione, è necessario modificare il campo state nella specifica a Stop. Questa azione interrompe normalmente l'esecuzione dell'aggiornamento, consentendo ai cluster in corso di completare gli aggiornamenti prima di arrestare l'intero processo di implementazione:

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

L'esecuzione dell'aggiornamento a fasi viene inizializzata e non è più in esecuzione:

kubectl get stagedupdaterun example-run -n test-namespace

L'output dovrebbe essere simile all'esempio seguente:

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

Uno stato più dettagliato dopo l'arresto dell'esecuzione dell'aggiornamento:

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

L'output dovrebbe essere simile all'esempio seguente:

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"

Avviare una seconda esecuzione di aggiornamento suddiviso in fasi per effettuare il rollback a una versione precedente

Si supponga che l'amministratore del carico di lavoro voglia eseguire il rollback della modifica di ConfigMap, ripristinando il valore value2 in value1. Anziché aggiornare manualmente ConfigMap dall'hub, è possibile crearne un nuovo oggetto StagedUpdateRun con un indice di snapshot della risorsa precedente ("0" in questo contesto) e riutilizzare la stessa strategia:

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

Esaminiamo il nuovo StagedUpdateRun:

kubectl get stagedupdaterun -n test-namespace

L'output dovrebbe essere simile all'esempio seguente:

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

Al termine di un minutoTimedWait, verrà visualizzato l'oggetto ApprovalRequest creato per il nuovo StagedUpdateRun.

kubectl get approvalrequests -n test-namespace

L'output dovrebbe essere simile all'esempio seguente:

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

Per approvare il nuovo ApprovalRequest oggetto, riutilizzare lo stesso approval.json file per applicare patch:

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

Verificare se il nuovo oggetto è approvato:

kubectl get approvalrequests -n test-namespace

L'output dovrebbe essere simile all'esempio seguente:

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

L'oggetto ConfigMap test-cm dovrebbe ora essere distribuito in tutti e tre i cluster membri, con i dati ripristinati in value1:

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

Pulire le risorse

Al termine di questa esercitazione, è possibile pulire le risorse create:

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

Differenze principali tra approcci

Aspetto Con ambito cluster Con ambito spazio dei nomi
Risorsa strategia ClusterStagedUpdateStrategy (nome breve: csus) StagedUpdateStrategy (nome breve: sus)
Aggiornare la risorsa di esecuzione ClusterStagedUpdateRun (nome breve: csur) StagedUpdateRun (nome breve: sur)
Posizionamento di destinazione ClusterResourcePlacement (nome breve: crp) ResourcePlacement (nome breve: rp)
Risorsa di approvazione ClusterApprovalRequest (nome breve: careq) ApprovalRequest (nome breve: areq)
Risorsa Snapshot ClusterResourceSnapshot ResourceSnapshot
Scope A livello di cluster Vincolato allo spazio dei nomi
Caso d'uso Implementazioni dell'infrastruttura Distribuzioni di applicazioni
Autorizzazioni Livello di amministratore del cluster Livello del namespace

Passaggi successivi

In questo articolo si è appreso come usare le esecuzioni di aggiornamento a fasi per orchestrare le implementazioni tra cluster membri. Sono state create strategie di aggiornamento a fasi sia per le distribuzioni con ambito cluster che per le distribuzioni con ambito spazio dei nomi, sono state eseguite implementazioni progressive ed è stato eseguito il rollback alle versioni precedenti.

Per altre informazioni sulle esecuzioni degli aggiornamenti a fasi e sui concetti correlati, vedere le risorse seguenti: