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
RKE2ControlPlaneen elcapi-provisioning-example.yamlque 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
rolloutStrategyTened en cuenta que en un clúster
RKE2de varios nodos con al menos 3 nodos de plano de control, establecer el campoRKE2ControlPlane.spec.rolloutStrategy.typeen"RollingUpdate"y elrolloutStrategy.rollingUpdate.maxSurgeen0garantiza que no se creen ni se necesiten nodos adicionales durante la actualización cuando elMetal3MachineTemplate.spec.nodeReuserelacionado también se establece enTrue(como se explica a continuación).
Cambiad la versión del clúster
RKE2a 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"Cambiad el bloque
Metal3MachineTemplateen elcapi-provisioning-example.yamlque se muestra en la siguiente sección (Capítulo 52, Aprovisionamiento de clústeres en sentido descendente con aprovisionamiento de red dirigida (nodo único)):Cambiad el nombre de la imagen y la suma de comprobación por la nueva versión generada en el paso anterior.
Añadid la directiva
nodeReuseatruepara evitar la creación de un nuevo nodo.Añadid la directiva
automatedCleaningModeametadatapara habilitar la limpieza automatizada del nodo.
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}.rawAntes 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'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.yaml60.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
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:
Descubrimiento: Identifica la versión más reciente de la imagen de Longhorn Engine desplegada en el clúster
Selección de candidatos: Localiza todas las versiones de imagen de Longhorn Engine más antiguas sin referencias de volumen activas
Ejecución de la limpieza: Elimina las imágenes de Longhorn Engine obsoletas que cumplen los criterios de seguridad
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.ioCronJob: 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.
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-cleanupConfigurad los permisos RBAC
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]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
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"Aplicad la configuración
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.yamlVerificad que el CronJob se haya creado:
kubectl get cronjob -n longhorn-system longhorn-engineimage-cleanupSalida 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-systemVigilad la ejecución manual:
Observad el estado del trabajo:
kubectl get jobs -n longhorn-system -wVisualizad 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-cleanupVisualizad 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-system60.4.5.4 Verificación #
Tras la ejecución de la limpieza, verificad que solo permanezcan las imágenes de Longhorn Engine actuales:
Enumerad todas las imágenes de Longhorn Engine:
kubectl get engineimages.longhorn.io -n longhorn-systemComprobad 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