Docker container environment variable file during runtime

Viewed 235

I have a docker image that basically schedules a cron job at a frequency defined when building the image using the below.

COPY myjobtime /etc/cron.d/myjobtime
RUN chmod 0644 /etc/cron.d/myjobtime &&\
    crontab /etc/cron.d/myjobtime
CMD cron 

I have the cron entry in the file myjobtime.

*/10 * * * * /usr/local/bin/sh /app/myscript.py

I would like to be able to pass the cron schedule during the runtime. Meaning, if someone wants to modify the cron schedule to a different frequency, they should be able to do that while running the container and passing an environment variable file with the new cron schedule in it. Can this be done?

1 Answers

The important detail is that you need to create and install the crontab file when the container starts up. I find an entrypoint wrapper script to be a useful pattern for this: set the image's ENTRYPOINT to be a shell script that does whatever first-time setup is required, then have it exec "$@" to run the image's CMD.

If your image is ultimately based on a Linux distribution based on the GNU toolset, then envsubst is a really helpful program here. It reads in a text file, expands environment variable references, and writes out the result. I'll assume you have this available; on Alpine-based images you can do similar tricks with sed(1) (though escaping around the cron schedule will become tricky).

This makes the entrypoint wrapper script something like:

#!/bin/sh
# entrypoint.sh

# Set a default schedule, if the user didn't provide one
if [ -z "$CRON_SCHEDULE" ]; then
  export CRON_SCHEDULE='*/10 * * * *'
fi

# Run substitutions on the template file and inject the crontab
envsubst < /app/myjobtime.cron.tmpl | crontab

# Run the main container command
exec "$@"

Since the template isn't a "normal" crontab, it can't go in the "normal" crontab directory; putting it in the application directory is fine. That file has an environment variable reference where the schedule would go

# myjobtime.cron.tmpl
${CRON_SCHEDULE} /app/myscript.py

In your image, set the wrapper script to be the ENTRYPOINT, make sure the template file is in the right place, and leave the CMD unchanged.

# (assuming there's not a broad `COPY . .`)
COPY myjobtime.cron.tmpl .
COPY entrypoint.sh .

ENTRYPOINT ["/app/entrypoint.sh"] # must be JSON-array syntax
CMD cron                          # unchanged

This should allow you to override the cron schedule.

docker run -d --name hourly myappcron
docker run -d --name daily -e 'CRON_SCHEDULE=0 0 * * *' myappcron

Since the entrypoint wrapper script runs whatever command was provided, and you can override the command pretty easily, this also lets you double-check that the right schedule got set.

docker run --rm -e 'CRON_SCHEDULE=0 0 * * *' myappcron \
  crontab -l  # runs instead of the cron daemon
Related