Docker Image: Alpine CRON

Introduction

Cron is a service that has been around for some time. For anyone unfamiliar, the service is used to run scheduled scripts/commands in Linux/Unix environments. My use case for cron is application and/or database maintenance.

When making a switch to Docker, the use case was still there, but I could not find a simple cron enabled container. So I built one: djpic/cron

I did take a look at Jobber but stuck with cron. The decision for me was as follows: cron familiarity, cron image has a smaller footprint, and I did not need any of the additional features Jobber supplied.

What is the image?

This Docker image is built from the official Alpine image. Cron service is already included in the Alpine image, however, a mechanism to run Hypertext Preprocessor (PHP) scripts on another PHP-RPM container is not.

To combat this, FastCGI is also installed in this image. FastCGI is a binary protocol for interfacing interactive programs with a web server. In short, it allows direct execution of PHP scripts running in a PHP-FPM container such as djpic/php.

How to use the image

Using the image is strait forward. Included is a crontab file that controls when jobs are run. The crontab file will execute items in specific folders in the following intervals:

To run scripts on any of these time intervals, copy the script(s) into the associated time directories:

Make sure the scripts have the correct execute permissions (i.e. chmod 755).

Image Tags

There are a total of 3 different tags with 2 different variants: standard, and default. The latest variant is the same as the standard image.

standard/latest
Standard it just that, the standard version. Jobs will run based on the intervals and directories stated in the how to use the image section above. FastCGI is installed in this version for any PHP scripts that need to run.
PHP
The PHP variant is built from the standard variant but already includes a script which runs every minute. This script uses FastCGI to execute a PHP file located at /app/private/scheduled_jobs/1min.php on the PHP-PFM container.
Note: The legacy name for this container is default.

Why include a PHP variant? Through building multiple applications, I found was easier for me to have cron run a script every minute. Then use the PHP script to control what runs when. For example, this PHP will execute a task daily at 2am:

<?php   if (date('Hi') === '0200') {     require_once('script_to_execute.php');   }

How the image is built

The build starts with Dockerfile for the standard variant. This Dockerfile will install FastCGI, create the additional crontab directories, copy the customized crontab file, and set the entry point to run cron as a daemon.

ARG alpine_version FROM alpine:${apline_version} # Install fastCGI to execute PHP scripts in PHP-FPM container RUN apk update \     && apk add fcgi # Create Crontab directories RUN mkdir /etc/periodic/1min \     && mkdir /etc/periodic/30min \     && mkdir /etc/periodic/12hour # Copy in customized crontab file COPY crontab /etc/crontabs/root # Copy in entrypoint.sh script; this allows cron to run as daemon COPY entrypoint.sh /entrypoint.sh RUN chmod 755 /entrypoint.sh ENTRYPOINT /entrypoint.sh

The customized crontab file is shown here:

# do daily/weekly/monthly maintenance # min    hour     day      month    weekday    command *        *        *        *        *          run-parts /etc/periodic/1min/ */15     *        *        *        *          run-parts /etc/periodic/15min/ */30     *        *        *        *          run-parts /etc/periodic/30min/ 0        *        *        *        *          run-parts /etc/periodic/hourly/ 0        */12     *        *        *          run-parts /etc/periodic/12hour/ 0        2        *        *        *          run-parts /etc/periodic/daily/ 0        3        *        *        6          run-parts /etc/periodic/weekly/ 0        5        1        *        *          run-parts /etc/periodic/monthly/

And entrypoint shell script which forces cron to run as a daemon:

#!/bin/sh echo "For more information, visit https://hub.docker.com/repository/docker/djpic/cron" echo "Starting DockerContainer..." crond -f -l 8 -d 8 -L /dev/stdout

The Dockerfile for the PHP variant uses the standard image as the base and copies in FastCGI shell script into the 1min crontab directory.

FROM djpic/cron:standard # Copy FastCGI script to run every minute COPY script.sh /etc/periodic/1min/default # Update permissions on FastCGI script RUN chmod 755 /etc/periodic/1min/default

