Dockerizaci贸n de aplicaciones Python

    Introducci贸n

    Docker es una herramienta ampliamente aceptada y utilizada por las principales empresas de TI para crear, gestionar y proteger sus aplicaciones.

    Los contenedores, como Docker, permiten a los desarrolladores aislar y ejecutar m煤ltiples aplicaciones en un solo sistema operativo, en lugar de dedicar una m谩quina virtual para cada aplicaci贸n en el servidor. El uso de estos contenedores m谩s livianos conduce a costos m谩s bajos, mejor uso de recursos y mayor rendimiento.

    Si est谩 interesado en leer m谩s, deber铆a echar un vistazo a Docker: una introducci贸n de alto nivel.

    En este art铆culo, escribiremos una aplicaci贸n web Python simple usando Flask y la prepararemos para “dockerizar”, seguido de la creaci贸n de una imagen de Docker y su implementaci贸n en un entorno de prueba y producci贸n.

    Nota : Este tutorial asume que tiene Docker instalado en su m谩quina. De lo contrario, puede seguir la Gu铆a de instalaci贸n oficial de Docker .

    驴Qu茅 es Docker?

    Docker es una herramienta que permite a los desarrolladores enviar sus aplicaciones (junto con bibliotecas y otras dependencias), lo que garantiza que puedan ejecutarse exactamente con la misma configuraci贸n, independientemente del entorno en el que est茅n implementadas.

    Esto se hace aislando las aplicaciones en contenedores individuales que, aunque separados por contenedores, comparten el sistema operativo y las bibliotecas adecuadas.

    Docker se puede dividir en:

    • Docker Engine : una herramienta de empaquetado de software utilizada para contener aplicaciones.
    • Docker Hub : una herramienta para administrar sus aplicaciones de contenedor en la nube.

    驴Por qu茅 contenedores?

    Es importante comprender la importancia y la utilidad de los contenedores. Aunque es posible que no marquen una gran diferencia con una sola aplicaci贸n implementada en el servidor o en proyectos dom茅sticos, los contenedores pueden salvar la vida cuando se trata de aplicaciones robustas y con muchos recursos, especialmente si comparten el mismo servidor o si se implementan en muchos entornos diferentes.

    Esto se resolvi贸 en primer lugar con m谩quinas virtuales como VMWare e hipervisores , aunque se ha demostrado que no son 贸ptimas en cuanto a eficiencia, velocidad y portabilidad.

    Los contenedores Docker son alternativas ligeras a las m谩quinas virtuales; a diferencia de las m谩quinas virtuales, no necesitamos preasignar RAM, CPU u otros recursos para ellos y no necesitamos iniciar una nueva m谩quina virtual para todas y cada una de las aplicaciones, ya que estamos trabajando con un solo sistema operativo.

    Los desarrolladores no necesitan cargarse con el env铆o de versiones especiales de software para diferentes entornos y pueden concentrarse en crear la l贸gica empresarial central detr谩s de la aplicaci贸n.

    Configuraci贸n del proyecto

    Flask es un micro-framework de Python que se utiliza para crear aplicaciones web simples y avanzadas. Debido a su facilidad de uso y configuraci贸n, lo usaremos para nuestra aplicaci贸n de demostraci贸n.

    Si a煤n no tiene Flask instalado, es f谩cil hacerlo con un solo comando:

    $ pip install flask
    

    Despu茅s de instalar Flask, cree una carpeta de proyecto, nombrada FlaskApppara un ejemplo. En esta carpeta, cree un archivo base, llamado algo as铆 como app.py.

    Dentro de app.pyimportar el Flaskm贸dulo y crear una aplicaci贸n web usando lo siguiente:

    from flask import Flask
    
    app = Flask(__name__)`
    

    A continuaci贸n, definamos la ruta b谩sica /y el controlador de solicitud correspondiente:

    @app.route("/")
    def index():
      return """
      <h1>Python Flask in Docker!</h1>
      <p>A sample web-app for running Flask inside Docker.</p>
      """
    

    Finalmente, iniciemos la aplicaci贸n si se invoca el script como programa principal:

    if __name__ == "__main__":
        app.run(debug=True, host="0.0.0.0")
    
    $ python3 app.py
    

    Navegue su navegador a http://localhost:5000/. 隆Deber铆a aparecer el mensaje “Dockerzing Python app using Flask”!

    Dockerizar la aplicaci贸n

    Para ejecutar una aplicaci贸n con Docker, tenemos que construir un contenedor con todas las dependencias utilizadas en 茅l, que en nuestro caso es solo Flask. Para hacer esto, incluiremos un requirements.txtarchivo que contiene las dependencias requeridas y crearemos un Dockerfile que se basa en el archivo para construir una imagen.

    Adem谩s, cuando lancemos el contenedor, tendremos que tener acceso a los puertos HTTP en los que se ejecuta la aplicaci贸n.

    Preparando la aplicaci贸n

    Incluir dependencias en el requirements.txtarchivo es muy f谩cil. Simplemente necesitamos incluir el nombre y la versi贸n de la dependencia:

    Flask==1.0.2
    

    A continuaci贸n, debemos asegurarnos de que todos los archivos de Python necesarios para que se ejecute nuestra aplicaci贸n est茅n dentro de una carpeta de nivel superior, por ejemplo, llamada app.

    Tambi茅n se recomienda nombrar el punto de entrada principal, app.pyya que es una buena pr谩ctica nombrar el objeto Flask creado en el script apppara facilitar la implementaci贸n.

    docker-flask-tutorial
        鈹溾攢鈹 requirements.txt
        鈹溾攢鈹 Dockerfile
        鈹斺攢鈹 app
         聽聽 鈹斺攢鈹 app.py
            鈹斺攢鈹 <other .py files>
    

    Creando un Dockerfile

    Un Dockerfile es esencialmente un archivo de texto con instrucciones claramente definidas sobre c贸mo crear una imagen de Docker para nuestro proyecto.

    A continuaci贸n, crearemos una imagen de Docker basada en Ubuntu 16.04 y Python 3.X:

    FROM ubuntu:16.04
    
    MAINTAINER Madhuri Koushik "[email聽protected]"
    
    RUN apt-get update -y && 
        apt-get install -y python3-pip python3-dev
    
    COPY ./requirements.txt /requirements.txt
    
    WORKDIR /
    
    RUN pip3 install -r requirements.txt
    
    COPY . /
    
    ENTRYPOINT [ "python3" ]
    
    CMD [ "app/app.py" ]
    

    Aqu铆 hay algunos comandos que merecen una explicaci贸n adecuada:

    • DE : cada Dockerfile comienza con una FROMpalabra clave. Se utiliza para especificar la imagen base a partir de la cual se construye la imagen. La siguiente l铆nea proporciona metadatos sobre el mantenedor de la imagen.
    • EJECUTAR : podemos agregar contenido adicional a la imagen ejecutando tareas de instalaci贸n y almacenando los resultados de estos comandos. Aqu铆, simplemente actualizamos la informaci贸n del paquete, instalamos python3y pip. Usamos pipen el segundo RUNcomando para instalar todos los paquetes en el requirements.txtarchivo.
    • COPY : el COPYcomando se usa para copiar archivos / directorios desde la m谩quina host al contenedor durante el proceso de compilaci贸n. En este caso, estamos copiando los archivos de la aplicaci贸n incluidos requirements.txt.
    • WORKDIR : establece el directorio de trabajo en el contenedor que utilizan RUN, COPY, etc.
    • ENTRYPOINT : define el punto de entrada de la aplicaci贸n
    • CMD : ejecuta el app.pyarchivo en el appdirectorio.

    C贸mo se crean las im谩genes de Docker

    Las im谩genes de Docker se crean mediante el docker buildcomando. Al construir una imagen, Docker crea las llamadas “capas”. Cada capa registra los cambios resultantes de un comando en el Dockerfile y el estado de la imagen despu茅s de ejecutar el comando.

    Docker almacena en cach茅 internamente estas capas para que, al reconstruir im谩genes, necesite volver a crear solo aquellas capas que han cambiado. Por ejemplo, una vez que carga la imagen base para ubuntu:16.04, todas las compilaciones posteriores del mismo contenedor pueden reutilizar esto, ya que esto no cambiar谩. Sin embargo, durante cada reconstrucci贸n, es probable que el contenido del directorio de la aplicaci贸n sea diferente y, por lo tanto, esta capa se reconstruir谩 cada vez.

    Siempre que se reconstruye una capa, todas las capas que le siguen en el Dockerfile tambi茅n deben reconstruirse. Es importante tener esto en cuenta al crear Dockerfiles. Por ejemplo, primero instalamos COPYel requirements.txtarchivo e instalamos las dependencias antes COPYde instalar el resto de la aplicaci贸n. Esto da como resultado una capa de Docker que contiene todas las dependencias. No es necesario reconstruir esta capa incluso si otros archivos de la aplicaci贸n cambian, siempre que no haya nuevas dependencias.

    Por lo tanto, optimizamos el proceso de compilaci贸n de nuestro contenedor separando pip installla implementaci贸n del resto de nuestra aplicaci贸n.

    Construyendo la imagen de Docker

    Ahora que nuestro Dockerfile est谩 listo y entendemos c贸mo funciona el proceso de compilaci贸n, sigamos adelante y creemos la imagen de Docker para nuestra aplicaci贸n:

    $ docker build -t docker-flask:latest .
    

    Ejecuci贸n de la aplicaci贸n en modo de depuraci贸n con reinicio autom谩tico

    Debido a las ventajas de la contenerizaci贸n descritas anteriormente, tiene sentido desarrollar aplicaciones que se implementar谩n en contenedores dentro del propio contenedor. Esto asegura que desde el principio, el entorno en el que se construye la aplicaci贸n est茅 limpio y, por lo tanto, elimine las sorpresas durante la entrega.

    Sin embargo, mientras se desarrolla una aplicaci贸n, es importante tener ciclos r谩pidos de reconstrucci贸n y prueba para verificar cada paso intermedio durante el desarrollo. Para este prop贸sito, los desarrolladores de aplicaciones web dependen de las funciones de reinicio autom谩tico proporcionadas por marcos como Flask. Tambi茅n es posible aprovechar esto desde dentro del contenedor.

    Para habilitar el reinicio autom谩tico, iniciamos el contenedor Docker mapeando nuestro directorio de desarrollo al directorio de la aplicaci贸n dentro del contenedor. Esto significa que Flask observar谩 los archivos en el host (a trav茅s de esta asignaci贸n) para detectar cualquier cambio y reiniciar谩 la aplicaci贸n autom谩ticamente cuando detecte alg煤n cambio.

    Adem谩s, tambi茅n necesitamos reenviar los puertos de la aplicaci贸n desde el contenedor al host. Esto es para permitir que un navegador que se ejecuta en el host acceda a la aplicaci贸n.

    Para lograr esto, iniciamos el contenedor Docker con opciones de mapeo de volumen y reenv铆o de puertos:

    $ docker run --name flaskapp -v$PWD/app:/app -p5000:5000 docker-flask:latest
    

    Esto hace lo siguiente:

    • Inicia un contenedor basado en la docker-flaskimagen que construimos previamente.
    • El nombre de este contenedor se establece en flaskapp. Sin la --nameopci贸n, Docker elige un nombre arbitrario (y muy interesante) para el contenedor. Especificar un nombre expl铆citamente nos ayudar谩 a localizar el contenedor (para detener, etc.)
    • La -vopci贸n monta la carpeta de la aplicaci贸n en el host en el contenedor.
    • La -popci贸n asigna el puerto del contenedor al host.

    Ahora se puede acceder a la aplicaci贸n en http://localhost:5000o http://0.0.0.0:5000/:

    Si realizamos cambios en la aplicaci贸n cuando el contenedor se est谩 ejecutando y guardamos el archivo, Flask detecta los cambios y reinicia la aplicaci贸n:

    Para detener el contenedor, presione Ctrl-C y retire el contenedor ejecutando docker rm flaskapp.

    Ejecuci贸n de la aplicaci贸n en modo de producci贸n

    Si bien ejecutar la aplicaci贸n con Flask directamente es lo suficientemente bueno para el desarrollo, necesitamos utilizar un m茅todo de implementaci贸n m谩s s贸lido para la producci贸n.

    Normalmente, una aplicaci贸n web Flask en producci贸n puede necesitar manejar m煤ltiples conexiones paralelas y, por lo tanto, generalmente se implementa en un servidor web compatible con WSGI.

    Una alternativa popular es nginx + uwsgi y en esta secci贸n veremos c贸mo configurar nuestra aplicaci贸n web para producci贸n. Nginx es un servidor web de c贸digo abierto y uWSGI es un “servidor de contenedores de aplicaciones r谩pido y con recuperaci贸n autom谩tica”.

    Primero, creamos una fachada que iniciar谩 nuestra aplicaci贸n en modo de desarrollo o de producci贸n y, dependiendo del modo, elegir谩 ejecutar nginx o Python directamente.

    Llamaremos a este archivo launch.shy ser谩 un simple script de shell. Este archivo se basa en entry-point.sh :

    #!/bin/bash
    
    if [ ! -f /debug0 ]; then
      touch /debug0
    
      while getopts 'hd:' flag; do
        case "${flag}" in
          h)
            echo "options:"
            echo "-h        show brief help"
            echo "-d        debug mode, no nginx or uwsgi, direct start with 'python3 app/app.py'"
            exit 0
            ;;
          d)
            touch /debug1
            ;;
          *)
            break
            ;;
        esac
      done
    fi
    
    if [ -e /debug1 ]; then
      echo "Running app in debug mode!"
      python3 app/app.py
    else
      echo "Running app in production mode!"
      nginx && uwsgi --ini /app.ini
    fi
    

    A continuaci贸n, creamos un archivo de configuraci贸n de uWSGI para nuestra aplicaci贸n y una configuraci贸n de nginx .

    B谩sicamente, este archivo describe el punto de entrada de nuestra aplicaci贸n a uWSGI / nginx:

    [uwsgi]
    plugins = /usr/lib/uwsgi/plugins/python3
    chdir = /app
    module = app:app
    uid = nginx
    gid = nginx
    socket = /run/uwsgiApp.sock
    pidfile = /run/.pid
    processes = 4
    threads = 2
    

    Finalmente, modificamos nuestro Dockerfile para incluir nginx y uWSGI. Adem谩s de instalar nginx, uWSGI y el complemento uWSGI Python3, ahora tambi茅n copia el nginx.confen la ubicaci贸n adecuada y configura los permisos de usuario necesarios para ejecutar nginx.

    Adem谩s, el Dockerfile ENTRYPOINTest谩 configurado para el script de shell que nos ayuda a ejecutar el contenedor en modo de depuraci贸n o producci贸n:

    FROM ubuntu:16.04
    
    MAINTAINER Madhuri Koushik "[email聽protected]"
    
    RUN apt-get update -y && 
        apt-get install -y python3-pip python3-dev && 
        apt-get install -y nginx uwsgi uwsgi-plugin-python3
    
    COPY ./requirements.txt /requirements.txt
    COPY ./nginx.conf /etc/nginx/nginx.conf
    
    WORKDIR /
    
    RUN pip3 install -r requirements.txt
    
    COPY . /
    
    RUN adduser --disabled-password --gecos '' nginx
      && chown -R nginx:nginx /app 
      && chmod 777 /run/ -R 
      && chmod 777 /root/ -R
    
    ENTRYPOINT [ "/bin/bash", "/launcher.sh"]
    

    Ahora, podemos reconstruir la imagen:

    $ docker build -t docker-flask:latest .
    

    Y ejecute la aplicaci贸n usando nginx:

    $ docker run -d --name flaskapp --restart=always -p 80:80 docker-flask:latest
    

    Esta imagen es aut贸noma y solo necesita que se especifique la asignaci贸n de puertos durante la implementaci贸n. Esto iniciar谩 y ejecutar谩 el comando en segundo plano. Para detener y eliminar este contenedor, ejecute el siguiente comando:

    $ docker stop flaskapp && docker rm flaskapp
    

    Adem谩s, si necesitamos depurar o agregar funciones, podemos ejecutar f谩cilmente el contenedor en modo de depuraci贸n montando nuestra propia versi贸n del 谩rbol de fuentes:

    $ docker run -it --name flaskapp -p 5000:5000 -v$PWD/app:/app docker-flask:latest -d
    

    Gesti贸n de dependencias externas

    Al enviar aplicaciones como contenedores, un elemento clave a recordar es que se incrementan las responsabilidades del desarrollador hacia la gesti贸n de dependencias. Adem谩s de identificar y especificar las dependencias y versiones correctas, tambi茅n son responsables de la instalaci贸n y configuraci贸n de estas dependencias en el entorno del contenedor.

    Afortunadamente, requirements.txtes un mecanismo sencillo para especificar dependencias. Se pippuede agregar cualquier paquete que est茅 disponible a trav茅s de .

    Pero nuevamente, cada vez requirements.txtque se modifica el archivo, la imagen de Docker debe reconstruirse.

    Instalaci贸n de dependencias al inicio

    Ocasionalmente, es posible que sea necesario instalar dependencias adicionales durante el inicio. Digamos que est谩 probando un nuevo paquete durante el desarrollo y no desea reconstruir la imagen de Docker cada vez o desea utilizar la 煤ltima versi贸n disponible en el momento del lanzamiento. Es posible lograr esto modificando el lanzador para que se ejecute pipal inicio del inicio de la aplicaci贸n.

    De manera similar, tambi茅n podemos instalar dependencias de paquetes adicionales a nivel de sistema operativo. Modifiquemos el launcher.sh:

    #!/bin/bash
    
    if [ ! -f /debug0 ]; then
        touch /debug0
    
        if [ -e requirements_os.txt ]; then
            apt-get install -y $(cat requirements_os.txt)
        fi
        if [ -e requirements.txt ]; then
            pip3 install -r requirements.txt
        fi
    
        while getopts 'hd' flag; do
            case "${flag}" in
                h)
                    echo "options:"
                    echo "-h        show brief help"
                    echo "-d        debug mode, no nginx or uwsgi, direct start with 'python3 app/app.py'"
                    exit 0
                    ;;
                d)
                    echo "Debug!"
                    touch /debug1
                    ;;
            esac
        done
    fi
    
    if [ -e /debug1 ]; then
        echo "Running app in debug mode!"
        python3 app/app.py
    else
        echo "Running app in production mode!"
        nginx && uwsgi --ini /app.ini
    fi
    

    Ahora, en el requirements_os.txt, podemos especificar una lista de nombres de paquetes separados por espacios en una l铆nea y estos, junto con los paquetes requirements.txt, se instalar谩n antes de que se inicie la aplicaci贸n.

    Aunque esto se proporciona para su comodidad durante el desarrollo, no es una buena pr谩ctica instalar dependencias durante el inicio por varias razones:

    • Derrota uno de los objetivos de la contenedorizaci贸n que es arreglar y probar las dependencias que no cambian debido al cambio del entorno de implementaci贸n.
    • Agrega una sobrecarga adicional al inicio de la aplicaci贸n, lo que aumentar谩 el tiempo de inicio del contenedor.
    • Extraer dependencias cada vez que se inicia la aplicaci贸n es un mal uso de los recursos de red.

    Conclusi贸n

    En este art铆culo, nos sumergimos en Docker, una herramienta de contenedorizaci贸n ampliamente utilizada. Creamos una aplicaci贸n web simple con Flask, una imagen Docker personalizada basada en Ubuntu para ejecutar nuestra aplicaci贸n web en modo de desarrollo y producci贸n.

    Finalmente, configuramos la implementaci贸n para nuestra aplicaci贸n web usando nginx y uWSGI dentro del contenedor Docker y exploramos m茅todos para instalar dependencias externas.

    La contenerizaci贸n es una tecnolog铆a poderosa que permite el desarrollo y la implementaci贸n r谩pidos de aplicaciones en la nube y esperamos que pueda aplicar lo que aprendi贸 aqu铆 en sus propias aplicaciones.

     

    Etiquetas:

    Deja una respuesta

    Tu direcci贸n de correo electr贸nico no ser谩 publicada. Los campos obligatorios est谩n marcados con *