Pasos para desplegar aplicación Flask con mod_wsgi y Apache.


Apache es el servidor web por excelencia, para desplegar aplicaciones y paginas web. Sin embargo, su funcionamiento en otros stacks, como por ejemplo Python Flask, se comporta bastante diferente. Sin embargo, existen varias formas de usarlo y las útil que encontre, que no requiere demasiadas configuraciones, es usar el módulo WSGI que este servidor posee, lo que permite conectar la aplicación desde donde esté ubicada e interpretarla para que sea usable en producción (y también desarrollo).
Ambiente.
Requerimientos.
Sistema Operativo: Ubuntu 24
Version de Apache: 2.4.58-1ubuntu8.6
Versión de Flask: 3.1.1
Base de Datos: PostgreSQL 16.9
Ruta de la aplicación:
/home/user/flask
Configuración.
Vamos a preparar el entorno para habilitar Apache. Para esto, requerimos instalar lo siguiente.
En primer lugar, hay que instalar mod_wsgi
en el ambiente. Esto debe hacerse con el entorno virtual activado (Despues se desactiva, ya que apache tomara el mando y desplegará la aplicación).
# Activa el entorno virtual
source venv/bin/activate
# Descargar paquete mod_wsgi
pip install mod_wsgi
# Desactivar el entorno virtual
deactivate
Lo siguiente es instalar el paquete mod_wsgi
en Apache y habilitarlo
sudo apt install libapache2-mod-wsgi-py3
# Habilitar
sudo a2enmod wsgi
# Reiniciar apache
sudo systemctl restart apache2
# Verificar
apache2ctl -M | grep wsgi
# La salida debe ser algo como esto
wsgi_module (shared)
El próximo paso, es crear el archivo app.wsgi
, dentro del directorio donde se encuentra la aplicacion (/home/user/flask
) que será el que apache utilizará para conectar con la aplicación.
vim app.wsgi
El archivo debe llevar lo siguiente.
import sys
sys.path.insert(0,'/home/user/flask') # Aqui va la ruta de la aplicacion
from flaskapp import app as application # El nombre de la aplicacion, debe coincidir con el de la app.py, en este caso flaskapp.py
Lo siguiente es crear el VirtualHost en la configuración de Apache. Dependiendo de la distribución, se encuentra en distintos lugars. En el caso de Ubuntu, por lo general está en /etc/apache2/site-avalaibles/
. Debe llevar lo siguiente.
sudo vim /etc/apache2/sites-available/flask.conf
<VirtualHost *:5000>
ServerAdmin webmaster@localhost
WSGIDaemonProcess flaskapp user=user group=www-data threads=5 python-home=/home/user/flaskapp/venv
WSGIScriptAlias / /home/user/flaskapp/flaskapp.wsgi
WSGIProcessGroup flaskapp
<Directory /home/user/flaskapp>
Require all granted
</Directory>
Alias /static/ /home/user/flaskapp/static/
<Directory /home/user/flaskapp/static/>
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/flaskapp-error.log
CustomLog ${APACHE_LOG_DIR}/flaskapp-access.log combined
</VirtualHost>
Este archivo contiene lo siguiente:
En encabezado, Virtualhost *:5000
que habilita el puerto 5000, aunque puede ser cualquiera que deseen utilizar. Se debe comprobar si el puerto objetivo esta abierto para escuchas con netstat
o ss
.
# Net stats
sudo netstats -tulnp|grep *:5000 # Puede ser cualquier otro puerto
# SS
sudo ss -tulnp|grep *:5000
En WSGIDaemonProcess
, se debe agregar lo siguiente.
el nombre de la aplicación, en este caso
flaskapp
el usuario dueño del directorio.
el grupo al que pertenece, en este caso debe ser si o si
www-data
en la ruta
python-home
, se agrega la ruta donde se encuentra la aplicación. Debe apuntar al directoriovenv
que es el que activa el entorno virtual y guarda las dependencias.
En WSGIScriptAlias
, apuntamos a la ruta de nuestra app.wsgi
, que es el puente entre Apache y la aplicacion.
Por ultimo en Alias
, se apunta al directorio static
, el cual almacena los archivos estáticos como imágenes, hojas de estilo y scripts de Javascript que la web utiliza.
Una vez realizado esto, se deba aplicar el comando a2ensite flaskapp
, para que agregue el nuevo archivo .conf
se vuelve a reinicar Apache, para que tome los cambios.
Se debe asegurar que el puerto sea escuchado. Para eso, abrimos /etc/apache2/ports.conf
y agregamos la siguiente linea
Listen 5000
Se agrega la aplicación al entorno.
# Añade la aplicacion al entorno de apache
sudo a2ensite flaskapp
Ahora, hay que considerar los permisos del directorio. Si estan trabajando con git, pueden tener ciertas dificultades, si el usuario que administra el control de versiones no es el duelo del directorio. Lo mejor en este caso, es agregarlo al grupo www-data
.
# Se agrega
sudo chown -R user:www-data /home/user/pyerp
# Se dan permisos de lectura y ejecucion
sudo chmod -R 755 /home/user/flaskapp
Por último, se recarga Apache.
sudo systemctl reload apache2
Con esto, ya estamos listos para el despliegue, salvo una cosa.
Normalmente, Apache, toma en consideración la ruta absoluta de los archivos, por lo que es imperativo, realizar ciertos cambios en la configuración misma de la aplicación. En este caso, la aplicación tiene dos archivos que hacen función de eso: config.ini
y config.py
. En este últimos nos centraremos.
Para empezar, la configuración básica es la siguiente. Definimos en el .ini
, los parámetros que serán llamados:
# En este archivo, se agregan los parámetros. Pueden ser varios, los cuales se pueden llamar en constantes dentro de cualquier otro archivo de la aplicacion.
[postgresql]
HOSTNAME = localhost
PORT = 5432
USERNAME = usuario
PASSWORD = clave
DATABASE = basededatos
Luego tenemos el .py
que lo llama.
from configparser import ConfigParser
def DBconfig(filename='config.ini', section='postgresql'):
parser = ConfigParser()
parser.read(filename)
db = {}
if section in parser:
for key in parser[section]:
db[key.upper()] = parser[section][key]
else:
raise Exception(f'La seccion {section} no existe en el archivo {filename}')
return db
if __name__ == '__main__':
DBconfig()
La función de este ultimo archivo, es llamar a config.ini
y cargar dentro de la aplicación, los parámetros que son leídos por cada sección. Normalmente, se escribe como en el ejemplo de arriba, pero Apache puede que no encuentre la ruta y vaya hacia otro lado.
Para asegurar que eso no pase, se agrega lo siguiente
def get_path(filename):
return os.path.join(os.path.dirname(__file__), filename)
Esta función, nos trae la ruta absoluta, donde se encuentra el archivo config.ini
, lo que hará que Apache lo encuentre sin problemas. El archivo entonces quedará asi.
import os # importamos las funcionalidades que usa el sistema operativo.
from configparser import ConfigParser
# Generamos la ruta absoluta, basada en la del directorio de la aplicacion
def get_path(filename):
return os.path.join(os.path.dirname(__file__), filename)
def DBconfig(filename='config.ini', section='postgresql'):
parser = ConfigParser()
# aqui llamamos a la funcion que trae la ruta.
parser.read(get_path(filename))
db = {}
if section in parser:
for key in parser[section]:
db[key.upper()] = parser[section][key]
else:
raise Exception(f'La seccion {section} no existe en el archivo {filename}')
return db
Consideraciones finales.
Este ejemplo, se hizo en un ambiente de desarrollo. Para desplegar en producción, hay otros detalles adicionales que se deben considerar. Aun así está configuración mínima, permite desplegar una aplicación, rápidamente y con mayor eficiencia.
Existen otras herramientas como gunicorn, que realiza la misma función, pero en este post, quise dar un ejemplo de algo que ya existe y que además, es el más masivo en su uso.
Espero que les sirva. Saludos.
Subscribe to my newsletter
Read articles from Hermann Pollack (hpollack95) directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
