|Index|SUSE Telco Cloud Documentación|Operaciones del día 2|Acciones del ciclo de vida
Se aplica a SUSE Telco Cloud 3.7

60 Acciones del ciclo de vida

Esta sección cubre las acciones de gestión del ciclo de vida para clústeres desplegados a través de SUSE Telco Cloud.

60.1 Exclusión del equilibrador de carga

Existen muchas acciones del ciclo de vida que requieren que los nodos sean vaciados. Durante el proceso de vaciado, todos los pods se moverán a otros nodos del clúster. Una vez finalizado el proceso de vaciado, el nodo no aloja ningún servicio y, por tanto, no debería recibir tráfico dirigido a él. Los equilibradores de carga, como MetalLB, pueden ser informados de esto aplicando una etiqueta al nodo:

node.kubernetes.io/exclude-from-external-load-balancers: "true"

Para obtener más detalles, consulte: Documentación de Kubernetes.

Para ver las etiquetas en todos vuestros nodos de un clúster, podéis ejecutar:

kubectl get nodes -o json | jq -r '.items[].metadata | .name, .labels'

En el caso de actualizaciones de clústeres descendentes, esto se puede automatizar añadiendo una anotación al RKE2ControlPlane en el clúster de gestión:

rke2.controlplane.cluster.x-k8s.io/load-balancer-exclusion="true"

Esto crea inmediatamente una anotación en todos los objetos de máquina del clúster de gestión para ese RKE2ControlPlane.

pre-drain.delete.hook.machine.cluster.x-k8s.io/rke2-lb-exclusion: ""

Con esta anotación en los objetos de máquina, cualquier nodo del clúster descendente que esté programado para ser vaciado recibirá la etiqueta de nodo mencionada anteriormente antes de que comience el proceso de vaciado. La etiqueta se eliminará del nodo una vez que esté disponible y listo de nuevo.

60.2 Actualizaciones del clúster de gestión

La actualización del clúster de gestión se describe en la documentación del Day 2 clúster de gestión (Capítulo 59, Clúster de gestión).

60.3 Actualizaciones de clústeres descendentes

La actualización de los clústeres descendentes implica actualizar varios componentes. Las siguientes secciones cubren el proceso de actualización para cada uno de los componentes.

Actualización del sistema operativo

Para este proceso, consultad la siguiente referencia (Capítulo 50, Preparar la imagen del clúster descendente para escenarios conectados) para crear la nueva imagen con una nueva versión del sistema operativo. Con esta nueva imagen generada por EIB, la siguiente fase de aprovisionamiento utiliza la nueva versión del sistema operativo proporcionada. En el siguiente paso, se utiliza la nueva imagen para actualizar los nodos.

Actualización del clúster RKE2

Los cambios necesarios para actualizar el clúster RKE2 mediante el flujo de trabajo automatizado son los siguientes:

  • Cambiad el bloque RKE2ControlPlane en el capi-provisioning-example.yaml que se muestra en la siguiente sección (Capítulo 52, Aprovisionamiento de clústeres en sentido descendente con aprovisionamiento de red dirigida (nodo único)):

    • Especificad la deseada rolloutStrategy

      • Tened en cuenta que en un clúster RKE2 de varios nodos con al menos 3 nodos de plano de control, establecer el campo RKE2ControlPlane.spec.rolloutStrategy.type en "RollingUpdate" y el rolloutStrategy.rollingUpdate.maxSurge en 0 garantiza que no se creen ni se necesiten nodos adicionales durante la actualización cuando el Metal3MachineTemplate.spec.nodeReuse relacionado también se establece en True (como se explica a continuación).

    • Cambiad la versión del clúster RKE2 a la nueva versión sustituyendo ${RKE2_NEW_VERSION}.

apiVersion: controlplane.cluster.x-k8s.io/v1beta2
kind: RKE2ControlPlane
metadata:
  name: multinode-cluster
  namespace: default
