Closed curry7313 closed 5 months ago
You can sync the current faked time / libfaketime settings across processes, containers, and machines as long as they have common access to one (shared) file (see, e.g., FAKETIME_FOLLOW_FILE
).
However, all libfaketime does is reporting faked times back to applications that use one of the intercepted time-related system calls. It does not stall or halt execution of any process until some other process is in the same state.
@wolfcw Thank you for your reply. In addition, I want to use cron in the time environment faked by libfaketime, such as executing it at 0 o'clock every day. Are there any relevant examples?
Please see section "Faking the date and time system-wide" in libfaketime's README file. It works for bare metal, VMs as well as containers. It's probably the easiest solution in your containers / Dockerfile. Otherwise, you can also selectively start the cron daemon with libfaketime.
@wolfcw Hi, As you said, I tried to change the time system-wide with using /etc/faketimerc
. However, the result was not what I expected.
This is my cron script file hello-cron
:
0 0 * * * echo "Hello $(date)" >>/var/log/cron.log 2>&1
#
Dockerfile:
FROM debian
RUN apt-get update && apt-get install -y cron
COPY --from=forlabs/libfaketime /faketime.so /faketime.so
ENV LD_PRELOAD=/faketime.so
# Push back 1 year and speed up a real day to one minute
RUN echo "+1y x1440" > /etc/faketimerc
COPY hello-cron /etc/cron.d/hello-cron
RUN crontab /etc/cron.d/hello-cron
CMD ["cron", "-f"]
In the running container, enter date
, the time offset and speed did change:
root@a3b68672eb9a:/# date
Tue Feb 4 20:42:28 UTC 2025
root@a3b68672eb9a:/# date
Wed Feb 5 21:04:55 UTC 2025
root@a3b68672eb9a:/# date
Wed Feb 5 21:56:22 UTC 2025
However, the date output to the file has not changed, although the output frequency is indeed one line per minute (x1440 causes 0 0 * * *
to become every minute)
root@a3b68672eb9a:/# cat /var/log/cron.log
Hello Sun Feb 4 15:50:23 UTC 2024
Hello Sun Feb 4 15:51:23 UTC 2024
Hello Sun Feb 4 15:52:23 UTC 2024
I am expecting one line per minute and the output should be:
Hello Sun Feb 4 00:00:00 UTC 2025
Hello Sun Feb 5 00:00:00 UTC 2025
What do you think is going on?
Well, since libfaketime apparently does not apply to the executed cronjob, it seems like environment variables are getting sanitized. I'd recommend setting up a cronjob which executes a shell script, in which you set LD_PRELOAD again.
@wolfcw Thanks. I reset up my cronjob:
0 0 * * * /app/main.sh >>/app/cron.log 2>&1
#
main.sh:
#! /bin/bash
export LD_PRELOAD=/faketime.so
echo "Hello $(date)"
The final result like this:
root@ba89bc3f0f51:/app# date
Fri Feb 7 05:37:53 UTC 2025
root@ba89bc3f0f51:/app# date
Fri Feb 7 08:17:18 UTC 2025
root@ba89bc3f0f51:/app# cat cron.log
Hello Tue Feb 4 15:39:38 UTC 2025
Hello Tue Feb 4 15:40:38 UTC 2025
Hello Tue Feb 4 15:41:38 UTC 2025
As you can see, although the offset of the time in the log has changed, the printed time is still not 0 o'clock every day. Is there a way for me to simulate a virtual environment(bare metal, VMs, containers are all acceptable) so that, except for time acceleration, the execution effects of other commands, programs, scheduled tasks, etc. are exactly the same as in the real world?
It's some progress, because apparently libfaketime now is loaded for the executed cronjobs (unlike before).
In the cronjob (main.sh), can you check / write to the logfile whether the environment variable FAKETIME_SHARED
is set?
If it's not set, does starting cron -f
via libfaketime's faketime
wrapper change anything about that?
What certainly always should work is using the mentioned FAKETIME_FOLLOW_FILE
instead of a relative time offset in the faketimerc file, but this requires another process/program (e.g., another cronjobs) to always adjust the given file's timestamp. However, I assume this will be needed for your cross-device/-container setup anyway sooner or later. You can have that process/cronjob running at x1440 speed and simply touch
the file as often as required.
@wolfcw Thank you very much for your patient explanation. I realized that the above result is because every time $(date)
is executed in the shell, it is faked based on my local time, so I need to synchronize the previously faked time throughFAKETIME_FOLLOW_FILE
, right?
However, there are still two problems here:
In the above results:
root@ba89bc3f0f51:/app# cat cron.log
Hello Tue Feb 4 15:39:38 UTC 2025
Hello Tue Feb 4 15:40:38 UTC 2025
Hello Tue Feb 4 15:41:38 UTC 2025
In fact, even if you don't care about the printed content, the printed time is not 0 o'clock every day of the faked time. Printing the time is just for testing. In fact, what I am more concerned about is that the execution time needs to occur at 0 o'clock every day, so as to simulate the real environment.
The file specified by FAKETIME_FOLLOW_FILE
is being written to 1440 times per second, is this really okay?
Yeah, it looks like time settings are not synchronized between your cron daemon and the cronjobs. That's why you might want to check whether FAKETIME_SHARED is set for the cronjobs, and if it's not, use the faketime wrapper when starting the cron daemon. :-)
FAKETIME_FOLLOW_FILE is the easiest way to make multiple independent processes use the exactly same faked time. When modifying the file quite often (such as 1440 times per second), things are usually cached by the file system in RAM and nothing really hits the disk. So yes, it should be OK, since it's not stressing your hardware.
@wolfcw Your answer is very useful to me, thanks.
@wolfcw I tried make multiple independent processes use the exactly same faked time by FAKETIME_FOLLOW_FILE
. This is the Dockerfile I use as a test, just install libfaketime:
FROM centos/python-36-centos7:latest
USER root
WORKDIR /app
RUN git clone https://github.com/wolfcw/libfaketime.git
RUN cd libfaketime && make && make install
CMD ["/bin/bash"]
I run this container as -i -t
, and I did the same operation as in the Readme about FAKETIME_FOLLOW_FILE
:
touch -t 0912241234.56 /tmp/my-demo-file.tmp
LD_PRELOAD=/usr/local/lib/faketime/libfaketime.so.1 \
FAKETIME='%' FAKETIME_FOLLOW_FILE=/tmp/my-demo-file.tmp \
FAKETIME_DONT_RESET=1 \
bash -c 'while true ; do date ; sleep 1 ; done'
However the return I get is:
libfaketime: Cannot get timestamp of file /tmp/my-demo-file.tmp as requested by % operator.
What did I do wrong?
It's hard to tell... it's either a filename (file does not exist) or permission issue (e.g., file created by root and not accessible by other users, eventually related to specific settings of your /tmp directory). Try FAKETIME_FOLLOW_TIME
with any file certainly available and accessible on the system (e.g., something like /bin/bash
), and if this still does not work, please try outside a container (e.g., on a VM or bare metal machine).
It was my mistake to ignore the above. Finally, thank you.
I'm working on an application related to time. In order to simulate a real environment, I need a virtual environment that is 1440 times faster than real time.
I was able to speed up the time of a container via libfaketime, but I need to run multiple time-accelerated containers on the same virtual machine and require their times to be synchronized.
Can libfaketime do it?