webdevops / Dockerfile

:package: Dockerfiles from WebDevOps for PHP, Apache and Nginx
https://webdevops.io/projects/dockerfiles/
MIT License
1.68k stars 493 forks source link

Question: use of cron #280

Open bkraul opened 5 years ago

bkraul commented 5 years ago

Hopefully this is a simple question. How does one properly implement cron jobs using the webdevops images? I am specifically using the webdevops/php-apache:alpine image.

I know the ENV var (SERVICE_CRON_OPTS) allows for cron options but I do not see how to properly set up the jobs in the documentation.

I am currently mounting the /etc/cron.d folder to a persisted volume, but I suspect this is not the best way to go about it...

Thank you for your help!

htuscher commented 5 years ago

The SERVICE_CRON_OPTS are actually just the arguments passed to the crond when it's starting (https://linux.die.net/man/8/crond).

The current solution is to either mount the cron.d folder or build a custom image with the crontab file copied into the image.

bkraul commented 5 years ago

Works for me! Thank you.

bkraul commented 5 years ago

Ok so maybe not so fast 😒 . I mounted the /etc/cron.d folder to my host's persisted storage and placed a file there called myfile with the following content:

* * * * * application echo "Cron is running" > /etc/cron.d/log.txt 2>&1 

The file is marked as chmod +x and is owner by root:root (Note the empty line at end, as I have read it matters).

supervisorctl says the crond service is running. This is on webdevops/php-apache:alpine.

However, the job does not seem to be running (no log.txt file being created/updated and no docker stdout either). What am I missing?

bkraul commented 5 years ago

I see a /opt/docker/bin/service.d/cron.d//10-init.sh that runs on starting the container. It looks for files on /opt/docker/etc/cron, and it copies them to /etc/cron.d after making some changes to them (adding an empty line at end and setting permissions to 644). I am a little confused as to what's going on.

bkraul commented 5 years ago

OK so, interestingly enough. I changed my image to webdevops/php-apache (which I assume is the debian/ubuntu variant), and my cron job runs like so:

I am mounting /opt/docker/etc/cron to my persisted storage, I am putting my file there, now as 20-myjob with the same content. The file ends up being copied to /etc/cron.d per my previous post.

I can see the job running on stdout.

Which begs the question, why does the job not run on the alpine tag?

bkraul commented 5 years ago

UPDATE: Went back to mounting /etc/cron.d after seeing that 10-init.sh keeps adding a blank line to the files every time the container starts. Job still runs fine on debian/ubuntu variant, but not on alpine.

bkraul commented 5 years ago

Any update on why cron.d jobs don't seem to be working on the alpine variant?

htuscher commented 5 years ago

Sorry, won't have time before christmas to look into it

bkraul commented 5 years ago

No worries. Have a merry Christmas!

bkraul commented 5 years ago

Hey man, just touching base with you on this. It would be awesome if cron would work as expected on the alpine tag, as the image is much leaner.

bemeyert commented 5 years ago

Same problem here. Jobs created via crontab -e work just fine. But stuff in /etc/cron.d/ is not running.

bemeyert commented 5 years ago

BTW: Starting the image with SERVICE_CRON_OPTS="-c /etc/cron.d/" doesn't help either.

bemeyert commented 5 years ago

Any news on this? Currently I work around this issue with:

crontab -l | cat - <( cat "$CRON_FILE" ) | crontab -
bkraul commented 5 years ago

@hhoechtl Is the alpine variant no longer being actively developed? I notice that you likely have most of your images on autobuild, as I see pretty recent image updates, even to the alpine one, which is why I was asking. As you can understand, a lot of us consider your images to be of utmost quality and maintenance, but it is important to know if you are intending to continue maintaining them long term. πŸ˜ƒ

htuscher commented 5 years ago

I'm maintaining as many versions as I can, but my focus is clearly on the official PHP7.x images.

