DEV Community

Cover image for Flask Deploy with Apache on CentOS - Minimal Setup
Sm0ke
Sm0ke

Posted on • Edited on • Originally published at app-generator.dev

Flask Deploy with Apache on CentOS - Minimal Setup

Hello Coders,

This article explains how to deploy a Flask application sandboxed with a virtualenv and served by Apache HTTP server using the mod_wsgi module.

For newcomers, Flask is a lightweight web application framework written in Python. Sometimes classified as a microframework, Flask provides a lightweight codebase that can be easily extended to become an API, a simple web app, or a complex eCommerce platform.

Thanks for reading! - Content provided by App Generator.


Dependencies

  • Apache / httpd (on CentOS) server
  • mod_wsgi
  • Flask web framework
  • Python3
  • Virtualenv

Install Apache server

$ sudo yum install httpd
$ 
$ # by default the server is down.
$ sudo systemctl start httpd
Enter fullscreen mode Exit fullscreen mode

Install mod_wsgi

$ sudo yum install mod_wsgi
$
$ # restart apache
$ sudo systemctl restart httpd
Enter fullscreen mode Exit fullscreen mode

Test if the mod_wsgi module is loaded

$ sudo httpd -M | grep wsgi
wsgi_module (shared) # <-- the OK response
Enter fullscreen mode Exit fullscreen mode

Install Virtualenv

Virtual environments will sandbox the app to run isolated from the global server environment

$ sudo pip install virtualenv
Enter fullscreen mode Exit fullscreen mode

Code the Flask App


We will use a simple Flask application that serves a simple Hello World message for all routes.
As mentioned before, this setup is executed on CentOs. The steps are:

Go to /var/www - the www_root of Apache server, and create the project directory.

$ cd /var/www
$ mkdir hitme
Enter fullscreen mode Exit fullscreen mode

To have a runnable Flask app, we need to create two files: run.py and app.py inside the app folder. The files are structured as below:

/var/www/hitme
          | - run.py
          | - app/__init__.py
Enter fullscreen mode Exit fullscreen mode

Where run.py is responsible to bootstrap the Flask app defined in the app directory.

app/init.py file contents


from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello world!"
Enter fullscreen mode Exit fullscreen mode

run.py file contents


import os
from app import app

#----------------------------------------
# launch
#----------------------------------------

if __name__ == "__main__":
    port = int(os.environ.get("PORT", 5000))
    app.run(host='0.0.0.0', port=port, debug=True)

Enter fullscreen mode Exit fullscreen mode

Test the Flask App


We have the test application, now let's start it to see something on the screen. First, we need to create and activate the virtual environment.

$ cd /var/www
$ virtualenv --python=python3 hitme # the venv is created inside the app folder
$ cd /var/www/hitme
$ source bin/activate
Enter fullscreen mode Exit fullscreen mode

At this point, we will run the next commands inside the VENV. Let's install Flask

$ pip install flask
Enter fullscreen mode Exit fullscreen mode

To run, a Flask application require FLASK_APP environment variable.

$ export FLASK_APP=run.py # please notice the name
$ flask run # start the app
$ # our app is running on port 5000
Enter fullscreen mode Exit fullscreen mode

Apache Configuration


To execute a Flask application under the Apache HTTP server we need to bridge our application to the Apache engine using the mod_wsgi module. For this we need to create a new file wsgi.py inside our project folder:

/var/www/hitme
          | - wsgi.py
          | - run.py
          | - app/__init__.py
Enter fullscreen mode Exit fullscreen mode

wsgi.py file contents

#!/usr/bin/env python

import sys
import site

site.addsitedir('/var/www/hitme/lib/python3.6/site-packages')

sys.path.insert(0, '/var/www/hitme')

from app import app as application
Enter fullscreen mode Exit fullscreen mode

The next step is to configure Apache to serve the app and use this wsgi loader. The following settings should be added to the httpd.conf. On CentOS the file location is /etc/httpd/conf/httpd.conf

<VirtualHost *:80>

     ServerName localhost

     WSGIDaemonProcess hitme user=apache group=apache threads=2

     WSGIScriptAlias / /var/www/hitme/wsgi.py

     <Directory /var/www/hitme>
         Require all granted
     </Directory>

</VirtualHost>
Enter fullscreen mode Exit fullscreen mode

Close and save the file and restart Apache to load the new settings.

$ # restart apache
$ sudo systemctl restart httpd
Enter fullscreen mode Exit fullscreen mode

Our Flask app should be served by the Apache HTTP server. We can test the deploy by using lynx command:

$ lynx localhost # lynx
Enter fullscreen mode Exit fullscreen mode

Flask App deployed on Apache


Thanks for reading! For more resources, feel free to access:

Latest comments (34)

Collapse
 
nb2831 profile image
nb2831

I am getting this random error with no idea how to debug this

Collapse
 
veeresh_babu_538d7dd32b5d profile image
Veeresh Babu

I tried the steps above, but stuck during "flask run" with below error, Can anyone please help / point out where I'm going wrong ?

  • Serving Flask app 'run.py' (lazy loading)
  • Environment: production WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
  • Debug mode: off Usage: flask run [OPTIONS] Try 'flask run --help' for help.

Error: While importing 'run', an ImportError was raised:

Traceback (most recent call last):
File "/var/www/hitme/lib/python3.6/site-packages/flask/cli.py", line 260, in locate_app
import(module_name)
File "/var/www/hitme/run.py", line 2, in
from app import app
ImportError: cannot import name 'app'

