Linux Crond定时任务详解:配置格式、实战脚本与避坑指南
发布时间:2026/10/16 9:24:57 锦皓数字建站

做Linux运维这些年我几乎每天都要跟Crond定时任务打交道。服务器上那些“到点自己干活”的活儿——日志轮转、数据库备份、服务健康检查、数据同步——十有八九都是靠Crond这个守护进程撑起来的。标题里写了“网络服务”四个字严谨一点说Crond本身是个时间驱动的任务调度服务跟网络没有直接关系但在实际服务器维护里它跟Nginx、MySQL这类网络服务总是绑在一起编排的比如凌晨定时重载Nginx配置、定时把数据库备份传送到远端存储。这篇东西适合刚接触Linux的人、被crontab搞到怀疑人生的新手、以及想把手头脚本规范化的运维同行。读完你能拿到完整的配置格式、三个拿来就能用的生产级脚本模板还有我踩了好多次才绕开的那些坑。1. 先搞清楚Crond是什么以及为什么非用它不可1.1 从“闹钟便利贴”说起Crond到底干了什么事想象一下你每天早上要看三份报表第一份是昨天服务器的错误日志第二份是数据库的备份结果第三份是几个核心进程的健康状态。人不可能每天凌晨三点爬起来盯着这时候就需要一个“能定时帮你跑腿”的东西。Crond就是Linux里干这个的它是一个常驻后台的守护进程名字叫crond启动之后每分钟醒来一次翻一遍“任务清单”看看当前时间有没有任务需要执行。有就fork一个子进程去跑没有就接着睡下等下个周期再醒。这个“任务清单”就是crontab文件。它可以是系统级的放在/etc/crontab和/etc/cron.d/目录下面也可以是用户级的每个人用crontab -e编辑自己的任务表实际存放在/var/spool/cron/下面Debian系是/var/spool/cron/crontabs/。我经常跟新人打比方把crond理解成家里的闹钟把crontab理解成贴在冰箱上的便利贴——闹钟到点就响响完照着便利贴上的事项干活干完把结果写进日志。那为什么说它有时候和网络服务放一起讲因为很多网络服务的“保养”动作是周期性的。比如Nginx的access_log长得飞快你不可能手动去做日志切割MySQL的binlog如果不定期清理磁盘迟早被撑爆。这些任务挂在某个网络服务身上但执行引擎是Crond。所以说懂Crond基本上等于会给一整批网络服务做“自动保养”。1.2 不是所有定时任务都得用Crond选型对照很多人问我我写个Python脚本常驻内存定时函数一调不就完事了吗干嘛非得用Crond。这是一个好问题我踩过这个坑。早期我维护过一个数据采集程序脚本里自己写了time.sleep做循环调度看起来挺灵活。结果跑了不到一个月内存就悄悄涨上去了最后进程被系统杀掉了数据断了整整两天没人知道。Crond的好处恰恰在于任务执行完这个子进程就销毁了不吃内存、不留状态不存在“跑久了泄漏”的问题。还有一类朋友一上来就推荐systemd timer觉得Crond不够“现代”。systemd timer确实更强也能做秒级触发但配置上要写两个unit文件一个service、一个timer要熟悉systemd的依赖语义调试成本高不少。我的个人建议是绝大多数生产服务器的定时任务用Crond就够了。它够老、够稳所有发行版都自带排错资料全网都是。如果用着用着发现确实需要“上次任务漏了这次补跑”那再考虑Anacron如果需要精确到秒的触发或者复杂的资源依赖控制再切systemd timer不迟。工具选型的核心原则是先满足需求再谈优雅别为了技术新鲜感给自己挖坑。下面用一张表说清楚这几条路怎么选方案优势劣势适合场景Crond简单、老牌、资源占用低、系统统一管理最小粒度1分钟、错过不补跑服务器上的绝大多数周期任务常驻脚本灵活、可控度高容易内存泄漏、进程死了没人拉不适合生产长期跑systemd timer秒级触发、可做服务依赖配置繁琐、学习曲线陡需要精确控制或专用依赖的任务Anacron能补跑关机期间错过的任务不适合7×24小时服务器笔记本、虚拟机、经常关机的机器一句话总结工作环境里Crond是“默认答案”。我之前在某家公司管过一百多台服务器每台机器上的crontab平均不超过十行但就是这十行替我们省掉了大量重复劳动。你说它不起眼但它从来不宕、从来不闹脾气。2. 核心细节解析格式、配置目录、命令与环境变量2.1 五段式格式拆解时间和命令到底怎么对crontab的配置行看起来很简单但格式里藏着不少容易搞错的细节。标准用户级crontab是这么写的分 时 日 月 周 命令五个时间字段从左到右分别是分钟0-59、小时0-23、日期1-31、月份1-12、星期0-7其中0和7都代表周日。最后接要执行的命令。举个例子30 2 * * * /opt/scripts/backup_db.sh意思是每天凌晨2点30分执行backup_db.sh。再来几个常用的写法*/5 * * * *每5分钟执行一次0 0 * * *每天午夜0点15 3 * * 1每周一凌晨3点15分0 0 1 * *每月1号0点0 0 1 1 *每年1月1日0点特殊符号要熟*表示任意值,表示枚举比如1,15表示第1分钟和第15分钟-表示范围比如1-5表示周一到周五/表示步长比如*/10表示每隔10个单位。组合起来可以写出很多复杂的调度节奏。但这里有个全网都会讲的“经典陷阱”日字段和周字段同时设置时是“或”的关系不是“与”的关系。比如你写0 0 1 * 5你以为的是“每月1号且正好是周五才执行”实际上它的意思是“每月1号执行一次并且每个周五也执行一次”。我在生产环境被这个坑过本来只想每月1号做一次全量数据校验结果每周五晚上都在跑硬生生把业务库拖慢了。要表达真正“且”的逻辑只能把判断逻辑写进脚本里比如在脚本内加一句“如果不是1号并且不是周五就退出”。这点务必记住。2.2 配置文件目录体系与权限控制crontab的存放位置有好几处很多人只知道一个crontab -e难免踩乱。我日常实际接触的是这四块/etc/crontab系统级主配置文件比用户级多一个“用户”字段分 时 日 月 周 用户 命令/etc/cron.d/存放零散的系统级任务文件格式和/etc/crontab一样也要写用户/etc/cron.hourly/、/etc/cron.daily/、/etc/cron.weekly/、/etc/cron.monthly/放脚本的目录到时间点系统自动执行里面的脚本具体调度由/etc/crontab里的run-parts控制/var/spool/cron/Debian是/var/spool/cron/crontabs/用户级crontab的存放目录不要手动编辑一律通过crontab -e来改注意一个细节/etc/cron.d/目录下的文件名不能包含“.”否则crond会忽略掉。我第一次在这个目录放了个backup.db.conf结果死活不执行查了半天日志才发现是文件名里的点在作祟。这种小细节文档里写了但不踩一次真的记不住。权限控制方面系统通过/etc/cron.allow和/etc/cron.deny来限制哪些用户可以用crontab。两个文件都存在时cron.allow优先如果两个都不存在在大多数发行版里默认所有用户都能用具体看发行版。如果你管理的机器上有开发用的普通账号建议把cron.allow写好白名单方式比黑名单省心得多。2.3 常用命令与一个最大的环境变量坑日常命令就那么几条crontab -e编辑当前用户的crontabcrontab -l列出当前用户的crontabcrontab -r删除当前用户的crontab慎用会清空crontab -u username -eroot编辑指定用户的crontab系统服务的操作systemctl status crond查看crond服务状态有的发行版是systemctl status cronsystemctl restart crond重启crondps -ef | grep crond确认进程是否活着关于“改完crontab要不要重启crond”这个问题我在这儿多说一句不需要。crond每次醒过来都会重新读取配置文件你改完保存下一个周期就会读到新内容。这也是它的设计优点之一。最大的坑来自环境变量。crontab执行任务时不会加载用户登录时的完整环境变量比如~/.bashrc和/etc/profile默认都不加载。可以这么说crontab里的shell环境是一个“极其精简”的环境——PATH往往只有/usr/bin:/bin你手动执行好好的脚本一旦进了crontab就各种“command not found”。我自己遇到过最惨的一次脚本里调用mysqldump手动执行没问题进了crontab之后报找不到命令因为mysqldump装在/usr/local/mysql/bin下面而这个路径根本不在crontab的PATH里。从那以后我定了两条铁规矩脚本内所有外部命令一律写绝对路径比如把mysqldump写成/usr/local/mysql/bin/mysqldump脚本开头加一行source /etc/profile或者至少export PATH/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:$PATH这两条规矩救过我太多次新脚本进crontab之前我都会先检查这两点。3. 实战过程三个拿来就能用的生产级脚本模板3.1 案例一Web日志按天滚动压缩与过期清理大多数Nginx或Tomcat的访问日志如果不处理一个月能吃掉好几个G磁盘。我的做法是每天凌晨把昨天的日志压缩归档同时把超过90天的压缩包删掉。脚本如下#!/bin/bash # /opt/scripts/clean_logs.sh # 日志压缩与过期清理 LOG_DIR/var/log/nginx STORE_DAYS7 # 保留7天的原始日志 ARCHIVE_DAYS90 # 压缩包保留90天 YESTERDAY$(date -d yesterday %Y%m%d) # 1. 把昨天的日志压缩归档 find ${LOG_DIR} -maxdepth 1 -name access.log -type f -mtime 1 | while read f; do tar czf ${LOG_DIR}/access_${YESTERDAY}.tar.gz -C ${LOG_DIR} access.log ${LOG_DIR}/access.log # 压缩后清空原文件而不是删除 done # 2. 删除超过保留时间的压缩包 find ${LOG_DIR} -name access_*.tar.gz -mtime ${ARCHIVE_DAYS} -delete # 3. 写一条执行日志 echo $(date %F %T) clean_logs.sh finished /var/log/cron_clean.log对应的crontab配置30 1 * * * /bin/bash /opt/scripts/clean_logs.sh /var/log/cron_clean.log 21这里有三个细节值得讲。第一压缩完用 日志文件清空而不是rm为什么因为Nginx进程还握着这个文件的句柄直接删掉文件Nginx不会自动重新创建会导致日志写丢。清空文件内容则不影响句柄Nginx接着往空文件里写等它下次重新打开文件时再正常滚动。第二find命令里用-mtime 1表示“昨天的文件”千万别写成-mtime 0那是“24小时内的文件”会把前天某些时刻的文件也算进去。第三所有输出重定向到日志文件避免crontab默认把输出塞进用户的mail目录——不给它处理邮件的机会我们主动接管输出。3.2 案例二MySQL全量备份保留最近7天数据库备份是运维的头等大事。我的备份策略是每天凌晨2点做一次全量备份保留最近7天并且用gzip压缩减小磁盘占用。脚本如下#!/bin/bash # /opt/scripts/backup_db.sh # MySQL全量备份 BACKUP_DIR/data/mysql_backup MYSQL_USERbackup_user MYSQL_PASSWORDyour_password MYSQL_HOST127.0.0.1 MYSQL_PORT3306 DATABASESorder_db user_db # 要备份的库空格分隔 KEEP_DAYS7 mkdir -p ${BACKUP_DIR} STAMP$(date %Y%m%d_%H%M%S) for db in ${DATABASES}; do # 用mysqldump备份单个库gzip压缩 /usr/local/mysql/bin/mysqldump \ -h${MYSQL_HOST} -P${MYSQL_PORT} \ -u${MYSQL_USER} -p${MYSQL_PASSWORD} \ --single-transaction --routines --triggers ${db} \ | gzip ${BACKUP_DIR}/${db}_${STAMP}.sql.gz # 检查备份是否成功 if [ $? -eq 0 ]; then echo $(date %F %T) backup ${db} success, size: $(du -h ${BACKUP_DIR}/${db}_${STAMP}.sql.gz | cut -f1) /var/log/cron_backup.log else echo $(date %F %T) backup ${db} FAILED /var/log/cron_backup.log fi done # 清理超过保留天数的备份 find ${BACKUP_DIR} -name *.sql.gz -mtime ${KEEP_DAYS} -deletecrontab配置0 2 * * * /bin/bash /opt/scripts/backup_db.sh /var/log/cron_backup.log 21几个要点。第一mysqldump和mysql必须写绝对路径这是我在2.3里说的铁规矩。第二--single-transaction参数特别重要它让备份基于InnoDB事务快照备份过程中不会锁表、不影响业务读写。第三密码直接写在脚本里有安全隐患生产环境可以用--login-path或者配置文件的方式管理凭据这里为简洁起见用了明文实际部署时一定要收敛权限至少把脚本的权限设为700属主设为root。我之前在一次大促前夜发现备份脚本已经连续失败三天了原因是磁盘满了备份文件写不进去但日志文件自己没有做轮转也没告警。从那以后我给这类脚本统一加了du -h输出、失败exit code判断还把失败情况接上告警通道。备份脚本不只要“能跑”更要“跑不出错还让人知道”。这个东西宁可多写五行业务日志也别省。3.3 案例三服务进程监控与自动拉起光监控机器层面的指标还不够业务的进程如果挂了越早发现越少损失。我习惯用Crond每5分钟做一次健康检查发现核心进程不在就立刻拉起同时写日志、发告警。这里拿Nginx举例#!/bin/bash # /opt/scripts/monitor_nginx.sh # Nginx进程存活监控与拉起 SERVICE_NAMEnginx MARK_LOG/var/log/cron_monitor.log CHECK_URLhttp://127.0.0.1:8080/healthz # 方法1进程检查 if ! pgrep -f nginx: master process /dev/null 21; then echo $(date %F %T) [WARN] ${SERVICE_NAME} process not found, try restart... ${MARK_LOG} systemctl start nginx sleep 2 # 拉起后再确认一次 if pgrep -f nginx: master process /dev/null 21; then echo $(date %F %T) [INFO] ${SERVICE_NAME} restarted OK ${MARK_LOG} else echo $(date %F %T) [ERROR] ${SERVICE_NAME} restart failed, need manual check ${MARK_LOG} fi fi # 方法2HTTP健康检查可选 HTTP_CODE$(curl -s -o /dev/null -w %{http_code} --connect-timeout 5 --max-time 10 ${CHECK_URL}) if [ ${HTTP_CODE} ! 200 ]; then echo $(date %F %T) [WARN] ${CHECK_URL} return ${HTTP_CODE}, not 200 ${MARK_LOG} ficrontab配置每5分钟检查*/5 * * * * /bin/bash /opt/scripts/monitor_nginx.sh /var/log/cron_monitor.log 21这里面有个经验进程在≠服务健康。Nginx的master进程可能活着但后端已经502了Java进程还挂着但端口不监听了。所以我一般进程检查和HTTP检查一起上进程检查负责“进程没了拉起来”HTTP检查负责“进程活着但业务挂了要报警”。两件事的处置策略不一样前者自动恢复后者通常得人工介入。还有个容易忽略的点告警要分级。进程被拉起来了写INFO进程拉都拉不起来写ERROR并且接上短信/钉钉/微信告警。你不能让脚本在凌晨三点把所有人都吵醒但真到起不来的情况该响就得响。我曾经因为告警阈值设置太松某业务挂了一整晚第二天业务方找上门来才发现——这种锅背一次就够了。3.4 定时策略设计错峰、时区与“整点风暴”写crontab时间字段的时候大多数人的第一反应是整点整分。比如“每天0点跑日志切割”于是所有服务器都在0点准时干活。这个习惯带来的后果我在运营多台服务器之后深有体会整点那一分钟所有机器的磁盘IO、负载、数据库连接数同时飙高日志切割脚本互相抢资源原本1分钟跑完的任务跑到5分钟甚至互相影响。正确做法是给任务“错峰”。我的经验是不同任务之间至少错开5~10分钟多台机器做同一件事比如多台web节点都做日志清理每台机器用不同的分钟偏移最重的任务排在凌晨人少时段但别堆在同一个点比如三台机器可以分别用30 2 * * *、35 2 * * *、40 2 * * *来错开重活。另外时区问题容易被忽略。如果你的服务器时区设置和业务时区不一致crontab里的0 2 * * *到底几点跑要以系统时区为准。排查“任务怎么提前一小时跑了”这种问题先看date输出的时区对不对。还要记住Crond最小粒度是分钟它本身不支持秒级任务。实在要每秒执行就把Crond当“调度器”用让它每分钟启动一个脚本脚本内部自己循环60次、每次1秒但这样做要自己控制好循环内的超时避免任务重叠。大多数情况下用不到秒级别给自己加戏。4. 常见问题与排查技巧实录4.1 任务就是没执行按这个顺序排查crontab任务不执行的排查我有一套固定的顺序建议你也照着来效率高很多。第一确认crond服务是活着的systemctl status crond或ps -ef | grep crond。如果服务挂了你写的所有任务都白搭但这种概率极低更多是配置的问题。第二看日志。CentOS系日志在/var/log/cronUbuntu系则在/var/log/syslog里搜CRON关键字。日志会记录每次调度的动作会显示“是执行了但命令找不着”还是“压根没到调度”。这一步能把问题分成两大类调度问题没跑和执行问题跑了但失败。第三验证脚本本身。在命令行手动执行一次脚本看有没有报错。如果手动执行成功但crontab里不行九成是环境变量和权限问题。检查脚本有没有执行权限chmod x别省。检查脚本开头有没有指定正确解释器#!/bin/bash不能少。检查脚本内部的所有命令是不是绝对路径。第四检查用户的crontab语法。crontab -l看一下当前内容确认时间字段没写错。有一回一个同事把小时和分钟写反了任务变成每小时的“某分钟”才执行看起来像没跑其实就是跑的点不对。crontab -e保存时一般会有语法提示但别完全依赖它。4.2 任务执行了但脚本报错多半是环境变量的锅这是最高频的问题。手动执行脚本一切正常crontab里跑就报“command not found”或者各种奇怪的找不到库、找不到配置文件的错。原因就是我前面说的crontab的执行环境不会加载你登录shell的环境变量。遇到这类问题我第一件事就是在脚本开头加两行source /etc/profile source ~/.bash_profile然后重新调度试跑。如果某个工具装在了自定义目录比如Python装在/usr/local/python3/bin还需要显式加PATH。有个技巧可以在脚本最前面加一行调试输出把环境变量打到日志里echo $(date %F %T) PATH$PATH /var/log/cron_env.log跑一次看日志PATH到底缺了什么一目了然。我见过太多人在这个问题上瞎猜半小时加一行调试信息就能解决的事别猜。4.3 特殊字符与语法陷阱%、引号与换行crontab里有个非常隐蔽的特殊字符%。在crontab中%会被解释成换行符。比如你写0 2 * * * echo today is $(date %Y%m%d) /tmp/test.log如果没有转义crontab会把%Y%m%d里的%当成换行命令就被“断开”了后面内容全变成了标准输入内容导致整个命令行为异常。解决办法有两个要么转义成\%Y\%m\%d要么干脆把命令写到shell脚本里脚本内部正常用%不受影响。我的建议是凡是稍复杂的逻辑一律写脚本别直接在crontab里堆长命令一个是转义问题多另一个是后续维护根本没法看。另一个坑是符号在crontab里如果直接裸写可能有解析问题一般建议用 logfile 21的方式把所有输出和错误都重定向到文件顺便把“后台”的需求交给脚本内部去控制。4.4 常见问题速查表症状可能原因排查/解决办法任务完全不跑crond服务挂了systemctl status crond重启crond跑了但命令找不到PATH不完整环境变量缺失脚本开头source /etc/profile所有命令写绝对路径脚本手动没问题crontab失败crond精简环境、相对路径错误检查脚本内路径是否绝对加调试日志输出PATH报了“Permission denied”脚本没有执行权限chmod x 脚本时间是整点但没跑时区不对日周逻辑理解错date检查时区确认日周“或”逻辑输出到邮件目录没有重定向输出命令后追加 log 21%导致命令被截断crontab将%解释为换行转义%或将逻辑写进脚本/etc/cron.d下文件不执行文件名带了“.”重命名不带点的文件任务执行了多次脚本重叠运行脚本内加flock锁或检查时间字段表达这张表是我在带团队时候整理的贴在运维wiki里新人出问题先查表省得我一遍遍重复讲。排查问题最大的效率来源是把经验沉淀成可检索的东西。5. 我在实际项目里最后再补两句写到这儿Crond定时任务的核心内容基本都覆盖了。如果要给这段运维经验画个重点我最想补的是这几点第一新写的脚本一定先在命令行手动跑通再挂到crontab里这个顺序十次有九次能帮你避开低级错误第二每个脚本都必须有自己的日志文件而且要带时间戳等到出问题的时候没有日志两眼一抹黑你会非常被动第三给脚本加锁防止重叠尤其是执行时间可能超过调度周期的任务一个简单的flock就能避免“上一个还没跑完下一个又起了一个”的灾难场面。我还想再多说一句踩过的坑别因为Crond简单就轻视它。它出问题的症状往往都很隐蔽比如任务确实执行了但输出全丢了、环境变量不对导致备份成功但数据不对、时区偏移导致凌晨任务变成上午跑。管理大批量服务器、长期维护项目的时候这些“小问题”带来的影响会被无限放大。建议团队里把每台机器上的crontab都纳入版本管理脚本和配置统一放git仓库每次改动都留痕。这样即使哪天某台机器被误操作清了任务我们也能在几分钟内恢复现场。Crond这东西看起来就是一个简单的“定时器”但用得规范不规范直接决定你凌晨三点是被电话吵醒还是安安心心睡大觉。从一个“会用”的阶段走到“用好”靠的正是这些细致到文件路径、环境变量、日志策略的偏执。希望这篇分享能帮你少走几步弯路。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。