spec:
  infrastructureRef:
    apiGroup: infrastructure.cluster.x-k8s.io
    kind: Metal3MachineTemplate
    name: multinode-cluster-controlplane
  version: ${RKE2_NEW_VERSION}
  replicas: 3
  rolloutStrategy:
    type: "RollingUpdate"
    rollingUpdate:
      maxSurge: 0
  serverConfig:
    cni: cilium
    tlsSan:
      - ${EDGE_VIP_ADDRESS_IPV4}
      - ${EDGE_VIP_ADDRESS_IPV6}
      - https://${EDGE_VIP_ADDRESS_IPV4}.sslip.io
      - https://${EDGE_VIP_ADDRESS_IPV6}.sslip.io
  rolloutStrategy:
    rollingUpdate:
      maxSurge: 0
  registrationMethod: "control-plane-endpoint"
  registrationAddress: ${EDGE_VIP_ADDRESS}
  agentConfig:
    format: ignition
    additionalUserData:
      config: |
        variant: fcos
        version: 1.4.0
        systemd:
          units:
          - name: rke2-preinstall.service
            enabled: true
            contents: |
              [Unit]
              Description=rke2-preinstall
              Wants=network-online.target
              Before=rke2-install.service
              ConditionPathExists=!/run/cluster-api/bootstrap-success.complete
              [Service]
              Type=oneshot
              User=root
              ExecStartPre=/bin/sh -c "mount -L config-2 /mnt"
              # Note we use _ as a delimiter for sed here because the providerid includes / characters
              ExecStart=/bin/sh -c "sed -i \"s_PROVIDERID_$(jq -r .providerid /mnt/openstack/latest/meta_data.json)_\" /etc/rancher/rke2/config.yaml"
              ExecStart=/bin/sh -c "echo \"node-name: $(jq -r .name /mnt/openstack/latest/meta_data.json)\" >> /etc/rancher/rke2/config.yaml"
              ExecStart=/bin/sh -c "echo \"node-label:\" >> /etc/rancher/rke2/config.yaml"
              ExecStart=/bin/sh -c "echo \"  - metal3.io/uuid=$(jq -r .uuid /mnt/openstack/latest/meta_data.json)\" >> /etc/rancher/rke2/config.yaml"
              ExecStartPost=/bin/sh -c "umount /mnt"
              [Install]
              WantedBy=multi-user.target
        storage:
          directories:
          - path: /var/lib/rancher/rke2/server/manifests
            overwrite: true
          files:
            - path: /var/lib/rancher/rke2/server/manifests/endpoint-copier-operator.yaml
              overwrite: true
              contents:
                inline: |
                  apiVersion: helm.cattle.io/v1
                  kind: HelmChart
                  metadata:
                    name: endpoint-copier-operator
                    namespace: kube-system
                  spec:
                    chart: oci://registry.suse.com/edge/charts/endpoint-copier-operator
                    targetNamespace: endpoint-copier-operator
                    version: {version-endpoint-copier-operator-chart}
                    createNamespace: true
            - path: /var/lib/rancher/rke2/server/manifests/metallb.yaml
              overwrite: true
              contents:
                inline: |
                  apiVersion: helm.cattle.io/v1
                  kind: HelmChart
                  metadata:
                    name: metallb
                    namespace: kube-system
                  spec:
                    chart: oci://registry.suse.com/edge/charts/metallb
                    targetNamespace: metallb-system
                    version: {version-metallb-chart}
                    createNamespace: true
            - path: /var/lib/rancher/rke2/server/manifests/metallb-cr.yaml
              overwrite: true
              contents:
                inline: |
                  apiVersion: metallb.io/v1beta1
                  kind: IPAddressPool
                  metadata:
                    name: kubernetes-vip-ip-pool
                    namespace: metallb-system
                  spec:
                    addresses:
                      - ${EDGE_VIP_ADDRESS_IPV4}/32
                      - ${EDGE_VIP_ADDRESS_IPV6}/128
                    serviceAllocation:
                      priority: 100
                      namespaces:
                        - default
                      serviceSelectors:
                        - matchExpressions:
                          - {key: "serviceType", operator: In, values: [kubernetes-vip]}
                  ---
                  apiVersion: metallb.io/v1beta1
                  kind: L2Advertisement
                  metadata:
                    name: ip-pool-l2-adv
                    namespace: metallb-system
                  spec:
                    ipAddressPools:
                      - kubernetes-vip-ip-pool
            - path: /var/lib/rancher/rke2/server/manifests/endpoint-svc.yaml
              overwrite: true
              contents:
                inline: |
                  apiVersion: v1
                  kind: Service
                  metadata:
                    name: kubernetes-vip
                    namespace: default
                    labels:
                      serviceType: kubernetes-vip
                  spec:
                    ipFamilyPolicy: PreferDualStack
                    ports:
                    - name: rke2-api
                      port: 9345
                      protocol: TCP
                      targetPort: 9345
                    - name: k8s-api
                      port: 6443
                      protocol: TCP
                      targetPort: 6443
                    type: LoadBalancer
            ###############################################################################
            # In case of deploying the RKE2 ingress controller, the following 2 files must
            # be added.
            # NOTE: The .providers stanza can be removed from the Helm parameters settings
            #       here below in case of not deploying/not planning to deploy Ingress
            #       objects including ingress-nginx annotations in its `.metadata` stanza.
            ###############################################################################
            - path: /var/lib/rancher/rke2/server/manifests/rke2-traefik-config.yaml
              overwrite: true
              contents:
                inline: |
                  apiVersion: helm.cattle.io/v1
                  kind: HelmChartConfig
                  metadata:
                    name: rke2-traefik
                    namespace: kube-system
                  spec:
                    valuesContent: |-
                      ingressClass:
                        isDefaultClass: false
                      ports:
                        web:
                          hostPort: null    # disallow hostPort
                          exposedPort: 80
                        websecure:
                          hostPort: null    # disallow hostPort
                          exposedPort: 443
                      service:
                        enabled: true
                        spec:
                          type: LoadBalancer
                          ipFamilyPolicy: PreferDualStack
                          externalTrafficPolicy: Local
                          allocateLoadBalancerNodePorts: false
                      providers:
                        kubernetesIngressNGINX:  # this provider allows Traefik to "understand" most of the Ingress-NGINX annotations
                          enabled: true
                          ingressClass: "rke2-ingress-nginx-migration"
                          controllerClass: "rke2.​cattle.​io/ingress-nginx-migration"
              mode: 0644
              user:
                name: root
              group:
                name: root
            - path: /var/lib/rancher/rke2/server/manifests/metallb-traefik-cr.yaml
              overwrite: true
              contents:
                inline: |
                  apiVersion: metallb.io/v1beta1
                  kind: IPAddressPool
                  metadata:
                    name: ingress-ippool
                    namespace: metallb-system
                  spec:
                    addresses:
                      - ${INGRESS_IPV4}/32
                      - ${INGRESS_IPV6}/128
                    serviceAllocation:
                      priority: 100
                      serviceSelectors:
                      - matchExpressions:
                        - {key: app.kubernetes.io/name, operator: In, values: [rke2-traefik]} # assign to rke2-traefik LB svc
                  ---
                  apiVersion: metallb.io/v1beta1
                  kind: L2Advertisement
                  metadata:
                    name: ingress-l2-adv
                    namespace: metallb-system
                  spec:
                    ipAddressPools:
                      - ingress-ippool
              mode: 0644
              user:
                name: root
              group:
                name: root
    kubelet:
      extraArgs:
      - provider-id=metal3://PROVIDERID
    nodeName: "localhost.localdomain"
