#!/bin/bash -eu # Docker for Mac Drift -- dfm-drift.txt (so named so that GitHub will attach it) # This shell script prints out timestamps from host Python and a # Docker container Python, merged onto its stdout. # See: https://github.com/docker/for-mac/issues/2076 # Requires local commands: docker and python3 # It will use the python and alpine images from DockerHub. # When you run this script, it first synchronizes the Docker clock # with the Mac host using alpine, as per the workaround in the issue # report. # # This script starts two processes, one on the host and one in a # Docker container. Each of these prints out a timestamp each second, # beginning shortly after the process began. Each process endeavors to # print the timestamp immediately after the Unix clock has rolled over # a second. The fractional part of each timestamp indicates the # inevitable, but irrelevant small delay between waking up and # sampling the clock. # # What am I looking for? # # In the beginning, the timestamps will come in interleaved pairs with # the same whole seconds, e.g.: # # $ bash dfm-drift.txt # # Docker for Mac drift demonstrator. # See: https://github.com/docker/for-mac/issues/2076 # # host : 1.005 # guest: 1.005 # host : 2.000 # guest: 2.002 # host : 3.001 # guest: 3.004 # host : 4.001 # guest: 4.002 # host : 5.001 # guest: 5.002 # # If there is significant drift accumulating between the clocks, # eventually the same whole second timestamps will not come in pairs, # e.g. # host : 3597.002 # guest: 3599.006 # host : 3598.005 # guest: 3600.003 # host : 3599.005 # guest: 3601.005 # host : 3600.001 # guest: 3602.006 # host : 3601.003 # guest: 3603.002 # # In the above you can see that guest printed out 3600.003 more than a # second before host printed out 3600.001 # Python script that prints out a timestamp shortly after each second # rollover of the system clock (of the system it's running on). # # Takes arguments: # label -- prefix on its emitted timestamps # start -- Unix timestamp at which to start emitting # Prints to stderr to avoid buffering issues... timestamp=" exec(''' import sys, time label, start = sys.argv[1:] sec = start = int(start) while True: sec += 1 delay = sec - time.time() # only sleep and timestamp on more than 1/8 second delay; could be any small fraction of a second if delay <= 0.125: # this updates sec if time.time() is ahead of sec (e.g. after computer wakes from sleep) sec += int(-delay) continue time.sleep(delay) print('{}: {:.3f}'.format(label, time.time()-start), file=sys.stderr) ''') " echo echo Docker for Mac drift demonstrator. echo See: https://github.com/docker/for-mac/issues/2076 # Start synchronized, also conceivably warms up Docker caches... # See: https://github.com/docker/for-mac/issues/2076 docker run --rm --privileged alpine hwclock -s # A delay so that both processes can get running before they start # timestamping. Make as large as needed for both timestamp streams to # start at (roughly) the same moment. DELAY=4 # Unix clock second upon which to start, warms up Python START=$(python3 -c "import time;print(int(time.time())+${DELAY})") echo # Host timestamping program, backgrounded, kill upon interupt python3 -c "$timestamp" "host " $START 2>&1 & pid=$! trap "echo -e '\nStopping PID $pid\n' ; kill -SIGTERM $pid" SIGINT SIGTERM ERR # Container timestamping program. You can use any image with a python3 in it docker run -it --rm python python3 -c "$timestamp" "guest" $START