Control de la secuencia del clúster para la distribución de recursos

Se aplica a: ✔️ Fleet Manager con clúster central

Las ejecuciones de actualización escalonadas basadas en la ubicación de Azure Kubernetes Fleet Manager proporcionan un enfoque controlado para implementar cargas de trabajo de Kubernetes en varios clústeres miembros a través de un proceso paso a paso. Para minimizar el riesgo, este enfoque se implementa en clústeres de destino secuencialmente, con tiempos de espera opcionales y puertas de aprobación entre fases.

En este artículo se muestra cómo crear y ejecutar ejecuciones de actualizaciones preconfiguradas para implementar cargas de trabajo progresivamente y revertirlas a versiones anteriores cuando sea necesario.

Azure Kubernetes Fleet Manager admite dos ámbitos para las actualizaciones por etapas:

  • Limitado a clúster: use ClusterStagedUpdateRun con ClusterResourcePlacement para administradores de flotas que se encarguen de los cambios a nivel de infraestructura.
  • Con ámbito de espacio de nombres: use StagedUpdateRun con ResourcePlacement para los equipos de gestión de aplicaciones que administran despliegues dentro de sus espacios de nombres específicos.

Importante

ResourcePlacement usa la versión de la placement.kubernetes-fleet.io/v1beta1 API y está actualmente en versión preliminar. Algunas características que se muestran en este artículo, como ResourceSnapshot, también forman parte de la API v1beta1 y no están disponibles en la API v1.

En los ejemplos de este artículo se muestran ambos enfoques mediante pestañas. Elija la pestaña que coincida con el ámbito de implementación.

Antes de empezar

Prerequisites

  • Necesita una cuenta de Azure con una suscripción activa. Cree una cuenta gratuita.

  • Para comprender los conceptos y la terminología que se usan en este artículo, lea la introducción conceptual de las estrategias de lanzamiento preconfigurado.

  • Necesita Azure CLI versión 2.58.0 o posterior instalada para completar este artículo. Para instalar o actualizar, consulte Instalar el Azure CLI.

  • Si aún no la tiene instalada, puede instalar la CLI de Kubernetes (kubectl) mediante este comando:

    az aks install-cli
    
  • Usted necesita la Azure CLI extensión fleet. Puede instalarlo ejecutando el siguiente comando:

    az extension add --name fleet
    

    Ejecute el comando az extension update para actualizar a la versión más reciente de la extensión:

    az extension update --name fleet
    

Configuración del entorno de demostración

Esta demostración se ejecuta en un Administrador de flotas con un clúster de concentrador y tres clústeres miembros. Si no tiene uno, siga el inicio rápido para crear un Gestor de Flotas con un clúster central. A continuación, únase a clústeres de Azure Kubernetes Service (AKS) como miembros.

En este tutorial se muestran las ejecuciones de actualizaciones por etapas en un entorno de flota de demostración que comprende tres clústeres miembros que tienen las etiquetas siguientes:

nombre de miembro labels
member1 environment=canary, order=2
member2 environment=staging
member3 environment=canary, order=1

Estas etiquetas permiten la creación de fases y controlar el orden de implementación dentro de cada fase.

Aplique etiquetas a los clústeres de miembros mediante el comando que se muestra.

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

Preparación de cargas de trabajo de Kubernetes para ubicación

Publique cargas de trabajo de Kubernetes en el clúster de concentrador para que se puedan colocar en clústeres miembro.

Cree un espacio de nombres y un ConfigMap para la carga de trabajo en el clúster central:

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

Para implementar los recursos, cree un ClusterResourcePlacement:

Note

spec.strategy.type se establece en External para permitir el lanzamiento desencadenado con un 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

Los tres clústeres deben programarse, puesto que usamos la directiva PickAll, pero no se deben desplegar recursos en los clústeres miembro porque aún no se ha creado un ClusterStagedUpdateRun.

Confirme que la colocación está planificada:

kubectl get clusterresourceplacement example-placement

La salida debería parecerse al siguiente ejemplo.

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

Creación de una estrategia de actualización preconfigurada

Un ClusterStagedUpdateStrategy define el patrón de orquestación que agrupa los clústeres en fases y especifica la secuencia de lanzamiento. Selecciona los clústeres de miembros por etiquetas. Para nuestra demostración, creamos una con dos fases, almacenamiento provisional y valor controlado:

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%

Trabajar con instantáneas de recursos

Fleet Manager crea instantáneas de recursos cuando los recursos cambian si la ubicación tiene una estrategia de lanzamiento de RollingUpdate. Cada instantánea tiene un índice único que puede usar para hacer referencia a versiones específicas de los recursos.

Cuando una ubicación usa la estrategia de lanzamiento External, las instantáneas de recursos no se crean automáticamente. En su lugar, se crean al ejecutar una actualización escalonada. Esto significa que, cuando se crea por primera vez una ubicación con una estrategia de lanzamiento External, no existen instantáneas de recursos hasta que se ejecuta la primera ejecución de actualización por etapas.

Note

Si una ubicación antes usaba la estrategia RollingUpdate y se cambia a External, las instantáneas de recursos existentes permanecen disponibles. Puede hacer referencia a estas instantáneas existentes al crear ejecuciones de actualización en etapas.

Tip

Para más información sobre las instantáneas de recursos y cómo funcionan, consulte Instantáneas de recursos.

Revise las instantáneas de recursos actuales

Dado que ClusterResourcePlacement usa la External estrategia, todavía no existen instantáneas de recursos. Vamos a comprobar:

kubectl get clusterresourcesnapshots --show-labels

La salida no debe mostrar ningún recurso:

No resources found

Creación de la primera instantánea de recursos

Para crear la primera instantánea de recursos, necesita crear un ClusterStagedUpdateRun con el campo resourceSnapshotIndex omitido. El controlador de ejecución de actualizaciones detecta que no existen instantáneas y crea una automáticamente.

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

Una vez completada la ejecución de la actualización, compruebe las instantáneas de recursos:

kubectl get clusterresourcesnapshots --show-labels

La salida debería parecerse al siguiente ejemplo.

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

Ahora tiene una versión de la instantánea. Es la versión más reciente (kubernetes-fleet.io/is-latest-snapshot=true) actual y tiene el índice de recursos 0 (kubernetes-fleet.io/resource-index=0).

Creación de una nueva instantánea de recursos

Ahora modifique configMap con un nuevo valor:

kubectl edit configmap test-cm -n test-namespace

Actualice el valor de value1 a value2:

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

La salida debería parecerse al siguiente ejemplo.

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

Dado que la ubicación usa la estrategia External, la nueva instantánea de recursos no se crea automáticamente. Cree otro ClusterStagedUpdateRun sin el campo resourceSnapshotIndex para provocar la creación de una nueva instantánea:

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

Una vez completada la ejecución de la actualización, debería ver dos versiones de instantáneas de recursos con el índice 0 y 1, respectivamente, la más reciente es el índice 1:

kubectl get clusterresourcesnapshots --show-labels

La salida debería parecerse al siguiente ejemplo.

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

La etiqueta más reciente se establece en example-placement-1-snapshot, que contiene los datos configMap más recientes:

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

La salida debería parecerse al siguiente ejemplo.

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

Preparar una ejecución de actualización por etapas para desplegar los cambios

Un ClusterStagedUpdateRun ejecuta el lanzamiento de un ClusterResourcePlacement tras un ClusterStagedUpdateStrategy. Para activar la ejecución de actualización escalonada en nuestro ClusterResourcePlacement (CRP), creamos un ClusterStagedUpdateRun que indica el nombre de CRP, el nombre de la estrategia de ejecución de updateRun, el último índice de instantáneas de recursos ("1") y el estado en "Initialize":

Note

Al usar la External estrategia de lanzamiento, puede omitir el resourceSnapshotIndex campo si desea implementar los recursos más recientes. El controlador de ejecución de actualización crea automáticamente una nueva instantánea de recursos cuando resourceSnapshotIndex se omite.

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

La ejecución de la actualización escalonada se inicializa pero no está en ejecución:

kubectl get clusterstagedupdaterrun example-run

La salida debería parecerse al siguiente ejemplo.

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

Iniciar una ejecución de actualización preconfigurada

Para iniciar la ejecución de una actualización escalonada, debe aplicar un parche al campo state en la especificación a Run:

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

Note

También puede crear una ejecución de actualización con el campo state inicialmente establecido en Run, lo cual inicializa e inicia la ejecución de la actualización en un solo paso.

La ejecución de la actualización preconfigurada se inicializa y ejecuta:

kubectl get clusterstagedupdaterrun example-run

La salida debería parecerse al siguiente ejemplo.

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

Un vistazo más detallado al estado después de que transcurre un minuto TimedWait :

kubectl get clusterstagedupdaterrun example-run -o yaml

La salida debería parecerse al siguiente ejemplo.

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"

Podemos ver que el período TimedWait para la fase de ensayo transcurre y también vemos que se creó el objeto ClusterApprovalRequest para la tarea de aprobación en la fase de valor controlado. Podemos comprobar el generado ClusterApprovalRequest y ver que nadie lo ha aprobado aún