apiVersion: infrastructure.cluster.x-k8s.io/v1beta2
kind: Metal3MachineTemplate
metadata:
  name: multinode-cluster-controlplane
  namespace: default
spec:
  nodeReuse: True
  template:
    spec:
      automatedCleaningMode: metadata
      dataTemplate:
        name: multinode-cluster-controlplane-template
      hostSelector:
        matchLabels:
          cluster-role: control-plane
      image:
        checksum: http://imagecache.local:8080/${NEW_IMAGE_GENERATED}.sha256
        checksumType: sha256
        diskFormat: raw
        url: http://imagecache.local:8080/${NEW_IMAGE_GENERATED}.raw

Antes de aplicar el archivo capi-provisioning-example.yaml, siempre es una buena práctica informar a los equilibradores de carga externos (p. ej., MetalLB) de que los nodos se están vaciando para que no dirijan tráfico a los nodos en este estado. Como se menciona en la sección Sección 60.1, “Exclusión del equilibrador de carga”, puedes automatizar esto anotando el RKE2ControlPlane en el clúster de gestión. En este ejemplo, se anota un objeto RKE2ControlPlane llamado multinode-cluster:

kubectl annotate  RKE2ControlPlane/multinode-cluster  rke2.controlplane.cluster.x-k8s.io/load-balancer-exclusion="true"

