限制CPU导致Kubelet延迟拉取镜像的原因

51次阅读
没有评论

问题描述

在使用Kubernetes时,通过spec.containers[].resources.limits.cpu限制了一个Job的CPU使用量。然而,用户观察到在Pod被调度和镜像开始拉取之间存在着显著的延迟。用户想知道Kubelet在被调度和拉取镜像之间做了什么操作,这些操作受到CPU限制的影响。

根据不同的集群,调度和拉取之间的延迟可能会更小或更大,但似乎与CPU限制有关:

Cluster 1:
- 无限制: 1秒
- limits.cpu=100m: 1-2秒
- limits.cpu=50m: 1-2秒
- limits.cpu=10m: 4-5秒

Cluster 2:
- 无限制: 1-2秒
- limits.cpu=100m: 3秒
- limits.cpu=50m: 6-7秒
- limits.cpu=10m: 30-33秒

这让我觉得,在容器启动之前,CPU限制已经生效,因此Kubelet在此之前所做的任何操作也受到了限制。这是预期的行为吗?我希望容器本身在CPU使用方面受到限制,但同时也希望作业能够快速启动。

根据CRI-O文档,拉取镜像本身应该是在容器运行时之外进行的,因此任何CPU限制都不应该生效:

Kubernetes联系kubelet以启动一个Pod。
Pod是一个Kubernetes的概念,由一个或多个共享相同IPC、NET和PID命名空间的容器组成,它们位于同一个cgroup中。
kubelet将请求转发给CRI-O守护进程,通过Kubernetes CRI(容器运行时接口)来启动新的Pod。
CRI-O使用containers/image库从容器注册表中拉取镜像。
下载的镜像被解压到容器的根文件系统中,存储在COW文件系统中,使用containers/storage库。
在为容器创建了rootfs之后,CRI-O生成一个OCI运行时规范的JSON文件,描述如何使用OCI生成工具运行容器。
然后,CRI-O使用规范启动一个兼容OCI的运行时来运行容器进程。默认的OCI运行时是runc。

在Pod启动后描述它,我发现Scheduled和Pulling之间有6秒的间隔(10秒 vs 4秒):

Kubectl describe pod结果:
Events:
  Type     Reason       Age              From               Message
  ----     ------       ----             ----               -------
  Normal   Scheduled    10s              default-scheduler  Successfully assigned emtyvgh-jobtest/jobtest50mcpu--1-5jx68 to node-10-63-135-34
  Normal   Pulling      4s               kubelet            Pulling image "busybox:latest"
  Normal   Pulled       3s               kubelet            Successfully pulled image "busybox:latest" in 940.736382ms
  Normal   Created      3s               kubelet            Created container test
  Normal   Started      3s               kubelet            Started container test

Job.yaml(使用kubectl apply -f执行):

apiVersion: batch/v1
kind: Job
metadata:
  name: jobtest50mcpu
spec:
  template:
    spec:
      containers:
        - name: test
          image: busybox:latest
          args:
            - "/bin/true"
          imagePullPolicy: Always
          resources:
            limits:
              cpu: "50m"
      restartPolicy: Never
  backoffLimit: 0

解决方案

请注意以下操作注意版本差异及修改前做好备份。

方案1

根据您的描述,Kubelet在容器被调度和镜像开始拉取之间存在延迟。这是因为在容器启动之前,CPU限制已经生效,Kubelet在此之前所做的任何操作也受到了限制。

为了解决这个问题,您可以尝试增加Kubelet的CPU限制,以便在容器启动之前,Kubelet能够更快地完成所需的操作。以下是一些可能的解决方案:
1. 增加Kubelet的CPU限制:您可以尝试增加Kubelet的CPU限制,以便它能够更快地完成所需的操作。您可以通过修改Kubelet的配置文件来增加CPU限制。请注意,增加CPU限制可能会导致Kubelet在其他方面受到限制,因此请根据您的需求和资源限制进行权衡。
2. 调整Pod的资源限制:您还可以尝试调整Pod的资源限制,以便在容器启动之前,Kubelet能够更快地完成所需的操作。您可以通过修改Pod的配置文件来调整资源限制。请注意,调整资源限制可能会影响容器的性能和可用性,因此请根据您的需求和资源限制进行权衡。

请注意,这些解决方案可能会因集群的不同而有所不同。建议您在实际操作之前先进行测试,并根据实际情况进行调整。

方案2

使用脚本或工具来管理容器的启动顺序可能会增加复杂性,并且需要确保容器A和容器B之间的依赖关系正确设置。
另一种方法是编写脚本或使用工具来控制容器的运行顺序。您可以使用docker run命令来手动控制容器的启动顺序,或者使用一些第三方工具来管理容器的依赖关系。

以下是一个简单的bash脚本示例,可以在容器A启动后启动容器B:

#!/bin/bash
# 启动容器A
docker run -d --name container_a your_image_a
# 等待容器A完全启动
while ! docker exec container_a echo "Container A is ready"; do
  sleep 1
done
# 启动容器B
docker run -d --name container_b your_image_b

在这个示例中,我们首先使用docker run命令启动容器A,并将其命名为container_a。然后,使用一个循环来等待容器A完全启动(这里是通过在容器内运行echo命令来测试)。一旦容器A就绪,我们再使用docker run命令启动容器B,并将其命名为container_b

请注意,这只是一个示例,您可以根据实际情况进行调整和修改。在实际使用中,请确保容器A和容器B之间的依赖关系正确设置,并根据实际需求进行调整。

总结

在使用Kubernetes时,通过限制CPU使用量可能会导致Kubelet在容器被调度和镜像开始拉取之间存在延迟。这是因为在容器启动之前,CPU限制已经生效,Kubelet在此之前所做的任何操作也受到了限制。

为了解决这个问题,您可以尝试增加Kubelet的CPU限制,以便在容器启动之前,Kubelet能够更快地完成所需的操作。您还可以尝试调整Pod的资源限制,以便在容器启动之前,Kubelet能够更快地完成所需的操作。请注意,这些解决方案可能会因集群的不同而有所不同。建议您在实际操作之前先进行测试,并根据实际情况进行调整。

另外,您还可以使用脚本或工具来控制容器的运行顺序。您可以使用docker run命令来手动控制容器的启动顺序,或者使用一些第三方工具来管理容器的依赖关系。请注意,使用脚本或工具来管理容器的启动顺序可能会增加复杂性,并且需要确保容器之间的依赖关系正确设置。

希望以上解决方案对您有所帮助!如果您有任何其他问题,请随时提问。

正文完