Graceful shutdown on Docker container
The ocserv
running inside a Docker container does not gracefully shut down when receiving a SIGTERM
signal from Docker.
The ocserv
process running as PID 1 in the container would receive the SIGTERM
signal from either docker stop ocserv
or docker kill --signal SIGTERM ocserv
commands. However, the ocserv
's main process does not propagate the received signal to the worker processes and causes the container to be terminated after the container graceful timeout. (or if the timeout is long, ocserv
kills the workers itself)
For example, if the container is running and the graceful timeout value is 10 seconds (default value), when docker stop ocserv
is executed, the container will terminate ungracefully in ~8 seconds:
ocserv-1 | note: vhost:default: setting 'plain' as primary authentication method
ocserv-1 | note: setting 'file' as supplemental config option
ocserv-1 | listening (TCP) on 0.0.0.0:443...
ocserv-1 | listening (TCP) on [::]:443...
ocserv-1 | listening (UDP) on 0.0.0.0:8443...
ocserv-1 | listening (UDP) on [::]:8443...
ocserv-1 | ocserv[1]: main: Starting 1 instances of ocserv-sm
ocserv-1 | ocserv[1]: main: created sec-mod socket file (/tmp/ocserv/ocserv.sock.c36c5d67.0)
ocserv-1 | ocserv[1]: main: initializing control unix socket: /tmp/ocserv/occtl.sock
ocserv-1 | ocserv[1]: main: initialized ocserv 1.2.2
ocserv-1 | ocserv[86]: sec-mod: reading supplemental config from files
ocserv-1 | ocserv[86]: sec-mod: loaded 1 keys
ocserv-1 | ocserv[86]: sec-mod: sec-mod initialized (socket: /tmp/ocserv/ocserv.sock.c36c5d67.0)
ocserv-1 | ocserv[1]: main: termination request received; waiting for sessions to die
ocserv-1 | ocserv[1]: main: not all sessions died; forcing kill
ocserv-1 exited with code 0
However, if the signal send to the ocserv
's process group by docker exec ocserv kill -TERM -1
command, the container gracefully terminates immediately:
ocserv-1 | note: vhost:default: setting 'plain' as primary authentication method
ocserv-1 | note: setting 'file' as supplemental config option
ocserv-1 | listening (TCP) on 0.0.0.0:443...
ocserv-1 | listening (TCP) on [::]:443...
ocserv-1 | listening (UDP) on 0.0.0.0:8443...
ocserv-1 | listening (UDP) on [::]:8443...
ocserv-1 | ocserv[1]: main: Starting 1 instances of ocserv-sm
ocserv-1 | ocserv[1]: main: created sec-mod socket file (/tmp/ocserv/ocserv.sock.d0e7d091.0)
ocserv-1 | ocserv[1]: main: initializing control unix socket: /tmp/ocserv/occtl.sock
ocserv-1 | ocserv[1]: main: initialized ocserv 1.2.2
ocserv-1 | ocserv[85]: sec-mod: reading supplemental config from files
ocserv-1 | ocserv[85]: sec-mod: loaded 1 keys
ocserv-1 | ocserv[85]: sec-mod: sec-mod initialized (socket: /tmp/ocserv/ocserv.sock.d0e7d091.0)
ocserv-1 | ocserv[1]: main: main-sec-mod-cmd.c:107: command socket for sec-mod closed
ocserv-1 | ocserv[1]: main: main.c:1228: error in command from sec-mod
ocserv-1 | ocserv[1]: main: termination request received; waiting for sessions to die
ocserv-1 exited with code 0
It's possible to manually handle the signal propagation to the worker processes with the CMD
instruction in shell form in the DOCKERFILE
:
CMD trap 'kill -TERM -$(< /tmp/ocserv/ocserv.pid)' TERM; \
ocserv --foreground --config /tmp/ocserv/ocserv.conf & \
pid=$!; \
while kill -0 $pid >/dev/null 2>&1; do \
wait $pid; \
done
But, there is no way to gracefully shut down the process with exec form:
CMD ["ocserv", "--foreground", "--config", "/tmp/ocserv/ocserv.conf"]