Volver al blog

Construir y desplegar una aplicación Flask con Docker en Ubuntu 20.04

Construir y desplegar una aplicación Flask con Docker en Ubuntu 20.04

Introducción

Docker es una plataforma de contenedores de código abierto. Es un entorno estandarizado, ligero, virtualizado, portátil y definido por software que permite que el software se ejecute de forma aislada de otro software que se ejecuta en la máquina host física. Docker ofrece una alternativa ligera a las máquinas virtuales. Al mismo tiempo, proporciona portabilidad, rendimiento, agilidad y escalabilidad de las aplicaciones. Para obtener una guía completa sobre el ecosistema de Docker, eche un vistazo a nuestra descripción detallada de la contenedorización con Docker.

Flask es un framework web mínimo de código abierto creado con Python. Algunas de las grandes características de Flask son que es ligero, flexible y altamente estructurado. Además, no requiere herramientas o complementos específicos para ejecutarse.

La combinación de Flask y Docker le brinda una aplicación ligera, flexible y escalable. Puede implementarla en muchos servidores e infraestructuras, gracias a la naturaleza portátil de los contenedores Dockerizados. El enfoque de este tutorial es mostrarle cómo implementar una aplicación Flask con Docker. También demostraremos cómo asegurarse de que las futuras actualizaciones de su aplicación surtan efecto.

Requisitos previos

Este será un tutorial práctico, y debe crear un entorno que le permita seguir los pasos:

¡Ahora, comencemos!

Paso 1: Preparar la aplicación Flask

Comenzaremos creando un directorio que contendrá nuestra aplicación Flask. Puede seleccionar el nombre de directorio que prefiera. Sin embargo, para este tutorial, lo nombraremos flask_demo. Guardaremos los archivos del proyecto dentro del directorio /var/www, que suele ser el directorio al que Ubuntu permite el acceso a la internet pública de forma predeterminada. Primero, ejecute los siguientes comandos para crear el directorio y navegar dentro de él:

Dentro de este directorio raíz de nuestro proyecto, crearemos la estructura de carpetas base de una aplicación Flask. A continuación, ejecute el siguiente comando para crear la estructura base, agregando la bandera -p para crear todas las carpetas principales en el camino:

La carpeta app contiene todos los archivos relacionados con una aplicación Flask, incluyendo views y blueprints. Las vistas contienen el código que escribe para responder a las solicitudes que llegan a su aplicación. Los blueprints ayudan a crear componentes de la aplicación y admiten patrones comunes en las aplicaciones Flask.

La carpeta acertadamente llamada static contiene recursos estáticos como imágenes, archivos CSS y JavaScript. El directorio templates contiene todas las plantillas HTML para el proyecto.

Ahora podemos comenzar a escribir los archivos necesarios para inicializar una aplicación Flask. Comience creando un archivo llamado __init__.py dentro del directorio app para indicarle al intérprete de Python interpreter que el directorio app debe ser tratado como un paquete. Ejecute el siguiente comando en su terminal para abrir el archivo con el editor nano:

Utilizamos paquetes en Python para agrupar módulos en espacios de nombres o jerarquías lógicas. La modularización permite dividir el código en bloques individuales y manejables que realizan funciones definidas.

Después de eso, dentro del archivo __init__.py abierto en su editor, agregue el siguiente fragmento de código para iniciar la instancia de Flask e importar la lógica de views.py que creará en los siguientes pasos:

Cuando haya terminado, presione Ctrl + O y ENTER para guardar el archivo, luego ciérrelo con Ctrl + X. A continuación, creemos el views.py dentro del directorio app . El archivo views.py contendrá la mayor parte de la lógica de la aplicación:

Dentro del archivo, agregue el siguiente fragmento de código. Este código mostrará una cadena simple para indicar que su aplicación se está ejecutando cuando los usuarios visiten su sitio web:

