View a markdown version of this page

Plano de controle do Kubernetes - Amazon EKS

As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.

Plano de controle do Kubernetes

dica

Explore as melhores práticas por meio de workshops do Amazon EKS.

O plano de controle do Kubernetes consiste no Kubernetes API Server, no Kubernetes Controller Manager, no Scheduler e em outros componentes necessários para o funcionamento do Kubernetes. Os limites de escalabilidade desses componentes são diferentes dependendo do que você está executando no cluster, mas as áreas com maior impacto na escalabilidade incluem a versão do Kubernetes, a utilização e o dimensionamento individual do Node.

Você pode executar o plano de controle do cluster em um dos dois modos para atender aos diferentes requisitos de carga de trabalho:

  • Modo padrão — Por padrão, todos os clusters EKS usam o modo padrão. O plano de controle aumenta e diminui automaticamente com base nas demandas de sua carga de trabalho. O modo padrão aloca dinamicamente capacidade suficiente do plano de controle e é a opção recomendada para a maioria dos casos de uso.

  • Modo provisionado — Se suas cargas de trabalho não tolerarem a variabilidade de desempenho decorrente da escalabilidade do plano de controle, ou se exigirem uma capacidade muito alta do plano de controle, você poderá usar o modo provisionado. Com o modo provisionado, você pré-aloca a capacidade do plano de controle que está sempre pronta para lidar com requisitos exigentes. Você obtém um desempenho consistente e previsível.

Com o modo EKS Provisioned, você escolhe entre um conjunto de níveis de escalabilidade (XL, 2XL, 4XL e 8XL). Com cada camada, você obtém um desempenho alto e previsível do plano de controle do cluster. O modo provisionado é particularmente valioso para os seguintes casos de uso:

  • Performance-critical cargas de trabalho

  • Large-scale Operações de IA e aprendizado de máquina

  • Eventos de alta demanda previstos

  • Ambientes que exigem consistência entre preparação e produção

Com o modo provisionado, você pode pré-alocar a capacidade do plano de controle com antecedência e se beneficiar de um Acordo de Nível de Serviço (SLA) aprimorado de 99,99% medido em intervalos de 1 minuto. Para obter mais informações sobre o modo provisionado EKS, incluindo especificações e preços de níveis, consulte Plano de controle provisionado do EKS no Guia do usuário do EKS.

Limite a carga de trabalho e a intermitência de nós

Importante

Para evitar atingir os limites da API no plano de controle, você deve limitar os picos de escalabilidade que aumentam o tamanho do cluster em porcentagens de dois dígitos por vez (por exemplo, 1.000 nós a 1.100 nós ou 4.000 a 4.500 pods de uma vez).

O plano de controle do EKS será escalado automaticamente à medida que seu cluster cresce, mas há limites na rapidez com que ele será escalado. Quando você cria um cluster EKS pela primeira vez, o Plano de Controle não poderá ser escalado imediatamente para centenas de nós ou milhares de pods. Para ler mais sobre como o EKS fez melhorias no dimensionamento, consulte esta postagem do blog.

O dimensionamento de grandes aplicativos exige que a infraestrutura se adapte para ficar totalmente pronta (por exemplo, balanceadores de carga de aquecimento). Para controlar a velocidade do escalonamento, certifique-se de escalar com base nas métricas corretas para seu aplicativo. O escalonamento de CPU e memória pode não prever com precisão as restrições do seu aplicativo, e usar métricas personalizadas (por exemplo, solicitações por segundo) no Kubernetes Horizontal Pod Autoscaler (HPA) pode ser uma melhor opção de escalabilidade.

Para usar uma métrica personalizada, consulte os exemplos na documentação do Kubernetes. Se você tiver necessidades de escalabilidade mais avançadas ou precisar escalar com base em fontes externas (por exemplo, fila do AWS SQS), use o KEDA para escalar cargas de trabalho com base em eventos.

Diminua a escala de nós e pods com segurança

Substitua instâncias de longa execução

A substituição regular dos nós mantém seu cluster saudável, evitando desvios de configuração e problemas que só acontecem após um tempo de atividade prolongado (por exemplo, vazamentos lentos de memória). A substituição automatizada fornecerá bons processos e práticas para atualizações de nós e patches de segurança. Se cada nó do seu cluster for substituído regularmente, haverá menos trabalho necessário para manter processos separados para manutenção contínua.

