Running NTP in a Container
Someone asked on IRC about running ntpd in a container on Atomic,
so I’ve put together a small example. We’ll start with a very simple
Dockerfile
:
FROM alpine
RUN apk update
RUN apk add openntpd
ENTRYPOINT ["ntpd"]
I’m using the alpine
image as my starting point because it’s very
small, which makes this whole process go a little faster. I’m
installing the openntpd package, which provides the ntpd
binary.
By setting an ENTRYPOINT
here, the ntpd
binary will be started by
default, and any arguments passed to docker run
after the image name
will be passed to ntpd
.
We need to first build the image:
# docker build -t larsks/ntpd .
And then we can try to run it:
# docker run larsks/ntpd -h
ntpd: unrecognized option: h
usage: ntpd [-dnSsv] [-f file] [-p file]
That confirms that we can run the command. Now we need to provide it
with a configuration file. I looked briefly at the ntpd.conf man
page, and I think we can get away with just providing the
name of an ntp server. I created /etc/ntpd.conf
on my atomic host
with the following content:
servers pool.ntp.org
And then I tried running the container like this:
docker run -v /etc/ntpd.conf:/etc/ntpd.conf larsks/ntpd -d -f /etc/ntpd.conf
The -v
in the above command line makes /etc/ntpd.conf
on the host
available as /etc/ntpd.conf
inside the container. This gets me:
ntpd: can't set priority: Permission denied
reset adjtime failed: Operation not permitted
adjtimex (2) failed: Operation not permitted
adjtimex adjusted frequency by 0.000000ppm
fatal: privsep dir /var/empty could not be opened: No such file or directory
Lost child: child exited
dispatch_imsg in main: pipe closed
Terminating
The first few errors (“Permission denied”) mean that we need to pass
--privileged
on the docker run
command line. Normally, Docker
runs containers with limited capabilities, but because an ntp service
needs to be able to set the time in the kernel it won’t run in that
limited environment.
The “fatal: privsep dir /var/empty could not be opened” suggests we
need an empty directory at /var/empty
. With the above two facts in
mind, I tried:
docker run --privileged -v /var/empty \
-v /etc/ntpd.conf:/etc/ntpd.conf larsks/ntpd -d -f /etc/ntpd.conf -s
The -s
on the end means “Try to set the time immediately at
startup.” This results in:
adjtimex adjusted frequency by 0.000000ppm
ntp engine ready
reply from 104.131.53.252: offset -3.543963 delay 0.018517, next query 5s
set local clock to Fri Oct 9 18:03:41 UTC 2015 (offset -3.543963s)
reply from 198.23.200.19: negative delay -7.039390s, next query 3190s
reply from 107.170.224.8: negative delay -6.983865s, next query 3139s
reply from 209.118.204.201: negative delay -6.982698s, next query 3216s
reply from 104.131.53.252: offset 3.523820 delay 0.018231, next query 8s
So that seems to work correctly. To make this service persistent, I
can add -d
to start the container in the background, and
--restart=always
to make Docker responsible for restarting it if it
fails:
docker run --privileged -v /var/empty \
--restart=always -d \
-v /etc/ntpd.conf:/etc/ntpd.conf larsks/ntpd -d -f /etc/ntpd.conf -s
And my Atomic host has an ntp service keeping the time in sync.