The FastCGI shell script sets the variables needed for FastCGI to execute the 1min.php file. This script assumes the file is located at /app/private/scheduled_jobs/1min.php. You can use this FastCGI shell script as an example for any customization you would like to do. This script also assumes the linked PHP container is named php.

#!/bin/sh echo "Running default command...." > /dev/stdout SCRIPT_NAME=/app/private/scheduled_jobs/1min.php \ SCRIPT_FILENAME=/app/private/scheduled_jobs/1min.php \ REQUEST_METHOD=GET \ cgi-fcgi -bind -connect php:9000

Finally is the build script. The script is executed to build all variants of the image and upload them to DockerHub. The current_alpine_version variable will be the only item in this script that would be updated regularly. The script builds and tags the standard/latest variant first then the PHP variant is built.

After all variants are built, the script finishes by pushing all the images to Dockerhub. Like all my image builds, the image is build using a Gitlab runner with a docker executor.

#!/bin/bash # What base image of Alpine to use for all builds current_alpine_version=3.17.4 # Build standard cron image docker build --build-arg alpine_version=$current_alpine_version --tag djpic/cron:$current_alpine_version-standard . docker tag djpic/cron:$current_alpine_version-standard djpic/cron:latest # Build php cron image cd php docker build --build-arg alpine_version=$current_alpine_version --tag djpic/cron:$current_alpine_version-php . # Push images to Dockerhub docker push djpic/cron:latest docker push djpic/cron:php docker push djpic/cron:$current_alpine_version-standard docker push djpic/cron:$current_alpine_version-php

Sample docker-compose file

This image is designed to used in conjunction with my other NGINX/PHP/TRAEFIK environment. For example, below is a sample docker-compose file which includes djpic/nginx and djpic/php.

There is a custom Traefik image that is also available and this docker compose file includes djpic/traefik labels.

version: "3.2" services:   nginx:     image: djpic/nginx:phpfpm     networks:       - Traefik       - BackEnd     depends_on:       - php     volumes:       - ./application/:/app     labels:       - "traefik.enable=true"       - "traefik.http.routers.djpicdemo-web.rule=Host(`demo.djpic.net`)"       - "traefik.http.routers.djpicdemo-web.entrypoints=web"       - "traefik.http.routers.djpicdemo-web.middlewares=https-redirect@file"       - "traefik.http.routers.djpicdemo-tls.rule=Host(`demo.djpic.net`)"       - "traefik.http.routers.djpicdemo-tls.entrypoints=websecure"       - "traefik.http.routers.djpicdemo-tls.tls.certresolver=letsencrypt"       - "traefik.http.routers.djpicdemo-tls.middlewares=secure-headers@file, compress-content@file"     restart: always   php:     image: djpic/php:mysqli     networks:       - BackEnd     volumes:       - ./application/:/app   memcached:     image: library/memcached:alpine     networks:       - BackEnd     command: ["-m", "64m"]     restart: always   cron:     image: djpic/cron:php     networks:       - BackEnd networks:   Treafik:     external: true   BackEnd:

Conclusion

This djpic/cron image rounds out my development. I hope you took something from this article and highly recommend taking a look at Jobber then decided what works best for your development. All the Dockerfiles, scripts, and supporting files to build these images are available on Gitlab.

Also, be sure to check out my other Dockerhub images and their associated articles: djpic/nginx, djpic/php, and djpic/traefik. All of these images are designed to work together.

Follow my twitter account @djpic_llc for updates to this article and other announcements. I also welcome all constructive input. If anything is incorrect or needs further explanation, feel free to contact me.

Disclaimer:
The contents of this article are of my opinion and based on my experience. Before applying any of the concepts or suggestions in this article, complete your own independent research and testing. By reading this article, the reader agrees that Dale M. Picou, Jr. shall not be held liable for any negative impact when applying these tutorials, concepts, and/or suggestions.