kubectl get clusterapprovalrequest

La salida debería parecerse al siguiente ejemplo.

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

Aprobar la ejecución de la actualización escalonada

Podemos aprobar el ClusterApprovalRequest creando un archivo de parche json y aplicándolo:

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: asegúrese de que observedGeneration coincide con la generación del objeto de aprobación.

Envíe una solicitud de revisión para obtener la aprobación utilizando el archivo JSON creado.

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

A continuación, compruebe que ha aprobado la solicitud:

kubectl get clusterapprovalrequest

La salida debería parecerse al siguiente ejemplo.

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

Ahora ClusterStagedUpdateRun puede continuar y completar:

kubectl get clusterstagedupdaterrun example-run

La salida debería parecerse al siguiente ejemplo.

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

Verifique la finalización del lanzamiento

ClusterResourcePlacement También muestra que el despliegue está completado y están disponibles todos los recursos en los clústeres miembros.

kubectl get clusterresourceplacement example-placement

La salida debería parecerse al siguiente ejemplo.

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

ConfigMap test-cm debe implementarse en los tres clústeres miembros, con los datos más recientes:

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

Detener una ejecución de actualización escalonada

Para detener la ejecución de una actualización programada de un clúster en funcionamiento, debe modificar el campo state en la especificación a Stop. Esta acción detiene correctamente la ejecución de la actualización, lo que permite que los clústeres en curso completen sus actualizaciones antes de detener todo el proceso de lanzamiento:

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

La ejecución de la actualización por etapas se ha inicializado y ya no está en ejecución.

kubectl get clusterstagedupdaterun example-run

La salida debería parecerse al siguiente ejemplo.

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

Un vistazo más detallado al estado después de que se detenga la ejecución de la actualización:

kubectl get clusterstagedupdaterun example-run -o yaml

La salida debería parecerse al siguiente ejemplo.

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"

Implementación de una segunda ejecución de actualización preconfigurada para revertir a una versión anterior

Supongamos que el administrador de la carga de trabajo quiere revertir el cambio de ConfigMap y revertir el valor value2 a value1. En lugar de actualizar manualmente el ConfigMap desde el hub, pueden crear un nuevo ClusterStagedUpdateRun con un índice de instantánea de recursos anterior, "0" en nuestro contexto, y pueden reutilizar la misma estrategia.

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

Vamos a comprobar el nuevo ClusterStagedUpdateRun:

kubectl get clusterstagedupdaterun

La salida debería parecerse al siguiente ejemplo.

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

Después de que transcurra un minuto TimedWait, deberíamos ver el objeto ClusterApprovalRequest creado para el nuevo ClusterStagedUpdateRun:

kubectl get clusterapprovalrequest

La salida debería parecerse al siguiente ejemplo.

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

Para aprobar el nuevo ClusterApprovalRequest objeto, vamos a reutilizar el mismo approval.json archivo para aplicarle revisiones:

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

Compruebe si el nuevo objeto está aprobado:

kubectl get clusterapprovalrequest                                                                            

La salida debería parecerse al siguiente ejemplo.

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 ahora debe implementarse en los tres clústeres miembros, con los datos revertidos a value1:

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

Limpieza de recursos

Cuando haya terminado con este tutorial, puede limpiar los recursos que ha creado:

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

Preparación de cargas de trabajo de Kubernetes para ubicación

Publique cargas de trabajo de Kubernetes en el clúster de concentrador para que se puedan colocar en clústeres miembro.

Cree un espacio de nombres y un ConfigMap para la carga de trabajo en el clúster central:

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

Como ResourcePlacement está limitado al espacio de nombres, implemente primero el espacio de nombres en todos los clústeres miembro mediante ClusterResourcePlacement, indicando NamespaceOnly según el ámbito de selección:

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

Compruebe que el espacio de nombres está implementado en todos los clústeres miembro:

kubectl get clusterresourceplacement test-namespace-placement

La salida debería parecerse al siguiente ejemplo.

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

Para implementar el ConfigMap, cree un ResourcePlacement limitado a un espacio de nombres.

Note

spec.strategy.type se establece en External para permitir el lanzamiento desencadenado con un 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

Los tres clústeres deben estar planificados, ya que se usará la directiva PickAll, pero el ConfigMap no se deberá implementar en los clústeres miembro porque aún no se ha creado un StagedUpdateRun.

Confirme que la colocación está planificada:

kubectl get resourceplacement example-placement -n test-namespace

La salida debería parecerse al siguiente ejemplo.

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

Creación de una estrategia de actualización preconfigurada