Collapse
 
veeresh_babu_538d7dd32b5d profile image
Veeresh Babu

I was able to work through this and resolve this by using

import web_tool
app = web_tool.app

Collapse
 
yashwantwaskel3 profile image
Yashwant Waskel

Hello sir, everything works fine as per the tutorial until 'lynx localhost' but when I try to open it in the web browser it just shows the directory and not the app. Please help me regarding.

Collapse
 
sm0ke profile image
Sm0ke

Hello,

Thank for stopping by! try to check the port 5000 and after double-check the apache configuration.

Not necessary to apply my suggestion, just configure Apache to act like a proxy for the Flask app.

Let me know your progress.
🚀🚀

Collapse
 
yashwantwaskel3 profile image
Yashwant Waskel • Edited

Hello sir, everything works fine as per the tutorial until 'lynx localhost', but when I am opening it in web browser it just shows the directory and not the app. Please help me out regarding.

Collapse
 
dlink profile image
David Link

Hi Sm0ke, Thank you for this great article. It is very helpful and well written.

I noticed what I believe is one small typo. Where it says "app/init.py file contents" I believe it should say "app/init.py file contents", and that is probably a Markdown issue.

Were you able to resolve @williamium3000 's issues regarding setting the python interpreter? I'm trying to get this to work with pyenv (pyenv install 3.9.5), but can't get mod_wsgi to see my locally installed interpreter in my venv, even if I put it in the she-bang

#!/data/apps/hitme/.venv/bin/python

import os
import sys

def application(environ, start_response):
    status = '200 OK'
    output = bytes(getEnv(), 'utf-8')
    response_headers = [('Content-type', 'text/plain'),
                        ('Content-Length', str(len(output)))]
    start_response(status, response_headers)
    return [output]

def getEnv():
    keys = list(os.environ.keys())
    keys.sort()
    o = ''
    o += "OS Environment\n"
    o += 'Python Version: %s\n' % sys.version
    o += 'Python prefix: %s\n' % sys.prefix
    for k in keys:
        v = os.environ[k]
        o += ("  %s: %s\n" % (k, v))
    return o
Enter fullscreen mode Exit fullscreen mode

Reports:

OS Environment
Python Version: 3.6.8 (default, Mar 18 2021, 08:58:41)
[GCC 8.4.1 20200928 (Red Hat 8.4.1-1)]
Python prefix: /apps/mod_wsgi/.venv
  INVOCATION_ID: 8c7e2ab82ab7415ca0e1e2c1fddde05a
  JOURNAL_STREAM: 9:1072847
  LANG: C
  NOTIFY_SOCKET: /run/systemd/notify
  PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
Enter fullscreen mode Exit fullscreen mode

3.6.8 is the OS's python version. Thanks for your help

Collapse
 
sm0ke profile image
Sm0ke • Edited

Hello @dlink ,

Indeed the app/\_\_init\_\_.py cannot be formatted being styled automatically by the Markdown parser.

Regarding the Python version, try to force the PATH update in wsgi.py as below

site.addsitedir('/data/apps/hitme/.venv/..../site-packages')
sys.path.insert(0, '/data/apps/hitme/')
Enter fullscreen mode Exit fullscreen mode

P.S. adapt the paths to be resolved properly.
Let me know the results.

Collapse
 
tayfunerbilen profile image
Tayfun Erbilen • Edited

Hey, thanks for article it helped a lot :) I have a problem, I try to import something like slugify etc. but I got Internal Server Error and I don't know why, any suggestion?

Collapse
 
bawbam profile image
Ivan López

Hi, thanks for this tutorial dude, I'm following the tutorial and work fine but with personal project not work and send me error "Target WSGI script .... cannot be loaded as python module". Any idea what happend? thank you

Collapse
 
neoxenon profile image
Darya

Hi!
I am on flask run part, in console it shows:
Serving Flask app "run.py" (lazy loading)

  • Environment: development
  • Debug mode: on
  • Running on 127.0.0.1:5000/ (Press CTRL+C to quit)
  • Restarting with stat
  • Debugger is active!
  • Debugger PIN: but in browser 127.0.0.1:5000 says site can't be reached
Collapse
 
abbddos profile image
Abdul Rahman Sabbagh

Hi.. Thanks for this tutorial.. I tried it line by line and it worked. However, I've been trying to deploy a more complicated flask app that I have been working on for a while, and it's not working. I hope you can help me out. I'll be very grateful.

Collapse
 
sm0ke profile image
Sm0ke

Hello Abdul, Thanks for reading.
I'll be glad to help. Tell me more about your app.

Collapse
 
abbddos profile image
Abdul Rahman Sabbagh

Hi,
It's an app about running warehouses and simple accounting and invoices. It has a lot of local imports. I originally placed the "run.py" inside the project folder and it worked perfectly on the development server, but that does not match the structure described in your tutorial and it would not deploy.

When I place the "run.py" outside the project folder, it would not recognize the local packages I'm importing from within the project.

If you like to take a better look... here's the link on github

github.com/abbddos/Enterprise_1

This is my first web project ever, so I know there's a small twist somewhere that I'm missing.

Thank you very very much

Thread Thread
 
sm0ke profile image
Sm0ke

Hello,
let's chat in private.

Collapse
 
anhtuan3996 profile image
Cao Anh Tuan • Edited

Thanks for this tutorial.
I have a problem, WSGT cannot load Python module. I checked to error_log
image!
Do you have any idea to resolve this problem?