Generic Top

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> CHECK OUT THE COURSE

1. Overview

As system administrators, we'll invariably encounter the need to schedule tasks. We can achieve this by using cron services in Linux systems. Also, we can enable the cron scheduling services in container systems.

Now, this tutorial will elucidate two different ways of enabling cron services in the Docker containers. In the first approach, we'll embed the cron services inside the docker image using Dockerfile, whereas the other method will illustrate how to install the scheduling services in a container.

2. Cron Services – Using the Dockerfile Approach

Building images using Dockerfile is one of the easiest ways to create container images. So, how do we do it? Basically, a Dockerfile is a simple text file that contains a set of instructions to build an image. We need to feed the scheduling task, cron details, and invoke the cron services from the Dockerfile.

2.1. Writing the Dockerfile

Let's have a quick look at an example:

$ tree
.
├── Dockerfile
└── get_date.sh
0 directories, 2 files

Generally, the first line of a Dockerfile starts with the FROM command, which will take the requested image from the configured registry. In our case, the default registry is configured as DockerHub. Then comes the MAINTAINER, which is the metadata for capturing the author's information. The ADD instruction copies the get_date.sh script from the image build path of the host machine to the destination path of the image.

After copying the script into the build image, the RUN instruction gives the executable permissions. Not just that – the RUN instruction helps to execute any shell command as a new image layer on top of the current layer and commits the results. RUN updates the apt repository and installs the latest cron services in the image. It also performs the cron scheduling in the crontab.

Lastly, we'll start the cron services using the CMD instruction:

$ cat Dockerfile

# Dockerfile to create image with cron services
FROM ubuntu:latest
MAINTAINER baeldung.com

# Add the script to the Docker Image
ADD get_date.sh /root/get_date.sh

# Give execution rights on the cron scripts
RUN chmod 0644 /root/get_date.sh

#Install Cron
RUN apt-get update
RUN apt-get -y install cron

# Add the cron job
RUN crontab -l | { cat; echo "* * * * * bash /root/get_date.sh"; } | crontab -

# Run the command on container startup
CMD cron

2.2. Building and Running the Cron Image

Once the Dockerfile is ready, we can then build the image using the docker build command. The dot (.) instructs the Docker Engine to take the Dockerfile from the current path. The build command creates the docker layers for every instruction given on the Dockerfile to form the final build image. The typical build output is showcased below:

$ docker build .
Sending build context to Docker daemon  3.072kB
Step 1/8 : FROM ubuntu:latest
---> ba6acccedd29
Step 2/8 : MAINTAINER baeldung.com
---> Using cache
---> e6b3946b2382
Step 3/8 : ADD get_date.sh /root/get_date.sh
---> 4976f058d428
Step 4/8 : RUN chmod 0644 /root/get_date.sh
---> Running in 423a4e9adbab
Removing intermediate container 423a4e9adbab
---> 76d972a082ba
Step 5/8 : RUN apt-get update
---> Running in badc0d84f6ff
Get:1 http://security.ubuntu.com/ubuntu focal-security InRelease [114 kB]
...
... output truncated ...
...
Removing intermediate container badc0d84f6ff
---> edb8a19b891c
Step 6/8 : RUN apt-get -y install cron
---> Running in efd9b8a67d98
Reading package lists...
Building dependency tree...
...
... output truncated ...
...
Done.
invoke-rc.d: could not determine current runlevel
invoke-rc.d: policy-rc.d denied execution of start.
Removing intermediate container efd9b8a67d98
---> 2b80000d32a1
Step 7/8 : RUN crontab -l | { cat; echo "* * * * * bash /root/get_date.sh"; } | crontab -
---> Running in 1bdd3e0cc877
no crontab for root
Removing intermediate container 1bdd3e0cc877
---> aa7c82aa7c11
Step 8/8 : CMD cron
---> Running in cf2d44873b36
Removing intermediate container cf2d44873b36
---> 8cee091ca87d
Successfully built 8cee091ca87d

Since we have pre-installed the cron services into the image and embedded the tasks in the crontab, the cron job gets activated automatically when we run the container. Alternatively, we can start the container using the docker run command. Subsequently, the “-it” option of docker run helps to get into the container with the bash prompt.

The below illustration shows the execution of get_date.sh script in the container using cron:

$ docker run -it 8cee091ca87d /bin/bash
[email protected]:/#
[email protected]:/# date
Mon Nov 15 14:30:21 UTC 2021

[email protected]:/# ls -ltrh ~/date.out
ls: cannot access '/root/date.out': No such file or directory

[email protected]:/# ls -ltrh /root/get_date.sh
-rw-r--r-- 1 root root 18 Nov 15 14:20 /root/get_date.sh

[email protected]:/# crontab -l
* * * * * bash /root/get_date.sh

[email protected]:/# ls -ltrh ~/date.out
-rw-r--r-- 1 root root 29 Nov 15 14:31 /root/date.out

[email protected]:/# cat /root/date.out
Mon Nov 15 14:31:01 UTC 2021

3. Cron Services – Live Container Approach

Alternatively, we can set up the cron services in Docker containers for running the cron jobs. So, what's the modus operandi?

Let's quickly run an ubuntu container using the docker run command. Usually, the containers are a lightweight OS that won't comprise cron services as their default package.

So, we need to get into the interactive shell of the container and install the cron services using apt repository commands:

$ docker run -it ubuntu:latest /bin/bash
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
7b1a6ab2e44d: Pull complete
Digest: sha256:626ffe58f6e7566e00254b638eb7e0f3b11d4da9675088f4781a50ae288f3322
Status: Downloaded newer image for ubuntu:latest
[email protected]:/#
[email protected]:/# which cron
[email protected]:/# apt update -y
Get:1 http://archive.ubuntu.com/ubuntu focal InRelease [265 kB]
...
... output truncated ...
...
All packages are up to date.
[email protected]:/# apt upgrade -y
... 
... output truncated ...
...
[email protected]:/# apt install cron vim -y
Reading package lists... Done
...
... output truncated ...
...
Done.
invoke-rc.d: could not determine current runlevel
invoke-rc.d: policy-rc.d denied execution of start.
[email protected]:/# which cron
/usr/sbin/cron

$ docker cp get_data.sh 77483fc20fc9: /root/get_date.sh

We can use the docker cp command to copy get_date.sh from the host to the container. The crontab -e edits the cron job using the vi editor. The below cron configuration runs the script every minute. Furthermore, the output indicates the timestamp of the script execution:

[email protected]:/# export EDITOR=vi
[email protected]:/# crontab -e
* * * * * bash /root/get_date.sh

[email protected]:/# date
Mon Nov 17 11:15:21 UTC 2021

[email protected]:/# ls -ltrh ~/date.out
ls: cannot access '/root/date.out': No such file or directory

[email protected]:/# ls -ltrh /root/get_date.sh
-rw-r--r-- 1 root root 18 Nov 17 11:09 /root/get_date.sh

[email protected]:/# ls -ltrh ~/date.out
-rw-r--r-- 1 root root 29 Nov 17 11:16 /root/date.out

[email protected]:/# cat /root/date.out
Mon Nov 17 11:16:01 UTC 2021

4. Conclusion

To summarize, we've explored the nuts and bolts of running cron jobs inside a Docker container. The method of using Dockerfile embeds the cron services and tasks into the image, which automatically executes the script in accordance with the cron schedule configuration.

Although cron can be installed and configured in a running container, it's only a runtime construct unless we construct an image using docker commit.

Besides, both alternatives have their advantages depending on our usage circumstances.

Generic bottom

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> CHECK OUT THE COURSE
Generic footer banner
Comments are closed on this article!