排查 Kubernetes 调度器问题

本页面介绍了如何解决 Google Distributed Cloud 的 Kubernetes 调度器 (kube-scheduler) 的问题。

如果您需要其他帮助,请与 Cloud Customer Care 联系。

Kubernetes 始终将 Pod 安排到同一组节点

此错误可能通过几种不同的方式出现:

  • 集群利用率不平衡。 您可以使用 kubectl top nodes 命令检查每个节点的集群利用率。以下夸大的示例输出显示某些节点上显著的利用率:

    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%
    
  • 请求过多。如果您同时将多个 Pod 安排到同一节点上,并且这些 Pod 发出 HTTP 请求,则该节点的速率可能会受到限制。在此情况下,服务器返回的常见错误是 429 Too Many Requests

  • 服务不可用。 例如,如果 Web 服务器托管在负载高的节点上,则可能会以 503 Service Unavailable 错误响应所有请求,直到其负载较低。

如需检查 Pod 是否始终安排到同一节点,请按以下步骤操作:

  1. 运行以下 kubectl 命令可查看 Pod 的状态:

    kubectl get pods -o wide -n default
    

    如需查看 Pod 在节点之间的分布情况,请查看输出中的 NODE 列。在以下示例输出中,所有 pod 都安排在同一节点上:

    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
    

Pod 有许多功能,可供您微调其安排行为。这些功能包括拓扑分布约束和反亲和性规则。您可以使用这些功能中的某一项功能,也可以使用多个功能的组合。您定义的要求由 kube-scheduler 用 AND 运算符连接。

系统不会以默认的日志记录详细程度级别捕获调度器日志。如果您需要调度器日志进行问题排查,请按照以下步骤捕获调度器日志:

  1. 提高日志记录详细程度级别:

    1. 修改 kube-scheduler 部署:

      kubectl --kubeconfig ADMIN_CLUSTER_KUBECONFIG edit deployment kube-scheduler \
        -n USER_CLUSTER_NAMESPACE
      
    2. spec.containers.command 部分下添加 --v=5 标志:

      containers:
      - command:
      - kube-scheduler
      - --profiling=false
      - --kubeconfig=/etc/kubernetes/scheduler.conf
      - --leader-elect=true
      - --v=5
      
  2. 完成问题排查后,将详细程度级别重置回默认级别:

    1. 修改 kube-scheduler 部署:

      kubectl --kubeconfig ADMIN_CLUSTER_KUBECONFIG edit deployment kube-scheduler \
        -n USER_CLUSTER_NAMESPACE
      
    2. 将详细程度级别设置回默认值:

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

拓扑分布约束

拓扑分布约束可用于根据 Pod 的 zonesregionsnode 或其他自定义拓扑在节点之间均匀分布 Pod。

以下示例清单显示了使用拓扑分布约束在所有可安排节点之间均匀分布副本的 Deployment:

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

使用拓扑分布约束时,需要注意以下事项:

  • Pod 的 labels.app: myapp 与约束的 labelSelector 匹配。
  • topologyKey 指定 kubernetes.io/hostname。此标签会自动关联到所有节点,并使用节点的主机名填充。
  • 在计算安排 Pod 的位置时,matchLabelKeys 可防止在发布新 Deployment 时考虑旧修订版本的 Pod。pod-template-hash 标签由 Deployment 自动填充。

Pod 反亲和性

借助 Pod 反亲和性,您可以定义哪些 Pod 可位于同一节点上的限制条件。

以下示例清单显示了使用反亲和性将副本限制为每个节点一个 Pod 的 Deployment:

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

此示例 Deployment 指定了 30 个副本,但只会分布到集群中可用的节点(数量不限)。

使用 Pod 反亲和性时,需要注意以下事项:

  • Pod 的 labels.app: myapp 与约束的 labelSelector 匹配。
  • topologyKey 指定 kubernetes.io/hostname。此标签会自动关联到所有节点,并使用节点的主机名填充。如果您的集群支持使用其他标签,例如 regionzone,则您可以选择使用其他标签。

预拉取容器映像

在没有任何其他约束的情况下,默认情况下,kube-scheduler 首选将 Pod 安排到已下载容器映像的节点上。在没有其他安排配置的情况下,这种行为可能适用于较小的集群,因为在这种节点上,每个节点都可以下载映像。但是,依赖这一概念应被视为最后的手段。更好的解决方案是使用 nodeSelector、拓扑分布约束或亲和性/反亲和性。如需了解详情,请参阅将 Pod 分配给节点

如果您要确保将容器映像预拉取到所有节点,则可以使用 DaemonSet,如以下示例所示:

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

在所有节点上的 Pod 都处于 Running 状态后,再次重新部署 Pod,以查看容器现在是否在各节点上均匀分布。

后续步骤

如果您需要其他帮助,请与 Cloud Customer Care 联系。