Mastering Docker Job Scheduling with Go: Build Your Own Custom Image For Cron

Hey there, Docker fans! Are you tired of Kubernetes making you feel like a CronJob object—always scheduled and never in charge? And why doesn't Docker include a built-in solution for task scheduling? Sometimes, you just need a simple orchestration tool like Docker Swarm for your itty-bitty project, but then you realize that Docker has left you high and dry without a scheduling lifeline. It's like a chef lacking a knife. But hey, don't panic, you can build your own unique Docker image and take control of your containerized operations thanks to the brilliance of Go and the tried-and-true cron function. So let's hop on and learn as we set out on a trip to truly understand Docker job scheduling.

We're going to create a docker image with a base of an alpine image. Well if you may ask why; it's because the alpine image is small and sweet. Also, we're going to have Go as our script, you may also use python. (sorry python lovers; I'm a huge fan of Go here).

Our script

Our main go script will have an important task to do, which is to tell a silly joke. Here it is:

package main

import "fmt"

func main() {
	fmt.Printf("%s\n\n\n", "Why don't scientists trust atoms?")

	fmt.Printf("%s\n", "Because they make up everything.")
}

We'll also have four more files to consider:


1. crontab.txt (For us to schedule timing for executing our script): Our task will run every 30 minutes.

2. script.sh (to find and execute our cron service)

3. entry.sh (entry point for our docker image):  Our 'crond' service will start running on the foreground daemon (-f parameter) and logging set for 'LOG_DEBUG' with the -l 8 parameter.

4. last but not least: our Dockerfile to make our image and send it to the moon.

All in one script

Here are the files in their respective order:

# ---> crontab.txt file
*/30 * * * * /script.sh

# ---> script.sh file
#!/bin/sh

echo "Running Go script ..."

/usr/src/app/cron-job-service

# ---> entry.sh file

#!/bin/sh

echo "Running service once before starting our cron service ... "

/usr/src/app/cron-job-service

# sleeping for 2 seconds because i love sleeping

sleep 2

echo "Running cron ..."

/usr/sbin/crond -f -l 8

Our Docker file will use a multistage build, the base is a GO alpine image to build our service and the second one is just an alpine image to host our pre-built service.

FROM golang:alpine AS base_alpine

RUN apk add bash ca-certificates git gcc g++ libc-dev

WORKDIR /temp

ADD . /temp

RUN go build -v -o cron-job-service && mkdir /final \
    && cp -r /temp/cron-job-service /final

FROM alpine:3.17

RUN apk update && apk add ca-certificates \
    && rm -rf /var/cache/apk/*

RUN apk add --no-cache --upgrade bash

WORKDIR /usr/src/app

COPY --from=base_alpine /final /usr/src/app/

COPY crontab.txt /crontab.txt
COPY script.sh /script.sh
COPY entry.sh /entry.sh
# setting owners permissions to read, write and excute
RUN chmod 755 /script.sh /entry.sh
RUN /usr/bin/crontab /crontab.txt

CMD ["/entry.sh"]

Finally, your scheduled docker service will tell you the same silly joke every 30 minutes. Isn't that cool?

Source: https://github.com/MerNat/cron-go

Till next time docker fans!

✌️