K8S 支持多副本部署,但不代表应用的高可用,因为多个副本可能部署到同一个节点上。
早上发现应用的某一个功能有一半的请求失败,排查之后发现,原来 容器服务中节点未设置反亲和性,导致 Pod 部署到同一个节点中,影响 API 网关请求后端服务。
架构图
先 review 以下架构图

- Web 应用:Vue 框架,运行在 容器服务 上
- 腾讯云 API 网关
- 业务 API:Golang 框架,运行在 容器服务上,使用 NodePort 方式对外暴露服务
1. 问题
在页面中使用页面功能时,发现有一半的请求失败,API 返回为空。
于是想到可能和 API 网关请求后端接口有关,下图为 API 网关对应后端通道的配置,有 2 个节点。

查看容器服务对应 Pod,发现两个副本都运行在同一个节点上,问题找到了。

副本跑在同一个节点的原因:随着集群的变化,一开始部署在两个节点上的 Pod, 因为其中一个节点的资源被占满, 导致 Pod 优先调度到另一个闲置的节点上。
2. 解决问题:设置反亲和性
解决问题的思路是让 Pod 不能调度在同一台 服务器上,需要打散调度到所有节点中。
ChatGPT 助手的 QA 详见附录,也可以查看文本的帮助文档(是通过关键字
Anti-Affinity
检索到的 )

容器服务虽然有
调度策略
功能,但无法满足当前的场景, 于是编辑 Deployment 的 YAML 配置,在 Pod 上新增
spec.template.spec.topologySpreadConstraints
segment。
topologySpreadConstraints: - labelSelector: matchLabels: k8s-app: XXX qcloud-app: XXX maxSkew: 1 topologyKey: kubernetes.io/hostname whenUnsatisfiable: ScheduleAnyway
解读:
maxSkew
设置为 1 表示允许的最大偏差为 1,即任一节点上的 Pod 数量不会比其他节点多 1 个以上。topologyKey
为"kubernetes.io/hostname"
,意味着在不同的节点上分布 Pods。
部署后,Pod 分布到多个节点上,问题解决。

3. 事后思考
- 开启 API 网关的健康检查,当 API 网关探测节点失效时从后端通道中剔除
- 了解 K8S 资源调度的基本概念
附录:ChatGPT 助手
Q: TKE 如何设置让 Deployment 的 2 个 Pod 分配到两个节点上,目前 两个 Pod 分配到同一个 节点中。
A(ChatGPT-4-turbo): TKE(Tencent Kubernetes Engine)是腾讯云提供的托管 Kubernetes 服务。在 Kubernetes 中,如果希望让 Deployment 的 Pods 分布在不同的节点上,可以使用几种策略,主要包括:
1. 亲和性(Affinity)和反亲和性(Anti-Affinity)规则:
使用 Pod 反亲和性规则可以指定 Kubernetes 调度器不要将属于同一 Deployment 的 Pods 调度到同一节点上。
例如,以下是一个 Deployment 配置的示例,它使用了 Pod 反亲和性来确保 Pods 分散到不同的节点上:
apiVersion: apps/v1kind: Deploymentmetadata: name: my-deploymentspec: replicas: 2 selector: matchLabels: app: myapp template: metadata: labels: app: myapp spec: containers: - name: myapp-container image: myapp:1.0 affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: "app" operator: In values: - myapp topologyKey: "kubernetes.io/hostname"
在这个例子中,
requiredDuringSchedulingIgnoredDuringExecution
确保调度器在调度时遵循这些规则,而
topologyKey
设置为
"kubernetes.io/hostname"
表示 Pods 不能在拥有相同主机名的节点上运行。
2. 使用 Pod Topology Spread Constraints:
Pod Topology Spread Constraints 允许你更细粒度地控制 Pods 的分布,这可以通过设置
maxSkew
、
topologyKey
和
whenUnsatisfiable
等参数来实现。
以下是一个使用 Pod Topology Spread Constraints 的 Deployment 示例:
apiVersion: apps/v1kind: Deploymentmetadata: name: my-deploymentspec: replicas: 2 selector: matchLabels: app: myapp template: metadata: labels: app: myapp spec: containers: - name: myapp-container image: myapp:1.0 topologySpreadConstraints: - maxSkew: 1 topologyKey: "kubernetes.io/hostname" whenUnsatisfiable: "ScheduleAnyway" labelSelector: matchLabels: app: myapp
在这个配置中,
maxSkew
设置为 1 表示允许的最大偏差为 1,即任一节点上的 Pod 数量不会比其他节点多 1 个以上。
topologyKey
同样设置为
"kubernetes.io/hostname"
,意味着在不同的节点上分布 Pods。
确保你的 Kubernetes 集群有足够的不同节点来支持这些分布策略。如果集群中只有一个可用节点,或者其他节点因为资源限制(如 CPU、内存不足)无法调度 Pods,那么这些策略可能不会有效。