本页面介绍了如何解决 Google Distributed Cloud 的 Kubernetes 调度器 (kube-scheduler
) 的问题。
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 是否始终安排到同一节点,请按以下步骤操作:
运行以下
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 运算符连接。
系统不会以默认的日志记录详细程度级别捕获调度器日志。如果您需要调度器日志进行问题排查,请按照以下步骤捕获调度器日志:
提高日志记录详细程度级别:
修改
kube-scheduler
部署:kubectl --kubeconfig ADMIN_CLUSTER_KUBECONFIG edit deployment kube-scheduler \ -n USER_CLUSTER_NAMESPACE
在
spec.containers.command
部分下添加--v=5
标志:containers: - command: - kube-scheduler - --profiling=false - --kubeconfig=/etc/kubernetes/scheduler.conf - --leader-elect=true - --v=5
完成问题排查后,将详细程度级别重置回默认级别:
修改
kube-scheduler
部署:kubectl --kubeconfig ADMIN_CLUSTER_KUBECONFIG edit deployment kube-scheduler \ -n USER_CLUSTER_NAMESPACE
将详细程度级别设置回默认值:
containers: - command: - kube-scheduler - --profiling=false - --kubeconfig=/etc/kubernetes/scheduler.conf - --leader-elect=true
拓扑分布约束
拓扑分布约束可用于根据 zones
、regions
、node
或其他自定义拓扑在节点之间均匀分布 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
。此标签会自动关联到所有节点,并使用节点的主机名填充。如果您的集群支持使用其他标签,例如region
或zone
,则您可以选择使用其他标签。
预拉取容器映像
在没有任何其他约束的情况下,默认情况下,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,以查看容器现在是否在各节点上均匀分布。