|Index|SUSE Telco Cloud Documentação|Operações do Dia Dois|Ações de ciclo de vida
Aplica-se a SUSE Telco Cloud 3.7

60 Ações de ciclo de vida

Esta seção aborda as ações de gerenciamento de ciclo de vida para clusters implantados via SUSE Telco Cloud.

60.1 Exclusão de balanceador de carga

Existem muitas ações de ciclo de vida que exigem que os nós sejam drenados. Durante o processo de drenagem, todos os pods serão movidos para outros nós no cluster. Após a conclusão do processo de drenagem, o nó não hospeda nenhum serviço e, portanto, não deve ter nenhum tráfego roteado para ele. Balanceadores de carga, como o MetalLB, podem ser informados disso aplicando um rótulo ao nó:

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

Para mais detalhes, consulte: Documentação do Kubernetes.

Para ver os rótulos em todos os seus nós em um cluster, você pode executar:

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

No caso de fazer upgrade de clusters downstream, isso pode ser automatizado anotando o RKE2ControlPlane no cluster de gerenciamento:

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

Isso cria imediatamente uma anotação em todos os objetos de máquina no cluster de gerenciamento para aquele RKE2ControlPlane.

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

Com essa anotação nos objetos de máquina, qualquer nó no cluster downstream que esteja agendado para drenagem receberá o rótulo de nó acima anexado antes do início do processo de drenagem. O rótulo será removido do nó assim que ele estiver disponível e pronto novamente.

60.2 Upgrades do cluster de gerenciamento

O upgrade do cluster de gerenciamento é descrito na documentação do Day 2 cluster de gerenciamento (Capítulo 59, Cluster de gerenciamento).

60.3 Upgrades de clusters downstream

O fazer upgrade de clusters downstream envolve a atualização de vários componentes. As seções a seguir cobrem o processo de fazer upgrade para cada um dos componentes.

Atualizando o sistema operacional

Para este processo, verifique a seguinte referência (Capítulo 50, Preparar a imagem do cluster downstream para cenários conectados) para criar a nova imagem com uma nova versão do sistema operacional. Com esta nova imagem gerada por EIB, a próxima fase de provisionamento utiliza a nova versão do sistema operacional fornecida. Na etapa a seguir, a nova imagem é usada para atualizar os nós.

Atualizando o cluster RKE2