Use as configurações de tempo de vida (TTL) do Karpenter para substituir as instâncias após elas estarem em execução por um determinado período de tempo. Grupos de nós autogerenciados podem usar a max-instance-lifetime configuração para alternar os nós automaticamente. No momento, os grupos de nós gerenciados não têm esse recurso, mas você pode acompanhar a solicitação aqui GitHub.

Remova nós subutilizados

Você pode remover os nós quando eles não têm cargas de trabalho em execução usando o limite de redução no Kubernetes Cluster Autoscaler com o --scale-down-utilization-threshold ou no Karpenter, você pode usar a configuração do provisionador. ttlSecondsAfterEmpty

Use orçamentos de interrupção do pod e desligamento seguro dos nós

A remoção de pods e nós de um cluster Kubernetes exige que os controladores façam atualizações em vários recursos (por exemplo). EndpointSlices Fazer isso com frequência ou com muita rapidez pode causar limitação do servidor de API e paralisações de aplicativos à medida que as alterações se propagam para os controladores. https://kubernetes.io/docs/concepts/workloads/pods/disruptions/Os orçamentos de interrupção de pods são uma prática recomendada para diminuir a rotatividade e proteger a disponibilidade da carga de trabalho à medida que os nós são removidos ou reprogramados em um cluster.

Use o Client-Side Cache ao executar o Kubectl

Usar o comando kubectl de forma ineficiente pode adicionar mais carga ao Kubernetes API Server. Você deve evitar executar scripts ou automatizações que usem o kubectl repetidamente (por exemplo, em um loop for) ou executar comandos sem um cache local.

kubectltem um cache do lado do cliente que armazena em cache as informações de descoberta do cluster para reduzir a quantidade de chamadas de API necessárias. O cache é ativado por padrão e é atualizado a cada 10 minutos.

Se você executar o kubectl de um contêiner ou sem um cache do lado do cliente, poderá ter problemas de limitação de API. É recomendável manter o cache do cluster montando-o --cache-dir para evitar fazer chamadas de API necessárias.

Desativar a compactação kubectl

Desativar a compactação kubectl em seu arquivo kubeconfig pode reduzir o uso da API e da CPU do cliente. Por padrão, o servidor comprimirá os dados enviados ao cliente para otimizar a largura de banda da rede. Isso adiciona carga de CPU no cliente e no servidor para cada solicitação e desativar a compactação pode reduzir a sobrecarga e a latência se você tiver largura de banda adequada. Para desativar a compactação, você pode usar o --disable-compression=true sinalizador ou definir disable-compression: true em seu arquivo kubeconfig.

apiVersion: v1
clusters:
- cluster:
    server: serverURL
    disable-compression: true
  name: cluster

Autoescalador de cluster de fragmentos

O autoescalador de cluster Kubernetes foi testado para escalar até 1000 nós. Em um cluster grande com mais de 1000 nós, é recomendável executar várias instâncias do Cluster Autoscaler no modo fragmentado. Cada instância do Cluster Autoscaler é configurada para escalar um conjunto de grupos de nós. O exemplo a seguir mostra duas configurações de escalonamento automático de cluster que são configuradas para cada escalar 4 grupos de nós.

ClusterAutoscaler-1

autoscalingGroups:
- name: eks-core-node-grp-20220823190924690000000011-80c1660e-030d-476d-cb0d-d04d585a8fcb
  maxSize: 50
  minSize: 2
- name: eks-data_m1-20220824130553925600000011-5ec167fa-ca93-8ca4-53a5-003e1ed8d306
  maxSize: 450
  minSize: 2
- name: eks-data_m2-20220824130733258600000015-aac167fb-8bf7-429d-d032-e195af4e25f5
  maxSize: 450
  minSize: 2
- name: eks-data_m3-20220824130553914900000003-18c167fa-ca7f-23c9-0fea-f9edefbda002
  maxSize: 450
  minSize: 2

ClusterAutoscaler-2

autoscalingGroups:
- name: eks-data_m4-2022082413055392550000000f-5ec167fa-ca86-6b83-ae9d-1e07ade3e7c4
  maxSize: 450
  minSize: 2
- name: eks-data_m5-20220824130744542100000017-02c167fb-a1f7-3d9e-a583-43b4975c050c
  maxSize: 450
  minSize: 2
- name: eks-data_m6-2022082413055392430000000d-9cc167fa-ca94-132a-04ad-e43166cef41f
  maxSize: 450
  minSize: 2
- name: eks-data_m7-20220824130553921000000009-96c167fa-ca91-d767-0427-91c879ddf5af
  maxSize: 450
  minSize: 2

