Search code examples
pythondockerflaskdockerpydocker-in-docker

Run a docker container from an existing container using docker-py


I have a Docker container which runs a Flask application. When Flask receives and http request, I would like to trigger the execution of a new ephemeral Docker container which shutdowns once it completes what it has to do.

I have read Docker-in-Docker should be avoided so this new container should be run as a sibling container on my host and not within the Flask container.

What would be the solution to do this with docker-py?


Solution

  • Answering my own question. Here is a complete setup which works. In one folder, create the following files:

    • requirements.txt
    • Dockerfile
    • docker-compose.yml
    • api.py

    requirements.txt

    docker==3.5.0
    flask==1.0.2
    

    Dockerfile

    FROM python:3.7-alpine3.7
    
    
    # Project files
    ARG PROJECT_DIR=/srv/api
    RUN mkdir -p $PROJECT_DIR
    WORKDIR $PROJECT_DIR
    COPY requirements.txt ./
    
    # Install Python dependencies
    RUN pip install --upgrade pip
    RUN pip install -r requirements.txt
    

    docker-compose.yml

    Make sure to mount docker.sock in volumes as mentioned in the previous answer above.

    version: '3'
    services:
      api:
        container_name: test
        restart: always
        image: test
        build:
          context: ./
        volumes:
          - ./:/srv/api/
          - /var/run/docker.sock:/var/run/docker.sock
        environment:
          FLASK_APP: api.py
        command: ["flask", "run", "--host=0.0.0.0"]
        ports:
          - 5000:5000
    

    api.py

    from flask import Flask
    import docker
    app = Flask(__name__)
    
    
    @app.route("/")
    def hello():
        client = docker.from_env()
        client.containers.run('alpine', 'echo hello world', detach=True, remove=True)
        return "Hello World!"
    

    Then open your browser and navigate to http://0.0.0.0:5000/

    It will trigger the execution of the alpine container. If you don't already have the alpine image, it will take a bit of time the first time because Docker will automatically download the image.

    The arguments detach=True allows to execute the container asynchronously so that Flask does not wait for the end of the process before returning its response.

    The argument remove=True indicates Docker to remove the container once its execution is completed.