kubernetes 在可能缩小规模时防止杀死一些pod?

nfzehxib  于 2022-11-21  发布在  Kubernetes
关注(0)|答案(6)|浏览(181)

我需要缩放一组运行基于队列的工作进程的pod。工作进程的作业可以运行很长时间(数小时),并且不应中断。pod的数量基于工作进程队列的长度。缩放可以使用使用自定义指标的水平自动缩放器,也可以使用更改副本数量的简单控制器。
这两种解决方案的问题是,在缩小规模时,无法控制终止的pod。在任何给定时间,大多数工作进程可能都在处理短时间运行的作业、空闲作业或(更罕见的情况下)处理长时间运行的作业。我希望避免删除长时间运行的作业工作进程,空闲或短时间运行的作业工作进程可以毫无问题地终止。
有什么方法可以以低复杂性完成此操作?我能想到的一件事是基于pod的CPU使用率来完成此操作。虽然不理想,但这可能已经足够好了。另一种方法可以是,工作进程以某种方式公开一个优先级,指示它们是否是要删除的首选pod。不过,每次工作进程选择一个新作业时,此优先级都可能发生变化。
最终所有的工作都将是短期的,这个问题将消失,但这是一个长期的目标,现在。

llmtgqce

llmtgqce1#

在终止pod的过程中,Kubernetes会向pod的容器发送SIGTERM信号。您可以使用该信号正常关闭应用程序。问题是Kubernetes不会一直等待应用程序完成,在您的情况下,应用程序可能需要很长时间才能退出。
在这种情况下,我建议你使用preStop钩子,它在Kubernetes向容器发送KILL信号之前完成。

apiVersion: v1
kind: Pod
metadata:
  name: lifecycle-demo
spec:
  containers:
  - name: lifecycle-demo-container
    image: nginx
    lifecycle:
      postStart:
        exec:
          command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
      preStop:
        exec:
          command: ["/bin/sh","-c","nginx -s quit; while killall -0 nginx; do sleep 1; done"]
ddarikpa

ddarikpa2#

有一种变通方法可以对pod的终止给予一些控制。不太确定这是否是最好的做法,但至少你可以尝试一下,测试它是否适合你的应用程序。
1.将Deployment宽限期增加terminationGracePeriodSeconds: 3600,其中3600是应用程序中可能的最长任务的时间(以秒为单位)。这可确保pod不会在宽限期结束时终止。请详细阅读有关pod终止过程的文档。
1.定义一个preStop处理程序。关于生命周期钩子的更多细节可以在docs和示例中找到。在我的例子中,我使用下面的脚本创建了一个文件,该文件稍后将用作终止pod的触发器(可能有更好的解决方案)。

lifecycle:
  preStop:
    exec:
      command: ["/bin/sh", "-c", "touch /home/node/app/preStop"]

1.一旦满足条件,立即停止应用运行。当应用退出时,pod也会终止。无法从preStop shell脚本使用PID 1结束进程,因此您需要向应用添加一些逻辑来终止自身。在我的示例中,它是一个NodeJS应用,存在每30秒运行一次的调度程序,并检查是否满足两个条件。!isNodeBusy标识是否允许结束应用程序,以及fs.existsSync('/home/node/app/preStop')标识是否触发了preStop挂钩。对于您的应用程序来说,逻辑可能会有所不同,但您已经了解了基本概念。

schedule.scheduleJob('*/30 * * * * *', () => {
  if(!isNodeBusy && fs.existsSync('/home/node/app/preStop')){
    process.exit();
  }
});

请记住,此变通方法仅适用于voluntary disruptions,显然对involuntary disruptions没有帮助。更多信息请参见docs

5kgi1eie

5kgi1eie3#

我认为,使用Deployment或类似工具运行此类工作负载,并使用HorizontalPodAutoscaler进行扩展是错误的做法。
1.定义一个控制器(可能是Deployment),其任务是定期创建Kubernetes Job对象。

  1. Job的规范中应该包含一个.spec.parallelism值,该值等于您可以接受的最大并发执行数。
    1.然后,由Job派生的Pods运行您的处理逻辑,它们应该各自从队列中提取一条消息,处理它,然后从队列中删除它(如果成功)。
  2. Job必须以正确的状态(成功或失败)退出,这确保了Job能够识别处理何时完成,因此不会启动额外的Pods
    使用这种方法,.spec.parallelism根据要完成的工作量控制自动缩放,而缩小是使用Job的一个自动优势。
oalqel3c

oalqel3c4#

从1.22版本开始,有一个测试版功能可以帮助你做到这一点。你可以添加注解controller.kubernetes.io/pod-deletion-cost,其值在[-2147483647, 2147483647]范围内,这将导致具有较低值的pod首先被杀死。默认值为0,因此一个pod上的任何负值都将导致一个pod在缩减过程中被杀死,例如:
kubectl annotate pods my-pod-12345678-abcde controller.kubernetes.io/pod-deletion-cost=-1000
链接到有关此功能实现的讨论:Scale down a deployment by removing specific pods (PodDeletionCost) #2255
文档链接:复制副本集/ Pod删除成本

x6h2sr28

x6h2sr285#

您正在寻找Pod Priority and Preemption。通过为您的pod配置高优先级PriorityClass,您可以确保它们不会被删除,从而为其他优先级较低的pod腾出空间。
1.建立新的PriorityClass

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority
value: 1000000
globalDefault: false
description: "This priority class will not cause other pods to be preempted."

1.在pod中设置新的PriorityClass

priorityClassName: high-priority

PriorityClass中的value: 1000000配置机架的调度优先级,值越高表示机架越重要。

lo8azlld

lo8azlld6#

对于那些在节点缩小时面临Pod被杀死问题的人来说-
这是Cluster Autoscaler的预期功能,因为CA将尝试优化单元,以便可以使用最小的群集大小。但是,您可以通过为作业单元创建具有maxUnavailable=0的PodDisruptionBudget来保护作业单元不被逐出(被终止)。

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: sample-pdb
spec:
  maxUnavailable: 0
  selector:
    matchLabels:
      app: <your_app_name>

相关问题