Prioridade e equidade da API

APF

Visão geral do

Para se proteger da sobrecarga durante períodos de aumento de solicitações, o API Server limita o número de solicitações de bordo que ele pode ter pendentes em um determinado momento. Quando esse limite for excedido, o servidor de API começará a rejeitar solicitações e retornará um código de resposta HTTP 429 para “Muitas solicitações” aos clientes. É preferível que o servidor descarte as solicitações e faça com que os clientes tentem novamente mais tarde do que não ter limites do servidor no número de solicitações e sobrecarregar o plano de controle, o que pode resultar em perda de desempenho ou indisponibilidade.

O mecanismo usado pelo Kubernetes para configurar como essas solicitações de bordo são divididas entre diferentes tipos de solicitação é chamado de Prioridade e Equidade da API. O servidor de API configura o número total de solicitações de bordo que ele pode aceitar ao somar os valores especificados pelos sinalizadores e. --max-requests-inflight --max-mutating-requests-inflight O EKS usa os valores padrão de 400 e 200 solicitações para esses sinalizadores, permitindo que um total de 600 solicitações sejam enviadas em um determinado momento. No entanto, ao escalar o plano de controle para tamanhos maiores em resposta ao aumento da utilização e da rotatividade de carga de trabalho, ele aumenta correspondentemente a cota de solicitações de bordo até 2000 (sujeita a alterações). O APF especifica como essa cota de solicitação de bordo é subdividida entre os diferentes tipos de solicitação. Observe que os planos de controle EKS estão altamente disponíveis com pelo menos 2 servidores de API registrados em cada cluster. Isso significa que o número total de solicitações de bordo que seu cluster pode processar é o dobro (ou mais, se for ampliado horizontalmente) da cota de bordo definida por kube-apiserver. Isso equivale a vários milhares requests/second nos maiores clusters EKS.

Dois tipos de objetos do Kubernetes, chamados PriorityLevelConfigurations e FlowSchemas, configuram como o número total de solicitações é dividido entre diferentes tipos de solicitação. Esses objetos são mantidos automaticamente pelo API Server e o EKS usa a configuração padrão desses objetos para a versão secundária do Kubernetes em questão. PriorityLevelConfigurations representam uma fração do número total de solicitações permitidas. Por exemplo, a carga de trabalho máxima PriorityLevelConfiguration é alocada em 98 de um total de 600 solicitações. A soma das solicitações alocadas para todos PriorityLevelConfigurations será igual a 600 (ou um pouco acima de 600, pois o servidor de API arredondará para cima se um determinado nível receber uma fração de uma solicitação). Para verificar o PriorityLevelConfigurations em seu cluster e o número de solicitações alocadas para cada um, você pode executar o seguinte comando. Estes são os padrões no EKS 1.32:

$ kubectl get --raw /metrics | grep apiserver_flowcontrol_nominal_limit_seats apiserver_flowcontrol_nominal_limit_seats{priority_level="catch-all"} 13 apiserver_flowcontrol_nominal_limit_seats{priority_level="exempt"} 0 apiserver_flowcontrol_nominal_limit_seats{priority_level="global-default"} 49 apiserver_flowcontrol_nominal_limit_seats{priority_level="leader-election"} 25 apiserver_flowcontrol_nominal_limit_seats{priority_level="node-high"} 98 apiserver_flowcontrol_nominal_limit_seats{priority_level="system"} 74 apiserver_flowcontrol_nominal_limit_seats{priority_level="workload-high"} 98 apiserver_flowcontrol_nominal_limit_seats{priority_level="workload-low"} 245

O segundo tipo de objeto é FlowSchemas. As solicitações do API Server com um determinado conjunto de propriedades são classificadas da mesma forma FlowSchema. Essas propriedades incluem o usuário autenticado ou os atributos da solicitação, como o grupo, o namespace ou o recurso da API. A FlowSchema também especifica para qual PriorityLevelConfiguration tipo de solicitação deve ser mapeado. Os dois objetos juntos dizem: “Quero que esse tipo de solicitação conte para essa parcela de solicitações a bordo”. Quando uma solicitação chega ao servidor de API, ele verifica cada uma delas FlowSchemas até encontrar uma que corresponda a todas as propriedades necessárias. Se várias FlowSchemas corresponderem a uma solicitação, o servidor de API escolherá aquela FlowSchema com a menor precedência correspondente, especificada como uma propriedade no objeto.

O mapeamento de FlowSchemas to PriorityLevelConfigurations pode ser visualizado usando este comando:

