Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document Steps that have been taken to configure the "Production" environment #47

Open
bhgrant8 opened this issue Apr 27, 2018 · 1 comment

Comments

@bhgrant8
Copy link
Member

bhgrant8 commented Apr 27, 2018

So as a starting point I am copying over and updating what was configured for the current state of the "production" environment.

Original: https://github.com/hackoregon/transportation-system-backend#run-in-staging-environment

Run in a Production Environment

While developing the API, using the built in dev server is useful as it allows for live reloading, and debug messages. When running in a production environment, this is a security risk, and not efficient.

As such a production environment has been configured to be run when using the build.sh -p and start.sh -p commands.

A quick summary of python packages and why they are in there:

Gunicorn - A "green" HTTP server - http://gunicorn.org/
Gevent - Asynchronous workers - http://www.gevent.org/
Pyscopgreen - A "green" version of the psycop database connector - https://pypi.org/project/psycogreen/
django_db_geventpool - DB pool using gevent for PostgreSQL DB. - https://github.com/jneight/django-db-geventpool/tree/master/django_db_geventpool
WhiteNoise - allows for hosting of static files by gunicorn in a prod environment vs. integrating a webserver - http://whitenoise.evans.io/en/stable/

This provides a fairly stable, often used stack for django deployment, basically staying within python for the bulk of our tools, and cutting out the need for a separate server for the swagger/browsable front end

To start it:

  1. Configure the PRODUCTION_ variables in the .env file (you will want to work with devops team to get these. They will need to match the production or staging server). The idea here is that you will now be connecting to a live database environment an AWS or otherwise externally hosted and not running the local database container. (see below for additional details)

  2. Run the build.sh script to build the project for the staging environment: $ ./bin/build.sh -p

  3. Start the project using the staging flag: $ ./bin/start.sh -p

Open your browser and you should be able to access the Django Restframework browserable front end at: http://localhost:8000/api and Swagger at http://localhost:8000/schema

Try going to an nonexistent page and you should see a generic 404 Not found page instead of the Django debug screen.

What was configured:
So this is what has been configured between various files:

  1. Add gunicorn, gevent, and whitenoise, django-db-geventpool, to requirements/production.txt
  2. Set the debug variable to false in the the production-docker-compose file
  3. make any other changes necessary to config vars, ie: database settings
  4. create a prod entrypoint file that runs the gunicorn start command instead of the ./manage.py runserver. Here is an example:
    gunicorn crash_data_api.wsgi -c gunicorn_config.py
  5. create the gunicorn_config.py file to hold gunicorn config, including using gevent worker_class.

Currently we are patching psycopg2 and django with gevent/psycogreen in the post_fork worker. Also using 4 workers (which maybe too many?):

try:
    # fail 'successfully' if either of these modules aren't installed
    from gevent import monkey
    from psycogreen.gevent import patch_psycopg


    # setting this inside the 'try' ensures that we only
    # activate the gevent worker pool if we have gevent installed
    worker_class = 'gevent'
    workers = 4
    # this ensures forked processes are patched with gevent/gevent-psycopg2
    def do_post_fork(server, worker):
        monkey.patch_all()
        patch_psycopg()

        # you should see this text in your gunicorn logs if it was successful
        worker.log.info("Made Psycopg2 Green")

    post_fork = do_post_fork
except ImportError:
    pass
  1. ChangeD the settings.py file to use django_db_geventpool when in production mode. You will add this after the current database settings.:
if os.environ.get('DEBUG') == "False":

    DATABASES = {
        'default': {
            'ENGINE': 'django_db_geventpool.backends.postgis',
            'PASSWORD': os.environ.get('POSTGRES_PASSWORD'),
            'NAME': os.environ.get('POSTGRES_NAME'),
            'USER': os.environ.get('POSTGRES_USER'),
            'HOST': os.environ.get('POSTGRES_HOST'),
            'PORT': os.environ.get('POSTGRES_PORT'),
            'CONN_MAX_AGE': 0,
            'OPTIONS': {
                'MAX_CONNS': 20
            }
        }
    }
  1. create a staging/production docker_compose file, using correct env vars, entrypoint command, and removing the database container:
version: '3.4'
services:
  api_production:
    build:
      context: .
      dockerfile: DOCKERFILE.api.production
    image: api_production
    command: ./bin/production-docker-entrypoint.sh
    volumes:
      - .:/code
    ports:
      - "8000:8000"
    environment:
      - PROJECT_NAME
      - DEBUG=False
      - POSTGRES_USER=${PRODUCTION_POSTGRES_USER}
      - POSTGRES_NAME=${PRODUCTION_POSTGRES_NAME}
      - POSTGRES_HOST=${PRODUCTION_POSTGRES_HOST}
      - POSTGRES_PORT=${PRODUCTION_POSTGRES_PORT}
      - POSTGRES_PASSWORD=${PRODUCTION_POSTGRES_PASSWORD}
      - DJANGO_SECRET_KEY=${PRODUCTION_DJANGO_SECRET_KEY}
  1. Make changes to settings.py to check the debug variable and use :
    Change DEBUG line:
DEBUG = os.environ.get('DEBUG') == "True" - handles os variables being treated as strings

ADD to MIDDLEWARE right after SECURITY:

'whitenoise.middleware.WhiteNoiseMiddleware',

ADD these just before the STATIC_URL so staticfiles are handled correctly and are compressed:

STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'

STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
@bhgrant8
Copy link
Member Author

one warning that has come up with this current setup is hackoregon/civic-devops#13

unknown impact, whether is just noise or needs to be addressed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant