kubernetes 调度程序在Pod完成初始化后仍计算InitContainer资源请求,

nkoocmlb  于 4个月前  发布在  Kubernetes
关注(0)|答案(9)|浏览(48)

发生了什么?
我创建了一个具有4个CPU核心的1节点EKS集群。在运行所有daemonsets后,我确保至少有3.5个核心是可分配的。
我创建了一个部署,其中包含InitContainers,以下是其外观概述(我用虚拟值替换了容器名称并删除了所有不必要的容器配置)。

initContainers:
    - image: 'test/initialization:v3.1.3'
      name: initialize
      resources:
        requests:
          cpu: 3
containers:
    - image: test/do-stuff
      resources:
        requests:
          cpu: 30m

我将副本数设置为1,并成功地将pod调度、初始化并转换为运行阶段。
然后我将副本数增加到2,期望第二个pod能够被调度,因为第一个pod已经完成了初始化。然而,它没有被调度。

Events:
  Type     Reason            Age                 From               Message
  ----     ------            ----                ----               -------
  Warning  FailedScheduling  57s (x2 over 6m3s)  default-scheduler  0/1 nodes are available: 1 Insufficient cpu. preemption: 0/1 nodes are available: 1 No preemption victims found for incoming pod.

预期会发生什么?

我预期第二个pod会被调度,因为第一个pod已经完成了初始化,并且它请求的CPU运行时肯定会为下一个pod留下超过3个核心的可分配资源。
从文档中,我知道调度器通过获取InitContainers和容器之间的最大请求来计算带有InitContainers的pod的有效资源请求。我知道这在决定将pod放置在哪里时非常重要。
然而,如果一个pod完成初始化并转换为运行阶段,我希望调度器在计算特定节点上已经安排了多少资源时丢弃InitContainers上指定的请求。

如何尽可能精确且最小地重现它?

  1. 从一个最小的集群开始,只有一个节点,并记下可用的CPU数量。
  2. 在集群上运行一个pod,其中包含占可分配CPU 75%的InitContainer和一个几乎没有CPU请求的容器。
  3. 等待该pod完成初始化,然后再运行另一个pod。另一个pod将无法被调度。

我们需要了解其他信息吗?

  • 无响应*

Kubernetes版本

$ kubectl version
Client Version: version.Info{Major:"1", Minor:"26", GitVersion:"v1.26.0", GitCommit:"b46a3f887ca979b1a5d14fd39cb1af43e7e5d12d", GitTreeState:"clean", BuildDate:"2022-12-08T19:58:30Z", GoVersion:"go1.19.4", Compiler:"gc", Platform:"darwin/amd64"}
Kustomize Version: v4.5.7
Server Version: version.Info{Major:"1", Minor:"29+", GitVersion:"v1.29.1-eks-b9c9ed7", GitCommit:"07600c74de018baffb16c82771a48adcb843a932", GitTreeState:"clean", BuildDate:"2024-03-02T03:46:35Z", GoVersion:"go1.21.6", Compiler:"gc", Platform:"linux/amd64"}

云提供商

EKS(亚马逊AWS)

OS版本

# On Linux:
$ cat /etc/os-release
NAME="Amazon Linux"
VERSION="2"
ID="amzn"
ID_LIKE="centos rhel fedora"
VERSION_ID="2"
PRETTY_NAME="Amazon Linux 2"
ANSI_COLOR="0;33"
CPE_NAME="cpe:2.3:o:amazon:amazon_linux:2"
HOME_URL="https://amazonlinux.com/"
SUPPORT_END="2025-06-30"
$ uname -a
Linux <hostname trimmed> 5.10.205-195.807.amzn2.x86_64 #1 SMP Tue Jan 16 18:28:59 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux

安装工具

  • EKS集群使用terraform安装

容器运行时(CRI)和版本(如适用)

containerd 1.7.11在EKS上

相关插件(CNI,CSI等)和版本(如适用)

  • Amazon EKS版本: v1.11.1-eksbuild.6
  • Amazon EKS版本: v1.16.2-eksbuild.1
  • Amazon EKS版本: v1.29.0-eksbuild.2
  • Amazon EKS版本: v1.27.0-eksbuild.1
djmepvbi

djmepvbi1#

这个问题目前正在等待分类。
如果SIG或子项目确定这是一个相关的问题,他们将通过应用triage/accepted标签并提供进一步的指导来接受它。
组织成员可以通过在评论中写入/triage accepted来添加triage/accepted标签。
有关使用PR评论与我互动的说明,请查看here。如果您对我的行为有任何问题或建议,请针对kubernetes/test-infra仓库提出一个问题。

yb3bgrhw

yb3bgrhw3#

这段文本描述了Kubernetes中资源分配和调度的过程。首先,kubelet计算可分配资源并上传,然后调度器组件计算已分配资源。在这个过程中,它不会检查容器的相应状态是否已经退出。

kubernetes/pkg/api/v1/resource/helpers.go文件的第96行到第120行,代码处理了initContainers的资源请求。如果容器的重启策略为Always,那么将其资源请求添加到结果累积的容器请求中,并将这些资源请求添加到可重启的初始化容器资源列表中。否则,将容器资源请求和可重启的初始化容器资源列表合并。

接下来,在kubernetes/pkg/scheduler/framework/plugins/noderesources/fit.go文件的第442行到第450行,代码判断pod是否可以被调度到节点。如果pod的CPU请求大于节点的可用CPU资源,那么将不可用资源添加到insufficientResources列表中。

在这个例子中,你可以像这样修改pod spec中的nodeName:

spec:
  nodeName: <your-node-name>

这样,资源确定过程可以跳过,pod可以直接调度到相应的节点,然后会出现以下情况:

[这里插入相关图片或示例]

我进行了一个简单的调试测试,修改了pod请求资源的逻辑,使得已退出的容器不计入已分配资源。然后重建调度器和kubelet并释放它们,再次运行上述示例,使其能够顺利运行。

whlutmcx

whlutmcx4#

此外,这不应被视为一个bug,因为如果pod容器退出(oom等),pod中的init容器需要重新启动并再次执行,因此需要准备足够的资源。
resource-sharing-within-contaienrs
在正常情况下,init容器不太可能需要比逻辑容器更多的资源。它主要用于初始化操作,资源应合理分配。

aemubtdh

aemubtdh5#

感谢您的回复@chengjoey ,我通过阅读您的回答学到了很多知识 :)

这不应该被认为是一个bug,因为pod中的init容器需要在容器退出(oom等)时重新启动并再次执行,因此需要准备足够的资源。

如果容器在完成init阶段之前退出(oom等),那么对我来说是有意义的。换句话说,init容器本身被OOMKilled并且需要重新启动。

然而,如果init阶段已经完成,并且pod已经在运行阶段(所有主容器都在运行),那么为什么InitContainer需要重新运行呢?如果一个或多个主容器崩溃并重启,我的理解是它们不需要重新运行InitContainers,因此InitContainers的资源请求无关紧要。

另一方面,如果pod由ReplicaSet管理并被删除,那么可能会有一个新的pod被重新调度,并可能因任何原因分配给不同的节点,因此在事后看来,为什么原始节点会保留旧pod所需的InitContainer(s)的资源是一个谜。

在正常情况下,init容器不太可能需要比逻辑容器更多的资源。它主要用于初始化操作,资源应该合理分配。

我理解您的观点,但认为在某些场景中,初始化比实际运行工作负载更耗费资源,我们有多种使用场景。例如:我们使用InitContainer(s)为我们的工作人员解析密钥,这至少需要200m的CPU。工作人员逻辑本身需要10m的CPU才能运行。乘以500个工作人员,我们浪费了95个CPU 😅

当我了解到您可以为InitContainers设置与主容器不同的资源请求时,我本能地认为这是为了支持初始化过程不同于“运行”阶段的使用场景,否则它的意义何在?🤔 也许超出了我的知识范围之外还有其他用途,但这是我对这可能是一个bug的想法。

2admgd59

2admgd596#

我认为即使InitContainer已经退出,资源仍然会被计为已分配资源,因为有几个pod重启原因会导致重新执行init容器。如果有一个机制可以确保pod不会重启,那么在初始化后应该释放InitContainer的资源。
systemctl restart containerd 将导致InitContainer重启,但pod重启是罕见的。

6ovsh4lw

6ovsh4lw7#

@chengjoey,那么如果pod可以重启的话,这就说得通了。我自己从来没有遇到过pod重启的情况😅
如果有一种机制可以确保pod不会重启,那么在初始化之后,InitContainer的资源应该被释放。
我认为这应该是默认行为,用户可以选择其他方式。
否则,为什么还要为InitContainer单独指定资源请求;如果主容器的资源需求更高,那么为InitContainer指定资源请求就是多余的,反之亦然。我宁愿将更高的请求分配给主容器,因为这些资源无论如何都会被分配给pod,并且在整个pod的生命周期中都不会被释放。
也许在pod规格下面添加一些东西,比如releaseInitContainerResourcesAfterInitialization: true会有所帮助🤔

bvjveswy

bvjveswy8#

@chengjoey,那么如果pod可以重启的话,这就说得通了。我自己从来没有遇到过pod重启的情况😅
如果有一种机制能确保pod不会重启,那么在初始化后,InitContainer的资源应该被释放。
我认为这应该是默认行为,用户可以选择其他方式。
否则,为什么还要为InitContainer单独指定资源请求呢?如果主容器的资源需求更高,那么为InitContainer指定更高的资源请求就是多余的,反之亦然。我宁愿将更高的资源请求分配给主容器,因为这些资源无论如何都会被分配给pod,并且在整个pod的生命周期内都不会被释放。
也许在pod规格下添加一些东西,比如releaseInitContainerResourcesAfterInitialization: true会有所帮助🤔
我认为你的想法很好,但在某些情况下,这会尽可能地分配资源。如果Pod基础设施容器被重启,可能会导致已分配的资源大于可分配的资源。我认为你的想法很好,可以让用户自己决定

o2g1uqev

o2g1uqev9#

我也在寻找一个解决方法,来处理那些资源消耗极大的初始化进程,这些进程需要 > 应用容器的请求之和(例如:数据库迁移、动态编译步骤等)。
我同意这是一个有用的可选功能。
目前,这被称为即将到来的 in-place updates 的主要用例,但这需要另一个控制器(VPA)来执行更新。理想情况下,这个问题可以通过简单地允许释放初始化资源来解决。

相关问题