Verificad que los objetos de máquina se hayan anotado:

pre-drain.delete.hook.machine.cluster.x-k8s.io/rke2-lb-exclusion: ""

Obtened las anotaciones de todos vuestros objetos de máquina:

kubectl get machines -o json | jq -r '.items[].metadata | .name, .annotations'
Nota
Nota

Sin estas anotaciones, los usuarios podrían experimentar tiempos de respuesta más largos en los servicios, ya que los equilibradores de carga no detectan los nodos vaciados.

Tras realizar estos cambios, el archivo capi-provisioning-example.yaml puede aplicarse al clúster mediante el siguiente comando:

kubectl apply -f capi-provisioning-example.yaml

60.4 Limpieza de imágenes de Longhorn Engine

Esta sección describe cómo implementar la limpieza automatizada de imágenes de Longhorn Engine obsoletas que se acumulan después de las actualizaciones del clúster.

60.4.1 Planteamiento del problema

Longhorn no elimina automáticamente las imágenes de Longhorn Engine antiguas después de las actualizaciones. Esta es una decisión de diseño intencionada para evitar la pérdida accidental de datos: Longhorn conserva las versiones anteriores de la imagen de Longhorn Engine como medida de seguridad en caso de que sea necesaria una reversión.

Sin embargo, en entornos de producción con actualizaciones frecuentes, estas imágenes de Longhorn Engine no utilizadas se acumulan con el tiempo, lo que resulta en:

  • Consumo de almacenamiento: Cada imagen de Longhorn Engine consume espacio en disco en los nodos del clúster

  • Sobrecarga de recursos: Kubernetes gestiona recursos de imagen adicionales innecesariamente

  • Complejidad operativa: Identificación y limpieza manual de imágenes obsoletas

60.4.2 Cuándo implementar la limpieza

Se recomienda la limpieza de imágenes de Longhorn Engine en los siguientes escenarios:

  • Actualizaciones regulares del clúster: Entornos que realizan actualizaciones frecuentes de la versión de Longhorn

  • Despliegues con almacenamiento limitado: Clústeres con capacidad de almacenamiento de nodo limitada

  • Higiene operativa: Como parte de los procedimientos de mantenimiento rutinarios del Día 2

Nota
Nota

Este procedimiento de limpieza se clasifica como bajo riesgo porque solo elimina imágenes de Longhorn Engine con cero referencias activas (refCount == 0). Las imágenes de Longhorn Engine que utilizan actualmente los volúmenes están protegidas y no se eliminarán.

60.4.3 Arquitectura de la solución

La solución de limpieza utiliza un CronJob de Kubernetes que ejecuta tareas de mantenimiento periódicas:

  1. Descubrimiento: Identifica la versión más reciente de la imagen de Longhorn Engine desplegada en el clúster

  2. Selección de candidatos: Localiza todas las versiones de imagen de Longhorn Engine más antiguas sin referencias de volumen activas

  3. Ejecución de la limpieza: Elimina las imágenes de Longhorn Engine obsoletas que cumplen los criterios de seguridad

  4. Registro de auditoría: Registra las operaciones de limpieza para la visibilidad operativa

La implementación requiere:

  • ServiceAccount: Para la autenticación en la API de Kubernetes

  • Permisos RBAC: Acceso de lectura y eliminación a los recursos engineimages.longhorn.io

  • CronJob: Contenedor de ejecución programada con la lógica de limpieza

60.4.4 Implementación