As alterações necessárias para atualizar o cluster RKE2 usando o fluxo de trabalho automatizado são as seguintes:

  • Altere o bloco RKE2ControlPlane no capi-provisioning-example.yaml mostrado na seguinte seção (Capítulo 52, Provisionamento de cluster downstream com provisionamento de rede direcionado (nó único)):

    • Especifique o desejado rolloutStrategy

      • Observe que, em um cluster RKE2 de vários nós com pelo menos 3 nós de plano de controle, definir o campo RKE2ControlPlane.spec.rolloutStrategy.type como "RollingUpdate" e o rolloutStrategy.rollingUpdate.maxSurge como 0 garante que nenhum nó extra seja criado ou necessário durante fazer upgrade, quando o Metal3MachineTemplate.spec.nodeReuse relacionado também estiver definido como True (conforme explicado abaixo).

    • Altere a versão do cluster RKE2 para a nova versão substituindo ${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 o arquivo capi-provisioning-example.yaml, é sempre uma boa prática informar os balanceadores de carga externos (por exemplo, MetalLB) sobre os nós que estão sendo drenados para que eles não encaminhem tráfego para nós nesse estado. Como mencionado na seção Seção 60.1, “Exclusão de balanceador de carga”, você pode automatizar isso anotando o RKE2ControlPlane no cluster de gerenciamento. Neste exemplo, um objeto RKE2ControlPlane chamado multinode-cluster é anotado:

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

Verifique se os objetos de máquina foram anotados:

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

Obtenha as anotações para todos os seus objetos de máquina:

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

Sem essas anotações, os usuários podem experimentar tempos de resposta mais longos para os serviços, já que os balanceadores de carga não estão cientes dos nós drenados.

Após fazer essas alterações, o arquivo capi-provisioning-example.yaml pode ser aplicado ao cluster usando o seguinte comando:

kubectl apply -f capi-provisioning-example.yaml

60.4 Limpeza de Imagem do Longhorn Engine

Esta seção descreve como implementar a limpeza automatizada de imagens obsoletas do Longhorn Engine que se acumulam após fazer upgrade do cluster.

60.4.1 Declaração do problema

O Longhorn não remove automaticamente imagens antigas do Longhorn Engine após fazer upgrade. Esta é uma decisão de design intencional para evitar perda acidental de dados — o Longhorn retém versões anteriores da imagem do Longhorn Engine como uma medida de segurança caso seja necessário um rollback.

No entanto, em ambientes de produção com upgrades frequentes, essas imagens do Longhorn Engine não utilizadas se acumulam com o tempo, resultando em:

  • Consumo de armazenamento: Cada imagem do Longhorn Engine consome espaço em disco nos nós do cluster

  • Sobrecarga de recursos: O Kubernetes gerencia recursos de imagem adicionais desnecessariamente

  • Complexidade operacional: Identificação e limpeza manual de imagens obsoletas

60.4.2 Quando implementar a limpeza

A limpeza de imagens do Longhorn Engine é recomendada nos seguintes cenários:

  • Atualizações regulares de cluster: Ambientes que realizam upgrades frequentes de versão do Longhorn

  • Implantações com restrição de armazenamento: Clusters com capacidade de armazenamento de nó limitada

  • Higiene operacional: Como parte dos procedimentos de manutenção de rotina do Dia 2

Nota
Nota

Este procedimento de limpeza é classificado como baixo risco porque remove apenas imagens do Longhorn Engine com zero referências ativas (refCount == 0). Imagens atualmente em uso por volumes são protegidas e não serão excluídas.

60.4.3 Arquitetura da solução

A solução de limpeza usa um CronJob do Kubernetes que executa tarefas periódicas de manutenção:

  1. Descoberta: Identifica a versão mais recente da imagem do Longhorn Engine implantada no cluster

  2. Seleção de candidatos: Localiza todas as versões de imagem do Longhorn Engine mais antigas sem referências de volume ativas

  3. Execução da limpeza: Exclui imagens do Longhorn Engine obsoletas que atendem aos critérios de segurança

  4. Registro de auditoria: Registra operações de limpeza para visibilidade operacional

A implementação requer:

  • ServiceAccount: Para autenticação na API do Kubernetes

  • Permissões RBAC: Acesso de leitura e exclusão aos recursos engineimages.longhorn.io

  • CronJob: Contêiner de execução agendada com lógica de limpeza

60.4.4 Implementação

A solução completa de limpeza consiste em quatro manifestos do Kubernetes implantados no namespace longhorn-system.

  1. Create ServiceAccount

    Crie uma ServiceAccount dedicada para o CronJob de limpeza:

    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: longhorn-engineimage-cleanup
      namespace: longhorn-system
      labels:
        app: longhorn-engineimage-cleanup
  2. Configurar permissões de RBAC

    1. Defina uma Role com as permissões mínimas necessárias:

      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. Vincule a Role à 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. Implantar CronJob de limpeza

    Implante o CronJob de limpeza 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. Aplique a configuração

    1. Salve todos os quatro manifestos em um único arquivo (por exemplo, longhorn-engine-cleanup.yaml), então implante-os no cluster:

      kubectl apply -f longhorn-engine-cleanup.yaml
    2. Verifique se o CronJob foi criado:

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

      Saída esperada:

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

60.4.5 Procedimentos operacionais

Esta seção descreve as tarefas operacionais diárias para gerenciar o CronJob de limpeza de imagem do Longhorn Engine, incluindo execução manual, personalização de agendamento, monitoramento e verificação.

60.4.5.1 Execução manual

Para executar a limpeza imediatamente sem esperar pelo horário agendado, execute o seguinte comando:

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

Monitore a execução manual:

  1. Observe o status do job:

    kubectl get jobs -n longhorn-system -w
  2. Visualize os logs do job:

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

60.4.5.2 Configuração de agendamento

A programação padrão executa a limpeza semanalmente aos domingos às 2:00 AM UTC:

schedule: "0 2 * * 0"

Alternativas de programação comuns:

  • Diariamente às 3:00 AM: "0 3 * * *"

  • Mensalmente no dia 1º às 4:00 AM: "0 4 1 * *"

  • A cada 6 horas: "0 */6 * * *"

Modifique a programação:

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

60.4.5.3 Monitoramento e logs

Visualizar histórico de limpeza:

  • Listar tarefas concluídas:

    kubectl get jobs -n longhorn-system -l app=longhorn-engineimage-cleanup
  • Visualizar logs da última execução bem-sucedida:

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

Inspecionar execução de tarefa específica:

# 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 Verificação

Após a execução da limpeza, verifique se apenas as imagens atuais do Longhorn Engine permanecem:

  1. Listar todas as imagens do Longhorn Engine:

    kubectl get engineimages.longhorn.io -n longhorn-system
  2. Verificar contagens de referência:

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

Saída esperada mostrando apenas a versão mais recente com referências ativas:

NAME                                VERSION     REFCOUNT
ei-acba2c08                         v1.12.1     3