Un StagedUpdateStrategy define el patrón de orquestación que agrupa los clústeres en fases y especifica la secuencia de lanzamiento. Selecciona los clústeres de miembros por etiquetas. Para nuestra demostración, creamos una con dos fases, almacenamiento provisional y valor controlado:

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%

Trabajar con instantáneas de recursos

Fleet Manager crea instantáneas de recursos cuando los recursos cambian si la ubicación tiene una estrategia de lanzamiento de RollingUpdate. Cada instantánea tiene un índice único que puede usar para hacer referencia a versiones específicas de los recursos.

Cuando una ubicación usa la estrategia de lanzamiento External, las instantáneas de recursos no se crean automáticamente. En su lugar, se crean al ejecutar una actualización escalonada. Esto significa que, cuando se crea por primera vez una ubicación con una External estrategia de despliegue, no hay instantáneas de recursos hasta que se ejecuta la primera ejecución de actualización escalonada.

Note

Si una ubicación antes usaba la estrategia RollingUpdate y se cambia a External, las instantáneas de recursos existentes permanecen disponibles. Puede hacer referencia a estas instantáneas existentes al crear ejecuciones de actualización en etapas.

Tip

Para más información sobre las instantáneas de recursos y cómo funcionan, consulte Instantáneas de recursos.

Revise las instantáneas de recursos actuales

Dado que ResourcePlacement usa la External estrategia, todavía no existen instantáneas de recursos. Vamos a comprobar:

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

La salida no debe mostrar ningún recurso:

No resources found in test-namespace namespace.

Creación de la primera instantánea de recursos

Para crear la primera captura de recursos, debe crear un con el campo omitido. El controlador de ejecución de actualizaciones detecta que no existen instantáneas y crea una automáticamente.

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

Una vez completada la ejecución de la actualización, compruebe las instantáneas de recursos:

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

La salida debería parecerse al siguiente ejemplo.

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

Ahora tiene una versión de la instantánea. Es la versión más reciente (kubernetes-fleet.io/is-latest-snapshot=true) actual y tiene el índice de recursos 0 (kubernetes-fleet.io/resource-index=0).

Creación de una nueva instantánea de recursos

Ahora modifique configMap con un nuevo valor:

kubectl edit configmap test-cm -n test-namespace

Actualice el valor de value1 a value2:

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

La salida debería parecerse al siguiente ejemplo.

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

Dado que la colocación utiliza la External estrategia, la nueva captura de estado del recurso no se crea automáticamente. Cree otra StagedUpdateRun con el campo resourceSnapshotIndexomitido para desencadenar la creación de una nueva instantánea:

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

Una vez completada la ejecución de la actualización, debería ver dos versiones de instantáneas de recursos con el índice 0 y 1 respectivamente:

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

La salida debería parecerse al siguiente ejemplo.

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

La etiqueta más reciente se establece en example-placement-1-snapshot, que contiene los datos configMap más recientes:

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

La salida debería parecerse al siguiente ejemplo.

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

Preparar una ejecución de actualización por etapas para desplegar los cambios

Un StagedUpdateRun ejecuta el lanzamiento de un ResourcePlacement tras un StagedUpdateStrategy. Para activar la ejecución de actualización escalonada en nuestro ResourcePlacement (RP), creamos un StagedUpdateRun que indica el nombre de RP, el nombre de la estrategia de ejecución de updateRun, el último índice de instantáneas de recursos ("1") y el estado en "Initialize":

Note

Al usar la External estrategia de lanzamiento, puede omitir el resourceSnapshotIndex campo si desea implementar los recursos más recientes. El controlador de ejecución de actualización crea automáticamente una nueva instantánea de recursos cuando resourceSnapshotIndex se omite.

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

La ejecución de la actualización escalonada se inicializa pero no está en ejecución:

kubectl get stagedupdaterrun example-run -n test-namespace

La salida debería parecerse al siguiente ejemplo.

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

Iniciar una ejecución de actualización preconfigurada

Para iniciar la ejecución de una actualización escalonada, debe aplicar un parche al campo state en la especificación a Run:

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

Note

También puede crear una ejecución de actualización con el campo state inicialmente establecido en Run, lo cual inicializa e inicia la ejecución de la actualización en un solo paso.

La ejecución de la actualización preconfigurada se inicializa y ejecuta:

kubectl get stagedupdaterrun example-run -n test-namespace

La salida debería parecerse al siguiente ejemplo.

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

Una vez transcurrido un minuto TimedWait, compruebe la solicitud de aprobación:

kubectl get approvalrequests -n test-namespace

