Recently I've had an interesting conversation in #docker on Freenode with a guy that's been trying to get crons working inside his Docker container. I hadn't yet had a chance to look at that, and so we took off on a late-night debug session exchanging Dockerfiles via Pastebin. He has a bunch of other stuff going on, but at the core, he's just running an Apache webserver instance and then wants to run some crons in that container as well. I took his Dockerfile and related scripts, and pared them down to the bare minimum, commenting out everything that wasn't related directly to getting Apache and cron to work. You can take a look at what I came up with:
FROM ubuntu:14.04 MAINTAINER curtisz <firstname.lastname@example.org> # we install stuff this way to keep it all on one layer # (which reduces the overall size of our image) RUN apt-get update -y && \ apt-get install -y \ cron \ apache2 \ supervisor && \ rm -rf /var/lib/apt/lists/* # apache stuff RUN mkdir -p /var/lock/apache2 /var/run/apache2 /etc/supervisor/conf.d/ ENV APACHE_RUN_USER www-data ENV APACHE_RUN_GROUP www-data ENV APACHE_LOG_DIR /var/log/apache2 ENV APACHE_LOCK_DIR /var/lock/apache2 ENV APACHE_PID_FILE /var/run/apache2.pid # empty out the default index file RUN echo "" /var/www/html/index.html # cron job which will run hourly # (remember that COPY is better than ADD for plain files or directories) COPY ./crons /etc/cron.hourly/ RUN chmod +x /etc/cron.hourly/crons # test crons added via crontab RUN echo "*/1 * * * * uptime >> /var/www/html/index.html" | crontab - RUN (crontab -l ; echo "*/2 * * * * free >> /var/www/html/index.html") 2>&1 | crontab - # supervisord config file COPY ./supervisord.conf /etc/supervisor/conf.d/supervisord.conf EXPOSE 80 WORKDIR /var/www/html/ CMD /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf
You can tell what's going on here, it's pretty straightforward. To get this working, we need to install apache2, supervisor, and of course, cron. The next few lines are configuration options for Apache. Then finally we get to the test crons. I'm dumping a simple cron shell script into
/etc/cron.hourly in the container and making it executable, and then creating two new crons via
crontab. Please note how I have added these crons with
crontab - by also listing the previously-added crons with
crontab -l. If you don't do this, whatever you add via
crontab will overwrite whatever's in there now. My crons are just stupid simple crons for dumping something easy into Apache's
index.html file so we can prove they're running. My little
crons file looks like this:
#!/bin/sh ps aux | grep apache > /var/www/html/index.html
Some things to notice about this file... First, it starts with a typical shell script shebang (
#!/bin/sh). It should also be executable. Lastly, this file can only contain alphanumeric characters in its filename (and underscores). It cannot contain a dot, which means naming it something like
crons.sh won't work.
Next, we're adding the config file for supervisord. Then simply exposing the port, changing our working directory, and then specifying our
CMD to start supervisord. Speaking of, this is what our
supervisord.conf file looks like:
[supervisord] nodaemon=true logfile = /var/log/supervisord.log logfile_maxbytes = 50MB logfile_backups=10 [program:cron] autorestart=false command=cron -f [program:apache2] autorestart=false command=/usr/sbin/apache2ctl -D FOREGROUND
Pretty standard fare. And so let's build our container and start it:
docker build -t="crontest" . docker run -it --name="crontest" -p 8080:80 crontest:latest
And there you have it! Give your crons a few minutes to execute, then fire up a browser on your localhost and point it to
http://localhost:8080/index.html. You should see the output of our test crons there at the tail of the file. Refresh to see more.
That's all there is to it! Previously, I've used the cron system available on the Docker host, which certainly has its benefits. First and foremost of which is not having to use supervisord inside a Docker container. Since Docker is just a fancy way to run a process, you want to avoid loading up your container with a bunch of cruft. It's not a VM! But when you can't avoid it and you really need to run crons alongside your containerized processes, it's no sweat to get it going.