How to run a cron job inside a docker container?

前端 未结 20 1281
無奈伤痛
無奈伤痛 2020-11-22 05:36

I am trying to run a cronjob inside a docker container that invokes a shell script.

Yesterday I have been searching all over the web and stack overflow, but I could

相关标签:
20条回答
  • 2020-11-22 06:01

    Try using clockwork gem to schedule tasks. Follow the steps provided in this link.

    http://fuzzyblog.io/blog/rails/2017/05/11/adding-cron-to-a-dockerized-rails-application-using-clockwork.html

    You can call the rake task inside lib/clock.rb file as below.

    every(1.day, 'Import large data from csv files', :at => '5:00') do |job|
      `rake 'portal:import_data_from_csv'`
    end
    

    Create a separate container in docker-compose file & run the below command inside the container.

    command: bundle exec clockwork lib/clock.rb
    
    0 讨论(0)
  • 2020-11-22 06:02

    From above examples I created this combination:

    Alpine Image & Edit Using Crontab in Nano (I hate vi)

    FROM alpine
    
    RUN apk update
    RUN apk add curl nano
    
    ENV EDITOR=/usr/bin/nano 
    
    # start crond with log level 8 in foreground, output to stderr
    CMD ["crond", "-f", "-d", "8"]
    
    # Shell Access
    # docker exec -it <CONTAINERID> /bin/sh
    
    # Example Cron Entry
    # crontab -e
    # * * * * * echo hello > /proc/1/fd/1 2>/proc/1/fd/2
    # DATE/TIME WILL BE IN UTC
    
    0 讨论(0)
  • 2020-11-22 06:06

    VonC's answer is pretty thorough. In addition I'd like to add one thing that helped me. If you just want to run a cron job without tailing a file, you'd be tempted to just remove the && tail -f /var/log/cron.log from the cron command.

    However this will cause the Docker container to exit shortly after running because when the cron command completes, Docker thinks the last command has exited and hence kills the container. This can be avoided by running cron in the foreground via cron -f.

    0 讨论(0)
  • 2020-11-22 06:10

    The adopted solution may be dangerous in a production environment.

    In docker you should only execute one process per container because if you don't, the process that forked and went background is not monitored and may stop without you knowing it.

    When you use CMD cron && tail -f /var/log/cron.log the cron process basically fork in order to execute cron in background, the main process exits and let you execute tailf in foreground. The background cron process could stop or fail you won't notice, your container will still run silently and your orchestration tool will not restart it.

    You can avoid such a thing by redirecting directly the cron's commands output into your docker stdout and stderr which are located respectively in /proc/1/fd/1 and /proc/1/fd/2.

    Using basic shell redirects you may want to do something like this :

    * * * * * root echo hello > /proc/1/fd/1 2>/proc/1/fd/2
    

    And your CMD will be : CMD ["cron", "-f"]

    0 讨论(0)
  • 2020-11-22 06:12

    The most robust way I found so far is run an independent cron container - install the docker client and bind mount the docker sock so you can talk to the docker server on the host.

    Then just use env vars for each cron job and an entrypoint script to generate the /etc/crontab

    Here is an image that I created using this principle and using it in production for the last 3-4 years.

    https://www.vip-consult.solutions/post/better-docker-cron#content

    0 讨论(0)
  • 2020-11-22 06:15

    You can copy your crontab into an image, in order for the container launched from said image to run the job.

    See "Run a cron job with Docker" from Julien Boulay in his Ekito/docker-cron:

    Let’s create a new file called "hello-cron" to describe our job.

    * * * * * echo "Hello world" >> /var/log/cron.log 2>&1
    # An empty line is required at the end of this file for a valid cron file.
    

    The following Dockerfile describes all the steps to build your image

    FROM ubuntu:latest
    MAINTAINER docker@ekito.fr
    
    RUN apt-get update && apt-get -y install cron
    
    # Copy hello-cron file to the cron.d directory
    COPY hello-cron /etc/cron.d/hello-cron
    
    # Give execution rights on the cron job
    RUN chmod 0644 /etc/cron.d/hello-cron
    
    # Apply cron job
    RUN crontab /etc/cron.d/hello-cron
    
    # Create the log file to be able to run tail
    RUN touch /var/log/cron.log
    
    # Run the command on container startup
    CMD cron && tail -f /var/log/cron.log
    

    (see Gaafar's comment and How do I make apt-get install less noisy?:
    apt-get -y install -qq --force-yes cron can work too)

    As noted by Nathan Lloyd in the comments:

    Quick note about a gotcha:
    If you're adding a script file and telling cron to run it, remember to
    RUN chmod 0744 /the_script
    Cron fails silently if you forget.


    OR, make sure your job itself redirect directly to stdout/stderr instead of a log file, as described in hugoShaka's answer:

     * * * * * root echo hello > /proc/1/fd/1 2>/proc/1/fd/2
    

    Replace the last Dockerfile line with

    CMD ["cron", "-f"]
    

    See also (about cron -f, which is to say cron "foreground") "docker ubuntu cron -f is not working"


    Build and run it:

    sudo docker build --rm -t ekito/cron-example .
    sudo docker run -t -i ekito/cron-example
    

    Be patient, wait for 2 minutes and your commandline should display:

    Hello world
    Hello world
    

    Eric adds in the comments:

    Do note that tail may not display the correct file if it is created during image build.
    If that is the case, you need to create or touch the file during container runtime in order for tail to pick up the correct file.

    See "Output of tail -f at the end of a docker CMD is not showing".

    0 讨论(0)
提交回复
热议问题