Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to restart Python Docker Container from inside

My Objective: I want to be able to restart a container based on the official Python Image using some command inside the container.

My system: I have a own Docker image based on the official python image which look like this:

FROM python:3.6.15-buster
WORKDIR /webserver
COPY requirements.txt /webserver
RUN /usr/local/bin/python -m pip install --upgrade pip
RUN pip3 install -r requirements.txt --no-binary :all:
COPY . /webserver
ENTRYPOINT ["./start.sh"]

As you can see, the image does not execute a single python file but it executes a script called start.sh, which looks like this:

#!/bin/bash

echo "Starting"
echo "Env: $ENTORNO"

exec python3 "$PATH_ENTORNO""Script1.py" &
exec python3 "$PATH_ENTORNO""Script2.py" &
exec python3 "$PATH_ENTORNO""Script3.py" &

All of this works perfectly, but, I want that if, for example, script 3 fails, the entire container based on this image get restarted.

My approach: I had two ideas about this problem. First, try to execute a reboot command in the python3 script, something like this:

from subprocess import call

[...]

call(["reboot"])

This does not work inside the Python Debian image, because of error:

reboot: command not found

The other approach was to mount the docker.sock inside the container, but the error this time is:

root@MachineName:/var/run# /var/run/docker.sock docker ps
bash: /var/run/docker.sock: Permission denied

I dont know if I'm doing right these two approach, or if anyone has any idea about this but any help will be very appreciated.

like image 711
Javi Martínez Avatar asked Nov 19 '25 16:11

Javi Martínez


2 Answers

Well, in the end the solution was much simpler than I expected.

I started from the base where I mount the docker socket inside the container (I know that this practice is not recommended, but in my case, I know that it does not pose security problems), using the command in docker-compose:

volumes:
  - /var/run/docker.sock:/var/run/docker.sock

Then, it was as simple as using the Docker library for python, which gives a complete SDK through that socket that allowed me to restart the container inside the python script in an ultra-simple way.

import docker

[...]

docker_client = docker.DockerClient(base_url='unix://var/run/docker.sock')
docker_client.containers.get("container_name").restart()
like image 121
Javi Martínez Avatar answered Nov 21 '25 04:11

Javi Martínez


Update

After thinking about it, I realised you could send some signal to the PID 1 (your entrypoint), trap it and use a handler to exit with an appropriate code so that docker will reschedule it.

Here's an MRE:

Dockerfile

FROM python:3.9
WORKDIR /app
COPY ./ /app
ENTRYPOINT ["./start.sh"]

start.sh

#!/usr/bin/env bash
python script.py &

# This traps user defined signal and kills the last command
# (`tail -f /dev/null`) before exiting with code 1.
trap 'kill ${!}; echo "Killed by backgrounded process"; exit 1' USR1

# Launches `tail` in the background and sets this program to wait
# for it to finish, so that it does not block execution
tail -f /dev/null & wait $!

script.py

import os
import signal

# Process 1 will be your entrypoint if you declared it in `exec-form`*
print("Sending signal to stop container")
os.kill(1, signal.SIGUSR1)

*exec form

Testing it

> docker build . -t test
> docker run test
Sending signal to stop container
Killed by backgrounded process
> docker inspect $(docker container ls -n 1 -q) --format='{{.State.ExitCode}}'
1

Original post

I think the safest bet would be to instruct docker to restart your container when there's some failure. Then you'd only have to exit your program with a non-zero code (i.e: run exit 1 from your start.sh) and docker will restart it from scratch.

Option 1: docker run --restart

Related documentation

docker run --restart on-failure <image>

Option 2: Using docker-compose

Version 3

In your docker-compose.yml you can set the restart_policy directive to the service you're interested on restarting. i.e:

version: "3"
services:
  app:
    ...
    restart_policy:
      condition: on-failure
    ...

Version 2

Before version 3, the same policy could be applied with the restart directive, which allows for less configuration.

version: "2"
services:
  app:
    ...
    restart: "on-failure"
    ...
like image 31
EDG956 Avatar answered Nov 21 '25 04:11

EDG956