dockersupervisord

Shutting down Docker containers via supervisor


I'm unable to shut down Docker containers that were launched by supervisor through supervisorctl stop all. Even through supervisorctl status shows the containers are down, docker ps and ps indicate that they are in fact still running.

Consulting the supervisor documentation on the action for supervisorctl stop <name> reveals that SIGTERM is sent to processes followed by SIGKILL if still running after some grace period. I tried to do this manually and found that

The questions is: How do I properly shut down a Docker container by supervisor?


Here's the result of my experiments simulating supervisor:

Starting position: foo-1 and bar-1 are running (I left the GCE containers in, in case they make a difference). ps aux and docker ps are in sync.

me@devenv:~$ sudo docker ps
CONTAINER ID        IMAGE                   COMMAND                CREATED             STATUS              PORTS                    NAMES
5ba70bf8937f        me/app:foo              "/bin/sh -c 'supervi   5 minutes ago       Up 5 minutes                                 foo-1
e1a684bcfceb        me/app:bar              "/bin/sh -c 'supervi   5 minutes ago       Up 5 minutes                                 bar-1
fce5db0517df        google/cadvisor:0.8.0   "/usr/bin/cadvisor"    35 minutes ago      Up 35 minutes                                bbbb 
db677eed47ef        kubernetes/pause:go     "/pause"               35 minutes ago      Up 35 minutes       0.0.0.0:4194->8080/tcp   aaaa

me@devenv:~$ ps aux | grep "docker run"
root     23358  0.0  0.1 124092 11856 pts/0    Sl   02:05   0:00 docker run --rm --name foo-1 ... -i me/app:foo
root     23365  0.0  0.1 124092 11928 pts/0    Sl   02:05   0:00 docker run --rm --name bar-1 ... -i me/app:bar

Simulate supervisorctl stop foo-1 by sending SIGTERM to the process. Result: process still active.

me@devenv:~$ sudo kill -SIGTERM 23358

... <waiting> ...

me@devenv:~$ ps aux | grep "docker run"
root     23358  0.0  0.1 124092 11856 pts/0    Sl   02:05   0:00 docker run --rm --name foo-1 ... -i me/app:foo
root     23365  0.0  0.1 124092 11928 pts/0    Sl   02:05   0:00 docker run --rm --name bar-1 ... -i me/app:bar

me@devenv:~$ sudo docker ps
CONTAINER ID        IMAGE                   COMMAND                CREATED             STATUS              PORTS                    NAMES
5ba70bf8937f        me/app:foo              "/bin/sh -c 'supervi   6 minutes ago       Up 6 minutes                                 foo-1
e1a684bcfceb        me/app:bar              "/bin/sh -c 'supervi   6 minutes ago       Up 6 minutes                                 bar-1
fce5db0517df        google/cadvisor:0.8.0   "/usr/bin/cadvisor"    36 minutes ago      Up 36 minutes                                bbbb 
db677eed47ef        kubernetes/pause:go     "/pause"               36 minutes ago      Up 36 minutes       0.0.0.0:4194->8080/tcp   aaaa

Next thing supervisor would do is issuing SIGKILL. Result: Process is killed (ps aux), but still showing as running docker process (docker ps).

me@devenv:~$ sudo kill -SIGKILL 23358
me@devenv:~$ ps aux | grep "docker run"
root     23365  0.0  0.1 124092 11928 pts/0    Sl   02:05   0:00 docker run --rm --name bar-1 ... -i me/app:bar

me@devenv:~$ sudo docker ps
CONTAINER ID        IMAGE                   COMMAND                CREATED             STATUS              PORTS                    NAMES
5ba70bf8937f        me/app:foo              "/bin/sh -c 'supervi   19 minutes ago      Up 19 minutes                                foo-1
e1a684bcfceb        me/app:bar              "/bin/sh -c 'supervi   19 minutes ago      Up 19 minutes                                bar-1
fce5db0517df        google/cadvisor:0.8.0   "/usr/bin/cadvisor"    49 minutes ago      Up 49 minutes                                bbbb 
db677eed47ef        kubernetes/pause:go     "/pause"               49 minutes ago      Up 49 minutes       0.0.0.0:4194->8080/tcp   aaaa