$ kubectl get flowschemas NAME PRIORITYLEVEL MATCHINGPRECEDENCE DISTINGUISHERMETHOD AGE MISSINGPL exempt exempt 1 <none> 7h19m False eks-exempt exempt 2 <none> 7h19m False probes exempt 2 <none> 7h19m False system-leader-election leader-election 100 ByUser 7h19m False endpoint-controller workload-high 150 ByUser 7h19m False workload-leader-election leader-election 200 ByUser 7h19m False system-node-high node-high 400 ByUser 7h19m False system-nodes system 500 ByUser 7h19m False kube-controller-manager workload-high 800 ByNamespace 7h19m False kube-scheduler workload-high 800 ByNamespace 7h19m False kube-system-service-accounts workload-high 900 ByNamespace 7h19m False eks-workload-high workload-high 1000 ByUser 7h14m False service-accounts workload-low 9000 ByUser 7h19m False global-default global-default 9900 ByUser 7h19m False catch-all catch-all 10000 ByUser 7h19m False

PriorityLevelConfigurations pode ter um tipo de Fila, Rejeitar ou Isentar. Para os tipos Queue e Reject, um limite é imposto ao número máximo de solicitações de bordo para esse nível de prioridade; no entanto, o comportamento difere quando esse limite é atingido. Por exemplo, o workload-high PriorityLevelConfiguration usa o tipo Queue e tem 98 solicitações disponíveis para uso pelo controller-manager, endpoint-controller, scheduler, controladores relacionados ao eks e de pods em execução no namespace kube-system. Como o tipo Queue é usado, o servidor de API tentará manter as solicitações na memória e espera que o número de solicitações a bordo caia abaixo de 98 antes que essas solicitações expirem. Se uma determinada solicitação atingir o tempo limite na fila ou se muitas solicitações já estiverem na fila, o servidor de API não terá escolha a não ser descartar a solicitação e devolver ao cliente um 429. Observe que o enfileiramento pode impedir que uma solicitação receba um 429, mas isso vem com a desvantagem de aumentar a latência de ponta a ponta na solicitação.

Agora, considere o catch-all FlowSchema que é mapeado para o catch-all com o tipo Reject PriorityLevelConfiguration . Se os clientes atingirem o limite de 13 solicitações em voo, o API Server não exercerá filas e eliminará as solicitações instantaneamente com um código de resposta 429. Finalmente, as solicitações mapeadas para um PriorityLevelConfiguration com o tipo Isento nunca receberão um 429 e sempre serão enviadas imediatamente. Isso é usado para solicitações de alta prioridade, como solicitações de healthz ou solicitações provenientes do grupo system:masters.

Monitorando APF e solicitações descartadas

Para confirmar se alguma solicitação está sendo descartada devido ao APF, as métricas do servidor de API para apiserver_flowcontrol_rejected_requests_total podem ser monitoradas para verificar o e. FlowSchemas PriorityLevelConfigurations Por exemplo, essa métrica mostra que 100 solicitações das contas de serviço FlowSchema foram descartadas devido ao tempo limite de solicitações em filas com baixa carga de trabalho:

% kubectl get --raw /metrics | grep apiserver_flowcontrol_rejected_requests_total
apiserver_flowcontrol_rejected_requests_total{flow_schema="service-accounts",priority_level="workload-low",reason="time-out"} 100

Para verificar se uma determinada pessoa PriorityLevelConfiguration está perto de receber 429s ou de ter uma latência aumentada devido ao enfileiramento, você pode comparar a diferença entre o limite de simultaneidade e a simultaneidade em uso. Neste exemplo, temos um buffer de 100 solicitações.

% kubectl get --raw /metrics | grep 'apiserver_flowcontrol_nominal_limit_seats.*workload-low'
apiserver_flowcontrol_nominal_limit_seats{priority_level="workload-low"} 245

% kubectl get --raw /metrics | grep 'apiserver_flowcontrol_current_executing_seats.*workload-low'
apiserver_flowcontrol_current_executing_seats{flow_schema="service-accounts",priority_level="workload-low"} 145

Para verificar se uma determinada solicitação PriorityLevelConfiguration está em fila, mas não necessariamente descartada, a métrica para apiserver_flowcontrol_current_inqueue_requests pode ser referenciada:

% kubectl get --raw /metrics | grep 'apiserver_flowcontrol_current_inqueue_requests.*workload-low'
apiserver_flowcontrol_current_inqueue_requests{flow_schema="service-accounts",priority_level="workload-low"} 10

