Compartilhar via


Configurar políticas de interrupção de nós para nós de provisionamento automático (NAP) no Serviço de Kubernetes do Azure (AKS)

Este artigo explica como configurar políticas de interrupção de nó para nós de provisionamento automático de nós (NAP) do Serviço de Kubernetes do Azure (AKS) e detalha como a interrupção otimiza a utilização de recursos e a eficiência de custos.

NAP otimiza seu cluster:

  • Removendo ou substituindo nós subutilizados.
  • Consolidando cargas de trabalho para reduzir custos.
  • Respeitando orçamentos para interrupção e janelas de manutenção.
  • Fornecendo controle manual quando necessário.

Antes de começar

Como funciona a interrupção dos nós NAP?

Karpenter define um finalizador do Kubernetes em cada nó e a declaração de nó que ele provisiona. O finalizador bloqueia a exclusão do objeto de nó, enquanto o Controlador de Terminação mancha e drena o nó antes de remover a declaração de nó subjacente.

Quando as cargas de trabalho em seus nós reduzem verticalmente, o NAP usa regras de interrupção na especificação do pool de nós para decidir quando e como remover esses nós e potencialmente reagendar suas cargas de trabalho para serem mais eficientes.

Métodos de perturbação do nó

O NAP descobre automaticamente nós elegíveis para interrupção e ativa substituições quando necessário. Você pode disparar a disrupção por meio de métodos automatizados como Expiração, Consolidação e Descompasso, métodos manuais ou sistemas externos.

Expiração

A configuração de expiração permite que você defina uma idade máxima para seus nós NAP. Os nós são marcados como expirados e interrompidos após atingirem a idade especificada para o valor spec.disruption.expireAfter pool de nós.

Configuração de expiração de exemplo

O exemplo a seguir mostra como definir o tempo de expiração para nós NAP em 24 horas:

spec:
  disruption:
    expireAfter: 24h  # Expire nodes after 24 hours

Consolidation

O NAP funciona para reduzir ativamente o custo do cluster identificando quando os nós podem ser removidos porque estão vazios ou subutilizados ou quando os nós podem ser substituídos por variantes com preços mais baixos. Esse processo é chamado consolidação. O NAP usa principalmente a consolidação para excluir ou substituir nós, para o posicionamento ideal do pod.

O NAP executa os seguintes tipos de consolidação para otimizar a utilização de recursos:

  • Consolidação de nós vazios: exclui nós vazios em paralelo.
  • Consolidação de vários nós: exclui vários nós, possivelmente iniciando uma única substituição.
  • Consolidação de nó único: exclui qualquer nó único, possivelmente iniciando uma substituição.

Você pode desencadear a consolidação por meio do campo spec.disruption.consolidationPolicy na especificação do pool de nós usando as configurações WhenEmpty ou WhenEmptyOrUnderUtilized. Você também pode definir o campo consolidateAfter, que é uma condição baseada em tempo que determina por quanto tempo o NAP aguarda após descobrir uma oportunidade de consolidação antes de interromper a operação do nó.

Exemplo de configuração de consolidação

O exemplo a seguir mostra como configurar o NAP para consolidar nós quando eles estiverem vazios e aguardar 30 segundos depois de descobrir uma oportunidade de consolidação antes de interromper o nó:

  disruption:
    # Describes which types of nodes NAP should consider for consolidation
    # `WhenEmptyOrUnderUtilized`: NAP considers all nodes for consolidation and attempts to remove or replace nodes when it discovers that the node is empty or underutilized and could be changed to reduce cost
    # `WhenEmpty`: NAP only considers nodes for consolidation that don't contain any workload pods
    
    consolidationPolicy: WhenEmpty

    # The amount of time NAP should wait after discovering a consolidation decision
    # Currently, you can only set this value when the consolidation policy is `WhenEmpty`
    # You can choose to disable consolidation entirely by setting the string value `Never`
    consolidateAfter: 30s

Drift

Drift gerencia as alterações nos recursos NodePool/AKSNodeClass. Valores em NodeClaimTemplateSpec/AKSNodeClassSpec são refletidos da mesma forma que são definidos. Um NodeClaim é detectado como em desvio se os valores nos elementos associados ao NodePool/AKSNodeClass não corresponderem aos valores no NodeClaim. Semelhante à relação upstream deployment.spec.template com pods, Karpenter anota o associado NodePool/AKSNodeClass com um hash NodeClaimTemplateSpec para verificar se há descompasso. Karpenter remove a condição de status Drifted nos seguintes cenários:

  • O gate de recurso Drift não está habilitado, mas o NodeClaim está desviado.
  • O NodeClaim não está desviado, mas tem a condição de estado.

O Karpenter ou a interface do provedor de nuvem podem descobrir casos especiais disparados por alterações NodeClaim/Instance/NodePool/AKSNodeClass.

Casos especiais em descompasso

Em casos especiais, o descompasso pode corresponder a vários valores e deve ser tratado de forma diferente. O descompasso em campos resolvidos pode criar casos em que o descompasso ocorre sem alterações nas CRDs (Definições de Recurso Personalizado) ou em que as alterações de CRD não resultam em descompasso.

Por exemplo, quando um NodeClaim tem node.kubernetes.io/instance-type: Standard_D2s_v3, e os requisitos mudam de node.kubernetes.io/instance-type In [Standard_D2s_v3] para node.kubernetes.io/instance-type In [Standard_D2s_v3, Standard_D4s_v3], o NodeClaim não está desalinhado pois seu valor ainda é compatível com os novos requisitos. Por outro lado, se um NodeClaim usa um NodeClaimimageFamily, mas o campo spec.imageFamily é alterado, Karpenter detecta o NodeClaim como descompasso e gira o nó para atender a essa especificação.

Importante

O Karpenter monitora as alterações de configuração de sub-rede e detecta desvio quando o vnetSubnetID em um AKSNodeClass é modificado. Entender esse comportamento é fundamental ao gerenciar configurações de rede personalizadas. Para obter mais informações, consulte comportamento de desvio de sub-rede.

Para obter mais informações, consulte Drift Design.

Período de carência de rescisão

Você pode definir um período de carência de término para nós NAP usando o campo spec.template.spec.terminationGracePeriod na especificação do pool de nós. Essa configuração permite que você configure por quanto tempo o Karpenter aguarda que os pods terminem normalmente. Essa configuração tem precedência sobre pod terminationGracePeriodSeconds e ignora PodDisruptionBudgets e a anotação karpenter.sh/do-not-disrupt.

Exemplo de configuração de período de carência de término

O exemplo a seguir mostra como definir um período de carência de término de 30 segundos para nós NAP:

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: default
spec:
  template:
    spec:
      terminationGracePeriod: 30s

Orçamentos para disrupções

Você pode limitar a interrupção de Karpenter modificando o campo spec.disruption.budgets na especificação do pool de nós. Se você deixar essa configuração indefinida, o Karpenter usará como padrão um orçamento com nodes: 10%. Os orçamentos levam em conta nós que estão sendo deletados por qualquer motivo, e impedem Karpenter apenas de interrupções voluntárias causadas por expiração, descompasso, ociosidade e consolidação.

Ao calcular se um orçamento impede a disrupção dos nós, o Karpenter conta o total de nós pertencentes a um pool de nós e, em seguida, subtrai os nós que estão sendo excluídos e os nós que são NotReady. Se o orçamento estiver configurado com um valor percentual, como 20%, Karpenter calculará o número de interrupções permitidas como allowed_disruptions = roundup(total * percentage) - total_deleting - total_notready. Para vários orçamentos em um grupo de nós, Karpenter usa o valor mínimo (mais restritivo) de cada um dos orçamentos.

Campos de agenda e duração