Supervisor was shut down during the above experiment (to avoid its autostart behaviour to interfere). The result for explicitly sending SIGKILL to the process could not be achieved by supervisor; the process was still alive (even though the supervisor logs state otherwise). docker stop <container_id> did however stop the container.

Update

Inside the Docker containers is also a supervisord process running which manages certain processes. Perhaps the problem is that signals aren't propagated and thus it won't shut down...

Update 2

I narrowed down the problem. I was able to directly start a containers process from the Dockerfile instead of going through starting supervisord and it makes a difference. I'm able to control this container through supervisor (the one outside of the docker containers which controls the containers).

Update 3

Setting stopasgroup=true as suggested here does not change anything for me.

Update 4

I was able to solve one of the problems: supervisorctl not being able to shut down the process. The problem was that I was launching the docker containers in the supervisor configuration file with command=sudo docker run... which created a sudo docker run... and a docker run... process. supervisorctl stop... just terminated the sudo docker run... process while the actual docker process was still running. When I omit the sudo command, only 1 process per supervisor program is started and supervisorctl stop terminates the process.

One problem remains which is that docker ps shows the container is still running while ps aux doesn't. Oddly enough the containers still seem to be active as they respond to requests. A quick look at the process list confirms that all processes spawned by the docker container are still active, yet the docker run... process is missing from the process list.

Update 5

Sending SIGTERM, SIGHUP or SIGQUIT to the docker run process doesn't seem to do anything to the process. Only SIGKILL terminates the docker process properly. Supervisor gets updated properly, but docker ps still shows the docker process running.


Solution

  • I think I found the problem. I didn't realise it, but there's multiple ways of starting a program when a docker container is fired up.

    Apparently CMD myexec param1 param2 starts a shell which in turn starts myexec (in fact these two processes are visible in the container with /bin/sh -c myexec... at PID 1. A better way is to start the program directly (in my case supervisord).

    On the other hand, CMD ["/usr/bin/python", "/usr/local/bin/supervisord", "-c", "/root/supervisord.conf", "--nodaemon"] worked fine. I am now able to start and stop the docker container through supervisor.

    Here's the relevant section in the docker docs:

    The CMD instruction has three forms:

    CMD ["executable","param1","param2"] (exec form, this is the preferred form)

    CMD ["param1","param2"] (as default parameters to ENTRYPOINT)

    CMD command param1 param2 (shell form)

    Update

    Example supervisor file (inside Docker container):

    [program:app]
    command=python run_web_server.py
    stdout_logfile=/var/log/app/app.log
    directory=/opt/app
    autostart=true
    autorestart=false
    stopsignal=INT
    redirect_stderr=true
    startretries=0
    stopasgroup=true
    killasgroup=true
    
    
    [unix_http_server]
    file=/var/run/supervisor.sock
    chmod=0700
    
    [supervisord]
    logfile=/var/log/supervisor/supervisord.log
    pidfile=/var/run/supervisord.pid
    childlogdir=/var/log/supervisor
    
    [rpcinterface:supervisor]
    supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
    
    [supervisorctl]
    serverurl=unix:///var/run/supervisor.sock
    

    The mako template to generate the Docker (outside) supervisor file:

    [program:container]
    command=docker run --rm --name ${name} \
    % if container_links is not UNDEFINED:
    % for host in container_hosts:
    --add-host ${host['name']}:${host['ip']} \
    % endfor
    % endif
    % if container_links is not UNDEFINED:
    % for link in container_links:
    --link ${link}:${link} \
    % endfor
    % endif
    % if port_mappings is not UNDEFINED:
    % for ext in port_mappings:
    -p ${ext}:${port_mappings[ext]} \
    % endfor
    % endif
    -e "INSTANCE_NAME=${name}" \
    -e "TZ=${timezone}" \
    % if environ is not UNDEFINED:
    % for k in environ:
    -e "${k}=${environ[k]}" \
    % endfor
    % endif
    -v ${deployment_dir}/tmp:${deployment_dir}/app/tmp \
    ... more -v
    -i foo/app-${version}:${type}
    stdout_logfile=${deployment_dir}/log/${name}.log
    redirect_stderr=true
    autostart=false
    autorestart=false
    % if priority is not UNDEFINED:
    priority=${priority}
    % endif
    startretries=0
    # stopasgroup=true
    # killasgroup=true