Sometime you need to run a timed function on your dockerized php-apache system. Googling around you find you have to build a dedicated container in which setup the crontab or "better" put your tasks in the host crontab. I don't like any of these solutions: I want my cronned jobs called from the apache stack.
After a couple of hour of work and the reading of the f***ing manual, I found a possible solution, may be not the best but it works.
Tree structure
Well, to setup a test stack, prepare the folder structure with the www
and log
directories, the Dockerfile
, the docker-compose.yml
and other things as shown below:
./
├── .env
├── Dockerfile
├── cron
├── docker-compose.yml
├── log/
│ ├── apache2/
│ └── cron/
└── www/
└── index.php
The main files are:
Dockerfile
: define your image inheriting from php:apache (latest)cron
: put your tasks with the usual crontab syntaxdocker-compose.yml
: define the system as a wholeindex.php
: a simple test file.env
: the customization environment variables
Let's have a fast look at each document
.env
Some basic settings, e.g the host port for final apache service:
HTTP_PORT=8080
cron
The crontab test file:
# ,------ MINUTE
# / ,----- HOUR
# / / ,---- DAY OF MONTH
# / / / ,--- MONTH
# / / / / ,-- DAY OF WEEK
#/ / / / /
* * * * * echo "`date`: hello, world" >> /var/log/cron/cron.log
#
Pay attention to the destination of log of the test command: the folder /var/log/cron
will be mounted from the local host folder ./log/cron
.
Dockerfile
The php:apache image enriched with the working cron process:
FROM php:apache
RUN apt-get update && \
apt-get -y install tzdata cron
RUN cp /usr/share/zoneinfo/Europe/Rome /etc/localtime && \
echo "Europe/Rome" > /etc/timezone
#RUN apt-get -y remove tzdata
RUN rm -rf /var/cache/apk/*
# Copy cron file to the cron.d directory
COPY cron /etc/cron.d/cron
# Give execution rights on the cron job
RUN chmod 0644 /etc/cron.d/cron
# Apply cron job
RUN crontab /etc/cron.d/cron
# Create the log file to be able to run tail
RUN mkdir -p /var/log/cron
# Add a command to base-image entrypont scritp
RUN sed -i 's/^exec /service cron start\n\nexec /' /usr/local/bin/apache2-foreground
The new docker image inherits from php:apache latest official image, adds the correct timezone, setups the cron job and, finally -the hardest poin-t: modifies the entrypoint script of the inherited image (apache2-foreground
) adding the service start cron
just before the launch of the apache process. Remember that each container is hung up to ta single running process. In php:apache
that should be apache itself (and not the cron). So the idea is to start cron as a service and before the container single running process.
docker-compose.yml
The instructions for the docker-compose
command:
version: '3.1'
services:
web:
build: .
image: phpcron
container_name: crontest
restart: unless-stopped
volumes:
- ./www:/var/www/html
- ./log/apache2:/var/log/apache2
- ./log/cron:/var/log/cron
ports:
- $HTTP_PORT:80
The stack is limited the unique service web
: build from the Dockerfile, defines the mount volumes and some other detail.
index.php
Finally put something like this in the index page:
<h1><?= date('Y-m-d H:i:s'); ?></h1>
<?php
phpinfo();
The running test
Now you can test the system with the command:
docker-compose up
and then look inside the log file cron.log
that should appear in the ./log/cron/
folder for the every minute 'hello, world' line.
Also test your localhost:8080
to get the Php Info page correctly shown.
;)