En este archivo, comenzamos importando la instancia de la aplicación Flask. Luego, necesitamos agregar una línea para definir la ruta: @app.route(/). La línea @app.route(/) se conoce como un decorador en Flask. Puede usar decoradores para inyectar funcionalidades adicionales en una o más funciones. En este caso, estamos pasando una llamada a la ruta / a la función home. Cuando un usuario visite esta ruta, verá el texto: "¡Nuestra aplicación Flask está funcionando!".

A continuación, creará el archivo uwsgi.ini para contener las configuraciones de uWSGI para la aplicación. uWSGI es una opción de despliegue para Nginx que sirve como protocolo y servidor de aplicaciones. Ejecute el siguiente comando para crear el archivo en el directorio raíz del proyecto con el editor nano:

Dentro del archivo abierto, agregue el siguiente fragmento de código:

Este archivo contiene algunas directivas. Definimos su propósito a continuación:

  • module – define el módulo desde el cual se servirá la aplicación Flask. Hemos configurado el módulo como main, haciendo referencia al archivo main.py en el directorio raíz. Crearemos este archivo en el siguiente paso.
  • callable – indica a uWSGI que use la instancia app exportada de la aplicación.
  • master – asegura que la aplicación siga ejecutándose para minimizar el tiempo de inactividad durante la recarga de toda la aplicación.

Guarde y cierre el archivo cuando haya terminado.

Ahora puede crear el archivo main.py para determinar el punto de entrada a su aplicación. uWSGI leerá este archivo para saber cómo interactuar con la aplicación. Ejecute el siguiente comando para crear el archivo main.py  con nano dentro del directorio raíz de su proyecto:

Dentro del archivo, agregue la siguiente línea que importará la instancia de Flask que se creó en el paquete de la aplicación:

Lo último que hará en este paso es definir las dependencias necesarias para que se ejecute la aplicación. Definiremos estas dependencias dentro de un archivo llamado dependencies.txt. Cuando Docker compile la imagen de su aplicación, ejecutará un comando del gestor de paquetes (pip manager) para instalar las dependencias. Abra el archivo en el directorio raíz con el siguiente comando:

Hasta este punto de nuestro proyecto, solo queremos una dependencia: Flask. Por lo tanto, podemos agregar la siguiente línea para hacer referencia a la versión correcta de Flask que queremos para nuestro proyecto:

Nos quedaremos con la versión de Flask 2.0.1 como dependencia. Es la última versión al momento de escribir este tutorial. Puede obtener más información sobre las distintas versiones en la página de Cambios de Flask. Eso completa la configuración de la aplicación Flask. Ahora preparemos las configuraciones de Docker para el despliegue.

Paso 2: Configurar Docker

Para configurar un despliegue de Docker, crearemos dos archivos, Dockerfile y start.sh. El Dockerfile contiene líneas declarativas que componen una imagen de Docker. El archivo start.sh es un script de shell básico para compilar la imagen y iniciar el contenedor a partir del Dockerfile. Mientras se encuentra dentro del directorio raíz del proyecto, ejecute el siguiente comando para crear el Dockerfile:

Este archivo contiene las configuraciones necesarias para una imagen de Docker. A continuación, agregue el siguiente fragmento de código para especificar las dependencias y cómo compilar la imagen:

La primera línea en un Dockerfile define la imagen base a partir de la cual estamos construyendo nuestra imagen. En este caso, construiremos basándonos en la tiangolo/uwsgi-nginx-flask, disponible en DockerHub. Elegimos esta imagen en particular porque es compatible con muchas versiones de Python.

También especificamos que queremos actualizar la imagen. Luego, necesitamos agregar el procesador de comandos bashprocesso , el editor de texto nanoeditor, y el cliente git para descargar y subir código fuente desde repositorios de control de versiones como GitHub, Bitbucket, o Gitlab. Las líneas con ENV especifican las variables de entorno que se utilizarán en el contenedor.

El COPY comando copia las dependencias en el contenedor. El RUN comando invoca al gestor de paquetes pip manager para analizar el archivo dependencies.txt e instalar las dependencias. Guarde y cierre el archivo cuando haya terminado de editar.