Ao usar orçamentos, você pode opcionalmente definir os campos schedule e duration para criar orçamentos de duração. Esses campos permitem definir janelas de manutenção ou quadros de tempo específicos quando os limites de interrupção são mais rigorosos.

  • O agendamento usa a sintaxe do trabalho cron com macros especiais como@yearly, , @monthly, @weekly, @daily. @hourly
  • A duração permite durações compostas como 10h5m, 30mou 160h. Duração e Agenda devem ser definidas em conjunto.

Exemplos de agenda e duração

Orçamento da janela de manutenção

Evitar interrupções durante o horário comercial:

budgets:
- nodes: "0"
  schedule: "0 9 * * 1-5"  # 9 AM Monday-Friday
  duration: 8h             # For 8 hours
Interrupções somente no fim de semana

Permitir apenas interrupções nos fins de semana:

budgets:
- nodes: "50%"
  schedule: "0 0 * * 6"    # Saturday midnight
  duration: 48h            # All weekend
- nodes: "0"               # Block all other times
Orçamento de distribuição gradual

Permitir taxas de interrupção crescentes:

budgets:
- nodes: "1"
  schedule: "0 2 * * *"    # 2 AM daily
  duration: 2h
- nodes: "3"
  schedule: "0 4 * * *"    # 4 AM daily
  duration: 4h

Exemplos de configuração de orçamento

A especificação a seguir NodePool tem três orçamentos configurados:

  • A primeira configuração permite que 20% dos nós pertencentes ao pool de nós sejam interrompidos ao mesmo tempo.
  • O segundo orçamento atua como um teto, permitindo apenas cinco interrupções quando há mais de 25 nós.
  • O último orçamento bloqueia interrupções durante os primeiros 10 minutos de cada dia.
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: default
spec:
  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    expireAfter: 720h # 30 * 24h = 720h
    budgets:
    - nodes: "20%"      # Allow 20% of nodes to be disrupted
    - nodes: "5"        # Cap at maximum 5 nodes
    - nodes: "0"        # Block all disruptions during maintenance window
      schedule: "@daily" # Scheduled daily
      duration: 10m # Duration of 10 minutes

Interrupção manual do nó

Você pode interromper manualmente os nós NAP usando kubectl ou excluindo NodePool recursos.

Remover nós com kubectl

Você pode remover os nós manualmente usando o kubectl delete node comando. Você pode excluir nós específicos, todos os nós gerenciados por NAP ou nós de um pool de nós específico usando rótulos, por exemplo:

# Delete a specific node
kubectl delete node $NODE_NAME

# Delete all NAP-managed nodes
kubectl delete nodes -l karpenter.sh/nodepool

# Delete nodes from a specific nodepool
kubectl delete nodes -l karpenter.sh/nodepool=$NODEPOOL_NAME

Excluir NodePool recursos

O NodePool é proprietário de NodeClaims por meio de uma referência de proprietário. O NAP finaliza elegantemente os nós por meio da exclusão em cascata quando você exclui o NodePool associado.

Controlar a interrupção usando anotações

Você pode bloquear ou desabilitar a interrupção para pods específicos, nós ou pools de nós inteiros usando anotações.

Controles de pod

Bloqueie o NAP de interromper determinados pods definindo a anotação karpenter.sh/do-not-disrupt: "true":

apiVersion: apps/v1
kind: Deployment
spec:
  template:
    metadata:
      annotations:
        karpenter.sh/do-not-disrupt: "true"

Essa anotação impede a interrupção voluntária para expiração, consolidação e desvio. No entanto, isso não impede interrupções de sistemas externos ou interrupções manuais por meio da exclusão de kubectl ou NodePool.

Controles de nó

Bloqueie o NAP de interromper nós específicos:

apiVersion: v1
kind: Node
metadata:
  annotations:
    karpenter.sh/do-not-disrupt: "true"

Controles do pool de nós

Desabilitar a interrupção para todos os nós em um NodePool:

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: default
spec:
  template:
    metadata:
      annotations:
        karpenter.sh/do-not-disrupt: "true"

Próximas etapas

Para obter mais informações sobre o provisionamento automático de nós no AKS, consulte os seguintes artigos: