Resolver problemas do programador do Kubernetes

Nesta página, mostramos como resolver problemas com o programador do Kubernetes (kube-scheduler) para o Google Distributed Cloud Virtual for VMware.

Se precisar de mais ajuda, entre em contato com o Cloud Customer Care.

O Kubernetes sempre programa os pods para o mesmo conjunto de nós

Esse erro pode surgir por alguns motivos diferentes:

  • Uso desequilibrado do cluster. É possível inspecionar a utilização do cluster para cada nó com o comando kubectl top nodes. Este exemplo exagerado de saída mostra a utilização pronunciada de determinados nós:

    NAME                   CPU(cores)   CPU%      MEMORY(bytes)   MEMORY%
    XXX.gke.internal       222m         101%       3237Mi          61%
    YYY.gke.internal       91m          0%         2217Mi          0%
    ZZZ.gke.internal       512m         0%         8214Mi          0%
    
  • Excesso de solicitações. Se você programar muitos pods de uma só vez no mesmo nó e esses pods fizerem solicitações HTTP, o nó poderá ter limitação de taxa. O erro comum retornado pelo servidor nesse cenário é 429 Too Many Requests.

  • Serviço indisponível. Um servidor da Web, por exemplo, hospedado em um nó com carga alta, pode responder a todas as solicitações com erros 503 Service Unavailable até que esteja sob carga mais leve.

Para verificar se você tem pods sempre programados para os mesmos nós, siga as etapas abaixo:

  1. Execute o comando kubectl a seguir para conferir o status dos pods:

    kubectl get pods -o wide -n default
    

    Para mostrar a distribuição de pods entre nós, marque a coluna NODE na saída. No exemplo de saída a seguir, todos os pods são programados no mesmo nó:

    NAME                               READY  STATUS   RESTARTS  AGE  IP             NODE
    nginx-deployment-84c6674589-cxp55  1/1    Running  0         55s  10.20.152.138  10.128.224.44
    nginx-deployment-84c6674589-hzmnn  1/1    Running  0         55s  10.20.155.70   10.128.226.44
    nginx-deployment-84c6674589-vq4l2  1/1    Running  0         55s  10.20.225.7    10.128.226.44
    

Os pods têm vários recursos para ajustar o comportamento de programação. Esses recursos incluem restrições de distribuição de topologia e regras antiafinidade. É possível usar um desses recursos ou uma combinação deles. Os requisitos definidos são unidos por kube-scheduler.

Os registros do programador não são capturados no nível de detalhamento de geração de registros padrão. Caso você precise capturar os registros do programador para solucionar problemas, siga estas etapas:

  1. Aumente o nível de detalhamento da geração de registros:

    1. Edite o Deployment kube-scheduler:

      kubectl --kubeconfig ADMIN_CLUSTER_KUBECONFIG edit deployment kube-scheduler \
        -n USER_CLUSTER_NAMESPACE
      
    2. Adicione a flag --v=5 na seção spec.containers.command:

      containers:
      - command:
      - kube-scheduler
      - --profiling=false
      - --kubeconfig=/etc/kubernetes/scheduler.conf
      - --leader-elect=true
      - --v=5
      
  2. Quando terminar a solução de problemas, redefina o nível de detalhamento para o padrão:

    1. Edite o Deployment kube-scheduler:

      kubectl --kubeconfig ADMIN_CLUSTER_KUBECONFIG edit deployment kube-scheduler \
        -n USER_CLUSTER_NAMESPACE
      
    2. Retorne o nível de detalhamento ao valor padrão:

      containers:
      - command:
      - kube-scheduler
      - --profiling=false
      - --kubeconfig=/etc/kubernetes/scheduler.conf
      - --leader-elect=true
      

Restrições de distribuição de topologia

As restrições de distribuição de topologia podem ser usadas para distribuir uniformemente os pods entre os nós de acordo com zones, regions, node ou outra topologia personalizada.

O manifesto de exemplo a seguir mostra uma implantação que distribui réplicas uniformemente entre todos os nós programáveis usando restrições de distribuição de topologia:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: topology-spread-deployment
  labels:
    app: myapp
spec:
  replicas: 30
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      topologySpreadConstraints:
      - maxSkew: 1 # Default. Spreads evenly. Maximum difference in scheduled Pods per Node.
        topologyKey: kubernetes.io/hostname
        whenUnsatisfiable: DoNotSchedule # Default. Alternatively can be ScheduleAnyway
        labelSelector:
          matchLabels:
            app: myapp
        matchLabelKeys: # beta in 1.27
        - pod-template-hash
      containers:
      # pause is a lightweight container that simply sleeps
      - name: pause
        image: registry.k8s.io/pause:3.2

As considerações a seguir se aplicam ao uso de restrições de distribuição de topologia:

  • O labels.app: myapp de um pod corresponde ao labelSelector da restrição.
  • O topologyKey especifica kubernetes.io/hostname. Esse rótulo é anexado automaticamente a todos os nós e é preenchido com o nome do host do nó.
  • O matchLabelKeys impede que os lançamentos de novas implantações considerem pods de revisões antigas ao calcular onde programar um pod. O rótulo pod-template-hash é preenchido automaticamente por uma implantação.

Antiafinidade de pods

A antiafinidade de pods serve para definir quais pods podem ser colocalizados no mesmo nó.

O manifesto de exemplo a seguir mostra uma implantação que usa antiafinidade para limitar as réplicas a um pod por nó:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-affinity-deployment
  labels:
    app: myapp
spec:
  replicas: 30
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      name: with-pod-affinity
      labels:
        app: myapp
    spec:
      affinity:
        podAntiAffinity:
          # requiredDuringSchedulingIgnoredDuringExecution
          # prevents Pod from being scheduled on a Node if it
          # does not meet criteria.
          # Alternatively can use 'preferred' with a weight
          # rather than 'required'.
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - myapp
            # Your nodes might be configured with other keys
            # to use as `topologyKey`. `kubernetes.io/region`
            # and `kubernetes.io/zone` are common.
            topologyKey: kubernetes.io/hostname
      containers:
      # pause is a lightweight container that simply sleeps
      - name: pause
        image: registry.k8s.io/pause:3.2

Este exemplo de implantação especifica 30 réplicas, mas se expande somente para o número de nós disponíveis no cluster.

As seguintes considerações se aplicam ao usar antiafinidade de pods:

  • O labels.app: myapp de um pod corresponde ao labelSelector da restrição.
  • O topologyKey especifica kubernetes.io/hostname. Esse rótulo é anexado automaticamente a todos os nós e é preenchido com o nome do host do nó. É possível usar outros rótulos se o cluster tiver suporte para eles, como region ou zone.

Pré-extrair imagens de contêiner

Na ausência de outras restrições, por padrão, kube-scheduler prefere programar pods em nós que já tenham feito o download da imagem do contêiner. Esse comportamento pode ser interessante para clusters menores, sem outras configurações de programação, em que seria possível fazer o download das imagens em cada nó. No entanto, use esse conceito como último recurso. Uma solução melhor é usar nodeSelector, restrições de distribuição de topologia ou afinidade / antiafinidade. Para mais informações, consulte Como atribuir pods a nós.

Se você quiser garantir que as imagens de contêiner sejam pré-extraídas para todos os nós, use um DaemonSet como no exemplo a seguir:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: prepulled-images
spec:
  selector:
    matchLabels:
      name: prepulled-images
  template:
    metadata:
      labels:
        name: prepulled-images
    spec:
      initContainers:
        - name: prepulled-image
          image: IMAGE
          # Use a command the terminates immediately
          command: ["sh", "-c", "'true'"]
      containers:
      # pause is a lightweight container that simply sleeps
      - name: pause
        image: registry.k8s.io/pause:3.2

Depois que o pod estiver Running em todos os nós, os podem devem ser reimplantados para confirmar se os contêineres estão distribuídos de maneira uniforme entre os nós.

A seguir

Se precisar de mais ajuda, entre em contato com o Cloud Customer Care.