Docker部署Rails配置crontab报no crontab for root问题排查
报错根因
- 基于ruby:2.7.2的Debian基础镜像中,刚安装完cron服务时root用户没有初始化对应的crontab文件,直接执行
crontab -l会直接抛出no crontab for root错误并返回非0退出码,后续管道写入逻辑直接中断,导致镜像构建失败。 - 现有配置存在多处逻辑缺陷:cron服务单独运行在
cron_job容器中,但未配置cron执行所需的环境变量、未正确挂载项目代码、路径存在拼写错误,就算构建阶段不报错,定时任务也无法正常触发。
分步修复
1. 修正Dockerfile逻辑,解决构建报错
首先删除Dockerfile中原有报错的crontab写入行:
# 删掉这行 RUN crontab -l | { cat; echo ""; } | crontab -
在项目目录下新建config/cron/my_crontab文件,提前写入定时任务规则,注意必须声明cron运行所需的环境变量,cron默认不会继承容器的全局环境变量:
SHELL=/bin/bash PATH=/usr/local/bundle/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin RAILS_ENV=production # 示例:每天凌晨2点触发数据统计任务,输出重定向到容器标准输出方便日志查看 0 2 * * * cd /var/www/quickcard && bundle exec rake sidekiq:stat_task:run >> /proc/1/fd/1 2>&1
在Dockerfile的COPY . .行之前添加crontab初始化逻辑,提前创建crontab文件并赋权,从根源解决no crontab报错:
# 初始化root用户crontab文件,设置cron要求的600权限 RUN touch /var/spool/cron/crontabs/root && chmod 600 /var/spool/cron/crontabs/root # 加载提前写好的定时规则 COPY config/cron/my_crontab /tmp/my_crontab RUN crontab /tmp/my_crontab && rm /tmp/my_crontab COPY . .
注意:原有Dockerfile中apt安装后的清理命令写错了,ruby镜像基础是Debian,apt缓存路径是/var/lib/apt/lists/*,原来写的/var/cache/apk/*是Alpine镜像的路径,不会起到清理作用,可以一并修正。
2. 修正docker-compose.yml配置
首先修正所有服务中写错的路径:原配置里的/var/wwww多写了一个w,正确路径是/var/www。
然后修改cron_job服务配置,补全环境变量和挂载规则,保证cron能正常找到项目代码和依赖:
cron_job: build: . command: cron -f -L 15 environment: RAILS_ENV: production # 此处补全和app、sidekiq服务一致的数据库、Redis连接等环境变量 DATABASE_URL: postgres://your_db_addr REDIS_URL: redis://your_redis_addr volumes: - ./:/var/www/quickcard - ./tmp:/var/www/quickcard/tmp - ./log:/var/www/quickcard/log restart: always
同时修正app、sidekiq服务的volumes配置,避免匿名卷覆盖构建好的项目文件:
app: # 其他原有配置不变 volumes: - ./:/var/www/quickcard - ./public:/var/www/quickcard/public - ./tmp:/var/www/quickcard/tmp - ./log:/var/www/quickcard/log sidekiq: # 其他原有配置不变 volumes: - ./:/var/www/quickcard - ./tmp:/var/www/quickcard/tmp - ./log:/var/www/quickcard/log
3. 可选优化(推荐)
如果你使用sidekiq-cron的内置调度能力,完全不需要安装系统cron、也不需要单独部署cron容器:sidekiq进程启动后会自动读取config/sidekiq.yml中配置的定时规则自主调度,没有额外依赖,也不会出现crontab相关报错。
这种场景下可以直接删掉Dockerfile中安装cron的步骤、删掉compose中的cron_job服务,只需要在sidekiq配置文件中声明任务即可,示例配置:
:schedule: stat_task: cron: '0 2 * * *' class: StatWorker queue: default
内容的提问来源于stack exchange,提问作者Saad Saddique