La solución de limpieza completa consta de cuatro manifiestos de Kubernetes desplegados en el espacio de nombres longhorn-system.

  1. Create ServiceAccount

    Cread una ServiceAccount dedicada para el CronJob de limpieza:

    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: longhorn-engineimage-cleanup
      namespace: longhorn-system
      labels:
        app: longhorn-engineimage-cleanup
  2. Configurad los permisos RBAC

    1. Definid un Rol con los permisos mínimos requeridos:

      apiVersion: rbac.authorization.k8s.io/v1
      kind: Role
      metadata:
        name: longhorn-engineimage-cleanup
        namespace: longhorn-system
        labels:
          app: longhorn-engineimage-cleanup
      rules:
      - apiGroups: [longhorn.io]
        resources: [engineimages]
        verbs: [get, list, delete]
    2. Vinculad el Rol a la ServiceAccount:

      apiVersion: rbac.authorization.k8s.io/v1
      kind: RoleBinding
      metadata:
        name: longhorn-engineimage-cleanup
        namespace: longhorn-system
        labels:
          app: longhorn-engineimage-cleanup
      subjects:
      - kind: ServiceAccount
        name: longhorn-engineimage-cleanup
        namespace: longhorn-system
      roleRef:
        kind: Role
        name: longhorn-engineimage-cleanup
        apiGroup: rbac.authorization.k8s.io
  3. Desplegad el CronJob de limpieza

    Desplegad el CronJob de limpieza automatizada:

    apiVersion: batch/v1
    kind: CronJob
    metadata:
      name: longhorn-engineimage-cleanup
      namespace: longhorn-system
      labels:
        app: longhorn-engineimage-cleanup
      annotations:
        description: "Cleanup old Longhorn engine images left over after upgrades"
        source: "SUSE Edge Telco Cloud - Day 2 Operations"
    spec:
      # Execute weekly on Sunday at 2:00 AM UTC
      schedule: "0 2 * * 0"
      successfulJobsHistoryLimit: 3
      failedJobsHistoryLimit: 3
      concurrencyPolicy: Forbid
      jobTemplate:
        spec:
          backoffLimit: 2
          activeDeadlineSeconds: 600
          template:
            metadata:
              labels:
                app: longhorn-engineimage-cleanup
            spec:
              serviceAccountName: longhorn-engineimage-cleanup
              restartPolicy: Never
              automountServiceAccountToken: true
              containers:
              - name: cleanup
                image: registry.suse.com/bci/bci-base:15.6
                command:
                - sh
                - -c
                - |
                  set -e
    
                  # Install required tools
                  zypper --non-interactive install curl jq
    
                  # Install kubectl from official Kubernetes
                  curl -LO "https://dl.k8s.io/release/v1.36.3/bin/linux/amd64/kubectl"
                  chmod +x kubectl
                  mv kubectl /usr/local/bin/
    
                  echo "=== Longhorn Engine Image Cleanup ==="
                  echo "Automated maintenance - Day 2 Operations"
                  echo ""
    
                  echo "Step 1: Discovering latest engine image version..."
    
                  ENGINE_IMAGES=$(kubectl get engineimages.longhorn.io -n longhorn-system -o json)
    
                  LATEST_VERSION=$(echo "$ENGINE_IMAGES" | jq -r '
                    .items[]
                    | select(.status.version != null)
                    | .status.version' |
                    sed 's/^v//' |
                    sort -V |
                    tail -1)
    
                  if [ -z "$LATEST_VERSION" ]; then
                    echo "No engine images found. Nothing to cleanup."
                    exit 0
                  fi
    
                  echo "Latest version detected: v${LATEST_VERSION}"
                  echo ""
    
                  echo "Step 2: Identifying obsolete engine images..."
    
                  TO_DELETE=$(echo "$ENGINE_IMAGES" | jq -r --arg latest "v${LATEST_VERSION}" '
                    .items[]
                    | select(.status.version != $latest)
                    | select(.status.refCount == 0)
                    | .metadata.name')
    
                  if [ -z "$TO_DELETE" ]; then
                    echo "No obsolete engine images found"
                    echo "All engine images are either:"
                    echo "  - Latest version (v${LATEST_VERSION})"
                    echo "  - Still in use (refCount > 0)"
                    exit 0
                  fi
    
                  DELETE_COUNT=$(echo "$TO_DELETE" | wc -l | tr -d ' ')
                  echo "Found $DELETE_COUNT obsolete engine image(s) for deletion:"
                  echo "$TO_DELETE"
                  echo ""
    
                  COUNT=0
                  FAILED=0
                  for IMAGE_NAME in $TO_DELETE; do
                    VERSION=$(echo "$ENGINE_IMAGES" | jq -r --arg name "$IMAGE_NAME" \
                      '.items[] | select(.metadata.name == $name) | .status.version')
                    REFCOUNT=$(echo "$ENGINE_IMAGES" | jq -r --arg name "$IMAGE_NAME" \
                      '.items[] | select(.metadata.name == $name) | .status.refCount')
    
                    echo "Deleting: $IMAGE_NAME (version: $VERSION, refCount: $REFCOUNT)"
    
                    if kubectl delete engineimages.longhorn.io "$IMAGE_NAME" -n longhorn-system; then
                      COUNT=$((COUNT + 1))
                      echo "  ✓ Deleted successfully"
                    else
                      FAILED=$((FAILED + 1))
                      echo "  ✗ Failed to delete"
                    fi
                  done
    
                  echo ""
                  echo "=== Cleanup Summary ==="
                  echo "Latest version: v${LATEST_VERSION}"
                  echo "Successfully deleted: $COUNT obsolete engine images"
                  echo "Failed deletions: $FAILED engine images"
    
                  if [ $FAILED -gt 0 ]; then
                    exit 1
                  fi
    
                  echo ""
                  echo "Cleanup completed successfully"
  4. Aplicad la configuración

    1. Guardad los cuatro manifiestos en un solo archivo (p. ej., longhorn-engine-cleanup.yaml), luego desplegadlos en el clúster:

      kubectl apply -f longhorn-engine-cleanup.yaml
    2. Verificad que el CronJob se haya creado:

      kubectl get cronjob -n longhorn-system longhorn-engineimage-cleanup

      Salida esperada:

      NAME                            SCHEDULE     SUSPEND   ACTIVE   LAST SCHEDULE   AGE
      longhorn-engineimage-cleanup    0 2 * * 0    False     0        <none>          5s

60.4.5 Procedimientos operativos

Esta sección describe las tareas operativas diarias para gestionar el CronJob de limpieza de imágenes de Longhorn Engine, incluida la ejecución manual, la personalización de la programación, la supervisión y la verificación.

60.4.5.1 Ejecución manual

Para ejecutar la limpieza inmediatamente sin esperar a la hora programada, ejecutad el siguiente comando:

kubectl create job --from=cronjob/longhorn-engineimage-cleanup \
  longhorn-engineimage-cleanup-manual-$(date +%Y%m%d-%H%M%S) \
  -n longhorn-system

Vigilad la ejecución manual:

  1. Observad el estado del trabajo:

    kubectl get jobs -n longhorn-system -w
  2. Visualizad los registros del trabajo:

    kubectl logs -n longhorn-system -l app=longhorn-engineimage-cleanup --tail=100

60.4.5.2 Configuración de la programación

La programación predeterminada ejecuta la limpieza semanalmente los domingos a las 2:00 AM UTC:

schedule: "0 2 * * 0"

Alternativas de programación comunes:

  • Diariamente a las 3:00 AM: "0 3 * * *"

  • Mensualmente el día 1 a las 4:00 AM: "0 4 1 * *"

  • Cada 6 horas: "0 */6 * * *"

Modificad la programación:

kubectl patch cronjob longhorn-engineimage-cleanup \
  -n longhorn-system \
  --type='merge' \
  -p '{"spec":{"schedule":"0 3 * * *"}}'

60.4.5.3 Monitorización y registros

Visualizad el historial de limpieza:

  • Enumerad los trabajos completados:

    kubectl get jobs -n longhorn-system -l app=longhorn-engineimage-cleanup
  • Visualizad los registros de la última ejecución correcta:

    kubectl logs -n longhorn-system \
      -l app=longhorn-engineimage-cleanup \
      --tail=200 \
      | grep -A 50 "Cleanup Summary"

Inspeccionad la ejecución de un trabajo específico:

# Get job name
JOB_NAME=$(kubectl get jobs -n longhorn-system \
  -l app=longhorn-engineimage-cleanup \
  --sort-by=.metadata.creationTimestamp \
  -o jsonpath='{.items[-1].metadata.name}')

# View job details
kubectl describe job $JOB_NAME -n longhorn-system

# View job logs
kubectl logs job/$JOB_NAME -n longhorn-system

60.4.5.4 Verificación

Tras la ejecución de la limpieza, verificad que solo permanezcan las imágenes de Longhorn Engine actuales:

  1. Enumerad todas las imágenes de Longhorn Engine:

    kubectl get engineimages.longhorn.io -n longhorn-system
  2. Comprobad los recuentos de referencia:

    kubectl get engineimages.longhorn.io -n longhorn-system \
      -o custom-columns=NAME:.metadata.name,VERSION:.status.version,REFCOUNT:.status.refCount

Salida esperada que muestra solo la última versión con referencias activas:

NAME                                VERSION     REFCOUNT
ei-acba2c08                         v1.12.1     3