仅在机器空闲时运行 cron 作业(linux)

Posted

技术标签:

【中文标题】仅在机器空闲时运行 cron 作业(linux)【英文标题】:run cron job only when machine is idle (linux) 【发布时间】:2012-12-28 20:08:50 【问题描述】:

如何仅在 CPU 空闲 >50% 时运行 cron 作业(bash 脚本)?

我可以从 TOP 获得 cpu 空闲

top -b -d 00.10 -n 3 |grep ^Cpu
Cpu(s): 0.3%us, 0.3%sy, 0.0%ni, 99.3%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st

我目前的设置是:

crontab
0,15,30,45 * * * * /usr/bin/php /home/user/batchprocess.php
# I could use a bash script here to call PHP, if it is a good solution.

我让 PHP 脚本检查 CPU 空闲:

batchprocess.php
proc_nice(10);
// wait for CPU idle
do
    $cpu_stat = exec('top -b -d 00.10 -n 3 |grep ^Cpu');    
    $tmp = stristr($cpu_stat,'%id',TRUE);
    $cpuidle = trim(substr($tmp,strrpos($tmp,',')+1));
while($cpuidle<$min_cpuidle);
// do actual processing here

我当前方法的问题是它启动程序而不管 CPU 利用率如何。而且运行TOP的while循环感觉效率不高。我希望它只在 CPU 空闲>50

时启动

一些附加信息:

Centos 6.2、PHP5.3

我有几个永远不会关闭的 EC2 实例,因此我想在空闲时利用它们的处理能力。但永远不要重载服务器。 (冗余数据库实例、开发实例、NAT 实例)

我知道 EC2 自动扩展,现货实例。只是想使用额外的容量。

后台作业是图像压缩(CPU 密集型,没有太多 I/O 或网络)。

欢迎提出任何建议。提前致谢!


根据下面的输入,我意识到“nice”在我的情况下是一个更好的解决方案。我应该重新调整我的目标,以尽量减少对服务器的影响,而不是跟踪 CPU 利用率。

所以新的设置是:

crontab
0,15,30,45 * * * * nice -20 /usr/bin/php /home/user/batchprocess.php

和 PHP 脚本:

batchprocess.php
if ($cpuidle < 50) 
    exit(0);
// do actual processing here

我会对其进行测试并将我的发现发回。


报告:我已经通过 DEV/PRD 输入了这段代码,它运行良好。它没有解决TOCTOU的问题,但现在已经足够了。

【问题讨论】:

空闲一秒 100% 下一秒,如何在你的 cronjob 上使用 nice 来设置它的优先级 @Dagon 因为我没有更好的解决方案,所以在运行时检查至少比什么都不做要好。例如当系统已经很忙时开始工作。 这既不是这里也不是那里,但是天哪,你为什么要在 php 中编写 cronjobs @Eevee 批处理作业使用 amazon AWS sdk、mysql 和 imagick。所以 PHP 是我可以让它工作的方式。不幸的是,我不知道任何 ruby​​/perl/python。 【参考方案1】:

如果您只是希望在系统加载时执行脚本,例如2.0 或更低,您可以使用这样的 shell 脚本:

#!/bin/sh
LOAD=`cat /proc/loadavg | cut -d" " -f1`
THRESHOLD=2.0
if [ $(bc <<< "$LOAD <= $THRESHOLD") -eq 1 ]; then
    $@
fi

另存为,例如/usr/local/bin/if-idle,并将 if-idle 粘贴在 crontab 文件中的命令前面。

【讨论】:

这仍然只是“系统现在在做什么”,它很可能会在下一个时钟滴答声中改变。 没错,但这种方法也有有效的用例。想象一下 cron 作业需要为共享资源持有锁的情况。在这种情况下,使用nice -20 运行作业存在将共享资源锁定任意时间的风险。 这非常适合我正在运行一批 CPU 密集型计算并希望在第一个计算完成时推迟开始下一个计算的情况,不知道它何时会发生,并且无法访问到它运行的终端。【参考方案2】:

这是TOCTOU 的典型案例——你检查系统是否空闲,然后启动你的进程——但是在你检查之后,或者在你的进程启动之前,有其他事情导致系统中的另一个进程启动并且你仍然加载系统超过必要的。

执行此操作的“正确”方法是使用nice 命令为您的进程设置低优先级。顺便说一句,您检查 cpu 使用情况的循环将使用 100% cpu,因此它可能无法工作,除非您第一次检查它是空闲的。

您已经有一个“proc_nice(10)”,所以应该可以完成这项工作。我认为花精力来确定系统是否繁忙没有任何意义。

如果您愿意,在代码中的适当位置,您可以执行以下操作:

 if (check_cpu_usage() > 50%) sleep(1second); 

但我不确定这是否有用 - 如果系统很忙,“好”进程将不会获得太多 CPU 时间,因此不会与以更高优先级运行的其他进程竞争。

【讨论】:

感谢您提供的扩展信息。我没有考虑这个过程在实践中有多“好”。更高优先级的进程会使其长时间停止吗?例如正在运行完整的数据库备份。 如果你让它“非常好”(命令行上的nice -n 20)那么它肯定会使用很少的CPU IF还有其他东西想要运行。我曾经这样做过一些我喜欢做的计算[这需要很多很多小时]。编译 Linux 内核时的差异并不明显,但一旦编译完成,计算就会再次启动并完成它的工作。 cron 和低优先级作业的组合理论上可以导致作业的多个低运行实例同时运行。

以上是关于仅在机器空闲时运行 cron 作业(linux)的主要内容,如果未能解决你的问题,请参考以下文章

仅在特定时间每分钟运行一次 cron 作业?

Selenium 脚本从控制台工作,不能在 CRON 中工作 - Geckodriver 错误

使用 R 和 SQL Server 的 Cron 作业

Kubernetes:在现有集群内启动一个容器,以运行cron作业

从 docker 堆栈运行 cron 作业的最佳实践

仅在未运行时使用 cron 运行 python 脚本