How should I use libfaketime and cron in a Docker container? - docker

I would like to set a Docker container up, which
fakes a chosen date with libfaketime;
runs a cron job with that fake time.
My attempt can be found at https://gitlab.com/gablab/proto_docker_cron_libfaketime. The most relevant lines are probably
# Dockerfile
CMD cron && tail -f /var/log/cron.log
# crontab_for_container
* * * * * for i in 1 2 3; do echo $i; flock -n /.flock_reprio /myscript.sh >> /var/log/cron.log 2>&1; sleep 10; done
More details
Running the script directly
docker-compose exec myservice bash
and then bash /myscript.sh gives the wanted date. Same for docker-compose exec myservice date.
Dockerfile
FROM debian:buster AS base
RUN set -ex; \
apt-get update;
ADD ./myscript.sh /myscript.sh
RUN apt install -y cron
COPY ./crontab_for_container /etc/cron.d/crontab_for_container
RUN chmod 0644 /etc/cron.d/crontab_for_container
RUN crontab /etc/cron.d/crontab_for_container
RUN touch /var/log/cron.log
CMD cron && tail -f /var/log/cron.log
from base as builder
# faketime
RUN apt-get update && apt-get install -y make gcc git
# Get the sources and checkout at stable release 0.98
# see https://github.com/wolfcw/libfaketime/releases
RUN git clone https://github.com/wolfcw/libfaketime.git && \
cd libfaketime && \
git checkout dc2ae5eef31c7a64ce3a976487d8e57d50b8d594 && \
make
from base as runtime
COPY --from=builder /libfaketime/src/libfaketime.so.1 /usr/local/lib
ENV LD_PRELOAD=/usr/local/lib/libfaketime.so.1
ARG FAKETIME_ARG="0d"
ENV FAKETIME=$FAKETIME_ARG

A quick, dirty fix that works is to hard-code into the crontab file the variables to be passed to the job:
# crontab
* * * * * for i in 1 2 3; do echo $i; LD_PRELOAD=/usr/local/lib/libfaketime.so.1 FAKETIME=FAKETIME_PLACEHOLDER TEST=ABCD flock -n /.flock_reprio /myscript.sh >> /var/log/cron.log 2>&1; sleep 10; done
# Dockerfile
RUN sed --in-place -e s/FAKETIME_PLACEHOLDER/"\"$FAKETIME_ARG\""/ /etc/cron.d/crontab_for_container
(note that the quotes \" are needed because the date could be provided as "#2020-02-02 10:00:00").

Related

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

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:
Docker Desktop for Windows (to avoid line ending problems I make the cron string in the Dockerfile)
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
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"]

Why isn't my Docker container running my cron script, but it does when I do service cron restart?

Cron isn't a running process when I go into my container, but when go into the container and bash service cron restart it starts running, I can't figure out why this isn't working with the service cron restart but not without it?
Dockerfile
FROM ubuntu:bionic
RUN apt-get update && apt-get -y install \
cron \
nano \
psmisc \
wget
COPY hello-cron /etc/cron.d/hello-cron
# Give execution rights on the cron job
RUN chmod +x /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
CMD ["cron", "-f"]
my hello-cron script
* * * * * echo "Hello world" > /usr/src/helloworldcron.log 2>&1

Why isn't a cron job running in a personally modified CentOS docker image?

I am trying to make a docker image using a CentOS 7 base image of a private registry.
A cron operation is needed, so I first tested with printing 'hello cron' with a public CentOS 7 image.
My Dockerfile using a public Centos 7 image:
FROM centos:7.4.1708
RUN localedef -c -i en_US -f UTF-8 en_US.UTF-8 && \
yum update -y
RUN yum install -y python3 cronie vim && \
pip3 install requests
WORKDIR /app
COPY . /app
COPY crontab /etc/cron.d/crontab
RUN chmod 0644 /etc/cron.d/crontab
RUN /usr/bin/crontab /etc/cron.d/crontab
CMD ["crond", "-n"]
In this case, it seems to work properly.
[root#091c362de99c app]# cat /var/log/cron.log
hello cron
hello cron
hello cron
So, I tried the same way using a personal CentOS 7 base image.
My Dockerfile using a private image:
FROM base.REGISTRY/centos7/jdk:8.x64
RUN localedef -c -i en_US -f UTF-8 en_US.UTF-8 && \
yum update -y
RUN yum install -y python3 cronie vim && \
pip3 install requests
WORKDIR /app
COPY . /app
COPY crontab /etc/cron.d/crontab
RUN chmod 0644 /etc/cron.d/crontab
RUN /usr/bin/crontab /etc/cron.d/crontab
CMD ["crond", "-n"]
However, it doesn't seem to work.
[root#31580092ecec app]# cat /var/log/cron.log
cat: /var/log/cron.log: No such file or directory
Commands done with a public image:
[root#091c362de99c app]# crontab -l
* * * * * python3 /app/hello.py >> /var/log/cron.log
[root#091c362de99c app]# cat /app/hello.py
print('hello cron')
[root#091c362de99c app]# cat /var/log/cron.log
hello cron
hello cron
hello cron
hello cron
hello cron
Commands done with a private image:
[root#31580092ecec app]# crontab -l
* * * * * python3 /app/hello.py >> /var/log/cron.log
[root#31580092ecec app]# cat /app/hello.py
print('hello cron')
[root#31580092ecec app]# cat /var/log/cron.log
cat: /var/log/cron.log: No such file or directory
I want to figure out the differences between the two, but what I can do was just checking crontab -l.
Other than a cron job, just running a python file once works well with the both Dockerfile.
CMD ["python", "./hello.py"]
What could be a way to find out the differences to solve this problem?

How to execute cron job as non-root user under Ubuntu inside Docker

This is the Dockerfile of an image I'm trying to create. The non-root user is simulating the host user on the local machine.
FROM ubuntu:bionic
RUN apt-get -yqq update && apt-get -yqq install cron passwd openssl strace
RUN groupadd -g 1000 hostuser && useradd -l -u 1000 -m -s /bin/bash -p $(openssl passwd -1 test1) -g hostuser hostuser
COPY hello-cron /etc/cron.d/hello-cron
RUN sudo chmod 0644 /etc/cron.d/hello-cron
COPY hello-cron-root /etc/cron.d/hello-cron-root
RUN sudo chmod 0644 /etc/cron.d/hello-cron-root
RUN touch /var/log/cron.log
COPY cron.allow /etc/cron.allow
#CMD /usr/sbin/cron -L 15 && tail -f /var/log/cron.log
CMD strace /usr/sbin/cron -f -L 15
This is the file hello-cron:
* * * * * hostuser echo "Hello World" >> /var/log/cron.log 2>&1
This is the file hello-cron-root:
* * * * * root echo "Hello World Root" >> /var/log/cron.log 2>&1
This is the file cron.allow:
hostuser
When I build and execute the image ONLY the job executed under root gives some output. strace did not help much. Any ideas?
Unfortunately I can't use Alpine or something else. I need to make this work using Ubuntu Bionic.
So far this is the only thing that works in this setup:
FROM ubuntu:bionic
RUN apt-get -yqq update && apt-get -yqq install cron passwd openssl sudo
RUN groupadd -g 1000 hostuser && useradd -l -u 1000 -m -s /bin/bash -p $(openssl passwd -1 test1) -g hostuser hostuser
COPY hello-cron-root /etc/cron.d/hello-cron-root
RUN sudo chmod 0644 /etc/cron.d/hello-cron-root
RUN touch /var/log/cron.log
COPY cron.allow /etc/cron.allow
COPY hostuser-run /usr/local/bin/hostuser-run
RUN chmod +x /usr/local/bin/hostuser-run
CMD /usr/sbin/cron -L 15 && tail -f /var/log/cron.log
This is hostuser-run:
#!/usr/bin/env bash
sudo -H -s -u 'hostuser' echo "We are running as $USER"
This is the file hello-cron-root:
* * * * * root /usr/local/bin/hostuser-run >> /var/log/cron.log 2>&1
To summarize - given that only root cron jobs are working under docker to execute stuff under different user use helper script with sudo -u.

Docker cron job

I need to schedule some cron jobs in docker/kubernetes environment, which will make some external service calls using curl commands. I was trying to use plain alpine:3.6 image but it doesn't have curl.
Any suggestion what base image will be good for this purpose? Also, it will be helpful if there are some examples.
Prepare your own docker image which will includes packages you need or just use something like this https://github.com/aylei/kubectl-debug
I am able to run curl as cronjob job using alpine image as
FROM alpine:3.6
RUN apk --no-cache add curl bash
RUN apk add --no-cache tzdata
ENV TZ=Asia/Kolkata
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ /etc/timezone
RUN mkdir -p /var/log/cron \
&& touch /var/log/cron/cron.log \
&& mkdir -m 0644 -p /etc/cron.d
ADD curlurl /etc/crontabs/root
CMD bash -c "crond -L /var/log/cron/cron.log && tail -F /var/log/cron/cron.log"
#ENTRYPOINT /bin/bash
Where curlurl is having simple curl command as cron job
* * * * * /usr/bin/curl http://dummy.restapiexample.com/api/v1/employees >> /var/log/cron/cron.log
# Leave one blank line below or cron will fail
This is working fine . But now I have tried to make alpine base image with push in private repo to avoid downloading every time. But it stopped working. How can i debug why cron job is not running anymore. Below is the way I have used.
BaseImage
FROM alpine:3.6
RUN apk --no-cache add curl bash
RUN apk add --no-cache tzdata
ENTRYPOINT /bin/bash
Docker for curl
FROM my/alpine:3.6
ENV TZ=Asia/Kolkata
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ /etc/timezone
RUN mkdir -p /var/log/cron \
&& touch /var/log/cron/cron.log \
&& mkdir -m 0644 -p /etc/cron.d
ADD curlurl /etc/crontabs/root
CMD bash -c "crond -L /var/log/cron/cron.log && tail -F /var/log/cron/cron.log"
After making it like this I am not able to get any cron output . Any suggestion on how can I debug it.

Resources