A continuación, creará el script start.sh. Este script incluirá comandos de Docker para construir y ejecutar la imagen. Aunque puede ejecutar estos comandos progresivamente en la terminal, pensamos que es más limpio agregarlos a un script de shell y simplemente invocarlo desde la terminal con un solo comando.

Antes de que podamos definir el contenido de este archivo, primero debemos establecer un puerto libre que otros servicios no estén usando. Usaremos el puerto 45644. Sin embargo, puede elegir un puerto diferente. Ejecute la siguiente línea para verificar si el puerto está libre:

Dependiendo del puerto que haya elegido, si la salida del comando anterior es 1, entonces está libre. De lo contrario, es posible que deba elegir otro puerto y probar el comando nuevamente:

Flask application Port Check

Dado que hemos establecido un puerto libre, ahora podemos crear el archivo con nano dentro del directorio raíz del proyecto ejecutando el siguiente comando:

Dentro de este archivo, agregue el siguiente fragmento de código:

La primera línea, conocida como shebang, especifica que este es un archivo bash y debe ejecutarse como comandos. La segunda línea declara una variable llamada app_name. Usamos esta variable para establecer los nombres de la imagen y del contenedor. La tercera línea le indica a Docker que construya la imagen basándose en la definición del Dockerfile en el directorio actual. La imagen se llamará docker-flask-demo según la variable.

La última línea crea un contenedor llamado docker-flask-demo según la variable que definimos. La -d bandera mantiene el contenedor ejecutándose en segundo plano en un estado desasociado (detached) después de que el comando haya terminado de ejecutarse. La -p bandera vincula un puerto en el servidor a un puerto particular en el contenedor. En este caso, estamos montando el puerto 45644 de la máquina host al puerto 80 que Docker expondrá en el contenedor.

Usamos la -v bandera para especificar un volumen de Docker para montar en el contenedor. La $PWD variable es una variable predeterminada de Linux que contiene la ruta al directorio actual en el que se encuentra en un momento dado:

Flask application pwd

En nuestro caso, estamos montando todo el directorio del proyecto en el directorio /var/www del contenedor. La configuración de Docker ya está lista. Puede construir la imagen e iniciar el contenedor basado en la imagen construida ejecutando el siguiente comando:

Espere a que el script termine de ejecutarse, luego ejecute el siguiente comando de Docker para listar todos los contenedores en ejecución:

La salida mostrará los contenedores en ejecución:

Demo Docker

Debería ver nuestro contenedor con el nombre docker-flask-demo en la lista de contenedores en ejecución. Busque la IP pública de su servidor IP y acceda a ella en su navegador en el puerto especificado: http://your-server-public-ip:45644.

Debería ver una salida similar:

Flask App Running

Si ve lo anterior en su navegador, entonces ha implementado con éxito una aplicación Flask. A continuación, modificaremos archivos y serviremos contenido a los usuarios a través de plantillas.

Paso 3: Servir contenido a través de archivos de plantilla

En Flask, Templates se utilizan para mostrar contenido estático y dinámico a los visitantes del sitio web. Le mostraremos cómo crear una plantilla HTML y servirla a sus usuarios cuando visiten una determinada ruta. Por ejemplo, esta puede ser una página de Inicio o una página Acerca de.

Ejecute el siguiente comando en su terminal para crear un archivo index.html en el directorio app/templates :

Luego, agregue el siguiente fragmento de código al archivo:

Guarde y cierre el archivo cuando haya terminado. Además, cree otra página, llamémosla la página Acerca de, con el siguiente comando:

Agregue el siguiente fragmento de código al archivo:

Guarde y cierre el archivo cuando haya terminado. A continuación, modifique el archivo app/views.py para hacer referencia a las plantillas, así como a las rutas de las páginas reales:

Modifique el archivo para que se vea así:

