Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question: use of cron #280

Open
bkraul opened this issue Dec 3, 2018 · 32 comments
Open

Question: use of cron #280

bkraul opened this issue Dec 3, 2018 · 32 comments

Comments

@bkraul
Copy link

bkraul commented Dec 3, 2018

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
Copy link
Member

htuscher commented Dec 4, 2018

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
Copy link
Author

bkraul commented Dec 4, 2018

Works for me! Thank you.

@bkraul bkraul closed this as completed Dec 4, 2018
@bkraul bkraul reopened this Dec 5, 2018
@bkraul
Copy link
Author

bkraul commented Dec 5, 2018

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
Copy link
Author

bkraul commented Dec 5, 2018

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
Copy link
Author

bkraul commented Dec 5, 2018

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
Copy link
Author

bkraul commented Dec 5, 2018

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
Copy link
Author

bkraul commented Dec 11, 2018

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

@htuscher
Copy link
Member

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

@bkraul
Copy link
Author

bkraul commented Dec 12, 2018

No worries. Have a merry Christmas!

@bkraul
Copy link
Author

bkraul commented Feb 4, 2019

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
Copy link

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

@bemeyert
Copy link

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

@bemeyert
Copy link

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

crontab -l | cat - <( cat "$CRON_FILE" ) | crontab -

@bkraul
Copy link
Author

bkraul commented Aug 13, 2019

@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
Copy link
Member

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
Copy link
Author

bkraul commented Aug 15, 2019

The alpine image does have the cron service enabled:

&& docker-service enable cron \

@jishi
Copy link

jishi commented Nov 4, 2019

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
Copy link
Author

bkraul commented Nov 4, 2019

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
Copy link

jishi commented Nov 4, 2019

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
Copy link

pomazanbohdan commented Jun 16, 2020

    volumes:
      - ./www/crm:/app:rw
      - ./www/crm.cron/:/var/spool/cron/crontabs/:rw
  1. touch file "root" file in ./www/crm.cron/
    image

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

@josefglatz
Copy link
Member

@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
Copy link

csaeum commented Oct 30, 2020

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
Copy link
Member

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
Copy link

csaeum commented Nov 13, 2020

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
Copy link

csaeum commented Mar 13, 2021

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
Copy link

ggergo commented Jan 17, 2022

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
Copy link

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:

  • RUN -> Dockerfile command to perform a shell command
  • echo " -> Begin output string
  • * * * * * -> The schedule (every minute of every day)
  • cd /app && php artisan schedule:run -> My command
  • >> /dev/null 2>&1 -> Discard any terminal output
  • " -> End output String
  • >> /var/spool/cron/crontabs/root -> Append the output to the given file

@prodigy7
Copy link

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
Copy link

KitePig commented Oct 9, 2023

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
Copy link

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
Copy link

damsma commented Aug 24, 2024

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.

Edit: There should be only one file in /opt/docker/etc/cron. If there are multiple files (including hidden files), only the last file will be used for the crontab.

You can check if it's working by executing the command. Your cron jobs should be listed.

docker exec -it your-docker-container-name crontab -l

@bkraul
Copy link
Author

bkraul commented Aug 24, 2024

@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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests