1、什么是定时任务?
| 定时+任务:每到达一个时间点,就会自动触发执行的指令。 |
| 举个例子: |
| 你给手机设置为每天固定早上6点响铃 |
| 定时 |
| 任务 |
2、为什么要用定时任务?
| 日常运维过程中会遇到很多周期性重复执行的任务,有执行起来耗费的时间还很长,需要有一种支持配置定时任务的软件来解放我们 |
| 例如这些任务: |
| 1、备份数据的脚本/命令 |
| 2、定时采集主机监控数据的脚本/命令 |
这就是Linux系统的定时任务Crond,这相当于我们平时生活中的闹钟功能,每天晚上提前设置定时,早晨按时叫醒你。
3、如何在linux中实现定时任务—》Crond
| crond: 是一款支持配置定时任务的软件,在几乎所有的Linux发行版中都是预安装的。 |
| 你只需要按照它规定的格式写好时间点+待执行的指令,就可以,非常简单 |
| |
| Crond服务会定期(默认每分钟检查一次)检查系统中是否有要执行的任务工作,如果有, |
| 便会根据其预先设定的定时任务规则自动执行该定时任务工作 |
4、crond日志文件
5、 crond里配置的定时任务分2类。
| Crontab定时任务一般可以分为两种主要类型:用户crontab任务和系统crontab任务。 |
| 1、用户级定时任务 |
| (1)通常由用户自己维护,系统管理员当然也可以维护 |
| (2)定时任务的配置最终都会记录到文件中,用户级的定时任务文件位于/var/spool/cron/下,文件名是用户名。 |
| (3)用户可以使用 crontab -e 命令来编辑自己的定时任务文件,使用 crontab -l 命令来列出自己的任务。 |
| |
| 2、系统级定时任务 |
| (1)只有系统管理员可以编辑。 |
| (2)系统级定时任务文件位于/etc目录下,文件名有crontab、cron.d/、cron.daily/、cron.hourly/、cron.monthly/及cron.weekly/。 |
| /etc/crontab:这是主crontab文件,包含了系统任务表,以及在预设时间执行的工作。 |
| /etc/cron.d/:此目录存放任何想被cron守护进程执行的任务。 |
| /etc/cron.daily/:此目录下存放的任务脚本会被系统安排在每天凌晨执行。 |
| /etc/cron.hourly/:此目录下存放的任务脚本会被系统安排每小时执行一次。 |
| /etc/cron.monthly/:此目录下存放的任务脚本会被系统安排在每月的某一天执行。 |
| /etc/cron.weekly/:此目录下的任务脚本会被系统安排在每周的某一天执行。 |
| |
| 系统级定时任务主要用于如:临时文件清理例/tmp和/var/tmp等、系统信息采集、日志文件切割等场景 |
| 用户级定时任务主要用于如:定时向互联网同步时间、定时备份系统配置文件、定时备份数据库的数据等场景 |
定义方式一:编辑文件/etc/crontab(必须指定用户名)
| [root@localhost ~] |
| [root@localhost ~] |
| SHELL=/bin/bash |
| PATH=/sbin:/bin:/usr/sbin:/usr/bin |
| MAILTO=root |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 分钟 |
| 小时 |
| 日 |
| 月 |
| 周 |
| * * * * * egon echo "$(date) 执行拉" >> /tmp/system_run.log |
定义方式二:把脚本放到该目录下,注意要给脚本x权限: chmod +x
| /etc/cron.hourly/ |
| |
| /etc/cron.daily/ |
| |
| /etc/cron.weekly/ |
| |
| /etc/cron.monthly/ |
| [root@localhost ~] |
| * * * * * echo `date` >> /tmp/a.log |
| [root@localhost ~] |
| * * * * * echo `date` >> /tmp/a.log |
| [root@localhost ~] |
| root |
| [root@localhost ~] |
| * * * * * echo `date` >> /tmp/a.log |
| [root@localhost ~] |
| [root@localhost ~] |
| |
| |
| [root@localhost ~] |
| [root@localhost ~] |
| 上一次登录:四 8月 13 19:44:10 CST 2020pts/1 上 |
| [egon@localhost ~]$ crontab -e |
| You (egon) are not allowed to use this program (crontab) |
| See crontab(1) for more information |
了解知识(不爱看你就别看,省事)
| 当/etc/cron.allow和/etc/cron.deny两个文件同时存在时,系统首先检查/etc/cron.allow文件。 |
| 如果你的用户名在/etc/cron.allow文件中,你就可以使用cron配置定时任务。 |
| 如果你的用户名不在/etc/cron.allow文件中,系统将不会再检查/etc/cron.deny文件,因此你将无法使用cron。 |
| 总的来说,/etc/cron.allow文件具有优先级,且只要你的用户名在该文件中,无论/etc/cron.deny的内容如何, |
| 你都将获得访问cron的权限。如果你没有在/etc/cron.allow文件中找到你的用户名, |
| 即使你的用户名也没有在/etc/cron.deny文件中,你也将不能够访问cron。 |
| 所以,在这两个文件同时存在的情况下,想要授权某个用户使用cron,你应在/etc/cron.allow文件中添加该用户的用户名。 |
| 相反,如果你想阻止某个用户访问cron, 你应确保该用户的用户名既不在/etc/cron.allow文件中,也在/etc/cron.deny文件中。 |
crond是按照分钟进行计算的,不支持秒
crontab的时间范围可以参考/etc/crontab中的注释,例如0或7代表周日
| * 表示任意时间 |
| |
| - 表示一个时间范围段, 如小时设置为:5-7,则表示从5点到7点 |
| |
| , 表示分隔时段, 例如日设置为6,0,4,则表示周六、周天、周四 |
| |
| /10 表示每隔n单位时间, 如分支设置为*/10,则表示每隔10分钟 |
| |
| 此外强调以下几点: |
| (1)cron 守护进程每分钟都会自动检查 /etc/crontab 文件、etc/cron.d/ 等目录中的改变。如果发现了改变,它们就会被载入内存。所以你如果修改了计划人文导致其crontab文件改变后,并不需要重启守护进程。 |
| |
| (2)crontab的用户手册中推荐每一个命令使用绝对路径,例如调用rm命令时写作:/bin/rm,这是为了防止由于每一个用户的PATH环境变量不同而导致命令无法找到的错误。 |
| |
| (3)编写定时任务时,先在命令行上面执行一次,查看是否可以执行成功。 |
| |
| (4)把定时任务执行的结果定向到空&>/dev/null,如果不定向到空的话,邮件服务postfix开启时,系统会一直发送邮件信息日积月累会白白耗费磁盘空间 |
了解定时任务crontab的编写规范。
| * * * * * |
| */1 * * * * |
| |
| */3 * * * * |
| */10 02 * * * |
| |
| 00 02 * * * |
| |
| 00 02 1 * * |
| |
| 00 02 14 2 * |
| |
| 00 02 * * 7 |
| |
| 00 02 * 6 5 |
| |
| "日期穿刺"问题:当日期字段和星期字段都被特定的值所指定时,那么满足其中任意一个字段的条件,任务就会被执行 |
| 00 02 14 * 7 |
| 00 02 14 2 7 |
| |
| 00 00 14 2 * |
| |
| 00 02 * 1,5,8 * |
| |
| 00 02 1-8 * * |
| |
| 00 21 * * * |
| |
| 45 4 1,10,22 * * |
| |
| 45 4 1-10 * * |
| |
| 3,15 8-11 */2 * * |
| |
| 0 23-7/2 * * * |
| |
| 15 21 * * 1-5 |
示例1
| [root@localhost ~] |
| [root@localhost test] |
| [root@localhost test] |
| |
| |
| echo "111" >> /tmp/access.log |
| [root@localhost test] |
| |
| |
| echo "222" >> /tmp/access.log |
| [root@localhost test] |
| |
| |
| echo "333" >> /tmp/access.log |
| [root@localhost test] |
| [root@localhost test] |
| [root@localhost test] |
| [root@localhost test] |
| [root@localhost ~] |
| 。。。。。。 |
| * * * * * egon run-parts /test |
| |
| [root@localhost ~] |
| [root@localhost ~] |
示例2:命令最好用绝对路径
| [root@localhost test] |
| [root@localhost test] |
| * * * * * ntpdate ntp.aliyun.com |
| 您在 /var/spool/mail/root 中有邮件 |
| |
| [root@localhost test] |
| |
| /bin/sh: ntpdate: 未找到命令 |
| |
| [root@localhost ~] |
| * * * * * /usr/sbin/ntpdate ntp.aliyun.com |
注意:在centos7.9中默认安装了邮件服务postfix并启动,当你的计划人中的命令执行失败后,邮件服务会自动发送邮件到/var/spool/mail/下以用户名命名的文件中,你可以去cat查看报错信息。
定时任务的执行有时候会不可避免的出一些各种各样的错误信息,如果每次都发送邮件到/var/spool/mail/下,日积月累会耗费过多磁盘空间
你可以在命令后加&>/dev/null,把定时任务命令执行的结果都丢掉,这样即便邮件服务开启的状态也没内容给它发送了
| [root@localhost ~] |
| * * * * * /usr/sbin/ntpdate ntp.aliyun.com &>/dev/null |
也可以干脆关闭到该服务
示例3:每分钟把系统时间追加到一个文件中,以2019-06-15_13:54:10格式
| [root@localhost ~] |
| * * * * * /usr/bin/date +\%F_\%T >> /root/time.txt |
务必注意加上转义符号,因为在定时任务中,有些特殊符号会被误解,不转义是无法执行的。
示例4:每天凌晨3点做一次备份?将/etc/目录进行打包,备份到/backup下面,且只能保留最近三天的数据。
| [root@localhost ~] |
| |
| export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin |
| mkdir /backup |
| cd / |
| tar -czf backup/$(hostname)_$(date +\%F_\%T)_etc.tar.gz /etc |
| find backup -type f -name "*.tar.gz" -mtime +3 |xargs rm -rf |
| [root@localhost ~] |
| * * * * * /bin/bash /root/bak.sh &>/dev/null |
查看cron服务是否起作用:
| 第一,脚本代码有问题,解决:先手动调试跑通 |
| |
| 第二,执行环境问题:手动执行成功而crontab不能执行的时候,很可能就是执行环境的问题,例如相关路径的设置问题,可以在代码最前面执行 source /home/user/.bash_profile |
| |
| 第三,系统时间不正确。这种问题最好理解,也是比较常见和隐蔽的问题,解决方案:date -s ******** |
| |
| 第四,就是我们的脚本是否有可执行权限。必须保证执行脚本的用户有执行改文件的权限。 |
| |
| 第五,crontab 守护进程死掉了。这种情况是极少发生的,但也不排除,当我们实在是找不到其他原因的时候可以用。解决方案:重启该进程。 |
| |
| 第六,crontab不执行的问题困扰了好长时间,脚本写的都正确,但是就是不执行,最终解决方法如下: |
| crontab -u root /var/spool/cron/root |
| 这样root用户的crontab就生效了 |
| [root@localhost ~] |
| 重启下服务就好了 |
| |
| 第七,crond没有启动 |
| |
| 第八,脚本编码问题,脚本在window下编写,传到linux下后报“锘?!/bin/bash”,用vi编辑器新建新shell脚本,输入内容后保存。 |
| |
| 第九:特殊符号无法识别,需要添加转义 |
| |
| * * * * * tar czf /tmp/`date '+%Y'` /etc 该计划任务中命令的执行流程是crond->tar命令,而crond在执行tar命令时,无法识别通配符%的意思(shell能识别),所以该命令无法正常执行 |
| |
| 解决方案一:添加转义符号 |
| * * * * * tar czf /tmp/`date '+\%Y'` /etc |
| |
| 解决方案二:直接将命令扔到脚本里 |
| 通常都会把要执行的操作放到文件中,然后/bin/bash a.sh去执行,* * * * * /bin/bash a.sh ,这样的执行流程就变成了crond->bash shell->a.sh,这样a.sh内即便是写%号,也能被识别出来 |
1.每分钟打印你的名字到egon.txt中。
2.每周六的早上8点到12点,执行/scripts/test.sh脚本。
3.每个月的1号,执行/scripts/egon.sh脚本
4.每年的10月份1号到7号,执行/scripts/holiday.sh脚本
5.每天的9点和11点,执行/scripts/test.sh脚本
配置163邮箱
开启POP3/SMTP(注意:开启后给你的授权码就是客户端程序的登录密码,只显示一次,如果忘记了,你只能关闭掉POP3/SMTP然后重启开启获取新的才行)
python2发送邮件代码
| |
| import smtplib |
| from email.mime.text import MIMEText |
| from email.header import Header |
| |
| |
| |
| sender = 'xxx@163.com' |
| |
| password = 'xxxxxx' |
| |
| receiver = 'xxxx@163.com' |
| |
| subject = 'Python SMTP 测试邮件' |
| |
| mail_msg = 'Python 测试邮件发送。。。。' |
| |
| |
| |
| smtp_server = 'smtp.163.com' |
| |
| message = MIMEText(mail_msg, 'plain', 'utf-8') |
| |
| message['From'] = sender |
| |
| message['To'] = receiver |
| |
| message['Subject'] = Header(subject,'utf-8') |
| try: |
| print("发送邮件") |
| |
| smtp = smtplib.SMTP_SSL(smtp_server) |
| smtp.set_debuglevel(1) |
| |
| smtp.login(sender, password) |
| |
| smtp.sendmail(sender, receiver, message.as_string()) |
| print('success:发送成功') |
| except smtplib.SMTPException: |
| print('error:邮件发送失败') |
| finally: |
| |
| smtp.quit() |
测试
用上面的脚本就可以,这里也顺便贴下 25 端口邮箱的发送方式
| import smtplib |
| from email.mime.text import MIMEText |
| from email.header import Header |
| |
| |
| |
| sender = 'xxx@163.com' |
| |
| password = 'xxxxxx' |
| |
| receiver = 'xxxx@163.com' |
| |
| subject = 'Python SMTP 测试邮件' |
| |
| mail_msg = 'Python 测试邮件发送。。。。' |
| |
| |
| |
| smtp_server = 'smtp.163.com' |
| |
| message = MIMEText(mail_msg, 'plain', 'utf-8') |
| |
| message['From'] = sender |
| |
| message['To'] = receiver |
| |
| message['Subject'] = Header(subject,'utf-8') |
| try: |
| print("发送邮件") |
| |
| smtp = smtplib.SMTP() |
| smtp = stmp.connect(smtp_server , 25) |
| smtp.set_debuglevel(1) |
| |
| smtp.login(sender, password) |
| |
| smtp.sendmail(sender, receiver, message.as_string()) |
| print('success:发送成功') |
| except smtplib.SMTPException: |
| print('error:邮件发送失败') |
| finally: |
| |
| smtp.quit() |
1、我们把上述代码改造成一个可以发送邮件的工具
(1)说明
| 改造起来非常简单 |
| (1)在文件头声明解释器 |
| 可以在命令行直接这么用 |
| /usr/bin/mail.py 邮件标题 邮件内容 |
| 而不是这么用 |
| python /usr/bin/mail.py 邮件标题 邮件内容 |
| |
| 此时你就必须在整个py文件的开头加上说明默认用什么解释器解释执行 |
| 你不加,那你不带前缀的执行/usr/bin/mail.py就会默认采用bash shell解释器执行文件代码 |
| |
| (2)导入模块sys,接收外部的传参,邮件标题改为sys.argv[1]、邮件内容值改为sys.argv[2] |
(2)创建/scripts/mail.py,整体内容如下
| |
| |
| import smtplib |
| import sys |
| from email.mime.text import MIMEText |
| from email.header import Header |
| |
| |
| |
| sender = 'xxx@163.com' |
| |
| password = 'xxxx' |
| |
| receiver = 'yyy@163.com' |
| |
| subject = sys.argv[1] |
| |
| mail_msg = sys.argv[2] |
| |
| |
| |
| |
| smtp_server = 'smtp.163.com' |
| message = MIMEText(mail_msg, 'plain', 'utf-8') |
| |
| message['From'] = sender |
| |
| message['To'] = receiver |
| |
| message['Subject'] = Header(subject,'utf-8') |
| try: |
| print("发送邮件") |
| |
| smtp = smtplib.SMTP_SSL(smtp_server) |
| smtp.set_debuglevel(1) |
| |
| smtp.login(sender, password) |
| |
| smtp.sendmail(sender, receiver, message.as_string()) |
| print('success:发送成功') |
| except smtplib.SMTPException: |
| print('error:邮件发送失败') |
| finally: |
| |
| smtp.quit() |
(3)授权,并链接PATH路径下
| chmod +x /scripts/mail.py |
| ln -s /scripts/mail.py /usr/bin/mail |
| |
| mail "测试邮件" "哈哈哈哈哈哈机房着火啦快跑啊下班啦" |
2、然后新建监控脚本servermonitor.sh
| #!/bin/bash |
| cpu_limit=0 |
| mem_limit=0 |
| disk='/dev/sda1' |
| disk_inode_limit=0 |
| disk_space_limit=0 |
| |
| function monitor_cpu(){ |
| cpu_free=`vmstat 1 5 |awk 'NR>=3{x = x + $15} END {print x/5}' |awk -F. '{print $1}'` |
| cpu_use=$((100-cpu_free)) |
| if [ $cpu_use -gt $cpu_limit ] |
| then |
| msg="TIME:$(date +%F_%T) |
| HOSTNAME:$(hostname) |
| IPADDR:$(ifconfig |awk 'NR==2{print $2}') |
| MSG:CPU usage exceeds the limit,current value is ${cpu_use}%" |
| echo $msg |
| /usr/bin/mail "cpu告警信息" "$msg" |
| fi |
| } |
| |
| function monitor_mem(){ |
| mem_total=`free |awk 'NR==2{print $2}'` |
| mem_use=`free |awk 'NR==2{print $3}'` |
| mem_per=`echo "scale=2;$mem_use/$mem_total" |bc -l|cut -d. -f2` |
| if [ $mem_per -gt $mem_limit ] |
| then |
| msg="TIME:$(date +%F_%T) |
| HOSTNAME:$(hostname) |
| IPADDR:$(ifconfig |awk 'NR==2{print $2}') |
| MSG:Memory usage exceeds the limit,current value is ${mem_per}%" |
| echo $msg |
| /usr/bin/mail "内存告警信息" "$msg" |
| fi |
| } |
| |
| function monitor_disk_inode(){ |
| inode_use=`df -i $disk |awk 'NR==2{print $5}' |cut -d% -f1` |
| if [ $inode_use -gt $disk_inode_limit ] |
| then |
| msg="TIME:$(date +%F_%T) |
| HOSTNAME:$(hostname) |
| IPADDR:$(ifconfig |awk 'NR==2{print $2}') |
| MSG:Disk inode usage exceeds the limit,current value is ${inode_use}%" |
| echo $msg |
| /usr/bin/mail "硬盘innode信息" "$msg" |
| fi |
| } |
| |
| function monitor_disk_space(){ |
| space_use=`df $disk |awk 'NR==2{print $5}'|cut -d% -f1` |
| if [ $space_use -gt $disk_space_limit ] |
| then |
| msg="TIME:$(date +%F_%T) |
| HOSTNAME:$(hostname) |
| IPADDR:$(ifconfig |awk 'NR==2{print $2}') |
| MSG:Disk space usage exceeds the limit,current value is ${space_use}%" |
| echo $msg |
| /usr/bin/mail "硬盘空间信息" "$msg" |
| fi |
| } |
| |
| monitor_cpu &>> /tmp/monitor.log |
| monitor_mem &>> /tmp/monitor.log |
| monitor_disk_inode &>> /tmp/monitor.log |
| monitor_disk_space &>> /tmp/monitor.log |
3、授权并本地先测试一下
| chmod +x /scripts/servermonitor.sh |
| /scripts/servermonitor.sh |
4、配置计划任务
| * * * * * /root/servermonitor.sh |
注意:我们用的是人家163的邮件服务器,发送邮件会受主机名解析,163邮箱自动屏蔽等方面的影响,所以平时我们通常也会搭建自己邮件服务器)
更多案例见:
https://www.cnblogs.com/linhaifeng/p/6602149.html#_label7
1.先自己手动把命令一条一条执行一下,确保你的命令符合你的预期
2.然后再把你调试后的一条条命令扔到文件里,这个文件我们称之为一个脚本
脚本的存放路径最好统一/scripts
3.执行脚本
使用bash命令执行, 防止脚本没有增加执行权限(/usr/bin/bash /bin/bash),确保可以正常执行且结果符合预期
4.编写定时任务
可以加上必要的注释信息, 人、时间、任务
| # 每分钟执行一条xxx命令 |
| * * * * * /bin/bash /scrips/run.sh |
设定定时任务执行的周期
粘贴步骤3中你执行脚本的命令(不要手敲)
5.调试定时任务
可以先用 * 让脚本在1分钟内存触发,确保真的可以触发
如果发现没有触发,则要:
检查环境变量问题(最好在脚本中重新定义环境变量PATH)
检查日志/var/log/cron
1.定时任务规则之前加注释
2.使用脚本执行定时任务(只有一条简单命令的可以直接使用命令执行)
3.运行脚本一定要用绝对路径执行,并且最好统一脚本位置。
4.定时任务中date命令的百分号需转义才能使用。
| * * * * * echo `date "+%F"` >> /tmp/1.log # 无法执行 |
| * * * * * echo `date "+\%Y_\%m_\%d \%H:\%M:\%S"` >> /tmp/2.log # 可以执行 |
5.命令或脚本结果(正确及错误)定向到空(&>/dev/null)或追加到文件中 &>>/tmp/run.log
6.避免不必要的程序及命令输出,如打包命令,tar -v的显示过程的选项。
7.打包压缩使用相对路径(切到目标目录的上一级打包目标,否则可能会带着一层目录)
| [root@rockylinux /]# tree /test1 |
| /test1 |
| ├── 1.txt |
| ├── 2.txt |
| └── 3.txt |
| |
| tar czf a.tar.gz /test1 # 打包的内容里带着一层文件夹test1 |
| |
| 建议这样打包 |
| cd /test1 |
| tar czf ../b.tar.gz . |
8.定时任务脚本中的程序文件 ,尽量用绝对路径
这一点值得强调一下,iptables命令在计划任务中必须写绝对路如/sbin/iptables,否则会导致执行失败,即便是root用户也不行!!!!
这一点值得强调一下,iptables命令在计划任务中必须写绝对路如/sbin/iptables,否则会导致执行失败,即便是root用户也不行!!!!
这一点值得强调一下,iptables命令在计划任务中必须写绝对路如/sbin/iptables,否则会导致执行失败,即便是root用户也不行!!!!
这一点值得强调一下,iptables命令在计划任务中必须写绝对路如/sbin/iptables,否则会导致执行失败,即便是root用户也不行!!!!
这一点值得强调一下,iptables命令在计划任务中必须写绝对路如/sbin/iptables,否则会导致执行失败,即便是root用户也不行!!!!
这一点值得强调一下,iptables命令在计划任务中必须写绝对路如/sbin/iptables,否则会导致执行失败,即便是root用户也不行!!!!
这一点值得强调一下,iptables命令在计划任务中必须写绝对路如/sbin/iptables,否则会导致执行失败,即便是root用户也不行!!!!
这一点值得强调一下,iptables命令在计划任务中必须写绝对路如/sbin/iptables,否则会导致执行失败,即便是root用户也不行!!!!
9.系统与命令位置有关的环境变量问题,建议脚本中重新定义环境变量PATH。
| 如下直接在脚本中声明 |
| [root@localhost ~] |
| |
| export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin |
| mkdir /backup |
| cd / |
| tar -czf backup/$(hostname)_$(date +\%F_\%T)_etc.tar.gz /etc |
| find backup -type f -name "*.tar.gz" -mtime +3 |xargs rm -rf |
| [root@localhost ~]# crontab -l |
| * * * * * /bin/bash /root/bak.sh &>/dev/null |