Guarde y cierre el archivo cuando haya terminado. Los cambios que ha realizado no tendrán efecto hasta que detenga y reinicie el contenedor. Ejecute los siguientes comandos de Docker para detener e iniciar el contenedor. Tome nota del nombre del contenedor como definimos anteriormente:

Una vez que el contenedor esté en funcionamiento, visite la página de Inicio y la página Acerca de para ver parte del nuevo contenido:

Flask application Index

 Flask application 1

Hasta ahora, ha creado una aplicación Flask que puede servir contenido a los visitantes de su sitio web. Aquí está la estructura de archivos para el proyecto:

File Structure

Probablemente haya notado que tuvimos que reiniciar el contenedor Docker para que detectara los nuevos cambios. En el siguiente paso, automatizaremos esto para garantizar un menor tiempo de inactividad.

Paso 4: Configurar las actualizaciones de los archivos de la aplicación para que se recarguen automáticamente

Cada cierto tiempo realizamos cambios en una aplicación para mejorar la lógica, las interfaces de usuario o añadir algunas dependencias. Para que dichos cambios surtan efecto, es posible que deba reiniciar el contenedor Docker. Afortunadamente, uWSGI tiene una función llamada touch-reload para recargar un script de Python sin reiniciar el contenedor.

De forma nativa, Python tiene una función de auto-recarga que vigila todo el sistema de archivos en busca de cambios y actualiza la aplicación cuando ocurre un cambio. Aunque la recarga automática es buena para minimizar los tiempos de inactividad, puede consumir muchos recursos. Por lo tanto, no se recomienda para entornos de producción.

Veamos cómo puede usar touch-reload para vigilar los cambios en un archivo en particular y recargar la aplicación cuando haya cambios. Modifique el archivo uwsgi.ini con el editor nano:

Añada la línea resaltada para que se vea así:

Guarde y cierre el archivo cuando haya terminado. La línea añadida ha especificado un archivo que se modificará para activar la recarga de la aplicación. Sin embargo, para que esta condición se active para futuras modificaciones, primero debe reiniciar el contenedor:

Ahora puede modificar el archivo app/views.py para demostrar cómo funciona la recarga automática:

Cambie la cadena devuelta por la función home como se resalta:

Guarde y cierre el archivo una vez terminado.

Abra la página de inicio de su aplicación en el navegador: http://your-server-public-ip:45644.

Aún no verá ningún cambio. Esto se debe a que la condición touch-reload detecta un cambio en el archivo uwsgi.ini . Puede usar touch para activar la condición, recargando así toda la aplicación con el siguiente comando:

Ahora, si recarga la página de inicio, verá que se muestran los nuevos cambios:

Touch Reload

En el futuro, si realiza algún cambio, solo necesitará ejecutar el comando sudo touch uwsgi.ini y toda la aplicación se recargará con menos tiempo de inactividad. Eso nos lleva al final de este tutorial.

Conclusión

En este tutorial, implementó y desplegó una aplicación Flask con imágenes y contenedores de Docker. Para minimizar el tiempo de inactividad evitando la necesidad de reiniciar el contenedor, configuró touch-reload para escuchar los cambios en un archivo en particular y recargar automáticamente toda la aplicación. Finalmente, probó todo esto en el navegador para asegurarse de que funciona.

Docker garantiza despliegues más rápidos y permite un escalado sencillo de las aplicaciones. Si desea obtener más información sobre los diversos comandos de Docker, consulte este tutorial sobre cómo instalar y usar Docker en Ubuntu.

Para obtener más recursos sobre Docker en nuestro blog, puede consultar lo siguiente:

¡Feliz computación!

author

Pranay Kapgate

Autor · CloudSigma

Preslav Dobrev es diseñador creativo en CloudSigma, centrado en una identidad empresarial coherente mediante el uso de canales de marketing tradicionales e innovadores. Es experto en fusionar la visión artística con el marketing estratégico para crear narrativas de marca impactantes.

Comentarios

Aún no hay comentarios. Sea el primero.