10 erros comuns ao usar Kubernetes

Observação. trad.: Os autores deste artigo são engenheiros de uma pequena empresa tcheca, a pipetail. Eles conseguiram reunir uma lista maravilhosa de problemas e equívocos [às vezes banais, mas ainda assim] muito urgentes relacionados à operação de clusters Kubernetes.

10 erros comuns ao usar Kubernetes

Ao longo dos anos de uso do Kubernetes, trabalhamos com um grande número de clusters (gerenciados e não gerenciados - no GCP, AWS e Azure). Com o tempo, começamos a perceber que alguns erros se repetiam constantemente. No entanto, não há vergonha nisso: nós mesmos fizemos a maioria deles!

O artigo contém os erros mais comuns e também menciona como corrigi-los.

1. Recursos: solicitações e limites

Este item definitivamente merece a maior atenção e o primeiro lugar na lista.

Solicitação de CPU normalmente não especificado ou tem um valor muito baixo (para colocar o máximo possível de pods em cada nó). Assim, os nós ficam sobrecarregados. Durante períodos de alta carga, o poder de processamento do nó é totalmente utilizado e uma determinada carga de trabalho recebe apenas o que foi “solicitado” pelo Estrangulamento da CPU. Isso leva ao aumento da latência do aplicativo, tempos limite e outras consequências desagradáveis. (Leia mais sobre isso em nossa outra tradução recente: “Limites de CPU e limitação agressiva no Kubernetes"- Aproximadamente. trad.)

Melhor esforço (extremamente não recomendado):

resources: {}

Solicitação de CPU extremamente baixa (extremamente não recomendado):

   resources:
      Requests:
        cpu: "1m"

Por outro lado, a presença de um limite de CPU pode levar a saltos injustificados dos ciclos de clock dos pods, mesmo que o processador do nó não esteja totalmente carregado. Novamente, isso pode levar a atrasos maiores. A polêmica continua em torno do parâmetro Cota CFS da CPU no kernel Linux e na otimização da CPU dependendo dos limites definidos, bem como na desativação da cota CFS... Infelizmente, os limites da CPU podem causar mais problemas do que podem resolver. Mais informações sobre isso podem ser encontradas no link abaixo.

Seleção excessiva (comprometimento excessivo) problemas de memória podem levar a problemas maiores. Atingir o limite da CPU implica pular os ciclos de clock, enquanto atingir o limite de memória implica encerrar o pod. Você já observou OOMkill? Sim, é exatamente disso que estamos falando.

Você quer minimizar a probabilidade de isso acontecer? Não aloque memória demais e use QoS (Qualidade de Serviço) Garantida definindo a solicitação de memória até o limite (como no exemplo abaixo). Leia mais sobre isso em Apresentações de Henning Jacobs (Engenheiro Chefe da Zalando).

Explosável (maior chance de ser morto pelo OOM):

   resources:
      requests:
        memory: "128Mi"
        cpu: "500m"
      limits:
        memory: "256Mi"
        cpu: 2

Garantida:

   resources:
      requests:
        memory: "128Mi"
        cpu: 2
      limits:
        memory: "128Mi"
        cpu: 2

O que potencialmente ajudará na configuração de recursos?

Com servidor de métricas você pode ver o consumo atual de recursos da CPU e o uso de memória por pods (e contêineres dentro deles). Muito provavelmente, você já o está usando. Basta executar os seguintes comandos:

kubectl top pods
kubectl top pods --containers
kubectl top nodes

No entanto, eles mostram apenas o uso atual. Pode lhe dar uma ideia aproximada da ordem de grandeza, mas no final das contas você precisará histórico de mudanças nas métricas ao longo do tempo (para responder perguntas como: “Qual foi o pico de carga da CPU?”, “Qual foi a carga ontem de manhã?”, etc.). Para isso você pode usar Prometeu, DataDog e outras ferramentas. Eles simplesmente obtêm métricas do servidor de métricas e as armazenam, e o usuário pode consultá-las e plotá-las de acordo.

VerticalPodAutoscaler permite automatizar Este processo. Ele rastreia o histórico de uso de CPU e memória e configura novas solicitações e limites com base nessas informações.

Usar o poder da computação de forma eficiente não é uma tarefa fácil. É como jogar Tetris o tempo todo. Se você está pagando muito pelo poder de computação com baixo consumo médio (digamos, aproximadamente 10%), recomendamos procurar produtos baseados em AWS Fargate ou Virtual Kubelet. Eles são construídos em um modelo de faturamento sem servidor/pagamento por uso, que pode ser mais barato nessas condições.

2. Sondas de vivacidade e prontidão

Por padrão, as verificações de atividade e prontidão não estão habilitadas no Kubernetes. E às vezes eles esquecem de ligá-los...

Mas de que outra forma você pode reiniciar o serviço no caso de um erro fatal? E como o balanceador de carga sabe que um pod está pronto para aceitar tráfego? Ou que pode lidar com mais tráfego?

Esses testes são frequentemente confundidos entre si:

  • Vivacidade — verificação de “capacidade de sobrevivência”, que reinicia o pod se ele falhar;
  • Prontidão — verificação de prontidão, se falhar, desconecta o pod do serviço Kubernetes (isso pode ser verificado usando kubectl get endpoints) e o tráfego não chega até ele até que a próxima verificação seja concluída com êxito.

Ambas as verificações REALIZADO DURANTE TODO O CICLO DE VIDA DO POD. É muito importante.

Um equívoco comum é que as sondagens de prontidão são executadas apenas na inicialização para que o balanceador possa saber que o pod está pronto (Ready) e pode começar a processar o tráfego. Porém, esta é apenas uma das opções para seu uso.

Outra é a possibilidade de descobrir que o tráfego no pod é excessivo e sobrecarrega (ou o pod executa cálculos que consomem muitos recursos). Neste caso, a verificação de prontidão ajuda reduza a carga no pod e “resfrie-o”. A conclusão bem-sucedida de uma verificação de prontidão no futuro permite aumente a carga no pod novamente. Neste caso (se o teste de prontidão falhar), a falha no teste de vivacidade seria muito contraproducente. Por que reiniciar um pod que está íntegro e funcionando duro?

Portanto, em alguns casos, nenhuma verificação é melhor do que ativá-las com parâmetros configurados incorretamente. Como dito acima, se verificação de vivacidade, verificação de prontidão de cópias, então você está em apuros. A opção possível é configurar apenas teste de prontidãoE vivacidade perigosa deixar de lado.

Ambos os tipos de verificações não devem falhar quando as dependências comuns falharem, caso contrário, isso levará a uma falha em cascata (semelhante a uma avalanche) de todos os pods. Em outras palavras, não se machuque.

3. LoadBalancer para cada serviço HTTP

Muito provavelmente, você tem serviços HTTP em seu cluster que gostaria de encaminhar para o mundo externo.

Se você abrir o serviço como type: LoadBalancer, seu controlador (dependendo do provedor de serviço) fornecerá e negociará um LoadBalancer externo (não necessariamente rodando em L7, mas até mesmo em L4), e isso pode afetar o custo (endereço IPv4 estático externo, poder de computação, faturamento por segundo ) devido à necessidade de criação de um grande número desses recursos.

Neste caso, é muito mais lógico utilizar um balanceador de carga externo, abrindo serviços como type: NodePort. Ou melhor ainda, expanda algo como controlador de entrada nginx (ou Traefik), que será o único Porta do nó endpoint associado ao balanceador de carga externo e roteará o tráfego no cluster usando preâmbulo-Recursos Kubernetes.

Outros (micro)serviços intracluster que interagem entre si podem “comunicar-se” usando serviços como ClusterIP e um mecanismo integrado de descoberta de serviço via DNS. Apenas não use seu DNS/IP público, pois isso pode afetar a latência e aumentar o custo dos serviços em nuvem.

4. Escalonamento automático de um cluster sem levar em conta seus recursos

Ao adicionar e remover nós de um cluster, você não deve confiar em algumas métricas básicas, como o uso da CPU nesses nós. O planejamento do pod deve levar em conta muitos restrições, como afinidade de pod/nó, taints e tolerâncias, solicitações de recursos, QoS, etc. Usar um autoescalador externo que não leve em consideração essas nuances pode causar problemas.

Imagine que um determinado pod deveria ser agendado, mas toda a potência disponível da CPU é solicitada/desmontada e o pod fica preso em um estado Pending. O escalonador automático externo vê a carga média atual da CPU (não a solicitada) e não inicia a expansão (dimensionar) - não adiciona outro nó. Como resultado, este pod não será agendado.

Neste caso, a escala reversa (aumento de escala) — remover um nó de um cluster é sempre mais difícil de implementar. Imagine que você tem um pod com estado (com armazenamento persistente conectado). Volumes persistentes geralmente pertencem a zona de disponibilidade específica e não são replicados na região. Assim, se um autoescalador externo excluir um nó com este pod, o agendador não poderá agendar este pod em outro nó, pois isso só pode ser feito na zona de disponibilidade onde está localizado o armazenamento persistente. O pod ficará preso no estado Pending.

Muito popular na comunidade Kubernetes escalonador automático de cluster. Ele roda em um cluster, suporta APIs dos principais provedores de nuvem, leva em consideração todas as restrições e pode ser escalonado nos casos acima. Também é capaz de aumentar a escala mantendo todos os limites definidos, poupando assim dinheiro (que de outra forma seria gasto em capacidade não utilizada).

5. Negligenciar capacidades IAM/RBAC

Cuidado ao usar usuários IAM com segredos persistentes para máquinas e aplicações. Organize o acesso temporário usando funções e contas de serviço (contas de serviço).

Freqüentemente nos deparamos com o fato de que as chaves de acesso (e segredos) são codificadas na configuração do aplicativo, além de negligenciarmos a rotação de segredos, apesar de termos acesso ao Cloud IAM. Use funções e contas de serviço do IAM em vez de usuários, quando apropriado.

10 erros comuns ao usar Kubernetes

Esqueça o kube2iam e vá direto para as funções do IAM para contas de serviço (conforme descrito em nota com o mesmo nome Štěpán Vraný):

apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/my-app-role
  name: my-serviceaccount
  namespace: default

Uma anotação. Não é tão difícil, certo?

Além disso, não conceda privilégios a contas de serviço e perfis de instância admin и cluster-adminse eles não precisarem. Isto é um pouco mais difícil de implementar, especialmente em RBAC K8s, mas definitivamente vale o esforço.

6. Não confie na antiafinidade automática para pods

Imagine que você tenha três réplicas de alguma implantação em um nó. O nó cai e junto com ele todas as réplicas. Situação desagradável, certo? Mas por que todas as réplicas estavam no mesmo nó? O Kubernetes não deveria fornecer alta disponibilidade (HA)?

Infelizmente, o agendador Kubernetes, por sua própria iniciativa, não cumpre as regras de existência separada (anti-afinidade) para vagens. Eles devem ser explicitamente declarados:

// опущено для краткости
      labels:
        app: zk
// опущено для краткости
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                  - key: "app"
                    operator: In
                    values:
                    - zk
              topologyKey: "kubernetes.io/hostname"

Isso é tudo. Agora os pods serão agendados em nós diferentes (esta condição é verificada apenas durante o agendamento, mas não durante sua operação - portanto requiredDuringSchedulingIgnoredDuringExecution).

Aqui estamos falando podAntiAffinity em nós diferentes: topologyKey: "kubernetes.io/hostname", - e não sobre diferentes zonas de disponibilidade. Para implementar um HA completo, você terá que se aprofundar neste tópico.

7. Ignorando PodDisruptionBudgets

Imagine que você tem uma carga de produção em um cluster Kubernetes. Periodicamente, os nós e o próprio cluster precisam ser atualizados (ou desativados). PodDisruptionBudget (PDB) é algo como um acordo de garantia de serviço entre administradores de cluster e usuários.

O PDB permite evitar interrupções de serviço causadas pela falta de nós:

apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: zk-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: zookeeper

Neste exemplo, você, como usuário do cluster, declara aos administradores: “Ei, tenho um serviço de zookeeper e, não importa o que vocês façam, gostaria de ter pelo menos 2 réplicas desse serviço sempre disponíveis”.

Você pode ler mais sobre isso aqui.

8. Vários usuários ou ambientes em um cluster comum

Namespaces do Kubernetes (espaços de nomes) não fornecem isolamento forte.

Um equívoco comum é que se você implantar uma carga não-prod em um namespace e uma carga prod em outro, então eles não influenciarão um ao outro de forma alguma... No entanto, um certo nível de isolamento pode ser alcançado usando solicitações/limitações de recursos, definindo cotas e definindo prioridadesClasses. Algum isolamento “físico” no plano de dados é fornecido por afinidades, tolerâncias, taints (ou seletores de nós), mas tal separação é bastante difícil implemento.

Aqueles que precisarem combinar os dois tipos de cargas de trabalho no mesmo cluster terão que lidar com a complexidade. Se não houver essa necessidade e você puder ter um mais um aglomerado (digamos, em uma nuvem pública), então é melhor fazer isso. Isto alcançará um nível de isolamento muito mais elevado.

9. política de tráfego externo: cluster

Muitas vezes vemos que todo o tráfego dentro do cluster vem através de um serviço como o NodePort, para o qual a política padrão é definida externalTrafficPolicy: Cluster. Isto significa que, Porta do nó está aberto em todos os nós do cluster e você pode usar qualquer um deles para interagir com o serviço desejado (conjunto de pods).

10 erros comuns ao usar Kubernetes

Ao mesmo tempo, os pods reais associados ao serviço NodePort mencionado acima geralmente estão disponíveis apenas em um determinado subconjunto desses nós. Em outras palavras, se eu me conectar a um nó que não possui o pod necessário, ele encaminhará o tráfego para outro nó, adicionando um salto e aumento da latência (se os nós estiverem localizados em diferentes zonas de disponibilidade/data centers, a latência pode ser bastante alta; além disso, os custos do tráfego de saída aumentarão).

Por outro lado, se um determinado serviço Kubernetes tiver um conjunto de políticas externalTrafficPolicy: Local, o NodePort será aberto apenas nos nós em que os pods necessários estão realmente em execução. Ao usar um balanceador de carga externo que verifica o estado (verificação de saúde) endpoints (como funciona AWSELB), Ele enviará tráfego apenas para os nós necessários, o que terá um efeito benéfico sobre atrasos, necessidades de computação, contas de saída (e o bom senso dita o mesmo).

Há uma grande chance de você já estar usando algo como Traefik ou controlador de entrada nginx como um endpoint NodePort (ou LoadBalancer, que também usa NodePort) para rotear o tráfego de entrada HTTP, e definir essa opção pode reduzir significativamente a latência para tais solicitações.

В esta publicação Você pode aprender mais sobre externalTrafficPolicy, suas vantagens e desvantagens.

10. Não fique preso a clusters e não abuse do plano de controle

Anteriormente, era costume chamar os servidores pelos nomes próprios: Anton, HAL9000 e Colossus... Hoje eles foram substituídos por identificadores gerados aleatoriamente. Porém, o hábito permaneceu, e agora os nomes próprios vão para clusters.

Uma história típica (baseada em acontecimentos reais): tudo começou com uma prova de conceito, por isso o cluster teve um nome orgulhoso ensaio… Anos se passaram e AINDA é usado na produção, e todo mundo tem medo de tocá-lo.

Não há nada de divertido em clusters se transformarem em animais de estimação, por isso recomendamos removê-los periodicamente durante a prática. recuperação de desastres (isso vai ajudar engenharia do caos - Aproximadamente. trad.). Além disso, não faria mal nenhum trabalhar na camada de controle (plano de controle). Ter medo de tocá-lo não é um bom sinal. etc. morto? Pessoal, vocês estão realmente em apuros!

Por outro lado, você não deve se deixar levar pela manipulação. Com tempo a camada de controle pode ficar lenta. Provavelmente, isso se deve a um grande número de objetos sendo criados sem sua rotação (uma situação comum ao usar o Helm com configurações padrão, e é por isso que seu estado em configmaps/secrets não é atualizado - como resultado, milhares de objetos se acumulam em a camada de controle) ou com edição constante de objetos kube-api (para escalonamento automático, para CI/CD, para monitoramento, logs de eventos, controladores, etc.).

Além disso, recomendamos verificar os acordos SLA/SLO com o provedor gerenciado de Kubernetes e prestar atenção às garantias. O fornecedor pode garantir disponibilidade da camada de controle (ou seus subcomponentes), mas não o atraso p99 das solicitações enviadas a ele. Em outras palavras, você pode inserir kubectl get nodes, e receberá uma resposta somente após 10 minutos, e isso não será uma violação dos termos do contrato de serviço.

11. Bônus: usando a tag mais recente

Mas isso já é um clássico. Ultimamente temos encontrado essa técnica com menos frequência, pois muitos, tendo aprendido com a amarga experiência, pararam de usar a tag :latest e comecei a fixar versões. Viva!

ECR mantém a imutabilidade das tags de imagem; Recomendamos que você se familiarize com esse recurso notável.

Resumo

Não espere que tudo funcione da noite para o dia: o Kubernetes não é uma panacéia. Aplicativo ruim permanecerá assim mesmo no Kubernetes (e provavelmente vai piorar). O descuido levará à complexidade excessiva e ao trabalho lento e estressante da camada de controle. Além disso, você corre o risco de ficar sem uma estratégia de recuperação de desastres. Não espere que o Kubernetes forneça isolamento e alta disponibilidade prontos para uso. Passe algum tempo tornando seu aplicativo verdadeiramente nativo da nuvem.

Você poderá conhecer as experiências malsucedidas de diversas equipes em esta coleção de histórias por Henning Jacobs.

Aqueles que desejam adicionar erros à lista de erros indicada neste artigo podem entrar em contato conosco no Twitter (@MarekBartik, @MstrsObserver).

PS do tradutor

Leia também em nosso blog:

Fonte: habr.com

Adicionar um comentário