Pages

Sunday, June 30, 2019

Docker - Trapping Signals inside a Container

A signal is a message to a process from the kernel to notify that some condition has occurred. When a signal is issued to a process, the process is interrupted and a signal handler is executed. If there is no signal handler, the default handler is called instead.

A Docker container will also receive signals. In docker , we have two commands that we can use to stop it, docker stop and docker kill. When we do a docker stop to a running container, it sends a SIGTERM signal to the main process running inside the container ( pid 1 process ) and after a grace period it issues a SIGKILL to terminate the process. At this moment the process can ignore the signal or let a default action occur or provide a callback function to respond to the signal.

Lets run a container with the sleep command and try to pass a signal to the container as,
jagadishm@[/Volumes/Work/build/trap]: docker run -it centos sleep 100
^C^C

In this case, the container will not exit until the sleep 100 is complete which means the signal that we passed is not received by the container process.

Now let's write a simple bash trap script as below,

jagadishm@[/Volumes/Work/build/trap]: cat signal.py 
import signal
import sys

def signal_handler(sig, frame):
        print('You pressed Ctrl+C!')
        sys.exit(0)

signal.signal(signal.SIGINT, signal_handler)

while True:
    print('Press Ctrl+C')
    signal.pause()
    time.sleep(10) #make function to sleep for 10 seconds

This script goes to infinite sleep mode but it has the trap command which handles the signals that we pass. Lets create a docker image trap with the below contents,

jagadishm@[/Volumes/Work/build/trap]: cat Dockerfile 
FROM centos
COPY signal.py /
WORKDIR /
ENTRYPOINT ["python”,”signal.py”]

Now when we run the container and try to send the signal “ctrl + c “ as below,
jagadishm@[/Volumes/Work/build/trap]: python signal.py 
Press Ctrl+C
^CYou pressed Ctrl+C!

We can see that the signal is sent to the process and even handled by the trap expression by executing the commands defined. One the above container is up and running ,from another terminal window run the “docker kill --signal

Signal by Docker - Similarly Docker allows to send signals to the process running inside them. Signals can be sent by stop,rm and kill commands in docker.

By Stop - Docker stop command allows to send a stop signal to the process running inside it. When we issue a stop command, the process will be asked nicely to stop and if it doesn’t respond in 10 seconds it will forcibly kill it. The docker stop command attempts to stop a running container first by sending a SIGTERM signal to the root process ( ie. process running with pid 1). If the process hasn't exited within the timeout period a SIGKILL signal will be sent.

The only thing that we can control with the stop command is the time to wait until the docker daemon will wait before sending a SIGKILL. A stop command with the time can be defined as “docker stop ----time=30 <Container ID>

By Kill - Similarly we can send the signal by using the docker kill command. The main process inside the container is sent a SIGKILL signal ( default ) , or a signal that is specified with the --signal option. 

The --stop-signal flag sets the system call signal that will be sent to the container to exit. The signal can be a valid number that matches a position in the kernel syscall table for instance 9, or by a signal name SIGNAME for instance SIGINT.

From the first terminal run the command “docker run -it trap /bin/bash” as below
jagadishm@[/Volumes/Work/build/trap]: docker run -it trap /bin/bash
Press Ctrl+C

Now from another terminal window, run the command “docker kill --signal="SIGINT" <Container ID>” . once we pass the SIGINT signal to the container, we can see the below output,
jagadishm@[/Volumes/Work/build/trap]: docker run -it trap /bin/bash
Press Ctrl+C
You pressed Ctrl+C!

By RM - docker rm command is used to remove already stopped container but when used in conjunction with --force flag a SIGKILL is passed to the pid 1 inside the container. It can be used as “docker rm --force <Container ID>

Hope this helps in understanding how to pass signals to a container and define a shutdown behavior for the process running inside the container.

No comments :

Post a Comment