Outras métricas úteis do Prometheus incluem:

  • apiserver_flowcontrol_dispatched_requests_total

  • apiserver_flowcontrol_request_execution_seconds

  • apiserver_flowcontrol_request_wait_duration_seconds

Consulte a documentação original para obter uma lista completa das métricas do APF.

Evitando solicitações descartadas

Evite 429 alterando sua carga de trabalho

Quando o APF está descartando solicitações devido ao fato de um determinado número PriorityLevelConfiguration exceder o número máximo permitido de solicitações de bordo, os clientes afetados FlowSchemas podem diminuir o número de solicitações em execução em um determinado momento. Isso pode ser feito reduzindo o número total de solicitações feitas durante o período em que 429 estão ocorrendo. Observe que solicitações de longa duração, como chamadas de lista caras, são especialmente problemáticas porque contam como uma solicitação de bordo durante toda a duração da execução. Reduzir o número dessas solicitações caras ou otimizar a latência dessas chamadas de lista (por exemplo, reduzindo o número de objetos buscados por solicitação ou passando a usar uma solicitação de monitoramento) pode ajudar a reduzir a simultaneidade total exigida por determinada carga de trabalho.

Evite 429s alterando suas configurações de APF

Atenção

Só altere as configurações padrão do APF se você souber o que está fazendo. Configurações de APF mal configuradas podem resultar na perda de solicitações do servidor de API e em interrupções significativas na carga de trabalho.

Outra abordagem para evitar solicitações descartadas é alterar o padrão FlowSchemas ou a PriorityLevelConfigurations instalação nos clusters EKS. O EKS instala as configurações padrão upstream para FlowSchemas e PriorityLevelConfigurations para a versão secundária do Kubernetes em questão. O servidor de API reconciliará automaticamente esses objetos com seus padrões se modificados, a menos que a seguinte anotação nos objetos seja definida como falsa:

  metadata:
    annotations:
      apf.kubernetes.io/autoupdate-spec: "false"

Em alto nível, as configurações do APF podem ser modificadas para:

  • Aloque mais capacidade de bordo às solicitações que lhe interessam.

  • Isole solicitações não essenciais ou caras que possam reduzir a capacidade de outros tipos de solicitação.

Isso pode ser feito alterando o padrão FlowSchemas PriorityLevelConfigurations e/ou criando novos objetos desses tipos. Os operadores podem aumentar os valores garantidos dos PriorityLevelConfigurations objetos relevantes ConcurrencyShares para aumentar a fração das solicitações de bordo que lhes são alocadas. Além disso, o número de solicitações que podem ser enfileiradas em um determinado momento também pode ser aumentado se o aplicativo puder lidar com a latência adicional causada pelas solicitações que estão na fila antes de serem despachadas.

Como alternativa, PriorityLevelConfigurations objetos novos FlowSchema e específicos para a carga de trabalho do cliente podem ser criados. Lembre-se de que alocar mais garantias ConcurrencyShares para existentes PriorityLevelConfigurations ou novas PriorityLevelConfigurations fará com que o número de solicitações que podem ser tratadas por outros buckets seja reduzido, pois o limite geral permanecerá em 600 em voo por servidor de API.

Ao fazer alterações nos padrões do APF, essas métricas devem ser monitoradas em um cluster que não seja de produção para garantir que a alteração das configurações não cause 429s não intencionais:

  1. A métrica para apiserver_flowcontrol_rejected_requests_total deve ser monitorada por todos FlowSchemas para garantir que nenhum bucket comece a descartar solicitações.

  2. Os valores de apiserver_flowcontrol_nominal_limit_seats e apiserver_flowcontrol_current_executing_seats devem ser comparados para garantir que a concorrência em uso não corra o risco de violar o limite desse nível de prioridade.

Um caso de uso comum para definir um novo FlowSchema e PriorityLevelConfiguration é o isolamento. Suponha que desejemos isolar chamadas de eventos de lista de longa duração dos pods para sua própria parcela de solicitações. Isso evitará que solicitações importantes de pods que usam as contas de serviço existentes FlowSchema recebam 429 e fiquem sem capacidade de solicitação. Lembre-se de que o número total de solicitações de bordo é finito; no entanto, este exemplo mostra que as configurações do APF podem ser modificadas para dividir melhor a capacidade de solicitação para a carga de trabalho especificada:

Exemplo de FlowSchema objeto para isolar solicitações de eventos da lista:

apiVersion: flowcontrol.apiserver.k8s.io/v1
kind: FlowSchema
metadata:
  name: list-events-default-service-accounts
spec:
  distinguisherMethod:
    type: ByUser
  matchingPrecedence: 8000
  priorityLevelConfiguration:
    name: catch-all
  rules:
  - resourceRules:
    - apiGroups:
      - '*'
      namespaces:
      - default
      resources:
      - events
      verbs:
      - list
    subjects:
    - kind: ServiceAccount
      serviceAccount:
        name: default
        namespace: default
  • Isso FlowSchema captura todas as chamadas de eventos de lista feitas por contas de serviço no namespace padrão.

  • A precedência correspondente 8000 é menor do que o valor de 9000 usado pelas contas de serviço existentes, FlowSchema portanto, essas chamadas de eventos de lista corresponderão a list-events-default-service-accounts em vez de service-accounts.

  • Estamos usando o catch-all PriorityLevelConfiguration para isolar essas solicitações. Esse bucket permite que apenas 13 solicitações de bordo sejam usadas por essas chamadas de eventos de lista de longa duração. Os pods começarão a receber 429 assim que tentarem emitir mais de 13 dessas solicitações simultaneamente.

Recuperação de recursos no servidor de API

Obter informações do servidor da API é um comportamento esperado para clusters de qualquer tamanho. À medida que você escala o número de recursos no cluster, a frequência das solicitações e o volume de dados podem rapidamente se tornar um gargalo para o plano de controle e levar à latência e lentidão da API. Dependendo da gravidade da latência, ela causará um tempo de inatividade inesperado se você não tomar cuidado.

Estar ciente do que você está solicitando e com que frequência são os primeiros passos para evitar esses tipos de problemas. Aqui está uma orientação para limitar o volume de consultas com base nas melhores práticas de escalabilidade. As sugestões desta seção são fornecidas em ordem, começando com as opções que são conhecidas por serem mais escaláveis.

Use informantes compartilhados

Ao criar controladores e automação que se integram à API do Kubernetes, você geralmente precisará obter informações dos recursos do Kubernetes. Se você pesquisar esses recursos regularmente, isso pode causar uma carga significativa no servidor da API.

Usar um informante da biblioteca client-go proporcionará a vantagem de observar as mudanças nos recursos com base em eventos, em vez de pesquisar as alterações. Os informantes reduzem ainda mais a carga usando o cache compartilhado para os eventos e as alterações, de forma que vários controladores observando os mesmos recursos não adicionem carga adicional.

Os controladores devem evitar pesquisar recursos de todo o cluster sem rótulos e seletores de campo, especialmente em clusters grandes. Cada pesquisa não filtrada exige que muitos dados desnecessários sejam enviados do etcd por meio do servidor da API para serem filtrados pelo cliente. Ao filtrar com base em rótulos e namespaces, você pode reduzir a quantidade de trabalho que o servidor de API precisa realizar para atender à solicitação e aos dados enviados ao cliente.

Otimize o uso da API Kubernetes

Ao chamar a API Kubernetes com controladores personalizados ou automação, é importante limitar as chamadas somente aos recursos necessários. Sem limites, você pode causar carga desnecessária no servidor da API e no etcd.

É recomendável usar o argumento watch sempre que possível. Sem argumentos, o comportamento padrão é listar objetos. Para usar watch em vez de list, você pode ?watch=true anexar ao final da sua solicitação de API. Por exemplo, para obter todos os pods no namespace padrão com um relógio, use:

/api/v1/namespaces/default/pods?watch=true

Se estiver listando objetos, você deve limitar o escopo do que está listando e a quantidade de dados retornados. Você pode limitar os dados retornados adicionando limit=500 argumentos às solicitações. O fieldSelector argumento e o /namespace/ caminho podem ser úteis para garantir que suas listas tenham um escopo tão restrito quanto necessário. Por exemplo, para listar somente pods em execução no namespace padrão, use o caminho e os argumentos da API a seguir.

/api/v1/namespaces/default/pods?fieldSelector=status.phase=Running&limit=500

Ou liste todos os pods que estão sendo executados com:

/api/v1/pods?fieldSelector=status.phase=Running&limit=500

Outra opção para limitar as chamadas de monitoramento ou os objetos listados é usar a resourceVersions qual você pode ler na documentação do Kubernetes. Sem resourceVersion discussão, você receberá a versão mais recente disponível, que requer uma leitura de quórum etcd, que é a leitura mais cara e mais lenta do banco de dados. A ResourceVersion depende dos recursos que você está tentando consultar e pode ser encontrada no metadata.resourseVersion campo. Isso também é recomendado no caso de usar chamadas de relógio e não apenas listar chamadas.

Há um especial resourceVersion=0 disponível que retornará os resultados do cache do servidor da API. Isso pode reduzir a carga do etcd, mas não oferece suporte à paginação.

/api/v1/namespaces/default/pods?resourceVersion=0

É recomendável usar watch com um resourceVersion definido como o valor conhecido mais recente recebido de sua lista ou observação anterior. Isso é tratado automaticamente no client-go. Mas é recomendável verificar se você estiver usando um cliente k8s em outros idiomas.

/api/v1/namespaces/default/pods?watch=true&resourceVersion=362812295

Se você chamar a API sem nenhum argumento, ela consumirá mais recursos para o servidor da API e para o etcd. Essa chamada obterá todos os pods em todos os namespaces sem paginação ou limitação do escopo e exigirá uma leitura de quórum do etcd.

/api/v1/pods

Evite DaemonSet rebanhos trovejantes

A DaemonSet garante que todos (ou alguns) nós executem uma cópia de um pod. À medida que os nós se juntam ao cluster, o controlador do daemonset cria pods para esses nós. Quando os nós saem do cluster, esses pods são coletados como lixo. A exclusão de um DaemonSet limpará os pods criados por ele.

Alguns usos típicos de um DaemonSet são:

  • Executando um daemon de armazenamento em cluster em cada nó

  • Executando um daemon de coleta de registros em cada nó

  • Executando um daemon de monitoramento de nós em cada nó

Em clusters com milhares de nós, criar um novo DaemonSet, atualizar ou aumentar o número de nós pode resultar em uma alta carga colocada no plano de controle. DaemonSet Se DaemonSet os pods emitirem solicitações caras do servidor de API na inicialização do pod, eles podem causar alto uso de recursos no plano de controle a partir de um grande número de solicitações simultâneas.

Em operação normal, você pode usar a RollingUpdate para garantir o lançamento gradual de novos DaemonSet pods. Com uma estratégia de RollingUpdate atualização, depois de atualizar um DaemonSet modelo, o controlador elimina os DaemonSet pods antigos e cria novos DaemonSet pods automaticamente de forma controlada. No máximo, um pod do DaemonSet estará em execução em cada nó durante todo o processo de atualização. Você pode realizar uma distribuição gradual definindo maxUnavailable como 1, maxSurge 0 e minReadySeconds 60. Se você não especificar uma estratégia de atualização, o Kubernetes usará maxUnavailable como padrão a criação de um RollingUpdate com 1, maxSurge 0 e minReadySeconds 0.

minReadySeconds: 60 strategy: type: RollingUpdate rollingUpdate: maxSurge: 0 maxUnavailable: 1

A RollingUpdate garante a implantação gradual de novos DaemonSet pods se eles já tiverem sido criados e tiverem o DaemonSet número esperado de Ready pods em todos os nós. Problemas estrondosos com o rebanho podem ocorrer sob certas condições que não são cobertas pelas estratégias. RollingUpdate

Evite rebanhos trovejantes na criação DaemonSet

Por padrão, independentemente da RollingUpdate configuração, o daemonset-controller no kube-controller-manager criará pods para todos os nós correspondentes simultaneamente quando você criar um novo. DaemonSet Para forçar uma distribuição gradual dos pods depois de criar um DaemonSet, você pode usar um ou. NodeSelector NodeAffinity Isso criará um DaemonSet que corresponda a zero nós e, em seguida, você poderá atualizar gradualmente os nós para torná-los elegíveis para executar um pod a partir do DaemonSet em uma taxa controlada. Você pode seguir essa abordagem:

  • Adicione um rótulo a todos os nós pararun-daemonset=false.

kubectl label nodes --all run-daemonset=false
  • Crie seu DaemonSet com uma NodeAffinity configuração que corresponda a qualquer nó sem um run-daemonset=false rótulo. Inicialmente, isso fará com que você não DaemonSet tenha pods correspondentes.

affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: run-daemonset
          operator: NotIn
          values:
          - "false"
  • Remova o run-daemonset=false rótulo de seus nós a uma taxa controlada. Você pode usar esse script bash como exemplo:

#!/bin/bash

nodes=$(kubectl get --raw "/api/v1/nodes" | jq -r '.items | .[].metadata.name')

for node in ${nodes[@]}; do
   echo "Removing run-daemonset label from node $node"
   kubectl label nodes $node run-daemonset-
   sleep 5
done
  • Opcionalmente, remova a NodeAffinity configuração do seu DaemonSet objeto. Observe que isso também acionará um RollingUpdate e substituirá gradualmente todos os DaemonSet pods existentes porque o DaemonSet modelo foi alterado.

Evite rebanhos trovejantes na expansão de nós

Da mesma forma que a DaemonSet criação, criar novos nós em um ritmo rápido pode resultar na inicialização simultânea de um grande número de DaemonSet pods. Você deve criar novos nós em uma taxa controlada para que o controlador crie DaemonSet pods nessa mesma taxa. Se isso não for possível, você pode tornar os novos nós inicialmente inelegíveis para os existentes DaemonSet usandoNodeAffinity. Em seguida, você pode adicionar gradualmente um rótulo aos novos nós para que o controlador do daemonset crie pods em uma taxa controlada. Você pode seguir essa abordagem:

  • Adicione um rótulo a todos os nós existentes para run-daemonset=true

kubectl label nodes --all run-daemonset=true
  • Atualize seu DaemonSet com uma NodeAffinity configuração para combinar qualquer nó com um run-daemonset=true rótulo. Observe que isso também acionará um RollingUpdate e substituirá gradualmente todos os DaemonSet pods existentes porque o DaemonSet modelo foi alterado. Você deve esperar que o RollingUpdate seja concluído antes de avançar para a próxima etapa.

affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: run-daemonset
          operator: In
          values:
          - "true"
  • Crie novos nós em seu cluster. Observe que esses nós não terão o run-daemonset=true rótulo, portanto, não DaemonSet corresponderão a esses nós.

  • Adicione o run-daemonset=true rótulo aos seus novos nós (que atualmente não têm o run-daemonset rótulo) em uma taxa controlada. Você pode usar esse script bash como exemplo:

#!/bin/bash

nodes=$(kubectl get --raw "/api/v1/nodes?labelSelector=%21run-daemonset" | jq -r '.items | .[].metadata.name')

for node in ${nodes[@]}; do
   echo "Adding run-daemonset=true label to node $node"
   kubectl label nodes $node run-daemonset=true
   sleep 5
done
  • Opcionalmente, remova a NodeAffinity configuração do seu DaemonSet objeto e remova o run-daemonset rótulo de todos os nós.

Evite rebanhos trovejantes nas atualizações DaemonSet

Uma RollingUpdate política respeitará apenas a maxUnavailable configuração dos DaemonSet pods que sãoReady. Se a DaemonSet tiver apenas NotReady pods ou uma grande porcentagem de NotReady pods e você atualizar seu modelo, o controlador do daemonset criará novos pods simultaneamente para qualquer pods. NotReady Isso pode resultar em problemas estrondosos com o rebanho se houver um número significativo de NotReady pods, por exemplo, se os pods estiverem continuamente em loop ou não conseguirem extrair imagens.

Para forçar o lançamento gradual de pods quando você atualiza um DaemonSet e há NotReady pods, você pode alterar temporariamente a estratégia de atualização de para. DaemonSet RollingUpdate OnDelete ComOnDelete, depois de atualizar um DaemonSet modelo, o controlador cria novos pods depois que você exclui manualmente os antigos para que você possa controlar a implantação de novos pods. Você pode seguir essa abordagem:

  • Verifique se você tem alguma NotReady cápsula em seu DaemonSet.

  • Se não, você pode atualizar o DaemonSet modelo com segurança e a RollingUpdate estratégia garantirá uma implantação gradual.

  • Se sim, você deve primeiro atualizá-lo DaemonSet para usar a OnDelete estratégia.

updateStrategy:
  type: OnDelete
  • Em seguida, atualize seu DaemonSet modelo com as alterações necessárias.

  • Após essa atualização, você pode excluir os DaemonSet pods antigos emitindo solicitações de exclusão de pods a uma taxa controlada. Você pode usar esse script bash como exemplo em que o DaemonSet nome é fluentd-elasticsearch no namespace kube-system:

#!/bin/bash

daemonset_pods=$(kubectl get --raw "/api/v1/namespaces/kube-system/pods?labelSelector=name%3Dfluentd-elasticsearch" | jq -r '.items | .[].metadata.name')

for pod in ${daemonset_pods[@]}; do
   echo "Deleting pod $pod"
   kubectl delete pod $pod -n kube-system
   sleep 5
done
  • Finalmente, você pode DaemonSet voltar para a RollingUpdate estratégia anterior.