Tdd Apps Blog

How to run any container on a schedule

Feb 18, 2017 5 minute read

Configuring a recurring task on a Docker container requires an innumerable number of steps. Moreover, an image designed to “compute an hourly database health report” can rarely be reused to “check the api server status every three minutes”. camilin87/docker-cron is a Docker image that interacts with the Docker Engine Api to create and run containers on a schedule. The following examples describe its usage and inner workings.

Task 1: Ping www.google.com

This snippet creates a new container named urlStatusChecker based on the dperson/smokeping:latest image. It will run the ping www.google.com -c 1 command on startup.

docker run --rm --name urlStatusChecker \
  dperson/smokeping:latest              \
  ping www.google.com -c 1

Task 1.1: Ping www.google.com every three minutes

The following snippet also creates a urlStatusChecker container to ping www.google.com. However, this new container will get recreated and executed every three minutes.

cat > containerInfo.json <<EOL
{
    "Image": "dperson/smokeping:latest",
    "Name": "urlStatusChecker",
    "Cmd": [
        "ping",
        "www.google.com",
        "-c",
        "1"
    ]
}
EOL

docker run --rm -d --name docker-cron                         \
  -v /var/run/docker.sock:/var/run/docker.sock                \
  --env TASK_SCHEDULE='*/3 * * * *'                           \
  -v $PWD/containerInfo.json:/usr/src/containerInfo.json      \
  camilin87/docker-cron

How does it work?

The code from Task 1.1 is running a container named docker-cron based on the camilin87/docker-cron image. That container will in turn create a new urlStatusChecker container based on the dperson/smokeping:latest every three minutes.

TASK_SCHEDULE: an environment variable to configure the frequency

containerInfo.json: a file that contains the detailed specification of the task to run. It can describe any aspect of the spawned container -volumes, networks, cmd, …- as long as it is supported on the Docker Engine Api. The camilin87/docker-cron image is built to POST this file to the /containers/create Docker Engine Api method.

/var/run/docker.sock: Is the socket where the Docker Daemon is listening to. It needs to be mounted as a volume for the docker-cron container to interact with the Docker Engine Api.

Things we didn’t have to do

Let’s analyze some tasks we didn’t have to do to ping www.google.com every three minutes:

Task 2: Parameterize the Ping url and count

The following task is very similar to Task 1. However, in Task 2 the url and the number of requests can be parameterized through the PING_URL and PING_COUNT environment variables.

docker run --rm --name urlStatusChecker     \
  --env PING_URL="www.facebook.com"         \
  --env PING_COUNT=1                        \
  dperson/smokeping:latest                  \
  sh -c "ping \$PING_URL -c \$PING_COUNT"

Task 2.1: Ping www.facebook.com every three minutes

This would be the equivalent parameterized scheduled Ping.

cat > containerInfo.json <<EOL
{
    "Image": "dperson/smokeping:latest",
    "Name": "urlStatusChecker",
    "Cmd": [
        "sh",
        "-c",
        "ping \$PING_URL -c \$PING_COUNT"
    ]
}
EOL

docker run --rm -d --name docker-cron                         \
  -v /var/run/docker.sock:/var/run/docker.sock                \
  --env TASK_SCHEDULE='*/3 * * * *'                           \
  --env PING_URL='www.facebook.com'                           \
  --env PING_COUNT=1                                          \
  --env COPY_ENV_VARS='PING_URL,PING_COUNT'                   \
  -v $PWD/containerInfo.json:/usr/src/containerInfo.json      \
  camilin87/docker-cron

How does it work?

The camilin87/docker-cron image copies environment variables from the docker-cron container into the urlStatusChecker container. However, -for security reasons- it does not copy all the environment variables. It only copies those environment variables specified in COPY_ENV_VARS.

Once more camilin87/docker-cron is leveraging existing functionality of the /containers/create Docker Engine Api method. It is injecting the environment variables specified in COPY_ENV_VARS into the Env field of the Api request.

Why so much hassle with environment variables?

Environment variables have traditionally been the foundation to provide non-persistent configuration to containers. Any decent container scheduler should provide a secure way to deal with them.

Bonus: Docker Swarm support

Docker Swarm provides some nice monitoring capabilities out of the box. The following snippet has the same functionality as the one from Task 2.

docker service create                                                                       \
  --replicas 1                                                                              \
  --name docker-cron                                                                        \
  --env TASK_SCHEDULE='*/3 * * * *'                                                         \
  --env PING_URL='www.facebook.com'                                                         \
  --env PING_COUNT=1                                                                        \
  --env COPY_ENV_VARS='PING_URL,PING_COUNT'                                                 \
  --mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock                 \
  --mount type=bind,source=$PWD/containerInfo.json,destination=/usr/src/containerInfo.json  \
  camilin87/docker-cron

Additional resources