La salida debería parecerse al siguiente ejemplo.

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

Aprobar la ejecución de la actualización escalonada

Podemos aprobar el ApprovalRequest creando un archivo de parche json y aplicándolo:

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

Asegúrese de que observedGeneration es el mismo que la generación del objeto de aprobación.

Envíe una solicitud de revisión para obtener la aprobación utilizando el archivo JSON creado.

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

A continuación, compruebe que ha aprobado la solicitud:

kubectl get approvalrequests -n test-namespace

La salida debería parecerse al siguiente ejemplo.

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

Ahora StagedUpdateRun puede continuar y completar:

kubectl get stagedupdaterrun example-run -n test-namespace

La salida debería parecerse al siguiente ejemplo.

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

Verifique la finalización del lanzamiento

ResourcePlacement También muestra que el despliegue está completado y están disponibles todos los recursos en los clústeres miembros.

kubectl get resourceplacement example-placement -n test-namespace

La salida debería parecerse al siguiente ejemplo.

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

ConfigMap test-cm debe implementarse en los tres clústeres miembros, con los datos más recientes:

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

Detener una ejecución de actualización escalonada

Para detener la ejecución de una actualización escalonada en curso, debe aplicar revisiones al campo state en la especificación para pasarlo a Stop. Esta acción detiene correctamente la ejecución de la actualización, lo que permite que los clústeres en curso completen sus actualizaciones antes de detener todo el proceso de lanzamiento:

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

La ejecución de la actualización por etapas se ha inicializado y ya no está en ejecución.

kubectl get stagedupdaterun example-run -n test-namespace

La salida debería parecerse al siguiente ejemplo.

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

Un vistazo más detallado al estado después de que se detenga la ejecución de la actualización:

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

La salida debería parecerse al siguiente ejemplo.

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"

Implementación de una segunda ejecución de actualización preconfigurada para revertir a una versión anterior

Supongamos que el administrador de la carga de trabajo quiere revertir el cambio de ConfigMap y revertir el valor value2 a value1. En lugar de actualizar manualmente el configmap desde el hub, pueden crear un nuevo StagedUpdateRun con un índice de instantánea de recursos anterior, "0" en nuestro contexto, y pueden reutilizar la misma estrategia:

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

Vamos a comprobar el nuevo StagedUpdateRun:

kubectl get stagedupdaterun -n test-namespace

La salida debería parecerse al siguiente ejemplo.

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

Después de que transcurra un minuto TimedWait, deberíamos ver el objeto ApprovalRequest creado para el nuevo StagedUpdateRun:

kubectl get approvalrequests -n test-namespace

La salida debería parecerse al siguiente ejemplo.

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

Para aprobar el nuevo ApprovalRequest objeto, vamos a reutilizar el mismo approval.json archivo para aplicarle revisiones:

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

Compruebe si el nuevo objeto está aprobado:

kubectl get approvalrequests -n test-namespace

La salida debería parecerse al siguiente ejemplo.

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 ahora debe implementarse en los tres clústeres miembros, con los datos revertidos a value1:

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

Limpieza de recursos

Cuando haya terminado con este tutorial, puede limpiar los recursos que ha creado:

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

Diferencias clave entre los enfoques

Aspecto De ámbito de clúster De ámbito de espacio de nombres
Recurso de estrategia ClusterStagedUpdateStrategy (nombre corto: csus) StagedUpdateStrategy (nombre corto: sus)
Actualizar recurso de ejecución ClusterStagedUpdateRun (nombre corto: csur) StagedUpdateRun (nombre corto: sur)
Selección de ubicación de destino ClusterResourcePlacement (nombre corto: crp) ResourcePlacement (nombre corto: rp)
Recurso de aprobación ClusterApprovalRequest (nombre corto: careq) ApprovalRequest (nombre corto: areq)
Recurso de instantánea ClusterResourceSnapshot ResourceSnapshot
Ámbito En todo el clúster Enlazado al espacio de nombres
Caso de uso Implementaciones de infraestructura Lanzamientos de aplicaciones
Permisos Nivel de administrador del clúster Nivel de espacio de nombres

Pasos siguientes

En este artículo, ha aprendido a usar ejecuciones de actualizaciones preconfiguradas para orquestar las implementaciones entre clústeres miembro. Ha creado estrategias de actualización preconfiguradas para despliegues con ámbito de clúster y con ámbito de espacio de nombres, ha ejecutado implementaciones progresivas y ha realizado reversiones a versiones anteriores.

Para más información sobre las ejecuciones de actualizaciones preconfiguradas y los conceptos relacionados, consulte los siguientes recursos: