How to set up a cron job on Docker Desktop for Windows?

797 views Asked by At

I'm trying to run a cron job in a selenium container in Docker Desktop for windows. Because I think I'm running into several problems, it's hard for me to figure out which details matter so I'll try to be as thorough as possible.

Environment:

  1. Docker Desktop for Windows (to avoid line ending problems I make the cron string in the Dockerfile)
  2. Selenium-Chrome (one thing to note is that most things are run under seluser instead of root here. I say that because some of the other solutions don't work because of this)

Problem:

I cannot run python in my cron job

Related stack overflow links I've checked:

There are a lot but this is the main one.

For example, this snippet logs to the log file shown appropriately:

FROM selenium/standalone-chrome

COPY . /home/seluser/

# # install selenium
RUN echo "**** install packages ****" && \
    sudo apt-get update && \
    sudo apt-get install -y cron && \
    echo "**** cleanup ****" && \
    sudo apt-get clean && \
    sudo rm -rf \
    /tmp/* \
    /var/lib/apt/lists/* \
    /var/tmp/*

# Create the log file to be able to run tail
RUN touch /home/seluser/cron.log

# Setup cron job
RUN echo "* * * * * echo "Hello, World!" >> /home/seluser/cron.log" | sudo crontab

# Run the command on container startup
CMD sudo cron && tail -f /home/seluser/cron.log

But this doesn't:

FROM selenium/standalone-chrome

COPY . /home/seluser/

# # install selenium
RUN echo "**** install packages ****" && \
    sudo apt-get update && \
    sudo apt-get install -y cron && \
    echo "**** cleanup ****" && \
    sudo apt-get clean && \
    sudo rm -rf \
    /tmp/* \
    /var/lib/apt/lists/* \
    /var/tmp/*

# Create the log file to be able to run tail
RUN touch /home/seluser/cron.log

# Setup cron job
RUN echo "* * * * * /usr/bin/python3 -c print("Hello world") >> /home/seluser/cron.log" | sudo crontab

# Run the command on container startup
CMD sudo cron && tail -f /home/seluser/cron.log
1

There are 1 answers

0
David Maze On BEST ANSWER

In your RUN command, you have both double quotes surrounding the entire command and also double quotes in the embedded Python script. The shell consumes these pairs of quotes, and so there are no quotes in the Python fragment at all.

You can work around this by using single quotes for either pair of quotes, or by backslash-escaping the double quotes within the double-quoted string. All of these will work:

RUN echo "... print(\"Hello world\") ..." | crontab
RUN echo "... print('Hello world') ..." | crontab
# NB: will not expand environment variables inside the string
RUN echo '... print("Hello world") ...' | crontab

The first form with echo appears to work because echo "Hello world", echo Hello world, and echo "Hello" "world" all print out the same string; it doesn't matter whether it's one or two parameters, and echo(1) itself never sees the double quotes. In the Python case, though, you need to pass the double quotes into the interpreter.

If you're facing challenges like this, often the best approach is to break out the embedded script into a separate file.

#!/usr/bin/env python3
# greet.py
if __name__ == '__main__':
  print('Hello world')

Then your cron job can just run the script.

RUN echo '* * * * * /home/seluser/greet.py >> /home/seluser/cron.log' | crontab

I've omitted sudo in my examples. In a Dockerfile you are generally running as root already, or you can specify USER root to switch to root. sudo is tricky to use in scripts and tends to lead to unnecessary and insecure configuration in Docker.

A final Dockerfile for this might look like:

FROM selenium/standalone-chrome

# Install OS packages _before_ copying any application code in.
# This avoids an expensive reinstallation on rebuild.
RUN apt-get update \
 && DEBIAN_FRONTEND=noninteractive \
    apt-get install --assume-yes --no-install-recommends \
      cron

# Now copy your own code in.  (Rebuilds will start here, if anything
# in the source tree has changed.)
COPY . /home/seluser

# Set up the cron environment.
RUN echo '* * * * * /home/seluser/greet.py >> /home/seluser/cron.log' | crontab \
 && touch /home/seluser/cron.log

# Declare the main container command.  (Actually run the cron daemon;
# don't make the container process be tail(1) with cron being an
# unmonitored side effect.)
CMD ["cron", "-f"]