Just a small hint (I don't have much time atm): the cron service itself must be enabled using RUN docker-service-enable cron.

Usually we built our own images based on the webdevops images, which have this command COPY etc/ /opt/docker/etc/.

etc
β”œβ”€β”€ cron
β”‚Β Β  └── crontab
β”œβ”€β”€ nginx
β”‚Β Β  β”œβ”€β”€ conf.d
β”‚Β Β  β”‚Β Β  └── 10-realip.conf
β”‚Β Β  └── vhost.common.d
β”‚Β Β      β”œβ”€β”€ 02-upload.conf
β”‚Β Β      β”œβ”€β”€ 10-general.conf
β”‚Β Β      └── 20-redirects.conf

I've personally never looked into or used the SERVICE_CRON_OPTS. Maybe @mblaschke can tell you how this was intended.

bkraul commented 5 years ago

The alpine image does have the cron service enabled: https://github.com/webdevops/Dockerfile/blob/5d307c8a5a1a34171dcebfdef0d2df8f8040c8e4/docker/php/alpine-php7/Dockerfile#L96

jishi commented 5 years ago

I'm struggling with this as well. The crond is running, but alpine uses the Busybox crond which probably bahaves different compared to the "real" crond. For starters, it looks for crontabs in /var/spool/cron/crontabs, not /etc/cron.d, and this is a symlink to /etc/crontabs.

If you create file /etc/crontabs/application and put a normal cron into it (without user):

* * * * * id

You would see that it starts outputting

uid=1000(application) gid=1000(application) groups=1000(application)

Meaning it is running this cron as user application. It has to be named application (or any other valid user), otherwise it won't execute.

bkraul commented 5 years ago

I don't know much about alpine but It might be a problem with busybox, as we are having a similar issue (cron not executing) on versions or Nextcloud that are build on top of alpine.

jishi commented 5 years ago

Problem and problem... the thing is that the alpine-image has the same cron.d structure and script as the ubuntu one, but they are running different crond implementations.

The busybox crond doesn't load the /etc/cron.d scripts. I'm not sure if it would be possible to run them either.

pomazanbohdan commented 4 years ago
  1. volumes:
      - ./www/crm:/app:rw
      - ./www/crm.cron/:/var/spool/cron/crontabs/:rw
  2. touch file "root" file in ./www/crm.cron/ image

  3. insert in ./www/crm.cron/root you cron line

josefglatz commented 4 years ago

@pomazanbohdan Thank you for your explanation. Works like expected, if the permissions are correct on the docker host!

I'm putting here my TYPO3ish everyday crontab line, if somebody else needs it. It's the content of my /var/spool/cron/crontabs/application file:

* * * * * TYPO3_CONTEXT=Production/Live /usr/local/bin/php /app/releases/current/typo3cms crontab:run > /dev/null 2>&1
csaeum commented 4 years ago

Hello, I've been trying for a long time to get the CronJob running properly. I've been through several situations now. Mount the cron files for root and application under / var / spool / cron / crontabs or under / etc / crontabs and also under /opt/docker/bin/service.d/cron.d/ now and then the root cronfile is partially executed but the cronjob under application is never executed. I slowly don't know anymore what is correct and how. Above, @hhoechtl asked if @mblaschke could say how SERVICE_CRON_OPTS is called correctly. I would be happy if you could say your results or how the crons are going right for you. I have: image: webdevops / php-apache: 7.4-alpine running

htuscher commented 4 years ago

etc/cron/crontab

0 8 * * * application /usr/local/bin/php /app/bin/console --env=production dosomething

Dockerfile

FROM webdevops/php-apache:7.4-alpine
COPY etc/             /opt/docker/etc/
exec /usr/sbin/crond -f $SERVICE_CRON_OPTS

Therefore this ENV variable is only for applicatoin parameters for cron itself like described here: https://linux.die.net/man/8/cron

csaeum commented 3 years ago

Hey guys, I want to come back to that. So I've tried a lot. If I mount the cron file of root and application under /opt/docker/etc/cron then this can also be seen under /etc/cron.d but nothing works.

If I mount this under /var/spool/cron/crontabs/ then it will be executed by user root but not by application.

So my approach was just to mount this and specify the user in the cron file. But the "column" with the user buw the username is interpreted as a command. I see that under Portainer in the logs that he does not find the command application.

I let it go like this now.

All crons run as root, but every 15 minutes I run a cron that changes the directory /tmp/ and everything below /app/ with chown -R 1000: 1000.

Not nice but it works for now. maybe someone can tell me what I have to do or where to mount that with the cron files of root and application with the correct rights etc.

csaeum commented 3 years ago

Sorry I have to ask again: I have included my cron files in the composer file like this.

volumes:

  • ./configs/php.ini:/opt/docker/etc/php/php.ini:ro
  • ./configs/crontabs/:/var/spool/cron/crontabs/:rw`

The 2 crons look like this. application

SHELL=/bin/bash

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

m h dom mon dow user command

          • ls -all /tmp >> /tmp/application-cron.log
          • ls -all /tmp > /tmp/application-ls-all.txt
          • /usr/local/bin/php /app/JTL-PLde-Projekt/includes/cron_inc.php

root

m h dom mon dow user command

          • ls -all /tmp >> /tmp/root-cron.log
          • ls -all /tmp > /tmp/root-ls-all.txt /2 * /bin/chown -R 1000:1000 /tmp/ /app/`

All crons work except for the one with / usr / local / bin / php and I don't see them in the logs

2021-03-13T00:11:00.410675090Z crond: can't execute '/bin/bash' for user application 2021-03-13T00:11:00.411270338Z crond: can't execute '/bin/bash' for user application`

I comment that /bin/bash from is in the logs

2021-03-13T00:29:00.969616281Z Could not open input file: /app/JTL-PLde-Projekt/includes/cron_inc.php

If I run it as a user application in the console, everything works Does anyone know?

ggergo commented 2 years ago

Alpine versions of "webdevops/php-nginx....-alpine" run BusyBox version of cron, you can check with: crond --help

You can put tasks in the /etc/periodic folders: 15min or daily or hourly or monthly or weekly

OR

you can replace the file at /var/spool/cron/crontabs/root which runs and therefore disables the default periodic schedule folders.

Dockerfile example: FROM webdevops/php-nginx-dev:7.4-alpine COPY mycrontask /var/spool/cron/crontabs/root

mycrontask example: #!/bin/sh # m h dom mon dow command * * * * * /usr/local/bin/php /app/bin/console your:symfony:command

note that there is no user parameter and I use absolute paths, thats why I left the symfony command in the example

shealavington commented 1 year ago

I've been having a similar issue with struggling to get the cron working.

I found the simplest way to set up my own cron schedule was to edit the /var/spool/cron/crontabs/root file. This file is the main cron that runs, I appended my own cron scheduled job to the end (Laravel scheduler).

Add this or similar to your ./Dockerfile:

RUN echo "* * * * * cd /app && php artisan schedule:run >> /dev/null 2>&1" >> /var/spool/cron/crontabs/root

Entry Breakdown:

prodigy7 commented 1 year ago

I invested a few hours to find out how exactly the execution of user crontabs works in the Alpine and Debian images. I think I understand it now and can provide a script to help you easily install a working user crontab:

#!/bin/bash

source /etc/os-release

if [ "$ID" == "alpine" ]; then
  CRONTAB_BASE="/etc/crontabs"
  if [[ -d "/opt/docker/etc/crontabs/" ]]; then
    find /opt/docker/etc/crontabs -type f | while read CRONTAB_FILE; do
      CRONTAB_FILENAME="$(basename $CRONTAB_FILE)"
      if [[ ! -d "$CRONTAB_BASE" ]]; then
        mkdir -p $CRONTAB_BASE
      fi

      echo "-> Deploy $CRONTAB_FILENAME to $CRONTAB_BASE"
      cat $CRONTAB_FILE >> /etc/crontabs/$CRONTAB_FILENAME
      chown root:root -- "$CRONTAB_BASE/$CRONTAB_FILENAME"
    done
  fi
elif [ "$ID" == "debian" ]; then
  CRONTAB_BASE="/var/spool/cron/crontabs"
  if [[ -d "/opt/docker/etc/crontabs/" ]]; then
    find /opt/docker/etc/crontabs -type f | while read CRONTAB_FILE; do
      CRONTAB_FILENAME="$(basename $CRONTAB_FILE)"
      if [[ ! -d "$CRONTAB_BASE" ]]; then
        mkdir -p $CRONTAB_BASE
      fi

      echo "-> Deploy $CRONTAB_FILENAME to $CRONTAB_BASE"
      cat $CRONTAB_FILE >> $CRONTAB_BASE/$CRONTAB_FILENAME
      chown $CRONTAB_FILENAME:$CRONTAB_FILENAME -- "$CRONTAB_BASE/$CRONTAB_FILENAME"
      chmod 0600 "$CRONTAB_BASE/$CRONTAB_FILENAME"
    done
  fi
else
  echo "Distribution $ID not supported by cron fix script!"
fi

Copy this file inside the docker image to /opt/docker/bin/service.d/cron.d and name it 20-cron-fix.sh and ensure it is executable. Next copy your crontab (in my case application for the user application) to /opt/docker/etc/crontabs. Depending on the type image you are using (debian or alpine) the script will copy the crontab to the right place and ensure, the permissions are set right.

During my research, I found that in the Alpine image, cron expects the file to be owned by the root user and group and located in /etc/crontabs. In the Debian image, the file must be located in /var/spool/cron/crontabs, must belong to the user for whom the file was created and must be read/write only for that user.

KitePig commented 1 year ago

I tested it for two days. I think it's a user privilege issue User of the code: root; user of Nginx: nginx; user of php-fpm: application

You can run the root /bin/echo 1&gt command. > /tmp/test.log Cannot run: /usr/local/bin/php.app/artisan schedule:run > > /dev/null 2> & 1

My solution: // The original cron remains unchanged COPY docker/etc/cron /opt/docker/etc/cron/ // Just add this new paragraph RUN echo " * /usr/local/bin/php /app/artisan schedule:run > > /dev/null 2> &1" > > /var/spool/cron/crontabs/application RUN chown application:crontab /var/spool/cron/crontabs/application RUN chmod 0600 /var/spool/cron/crontabs/application

Tortured for a long time😭

remotemerge commented 7 months ago

Commands in your Dockerfile

# Copy crontab file
COPY ./docker/crontabs/application /var/spool/cron/crontabs/application

# Set the correct permission
RUN chmod 0600 /var/spool/cron/crontabs/application

Content in the application file:

* * * * * cd /var/www/html && /usr/local/bin/php artisan schedule:run >> /dev/null 2>&1

This setup is tailored for the Laravel scheduler. Adjust the command and path according to your configuration.

damsma commented 2 months ago

Here is my solution for CRON not working in webdevops/php-apache:5.6

The problem is, that the script /opt/docker/bin/service.d/cron.d/10-init.sh is copying the cronjobs from /opt/docker/etc/cron/ to /etc/cron.d/ and doing some other stuff, but is not applying the cronjob at the end.

The solution is to add an extra line before "done":

# Install crontab files

if [[ -d "/opt/docker/etc/cron" ]]; then
    mkdir -p /etc/cron.d/

    find /opt/docker/etc/cron -type f | while read CRONTAB_FILE; do
        # fix permissions
        chmod 0644 -- "$CRONTAB_FILE"

        # add newline, cron needs this
        echo >> "$CRONTAB_FILE"

        # Install files
        cp -a -- "$CRONTAB_FILE" "/etc/cron.d/$(basename "$CRONTAB_FILE")"
        crontab "/etc/cron.d/$(basename "$CRONTAB_FILE")"
    done
fi

Then mount the fixed script and the cronjobs

  some-project:
    image: webdevops/php-apache:5.6
    restart: always
    volumes:
      - type: bind
        source: /somedir/cron
        target: /opt/docker/etc/cron
        consistency: cached
      - type: bind
        source: /somedir/10-init-fixed.sh
        target: /opt/docker/bin/service.d/cron.d/10-init.sh
        consistency: cached

would be nice if it would be fixed in future releases.

bkraul commented 2 months ago

@damsma wouldn't it make more sense to submit this as a PR? Thank you for making it work but seems it's still a workaround. @htuscher would it make sense to make this change upstream?