Running cron jobs inside a Docker container
September 07, 2016
Probably the most basic use of a Docker image is to run it, and let it exist - like docker run hello-world. If you want to do this on a regular schedule, you have several options but the cron one is narrowed down to running cron on the Docker host or in the container.
I wanted to run a dotnet core console app that parses Reddit posts and posts itself, inside a Docker container, every 30 minutes on set days of the week.
Running the Docker image as a daemon with cron running inside it makes it a lot easier to update the Docker image and also bring up on other machines.
There’s quite a few gotchas to doing this however, which I discovered and spent hours Googling the answers for. Most of the answers were dredged from Github repositories which luckily Google managed to find the keywords for. This included:
- Cron doesn’t run as an interactive shell - you don’t get any environmental variables - a well known restriction but nobody seemed to know the answer. I found my solution in this repository.
- I’m running a .NET Core console app. It writes a temp file to its folder on the filesystem, but you don’t have permission to do this without CHMOD’ing the app directory.
- Cron inside a Docker container can’t write to a cron.log so you end up having to map a volume. Without this you get the error “tail: unrecognized file system type 0x794c7630 for ‘/var/log/cron.log’“. This makes the Docker image rely on the host volume for storage, but it’s only a log file. The fix came from here.
Below is the Docker file and the scripts are with the Gist.
| env - `cat /root/env.sh` /bin/bash -c $1 >> /var/log/cron.log 2>&1 |
| # Run every 30 minutes on Wednesday | |
| */30 * * * 3 /root/cron_task.sh /root/run_app.sh | |
| # cron needs an empty line at the end, leave this here. |
| FROM microsoft/dotnet | |
| MAINTAINER Chris | |
| # Fix from: | |
| # https://github.com/bringnow/docker-letsencrypt-manager/commit/7a157dcd05ea8e745ec604734f6e7aa2e9e7b7cc | |
| # Put cron logfiles into a volume. This also works around bug | |
| # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=810669 | |
| # caused by base image using old version of coreutils | |
| # which causes "tail: unrecognized file system type 0x794c7630 for '/var/log/cron.log'" | |
| # when using docker with overlay storage driver. | |
| VOLUME /var/log/ | |
| RUN mkdir /app | |
| COPY ./src/MyApp/bin/Debug/netcoreapp1.0/publish /app | |
| WORKDIR /app | |
| RUN apt-get update | |
| RUN apt-get install -y cron | |
| ADD *.sh /root/ | |
| RUN chmod 750 /root/cron_task.sh | |
| RUN chmod 750 /root/run_redditbot.sh | |
| RUN chmod 750 /app/ | |
| ADD crontab /var/spool/cron/crontabs/root | |
| RUN chmod 0600 /var/spool/cron/crontabs/root | |
| CMD touch /var/log/cron.log && cron && env > /root/env.sh && tail -f /var/log/cron.log |
| dotnet /app/MyApp.dll |
Some relevant Stackoverflow posts that helped:
I'm Chris Small, a software engineer working in London. This is my tech blog. Find out more about me via Github, Stackoverflow, Resume