/docker-flask

template project for dockerized flask apps to run in cloud environment

Primary LanguagePython

Docker Flask Example

Demo flask application connected to mongodb with kubernetes compliant health probe endpoints.

version: "3.9"

volumes: 
  prod_data:

services:
  flask:
    image: flaskapp:0.1.8-prod
    build:
      context: ./
      dockerfile: Dockerfile
      args:
        build_date: '2021-04-25'
        version: 0.1.8-prod
    ports: [5000:5000]
    volumes: ["./log:/var/log"]
    environment: 
      MONGO_URI: mongodb://root:rootpassword@mongo/     # valid rfc connection string
      GUNICORN_CMD_ARGS: --capture-output               # see docs for all options
      LOG_LEVEL: error                                  # debug|info|warning|error|critical
      LOG_FORMAT: json                                  # json|text
      FILTER_PROBES: '1'                                # 0|1 - don't log requests to healthcheck endpoints with access logger

  mongo:
    image: mongo:latest
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: rootpassword
    volumes: [prod_data:/data/db]

Start the app

The below command will start the flask application and a mongodb in docker container.

docker-compose up

Once the container are running, the flask documentation can be downloaded as pdf

curl localhost:5000/pdf > flask.pdf

There is also an endpoint to accept message posts.

$ curl -i -H "Content-Type: application/json" -X POST -d '{"message":"hello, flask"}' http://localhost:5000/msg
HTTP/1.1 201 CREATED
Server: gunicorn
Date: Sat, 24 Apr 2021 15:08:22 GMT
Connection: keep-alive
Content-Type: application/json
Content-Length: 34

{"id":"60843466092d7c719ec5063b"}

A list of all messages can be retrieved on the msg endpoint.

$ curl localhost:5000/msg
[{"_id": {"$oid": "60843466092d7c719ec5063b"}, "message": "hello, flask"}]

Health Probe

As long as the app is running it will serve a 200 response on the /alive endpoint.

$ curl -i localhost:5000/alive
HTTP/1.1 200 OK
Server: gunicorn
Date: Mon, 26 Apr 2021 01:33:25 GMT
Connection: keep-alive
Content-Type: text/html; charset=utf-8
Content-Length: 0

As long as the database connection is working the app will return a 200 response on /ready.

$ curl -Is localhost:5000/ready | head -n 1
HTTP/1.0 200 OK

The behavior can be be tested by shutting down the database container. Once the database is down, the application with return a status 503 response.

$ docker-compose stop mongo
$ curl -Is localhost:5000/ready | head -n 1
HTTP/1.0 503 SERVICE UNAVAILABLE

Once the database is started up again the app will return again a 200.

$ docker-compose start mongo
$ curl -Is localhost:5000/ready | head -n 1
HTTP/1.0 200 OK

Environment

Logging

The format of the output is by default json to play nicely with modern tools. However it can be set to text via the environment variable LOG_FORMAT. Likewise the log level can be set via LOG_LEVEL.

LOG_LEVEL: info     # debug|info|warning|error|critical
LOG_FORMAT: json    # json|text

The application logs by default into to stdout and stderr. The output can also be directed to log files if needed. For this the gunicorns command line args or flags can be used.

volumes: ["./log:/var/log"]
environment: ["GUNICORN_CMD_ARGS=--capture-output"]

Database URI

The connection string is set via

MONGO_URI: mongodb://user:password@server/

Gunicorn

The GUNICORN_CMD_ARGS variable can be used as per documentation. This can be useful do do some tweaking ad hoc.

GUNICORN_CMD_ARGS: "--workers=6 --threads=4"

Proxy Fix

When running behind a proxy, a fix can be applied which reads information from X-Proxy-Headers and puts them accordingly in the WSGI request object. The parameters are passed to ProxyFix.

PROXY_FIX: x_for=1 x_host=1 x_port=1 x_prefix=1 x_proto=1

Development

The project is using pipenv to manage dependencies. It can be useful to set up the virtual environment locally.

pipenv shell
pipenv install --dev

Install the pre-commit hook

pre-commit install

Flake8 has been configured to accept a maximum line length of 119. When using VS Code, the following settings can be used in order to have alignment with the pre commit hook.

{
    "python.linting.enabled": true,
    "python.linting.pylintEnabled": false,
    "python.linting.flake8Enabled": true,
    "python.formatting.provider": "black"
}

A test environment with hot reload can be started, it will mount the app directory so code changes are picked up and the workers are restarted.

docker-compose -p dev -f dev-compose.yml up

Testing

For testing a compose file exists at the root directory. It is configured to connect to a silent test database.

The following command will start the app and a test database container and run the test suit. Afterwards the db container is shut down.

docker-compose -p test -f test-compose.yml up && \
  docker-compose -p test -f test-compose.yml down

Nginx

A nginx-compose exists that creates an additional nginx container to reverse proxy the requests to gunicorn over unix sockets in a shared volume.

This is because gunicorn inst a fully fletched webserver, is is recommend to use a reverse proxy. Nginx plays well with gunicorn.

docker-compose -p proxy -f nginx-compose.yml up --build

Makefile

For convenience the commands are available via make.

make
make proxy
make test
make dev