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
RKE2ControlPlanenocapi-provisioning-example.yamlmostrado na seguinte seção (Capítulo 52, Provisionamento de cluster downstream com provisionamento de rede direcionado (nó único)):Especifique o desejado
rolloutStrategyObserve que, em um cluster
RKE2de vários nós com pelo menos 3 nós de plano de controle, definir o campoRKE2ControlPlane.spec.rolloutStrategy.typecomo"RollingUpdate"e orolloutStrategy.rollingUpdate.maxSurgecomo0garante que nenhum nó extra seja criado ou necessário durante fazer upgrade, quando oMetal3MachineTemplate.spec.nodeReuserelacionado também estiver definido comoTrue(conforme explicado abaixo).
Altere a versão do cluster
RKE2para 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"Altere o bloco
Metal3MachineTemplatenocapi-provisioning-example.yamlmostrado na seguinte seção (Capítulo 52, Provisionamento de cluster downstream com provisionamento de rede direcionado (nó único)):Altere o nome da imagem e o checksum para a nova versão gerada na etapa anterior.
Adicione a diretiva
nodeReuseatruepara evitar a criação de um novo nó.Adicione a diretiva
automatedCleaningModeametadatapara habilitar a limpeza automatizada para o nó.
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 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'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.yaml60.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
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:
Descoberta: Identifica a versão mais recente da imagem do Longhorn Engine implantada no cluster
Seleção de candidatos: Localiza todas as versões de imagem do Longhorn Engine mais antigas sem referências de volume ativas
Execução da limpeza: Exclui imagens do Longhorn Engine obsoletas que atendem aos critérios de segurança
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.ioCronJob: 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.
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-cleanupConfigurar permissões de RBAC
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]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
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"Aplique a configuração
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.yamlVerifique se o CronJob foi criado:
kubectl get cronjob -n longhorn-system longhorn-engineimage-cleanupSaí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-systemMonitore a execução manual:
Observe o status do job:
kubectl get jobs -n longhorn-system -wVisualize 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-cleanupVisualizar 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-system60.4.5.4 Verificação #
Após a execução da limpeza, verifique se apenas as imagens atuais do Longhorn Engine permanecem:
Listar todas as imagens do Longhorn Engine:
kubectl get engineimages.longhorn.io -n longhorn-systemVerificar 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