[[start|Nivel superior]]
|[[Flask app 1|Parte 1]]|[[Flask app 3|Parte 3]]|
====== Flask - 2 - Producción ======
===== Introduction =====
Tutorial de Desarrollo de una Aplicación con Flask y Gestión con ''systemd''
Referencias: [[https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world|Flask mega tutorial]]
===== Preparación del Entorno de Producción en Linux Ubuntu =====
==== Instalación de Dependencias ====
En tu máquina de producción con Linux Ubuntu, instala las dependencias
necesarias:
sudo apt update
sudo apt install python3-pip nginx pipenv
sudo systemctl enable nginx
Una alternativa para usar un ''pipenv'' más actualizado es instalarlo con pip:
sudo apt install python3-pip nginx
pip install --user pipenv
==== Directorio de producción. ====
En lugar de ceder toda la propiedad a ''www-data'', utilizaremos un esquema de
**permisos compartidos**. Esto permite que tu usuario edite archivos y ejecute
''pipenv'' sin usar ''sudo'', mientras que ''www-data'' mantiene el acceso
necesario para servir la aplicación.
sudo mkdir /var/www/my_flask_app
Debemos copiar todos los ficheros que hemos preparado en el entorno de
desarrollo al nuevo entorno de producción, para ello podemos usar ''scp'',
''rsync'' o ''unison''.
==== Fichero wsgi.py ====
En entornos de producción, Flask no se ejecuta mediante app.run(), ya que este
método lanza un servidor de desarrollo que no está diseñado para ser seguro,
eficiente ni persistente. En su lugar, se utiliza un servidor WSGI (Web Server
Gateway Interface), como Gunicorn o uWSGI, que actúa como intermediario entre
el servidor web (por ejemplo, Nginx) y la aplicación Python.
El fichero ''wsgi.py'' define el punto de entrada estándar de la aplicación,
exponiendo el objeto app para que el servidor WSGI pueda importarlo y
gestionarlo. De este modo, ''wsgi.py'' desacopla el código de la aplicación del
mecanismo de ejecución, permitiendo una ejecución robusta, escalable y adecuada
para producción.
Crea el fichero ''wsgi.py'' con el siguiente contenido:
====== wsgi.py ======
from app import app
=== Configuración de Grupos ===
Añade tu usuario personal al grupo de Nginx para que ambos compartan
privilegios:
====== Añadir el usuario actual al grupo www-data ======
sudo usermod -aG www-data $USER
Nota: Se debe cerrar sesión y volver a entrar (o reiniciar el servidor) para que
este cambio se aplique.
=== Asignación de Propiedad y Bit de Grupo ===
Configuramos tu usuario como propietario y ''www-data'' como grupo responsable.
Aplicaremos el **setgid bit**, que garantiza que cualquier archivo nuevo creado
herede automáticamente el grupo ''www-data''.
====== Cambiar propietario al usuario actual y grupo a www-data ======
sudo chown -R $USER:www-data /var/www/my_flask_app
====== Dar permisos de lectura/escritura/ejecución a dueño y grupo (775) ======
sudo find /var/www/my_flask_app -type d -exec chmod 775 {} +
sudo find /var/www/my_flask_app -type f -exec chmod g+rw {} +
====== Aplicar el bit de grupo persistente (setgid) a todos los directorios ======
sudo find /var/www/my_flask_app -type d -exec chmod g+s {} +
=== Ventajas de este método ===
* **Tu Usuario**: Puedes usar ''git pull'', ''pipenv install'' y editar código sin ''sudo''.
* **Nginx / www-data**: Grupo (rwx) Puede leer archivos estáticos y escribir en el socket o logs.
* **Seguridad**: Los usuarios que no pertenecen al grupo ''www-data'' no tienen acceso.
=== Verificación de permisos ===
Para confirmar que la configuración es correcta, ejecuta:
ls -la /var/www/my_flask_app
La salida debería mostrar los permisos como ''drwxrwxr-x'' y los dueños como
''tu_usuario www-data''.
==== Configuración de Nginx ====
Configura Nginx para servir la aplicación Flask. Crea un nuevo archivo de
configuración en ''/etc/nginx/sites-available/my\_flask\_app'':
server {
listen 80;
server_name your_domain;
access_log /var/log/nginx/my_flask_app.access.log;
error_log /var/log/nginx/my_flask_app.error.log;
location / {
proxy_pass http://unix:/var/www/my_flask_app/my_flask_app.sock;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
proxy_buffering off;
}
}
* **server\_name** es donde va el dominio. Es el que usarás para acceder a la aplicación desde internet.
* **access\_log** y **error\_logs** especifican la ruta de los archivos de log.
* **location** es donde el ''reverse proxy'' de nginx accede a la aplicación Flask.
* **proxy\_redirect off**: Evita que Nginx reescriba automáticamente cabeceras Location y Refresh que devuelve la aplicación backend (Gunicorn/Flask). Evitamos problemas de redirección difíciles de depurar.
* **proxy\_buffering off**: Desactivamos el buffering de respuestas, que es lo más conveniente para webs dinámicas.
Habilitamos el sitio creando un enlace al fichero correspondiente de
''sites-enabled'' y reiniciando ''nginx''.
sudo ln -s /etc/nginx/sites-available/my_flask_app /etc/nginx/sites-enabled
sudo nginx -t
sudo systemctl restart nginx
sudo systemctl status nginx
==== Instalación de Gunicorn ====
Instala Gunicorn en el entorno virtual de producción. Se debe ejecutar export
''PIPENV_VENV_IN_PROJECT=1'' antes de hacer el pipenv install en el servidor,
o el servicio de ''systemd'' fallará al no encontrar la ruta de Gunicorn.
pipenv install gunicorn
Mientras estas en el entorno virtual comprueba la ruta de ''gunicorn''. Toma
nota de esta ruta. Se necesitará para configurar el servicio ''systemd''.
$ which gunicorn
/var/www/my_flask_app/.venv/bin/gunicorn
==== Testing with gunicorn ====
Ahora comprobaremos el funcionamiento con ''gunicorn''. Para la prueba ejecutamos
''gunicorn'' directamente. Después lo configuraremos en el servicio ''systemd''.
gunicorn --workers 4 --bind 0.0.0.0:5000 wsgi:app
[2024-02-20 20:57:21 -0500] [4936] [INFO] Starting gunicorn 21.2.0
[2024-02-20 20:57:21 -0500] [4936] [INFO] Listening at: http://0.0.0.0:5000 (4936)
[2024-02-20 20:57:21 -0500] [4936] [INFO] Using worker: sync
[2024-02-20 20:57:21 -0500] [4937] [INFO] Booting worker with pid: 4937
[...]
Hacemos la comprobación:
curl -O - localhost:5000
Deberíamos obtener la salida de la página inicial de la aplicación.
Press CTRL+C to stop the Gunicorn server.
Esta prueba NO usa ''nginx'', NO usa ''systemd'' y NO representa el entorno
final, sólo comprueba el funcionamiento de ''gunicorn''.
==== Creación del Servicio systemd ====
Crea un archivo de servicio ''systemd'' para gestionar la aplicación. Crea un
archivo ''/etc/systemd/system/my_flask_app.service'' con el siguiente
contenido:
[Unit]
Description=Gunicorn instance to serve my_flask_app
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/my_flask_app
Environment="PATH=/var/www/my_flask_app/.venv/bin"
ExecStart=/var/www/my_flask_app/.venv/bin/gunicorn --workers 3 --bind unix:/var/www/my_flask_app/my_flask_app.sock -m 007 wsgi:app
[Install]
WantedBy=multi-user.target
* **User**: Establece el usuario con permisos para el directorio del proyecto.
* **Group**: Establece el grupo con permisos para el directorio del proyecto.
* **Environment**: Establece la ruta al directorio de ejecutables dentro del entorno virtual.
* **WorkingDirectory**: Establece el directorio base donde están los ficheos del proyecto.
* **ExecStart**: Establece la ruta al ejecutable de ''gunicorn'' dentro del entorno virtual, así como opciones de ejecución.
* **--workers N**: Establece el número de workers. Ajustar el número posteriormente si hay algún problema. No más de 12.
* **--bind unix:**: Asociamos la ejecución de ''gunicorn'' a un socket Unix.
* **wsgi:app**: wsgi es el nombre del fichero sin la extensión ''.py''. ''app'' es la instancia de la aplicación Flask dentro del fichero.
* **-m 007**: Para que Nginx pueda comunicarse con el socket creado por Gunicorn sin errores de "Permission Denied", es fundamental añadir la máscara de permisos ''-m 007'' en la configuración del servicio.
==== Habilitar y Iniciar el Servicio ====
Habilita y inicia el servicio con los siguientes comandos:
sudo systemctl enable my_flask_app
sudo systemctl start my_flask_app
==== /etc/hosts provisional para pruebas en local====
La aplicación Flask ya no es accesible a través del puerto 5000 que usábamos
antes, sino a través de ''systemd'', ''gunicorn'' y ''nginx''. Para acceder a la
aplicación hay que usar el nombre de dominio que hemos puesto en la
configuración. Pero es conveniente probar en local antes de intentar un acceso
desde internet. Para ello vamos a editar el fichero ''/etc/hosts'' del cliente e
incluiremos una sentencia similar a esta, con la IP de la red local del
servidor.
192.168.12.34 my_flask_app
Ahora podemos acceder a la aplicación con un browser a través del nombre de
dominio: ''http://my_flask_app''.
Deberíamos obtener la primera página de nuestra aplicación nuevamente.
Ahora la aplicación está probada y es posible modificar el DNS para que apunte a
la dirección pública del servidor, volviendo a dejar el fichero ''/etc/hosts''
del cliente como estaba.
En caso de que no funcione desde internet habrá que comprobar direcciones IP
externas y firewalls tanto en el servidor cómo en el router.