Communicating with Docker Containers via Linux Signals and Python
Normally if we need to refresh a config in a Container we will spawn a new one, or we will access with sudo docker exec -it /bin/sh mycontainer
for instance and force a reload, or we will have to restart the Container.
What if we want to be able to reload the config at any moment without restarting the process, or to trigger a process in our Container (like a dump or a flush) in another way than implementing an API?.
An unexplored way, for many, to communicate with your Container’s main process is to send Signals.
So basically I will show you how you can trap Signals within a Python process which is the main process for your Docker Container, and send them from your Hypervisor with the command:
sudo docker kill --signal=SIGUSR1
I choose to use SIGUSR1 as it is reserved for user defined Signals.
You can clone the project or get the source code from:
https://gitlab.com/carles.mateo/docker-signal
The Dockerfile
FROM ubuntu:20.04 MAINTAINER Carles Mateo RUN apt update && apt install -y python3 python3-pip vim less && apt-get clean # This will make sure printing in the Screen when running in dettached mode ENV PYTHONUNBUFFERED=1 ENV DOCKERSIGNAL /var/dockersignal RUN mkdir -p $DOCKERSIGNAL COPY *.py $DOCKERSIGNAL WORKDIR $DOCKERSIGNAL # Again to enforce printing to the Screen when running dettached CMD ["python3", "-u", "/var/dockersignal/dockersignal.py"]
The dockersignal.py file
# By Carles Mateo https://blog.carlesmateo.com import signal import time def handler(signum, frame): print('Signal handler called with signal', signum) if signum == 10: # 10 is the equivalent to SIGUSR1 for most x86/ARM (not for Alpha/Sparc, MIPS, PARISC) print("Simulated action: Reload config") if __name__ == "__main__": print("Waiting for a Signal") # Listed for this signal, so can listen for more signal.signal(signal.SIGUSR1, handler) while True: # Do Whatever time.sleep(1)
A shell file to build and run the Container like a pro
#!/bin/bash DOCKER_CONTAINER_NAME="docker-signal" DOCKER_IMAGE_NAME="docker-signal" printf "Removing old Container %s\n" "${DOCKER_CONTAINER_NAME}" sudo docker rm "${DOCKER_IMAGE_NAME}" printf "Removing old Image %s\n" "${DOCKER_IMAGE_NAME}" sudo docker image rm "${DOCKER_IMAGE_NAME}" echo "Creating Docker Image" sudo docker build -t ${DOCKER_IMAGE_NAME} . --no-cache retVal=$? if [ $retVal -ne 0 ]; then printf "Error. Exit code %s\n" ${retVal} exit fi echo "Running Docker Container ${DOCKER_CONTAINER_NAME} based in image ${DOCKER_IMAGE_NAME}" sudo docker run --cpus="1.0" --name ${DOCKER_CONTAINER_NAME} ${DOCKER_IMAGE_NAME}
See it in action
References
https://docs.docker.com/engine/reference/commandline/kill/
https://man7.org/linux/man-pages/man7/signal.7.html
https://docs.python.org/3/library/signal.html
If you want you can buy my Docker Combat Guide book.
Rules for writing a Comment